Merge branch 'topic/ctxfi' into for-linus
* topic/ctxfi: (35 commits) ALSA: ctxfi - Clear PCM resources at hw_params and hw_free ALSA: ctxfi - Check the presence of SRC instance in PCM pointer callbacks ALSA: ctxfi - Add missing start check in atc_pcm_playback_start() ALSA: ctxfi - Add use_system_timer module option ALSA: ctxfi - Fix wrong model id for UAA ALSA: ctxfi - Clean up probe routines ALSA: ctxfi - Fix / clean up hw20k2 chip code ALSA: ctxfi - Fix possible buffer pointer overrun ALSA: ctxfi - Remove useless initializations and cast ALSA: ctxfi - Fix DMA mask for emu20k2 chip ALSA: ctxfi - Make volume controls more intuitive ALSA: ctxfi - Optimize the native timer handling using wc counter ALSA: ctxfi - Add missing inclusion of linux/math64.h ALSA: ctxfi - Set device 0 for mixer control elements ALSA: ctxfi - Clean up / optimize ALSA: ctxfi - Set periods_min to 2 ALSA: ctxfi - Use native timer interrupt on emu20k1 ALSA: ctxfi - Fix previous fix for 64bit DMA ALSA: ctxfi - Fix endian-dependent codes ALSA: ctxfi - Allow 64bit DMA ...
This commit is contained in:
commit
e618a5609e
|
@ -460,6 +460,25 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-ctxfi
|
||||
----------------
|
||||
|
||||
Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
|
||||
* Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
|
||||
* Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
|
||||
* Creative Sound Blaster X-Fi Titanium Professional Audio
|
||||
* Creative Sound Blaster X-Fi Titanium
|
||||
* Creative Sound Blaster X-Fi Elite Pro
|
||||
* Creative Sound Blaster X-Fi Platinum
|
||||
* Creative Sound Blaster X-Fi Fatal1ty
|
||||
* Creative Sound Blaster X-Fi XtremeGamer
|
||||
* Creative Sound Blaster X-Fi XtremeMusic
|
||||
|
||||
reference_rate - reference sample rate, 44100 or 48000 (default)
|
||||
multiple - multiple to ref. sample rate, 1 or 2 (default)
|
||||
|
||||
This module supports multiple cards.
|
||||
|
||||
Module snd-darla20
|
||||
------------------
|
||||
|
||||
|
|
|
@ -1314,6 +1314,13 @@
|
|||
|
||||
#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */
|
||||
#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
|
||||
#define PCI_DEVICE_ID_CREATIVE_20K1 0x0005
|
||||
#define PCI_DEVICE_ID_CREATIVE_20K2 0x000b
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB0760 0x0024
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000
|
||||
|
||||
#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */
|
||||
#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938
|
||||
|
|
|
@ -275,6 +275,16 @@ config SND_CS5535AUDIO
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-cs5535audio.
|
||||
|
||||
config SND_CTXFI
|
||||
tristate "Creative Sound Blaster X-Fi"
|
||||
select SND_PCM
|
||||
help
|
||||
If you want to use soundcards based on Creative Sound Blastr X-Fi
|
||||
boards with 20k1 or 20k2 chips, say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-ctxfi.
|
||||
|
||||
config SND_DARLA20
|
||||
tristate "(Echoaudio) Darla20"
|
||||
select FW_LOADER
|
||||
|
|
|
@ -59,6 +59,7 @@ obj-$(CONFIG_SND) += \
|
|||
ali5451/ \
|
||||
au88x0/ \
|
||||
aw2/ \
|
||||
ctxfi/ \
|
||||
ca0106/ \
|
||||
cs46xx/ \
|
||||
cs5535audio/ \
|
||||
|
|
|
@ -0,0 +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 cttimer.o \
|
||||
cthw20k2.o cthw20k1.o
|
||||
|
||||
obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o
|
|
@ -0,0 +1,636 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CT20K1REG_H
|
||||
#define CT20k1REG_H
|
||||
|
||||
/* 20k1 registers */
|
||||
#define DSPXRAM_START 0x000000
|
||||
#define DSPXRAM_END 0x013FFC
|
||||
#define DSPAXRAM_START 0x020000
|
||||
#define DSPAXRAM_END 0x023FFC
|
||||
#define DSPYRAM_START 0x040000
|
||||
#define DSPYRAM_END 0x04FFFC
|
||||
#define DSPAYRAM_START 0x020000
|
||||
#define DSPAYRAM_END 0x063FFC
|
||||
#define DSPMICRO_START 0x080000
|
||||
#define DSPMICRO_END 0x0B3FFC
|
||||
#define DSP0IO_START 0x100000
|
||||
#define DSP0IO_END 0x101FFC
|
||||
#define AUDIORINGIPDSP0_START 0x100000
|
||||
#define AUDIORINGIPDSP0_END 0x1003FC
|
||||
#define AUDIORINGOPDSP0_START 0x100400
|
||||
#define AUDIORINGOPDSP0_END 0x1007FC
|
||||
#define AUDPARARINGIODSP0_START 0x100800
|
||||
#define AUDPARARINGIODSP0_END 0x100BFC
|
||||
#define DSP0LOCALHWREG_START 0x100C00
|
||||
#define DSP0LOCALHWREG_END 0x100C3C
|
||||
#define DSP0XYRAMAGINDEX_START 0x100C40
|
||||
#define DSP0XYRAMAGINDEX_END 0x100C5C
|
||||
#define DSP0XYRAMAGMDFR_START 0x100C60
|
||||
#define DSP0XYRAMAGMDFR_END 0x100C7C
|
||||
#define DSP0INTCONTLVEC_START 0x100C80
|
||||
#define DSP0INTCONTLVEC_END 0x100CD8
|
||||
#define INTCONTLGLOBALREG_START 0x100D1C
|
||||
#define INTCONTLGLOBALREG_END 0x100D3C
|
||||
#define HOSTINTFPORTADDRCONTDSP0 0x100D40
|
||||
#define HOSTINTFPORTDATADSP0 0x100D44
|
||||
#define TIME0PERENBDSP0 0x100D60
|
||||
#define TIME0COUNTERDSP0 0x100D64
|
||||
#define TIME1PERENBDSP0 0x100D68
|
||||
#define TIME1COUNTERDSP0 0x100D6C
|
||||
#define TIME2PERENBDSP0 0x100D70
|
||||
#define TIME2COUNTERDSP0 0x100D74
|
||||
#define TIME3PERENBDSP0 0x100D78
|
||||
#define TIME3COUNTERDSP0 0x100D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP0 0x100D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP0 0x100D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP0 0x100DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP0 0x100DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP0 0x100DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP0 0x100DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP0 0x100DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP0 0x100DFC
|
||||
#define DSP0CONDCODE 0x100E00
|
||||
#define DSP0STACKFLAG 0x100E04
|
||||
#define DSP0PROGCOUNTSTACKPTREG 0x100E08
|
||||
#define DSP0PROGCOUNTSTACKDATAREG 0x100E0C
|
||||
#define DSP0CURLOOPADDRREG 0x100E10
|
||||
#define DSP0CURLOOPCOUNT 0x100E14
|
||||
#define DSP0TOPLOOPCOUNTSTACK 0x100E18
|
||||
#define DSP0TOPLOOPADDRSTACK 0x100E1C
|
||||
#define DSP0LOOPSTACKPTR 0x100E20
|
||||
#define DSP0STASSTACKDATAREG 0x100E24
|
||||
#define DSP0STASSTACKPTR 0x100E28
|
||||
#define DSP0PROGCOUNT 0x100E2C
|
||||
#define GLOBDSPDEBGREG 0x100E30
|
||||
#define GLOBDSPBREPTRREG 0x100E30
|
||||
#define DSP0XYRAMBASE_START 0x100EA0
|
||||
#define DSP0XYRAMBASE_END 0x100EBC
|
||||
#define DSP0XYRAMLENG_START 0x100EC0
|
||||
#define DSP0XYRAMLENG_END 0x100EDC
|
||||
#define SEMAPHOREREGDSP0 0x100EE0
|
||||
#define DSP0INTCONTMASKREG 0x100EE4
|
||||
#define DSP0INTCONTPENDREG 0x100EE8
|
||||
#define DSP0INTCONTSERVINT 0x100EEC
|
||||
#define DSPINTCONTEXTINTMODREG 0x100EEC
|
||||
#define GPIODSP0 0x100EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP0 0x100F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP0 0x100F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP0 0x100F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP0 0x100F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP0 0x100F40
|
||||
#define DMADSPCURADDRREG_ENDDSP0 0x100F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP0 0x100F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP0 0x100F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP0 0x100F80
|
||||
#define DMATANXCOUNTREG_ENDDSP0 0x100F9C
|
||||
#define DMATIMEBUGREG_STARTDSP0 0x100FA0
|
||||
#define DMATIMEBUGREG_ENDDSP0 0x100FAC
|
||||
#define DMACNTLMODFREG_STARTDSP0 0x100FA0
|
||||
#define DMACNTLMODFREG_ENDDSP0 0x100FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP0 0x100FEC
|
||||
#define DSP0XGPRAM_START 0x101000
|
||||
#define DSP0XGPRAM_END 0x1017FC
|
||||
#define DSP0YGPRAM_START 0x101800
|
||||
#define DSP0YGPRAM_END 0x101FFC
|
||||
|
||||
|
||||
|
||||
|
||||
#define AUDIORINGIPDSP1_START 0x102000
|
||||
#define AUDIORINGIPDSP1_END 0x1023FC
|
||||
#define AUDIORINGOPDSP1_START 0x102400
|
||||
#define AUDIORINGOPDSP1_END 0x1027FC
|
||||
#define AUDPARARINGIODSP1_START 0x102800
|
||||
#define AUDPARARINGIODSP1_END 0x102BFC
|
||||
#define DSP1LOCALHWREG_START 0x102C00
|
||||
#define DSP1LOCALHWREG_END 0x102C3C
|
||||
#define DSP1XYRAMAGINDEX_START 0x102C40
|
||||
#define DSP1XYRAMAGINDEX_END 0x102C5C
|
||||
#define DSP1XYRAMAGMDFR_START 0x102C60
|
||||
#define DSP1XYRAMAGMDFR_END 0x102C7C
|
||||
#define DSP1INTCONTLVEC_START 0x102C80
|
||||
#define DSP1INTCONTLVEC_END 0x102CD8
|
||||
#define HOSTINTFPORTADDRCONTDSP1 0x102D40
|
||||
#define HOSTINTFPORTDATADSP1 0x102D44
|
||||
#define TIME0PERENBDSP1 0x102D60
|
||||
#define TIME0COUNTERDSP1 0x102D64
|
||||
#define TIME1PERENBDSP1 0x102D68
|
||||
#define TIME1COUNTERDSP1 0x102D6C
|
||||
#define TIME2PERENBDSP1 0x102D70
|
||||
#define TIME2COUNTERDSP1 0x102D74
|
||||
#define TIME3PERENBDSP1 0x102D78
|
||||
#define TIME3COUNTERDSP1 0x102D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP1 0x102D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP1 0x102D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP1 0x102DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP1 0x102DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP1 0x102DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP1 0x102DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP1 0x102DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP1 0x102DFC
|
||||
|
||||
#define DSP1CONDCODE 0x102E00
|
||||
#define DSP1STACKFLAG 0x102E04
|
||||
#define DSP1PROGCOUNTSTACKPTREG 0x102E08
|
||||
#define DSP1PROGCOUNTSTACKDATAREG 0x102E0C
|
||||
#define DSP1CURLOOPADDRREG 0x102E10
|
||||
#define DSP1CURLOOPCOUNT 0x102E14
|
||||
#define DSP1TOPLOOPCOUNTSTACK 0x102E18
|
||||
#define DSP1TOPLOOPADDRSTACK 0x102E1C
|
||||
#define DSP1LOOPSTACKPTR 0x102E20
|
||||
#define DSP1STASSTACKDATAREG 0x102E24
|
||||
#define DSP1STASSTACKPTR 0x102E28
|
||||
#define DSP1PROGCOUNT 0x102E2C
|
||||
#define DSP1XYRAMBASE_START 0x102EA0
|
||||
#define DSP1XYRAMBASE_END 0x102EBC
|
||||
#define DSP1XYRAMLENG_START 0x102EC0
|
||||
#define DSP1XYRAMLENG_END 0x102EDC
|
||||
#define SEMAPHOREREGDSP1 0x102EE0
|
||||
#define DSP1INTCONTMASKREG 0x102EE4
|
||||
#define DSP1INTCONTPENDREG 0x102EE8
|
||||
#define DSP1INTCONTSERVINT 0x102EEC
|
||||
#define GPIODSP1 0x102EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP1 0x102F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP1 0x102F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP1 0x102F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP1 0x102F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP1 0x102F40
|
||||
#define DMADSPCURADDRREG_ENDDSP1 0x102F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP1 0x102F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP1 0x102F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP1 0x102F80
|
||||
#define DMATANXCOUNTREG_ENDDSP1 0x102F9C
|
||||
#define DMATIMEBUGREG_STARTDSP1 0x102FA0
|
||||
#define DMATIMEBUGREG_ENDDSP1 0x102FAC
|
||||
#define DMACNTLMODFREG_STARTDSP1 0x102FA0
|
||||
#define DMACNTLMODFREG_ENDDSP1 0x102FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP1 0x102FEC
|
||||
#define DSP1XGPRAM_START 0x103000
|
||||
#define DSP1XGPRAM_END 0x1033FC
|
||||
#define DSP1YGPRAM_START 0x103400
|
||||
#define DSP1YGPRAM_END 0x1037FC
|
||||
|
||||
|
||||
|
||||
#define AUDIORINGIPDSP2_START 0x104000
|
||||
#define AUDIORINGIPDSP2_END 0x1043FC
|
||||
#define AUDIORINGOPDSP2_START 0x104400
|
||||
#define AUDIORINGOPDSP2_END 0x1047FC
|
||||
#define AUDPARARINGIODSP2_START 0x104800
|
||||
#define AUDPARARINGIODSP2_END 0x104BFC
|
||||
#define DSP2LOCALHWREG_START 0x104C00
|
||||
#define DSP2LOCALHWREG_END 0x104C3C
|
||||
#define DSP2XYRAMAGINDEX_START 0x104C40
|
||||
#define DSP2XYRAMAGINDEX_END 0x104C5C
|
||||
#define DSP2XYRAMAGMDFR_START 0x104C60
|
||||
#define DSP2XYRAMAGMDFR_END 0x104C7C
|
||||
#define DSP2INTCONTLVEC_START 0x104C80
|
||||
#define DSP2INTCONTLVEC_END 0x104CD8
|
||||
#define HOSTINTFPORTADDRCONTDSP2 0x104D40
|
||||
#define HOSTINTFPORTDATADSP2 0x104D44
|
||||
#define TIME0PERENBDSP2 0x104D60
|
||||
#define TIME0COUNTERDSP2 0x104D64
|
||||
#define TIME1PERENBDSP2 0x104D68
|
||||
#define TIME1COUNTERDSP2 0x104D6C
|
||||
#define TIME2PERENBDSP2 0x104D70
|
||||
#define TIME2COUNTERDSP2 0x104D74
|
||||
#define TIME3PERENBDSP2 0x104D78
|
||||
#define TIME3COUNTERDSP2 0x104D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP2 0x104D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP2 0x104D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP2 0x104DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP2 0x104DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP2 0x104DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP2 0x104DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP2 0x104DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP2 0x104DFC
|
||||
#define DSP2CONDCODE 0x104E00
|
||||
#define DSP2STACKFLAG 0x104E04
|
||||
#define DSP2PROGCOUNTSTACKPTREG 0x104E08
|
||||
#define DSP2PROGCOUNTSTACKDATAREG 0x104E0C
|
||||
#define DSP2CURLOOPADDRREG 0x104E10
|
||||
#define DSP2CURLOOPCOUNT 0x104E14
|
||||
#define DSP2TOPLOOPCOUNTSTACK 0x104E18
|
||||
#define DSP2TOPLOOPADDRSTACK 0x104E1C
|
||||
#define DSP2LOOPSTACKPTR 0x104E20
|
||||
#define DSP2STASSTACKDATAREG 0x104E24
|
||||
#define DSP2STASSTACKPTR 0x104E28
|
||||
#define DSP2PROGCOUNT 0x104E2C
|
||||
#define DSP2XYRAMBASE_START 0x104EA0
|
||||
#define DSP2XYRAMBASE_END 0x104EBC
|
||||
#define DSP2XYRAMLENG_START 0x104EC0
|
||||
#define DSP2XYRAMLENG_END 0x104EDC
|
||||
#define SEMAPHOREREGDSP2 0x104EE0
|
||||
#define DSP2INTCONTMASKREG 0x104EE4
|
||||
#define DSP2INTCONTPENDREG 0x104EE8
|
||||
#define DSP2INTCONTSERVINT 0x104EEC
|
||||
#define GPIODSP2 0x104EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP2 0x104F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP2 0x104F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP2 0x104F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP2 0x104F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP2 0x104F40
|
||||
#define DMADSPCURADDRREG_ENDDSP2 0x104F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP2 0x104F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP2 0x104F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP2 0x104F80
|
||||
#define DMATANXCOUNTREG_ENDDSP2 0x104F9C
|
||||
#define DMATIMEBUGREG_STARTDSP2 0x104FA0
|
||||
#define DMATIMEBUGREG_ENDDSP2 0x104FAC
|
||||
#define DMACNTLMODFREG_STARTDSP2 0x104FA0
|
||||
#define DMACNTLMODFREG_ENDDSP2 0x104FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP2 0x104FEC
|
||||
#define DSP2XGPRAM_START 0x105000
|
||||
#define DSP2XGPRAM_END 0x1051FC
|
||||
#define DSP2YGPRAM_START 0x105800
|
||||
#define DSP2YGPRAM_END 0x1059FC
|
||||
|
||||
|
||||
|
||||
#define AUDIORINGIPDSP3_START 0x106000
|
||||
#define AUDIORINGIPDSP3_END 0x1063FC
|
||||
#define AUDIORINGOPDSP3_START 0x106400
|
||||
#define AUDIORINGOPDSP3_END 0x1067FC
|
||||
#define AUDPARARINGIODSP3_START 0x106800
|
||||
#define AUDPARARINGIODSP3_END 0x106BFC
|
||||
#define DSP3LOCALHWREG_START 0x106C00
|
||||
#define DSP3LOCALHWREG_END 0x106C3C
|
||||
#define DSP3XYRAMAGINDEX_START 0x106C40
|
||||
#define DSP3XYRAMAGINDEX_END 0x106C5C
|
||||
#define DSP3XYRAMAGMDFR_START 0x106C60
|
||||
#define DSP3XYRAMAGMDFR_END 0x106C7C
|
||||
#define DSP3INTCONTLVEC_START 0x106C80
|
||||
#define DSP3INTCONTLVEC_END 0x106CD8
|
||||
#define HOSTINTFPORTADDRCONTDSP3 0x106D40
|
||||
#define HOSTINTFPORTDATADSP3 0x106D44
|
||||
#define TIME0PERENBDSP3 0x106D60
|
||||
#define TIME0COUNTERDSP3 0x106D64
|
||||
#define TIME1PERENBDSP3 0x106D68
|
||||
#define TIME1COUNTERDSP3 0x106D6C
|
||||
#define TIME2PERENBDSP3 0x106D70
|
||||
#define TIME2COUNTERDSP3 0x106D74
|
||||
#define TIME3PERENBDSP3 0x106D78
|
||||
#define TIME3COUNTERDSP3 0x106D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP3 0x106D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP3 0x106D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP3 0x106DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP3 0x106DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP3 0x106DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP3 0x106DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP3 0x106DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP3 0x100DFC
|
||||
|
||||
#define DSP3CONDCODE 0x106E00
|
||||
#define DSP3STACKFLAG 0x106E04
|
||||
#define DSP3PROGCOUNTSTACKPTREG 0x106E08
|
||||
#define DSP3PROGCOUNTSTACKDATAREG 0x106E0C
|
||||
#define DSP3CURLOOPADDRREG 0x106E10
|
||||
#define DSP3CURLOOPCOUNT 0x106E14
|
||||
#define DSP3TOPLOOPCOUNTSTACK 0x106E18
|
||||
#define DSP3TOPLOOPADDRSTACK 0x106E1C
|
||||
#define DSP3LOOPSTACKPTR 0x106E20
|
||||
#define DSP3STASSTACKDATAREG 0x106E24
|
||||
#define DSP3STASSTACKPTR 0x106E28
|
||||
#define DSP3PROGCOUNT 0x106E2C
|
||||
#define DSP3XYRAMBASE_START 0x106EA0
|
||||
#define DSP3XYRAMBASE_END 0x106EBC
|
||||
#define DSP3XYRAMLENG_START 0x106EC0
|
||||
#define DSP3XYRAMLENG_END 0x106EDC
|
||||
#define SEMAPHOREREGDSP3 0x106EE0
|
||||
#define DSP3INTCONTMASKREG 0x106EE4
|
||||
#define DSP3INTCONTPENDREG 0x106EE8
|
||||
#define DSP3INTCONTSERVINT 0x106EEC
|
||||
#define GPIODSP3 0x106EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP3 0x106F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP3 0x106F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP3 0x106F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP3 0x106F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP3 0x106F40
|
||||
#define DMADSPCURADDRREG_ENDDSP3 0x106F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP3 0x106F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP3 0x106F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP3 0x106F80
|
||||
#define DMATANXCOUNTREG_ENDDSP3 0x106F9C
|
||||
#define DMATIMEBUGREG_STARTDSP3 0x106FA0
|
||||
#define DMATIMEBUGREG_ENDDSP3 0x106FAC
|
||||
#define DMACNTLMODFREG_STARTDSP3 0x106FA0
|
||||
#define DMACNTLMODFREG_ENDDSP3 0x106FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP3 0x106FEC
|
||||
#define DSP3XGPRAM_START 0x107000
|
||||
#define DSP3XGPRAM_END 0x1071FC
|
||||
#define DSP3YGPRAM_START 0x107800
|
||||
#define DSP3YGPRAM_END 0x1079FC
|
||||
|
||||
/* end of DSP reg definitions */
|
||||
|
||||
#define DSPAIMAP_START 0x108000
|
||||
#define DSPAIMAP_END 0x1083FC
|
||||
#define DSPPIMAP_START 0x108400
|
||||
#define DSPPIMAP_END 0x1087FC
|
||||
#define DSPPOMAP_START 0x108800
|
||||
#define DSPPOMAP_END 0x108BFC
|
||||
#define DSPPOCTL 0x108C00
|
||||
#define TKCTL_START 0x110000
|
||||
#define TKCTL_END 0x110FFC
|
||||
#define TKCC_START 0x111000
|
||||
#define TKCC_END 0x111FFC
|
||||
#define TKIMAP_START 0x112000
|
||||
#define TKIMAP_END 0x112FFC
|
||||
#define TKDCTR16 0x113000
|
||||
#define TKPB16 0x113004
|
||||
#define TKBS16 0x113008
|
||||
#define TKDCTR32 0x11300C
|
||||
#define TKPB32 0x113010
|
||||
#define TKBS32 0x113014
|
||||
#define ICDCTR16 0x113018
|
||||
#define ITBS16 0x11301C
|
||||
#define ICDCTR32 0x113020
|
||||
#define ITBS32 0x113024
|
||||
#define ITSTART 0x113028
|
||||
#define TKSQ 0x11302C
|
||||
|
||||
#define TKSCCTL_START 0x114000
|
||||
#define TKSCCTL_END 0x11403C
|
||||
#define TKSCADR_START 0x114100
|
||||
#define TKSCADR_END 0x11413C
|
||||
#define TKSCDATAX_START 0x114800
|
||||
#define TKSCDATAX_END 0x1149FC
|
||||
#define TKPCDATAX_START 0x120000
|
||||
#define TKPCDATAX_END 0x12FFFC
|
||||
|
||||
#define MALSA 0x130000
|
||||
#define MAPPHA 0x130004
|
||||
#define MAPPLA 0x130008
|
||||
#define MALSB 0x130010
|
||||
#define MAPPHB 0x130014
|
||||
#define MAPPLB 0x130018
|
||||
|
||||
#define TANSPORTMAPABREGS_START 0x130020
|
||||
#define TANSPORTMAPABREGS_END 0x13A2FC
|
||||
|
||||
#define PTPAHX 0x13B000
|
||||
#define PTPALX 0x13B004
|
||||
|
||||
#define TANSPPAGETABLEPHYADDR015_START 0x13B008
|
||||
#define TANSPPAGETABLEPHYADDR015_END 0x13B07C
|
||||
#define TRNQADRX_START 0x13B100
|
||||
#define TRNQADRX_END 0x13B13C
|
||||
#define TRNQTIMX_START 0x13B200
|
||||
#define TRNQTIMX_END 0x13B23C
|
||||
#define TRNQAPARMX_START 0x13B300
|
||||
#define TRNQAPARMX_END 0x13B33C
|
||||
|
||||
#define TRNQCNT 0x13B400
|
||||
#define TRNCTL 0x13B404
|
||||
#define TRNIS 0x13B408
|
||||
#define TRNCURTS 0x13B40C
|
||||
|
||||
#define AMOP_START 0x140000
|
||||
#define AMOPLO 0x140000
|
||||
#define AMOPHI 0x140004
|
||||
#define AMOP_END 0x147FFC
|
||||
#define PMOP_START 0x148000
|
||||
#define PMOPLO 0x148000
|
||||
#define PMOPHI 0x148004
|
||||
#define PMOP_END 0x14FFFC
|
||||
#define PCURR_START 0x150000
|
||||
#define PCURR_END 0x153FFC
|
||||
#define PTRAG_START 0x154000
|
||||
#define PTRAG_END 0x157FFC
|
||||
#define PSR_START 0x158000
|
||||
#define PSR_END 0x15BFFC
|
||||
|
||||
#define PFSTAT4SEG_START 0x160000
|
||||
#define PFSTAT4SEG_END 0x160BFC
|
||||
#define PFSTAT2SEG_START 0x160C00
|
||||
#define PFSTAT2SEG_END 0x1617FC
|
||||
#define PFTARG4SEG_START 0x164000
|
||||
#define PFTARG4SEG_END 0x164BFC
|
||||
#define PFTARG2SEG_START 0x164C00
|
||||
#define PFTARG2SEG_END 0x1657FC
|
||||
#define PFSR4SEG_START 0x168000
|
||||
#define PFSR4SEG_END 0x168BFC
|
||||
#define PFSR2SEG_START 0x168C00
|
||||
#define PFSR2SEG_END 0x1697FC
|
||||
#define PCURRMS4SEG_START 0x16C000
|
||||
#define PCURRMS4SEG_END 0x16CCFC
|
||||
#define PCURRMS2SEG_START 0x16CC00
|
||||
#define PCURRMS2SEG_END 0x16D7FC
|
||||
#define PTARGMS4SEG_START 0x170000
|
||||
#define PTARGMS4SEG_END 0x172FFC
|
||||
#define PTARGMS2SEG_START 0x173000
|
||||
#define PTARGMS2SEG_END 0x1747FC
|
||||
#define PSRMS4SEG_START 0x170000
|
||||
#define PSRMS4SEG_END 0x172FFC
|
||||
#define PSRMS2SEG_START 0x173000
|
||||
#define PSRMS2SEG_END 0x1747FC
|
||||
|
||||
#define PRING_LO_START 0x190000
|
||||
#define PRING_LO_END 0x193FFC
|
||||
#define PRING_HI_START 0x194000
|
||||
#define PRING_HI_END 0x197FFC
|
||||
#define PRING_LO_HI_START 0x198000
|
||||
#define PRING_LO_HI 0x198000
|
||||
#define PRING_LO_HI_END 0x19BFFC
|
||||
|
||||
#define PINTFIFO 0x1A0000
|
||||
#define SRCCTL 0x1B0000
|
||||
#define SRCCCR 0x1B0004
|
||||
#define SRCIMAP 0x1B0008
|
||||
#define SRCODDC 0x1B000C
|
||||
#define SRCCA 0x1B0010
|
||||
#define SRCCF 0x1B0014
|
||||
#define SRCSA 0x1B0018
|
||||
#define SRCLA 0x1B001C
|
||||
#define SRCCTLSWR 0x1B0020
|
||||
|
||||
/* SRC HERE */
|
||||
#define SRCALBA 0x1B002C
|
||||
#define SRCMCTL 0x1B012C
|
||||
#define SRCCERR 0x1B022C
|
||||
#define SRCITB 0x1B032C
|
||||
#define SRCIPM 0x1B082C
|
||||
#define SRCIP 0x1B102C
|
||||
#define SRCENBSTAT 0x1B202C
|
||||
#define SRCENBLO 0x1B212C
|
||||
#define SRCENBHI 0x1B222C
|
||||
#define SRCENBS 0x1B232C
|
||||
#define SRCENB 0x1B282C
|
||||
#define SRCENB07 0x1B282C
|
||||
#define SRCENBS07 0x1B302C
|
||||
|
||||
#define SRCDN0Z 0x1B0030
|
||||
#define SRCDN0Z0 0x1B0030
|
||||
#define SRCDN0Z1 0x1B0034
|
||||
#define SRCDN0Z2 0x1B0038
|
||||
#define SRCDN0Z3 0x1B003C
|
||||
#define SRCDN1Z 0x1B0040
|
||||
#define SRCDN1Z0 0x1B0040
|
||||
#define SRCDN1Z1 0x1B0044
|
||||
#define SRCDN1Z2 0x1B0048
|
||||
#define SRCDN1Z3 0x1B004C
|
||||
#define SRCDN1Z4 0x1B0050
|
||||
#define SRCDN1Z5 0x1B0054
|
||||
#define SRCDN1Z6 0x1B0058
|
||||
#define SRCDN1Z7 0x1B005C
|
||||
#define SRCUPZ 0x1B0060
|
||||
#define SRCUPZ0 0x1B0060
|
||||
#define SRCUPZ1 0x1B0064
|
||||
#define SRCUPZ2 0x1B0068
|
||||
#define SRCUPZ3 0x1B006C
|
||||
#define SRCUPZ4 0x1B0070
|
||||
#define SRCUPZ5 0x1B0074
|
||||
#define SRCUPZ6 0x1B0078
|
||||
#define SRCUPZ7 0x1B007C
|
||||
#define SRCCD0 0x1B0080
|
||||
#define SRCCD1 0x1B0084
|
||||
#define SRCCD2 0x1B0088
|
||||
#define SRCCD3 0x1B008C
|
||||
#define SRCCD4 0x1B0090
|
||||
#define SRCCD5 0x1B0094
|
||||
#define SRCCD6 0x1B0098
|
||||
#define SRCCD7 0x1B009C
|
||||
#define SRCCD8 0x1B00A0
|
||||
#define SRCCD9 0x1B00A4
|
||||
#define SRCCDA 0x1B00A8
|
||||
#define SRCCDB 0x1B00AC
|
||||
#define SRCCDC 0x1B00B0
|
||||
#define SRCCDD 0x1B00B4
|
||||
#define SRCCDE 0x1B00B8
|
||||
#define SRCCDF 0x1B00BC
|
||||
#define SRCCD10 0x1B00C0
|
||||
#define SRCCD11 0x1B00C4
|
||||
#define SRCCD12 0x1B00C8
|
||||
#define SRCCD13 0x1B00CC
|
||||
#define SRCCD14 0x1B00D0
|
||||
#define SRCCD15 0x1B00D4
|
||||
#define SRCCD16 0x1B00D8
|
||||
#define SRCCD17 0x1B00DC
|
||||
#define SRCCD18 0x1B00E0
|
||||
#define SRCCD19 0x1B00E4
|
||||
#define SRCCD1A 0x1B00E8
|
||||
#define SRCCD1B 0x1B00EC
|
||||
#define SRCCD1C 0x1B00F0
|
||||
#define SRCCD1D 0x1B00F4
|
||||
#define SRCCD1E 0x1B00F8
|
||||
#define SRCCD1F 0x1B00FC
|
||||
|
||||
#define SRCCONTRBLOCK_START 0x1B0100
|
||||
#define SRCCONTRBLOCK_END 0x1BFFFC
|
||||
#define FILTOP_START 0x1C0000
|
||||
#define FILTOP_END 0x1C05FC
|
||||
#define FILTIMAP_START 0x1C0800
|
||||
#define FILTIMAP_END 0x1C0DFC
|
||||
#define FILTZ1_START 0x1C1000
|
||||
#define FILTZ1_END 0x1C15FC
|
||||
#define FILTZ2_START 0x1C1800
|
||||
#define FILTZ2_END 0x1C1DFC
|
||||
#define DAOIMAP_START 0x1C5000
|
||||
#define DAOIMAP 0x1C5000
|
||||
#define DAOIMAP_END 0x1C5124
|
||||
|
||||
#define AC97D 0x1C5400
|
||||
#define AC97A 0x1C5404
|
||||
#define AC97CTL 0x1C5408
|
||||
#define I2SCTL 0x1C5420
|
||||
|
||||
#define SPOS 0x1C5440
|
||||
#define SPOSA 0x1C5440
|
||||
#define SPOSB 0x1C5444
|
||||
#define SPOSC 0x1C5448
|
||||
#define SPOSD 0x1C544C
|
||||
|
||||
#define SPISA 0x1C5450
|
||||
#define SPISB 0x1C5454
|
||||
#define SPISC 0x1C5458
|
||||
#define SPISD 0x1C545C
|
||||
|
||||
#define SPFSCTL 0x1C5460
|
||||
|
||||
#define SPFS0 0x1C5468
|
||||
#define SPFS1 0x1C546C
|
||||
#define SPFS2 0x1C5470
|
||||
#define SPFS3 0x1C5474
|
||||
#define SPFS4 0x1C5478
|
||||
#define SPFS5 0x1C547C
|
||||
|
||||
#define SPOCTL 0x1C5480
|
||||
#define SPICTL 0x1C5484
|
||||
#define SPISTS 0x1C5488
|
||||
#define SPINTP 0x1C548C
|
||||
#define SPINTE 0x1C5490
|
||||
#define SPUTCTLAB 0x1C5494
|
||||
#define SPUTCTLCD 0x1C5498
|
||||
|
||||
#define SRTSPA 0x1C54C0
|
||||
#define SRTSPB 0x1C54C4
|
||||
#define SRTSPC 0x1C54C8
|
||||
#define SRTSPD 0x1C54CC
|
||||
|
||||
#define SRTSCTL 0x1C54D0
|
||||
#define SRTSCTLA 0x1C54D0
|
||||
#define SRTSCTLB 0x1C54D4
|
||||
#define SRTSCTLC 0x1C54D8
|
||||
#define SRTSCTLD 0x1C54DC
|
||||
|
||||
#define SRTI2S 0x1C54E0
|
||||
#define SRTICTL 0x1C54F0
|
||||
|
||||
#define WC 0x1C6000
|
||||
#define TIMR 0x1C6004
|
||||
# define TIMR_IE (1<<15)
|
||||
# define TIMR_IP (1<<14)
|
||||
|
||||
#define GIP 0x1C6010
|
||||
#define GIE 0x1C6014
|
||||
#define DIE 0x1C6018
|
||||
#define DIC 0x1C601C
|
||||
#define GPIO 0x1C6020
|
||||
#define GPIOCTL 0x1C6024
|
||||
#define GPIP 0x1C6028
|
||||
#define GPIE 0x1C602C
|
||||
#define DSPINT0 0x1C6030
|
||||
#define DSPEIOC 0x1C6034
|
||||
#define MUADAT 0x1C6040
|
||||
#define MUACMD 0x1C6044
|
||||
#define MUASTAT 0x1C6044
|
||||
#define MUBDAT 0x1C6048
|
||||
#define MUBCMD 0x1C604C
|
||||
#define MUBSTAT 0x1C604C
|
||||
#define UARTCMA 0x1C6050
|
||||
#define UARTCMB 0x1C6054
|
||||
#define UARTIP 0x1C6058
|
||||
#define UARTIE 0x1C605C
|
||||
#define PLLCTL 0x1C6060
|
||||
#define PLLDCD 0x1C6064
|
||||
#define GCTL 0x1C6070
|
||||
#define ID0 0x1C6080
|
||||
#define ID1 0x1C6084
|
||||
#define ID2 0x1C6088
|
||||
#define ID3 0x1C608C
|
||||
#define SDRCTL 0x1C7000
|
||||
|
||||
|
||||
#define I2SA_L 0x0L
|
||||
#define I2SA_R 0x1L
|
||||
#define I2SB_L 0x8L
|
||||
#define I2SB_R 0x9L
|
||||
#define I2SC_L 0x10L
|
||||
#define I2SC_R 0x11L
|
||||
#define I2SD_L 0x18L
|
||||
#define I2SD_R 0x19L
|
||||
|
||||
#endif /* CT20K1REG_H */
|
||||
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _20K2REGISTERS_H_
|
||||
#define _20K2REGISTERS_H_
|
||||
|
||||
|
||||
/* Timer Registers */
|
||||
#define TIMER_TIMR 0x1B7004
|
||||
#define INTERRUPT_GIP 0x1B7010
|
||||
#define INTERRUPT_GIE 0x1B7014
|
||||
|
||||
/* I2C Registers */
|
||||
#define I2C_IF_ADDRESS 0x1B9000
|
||||
#define I2C_IF_WDATA 0x1B9004
|
||||
#define I2C_IF_RDATA 0x1B9008
|
||||
#define I2C_IF_STATUS 0x1B900C
|
||||
#define I2C_IF_WLOCK 0x1B9010
|
||||
|
||||
/* Global Control Registers */
|
||||
#define GLOBAL_CNTL_GCTL 0x1B7090
|
||||
|
||||
/* PLL Registers */
|
||||
#define PLL_CTL 0x1B7080
|
||||
#define PLL_STAT 0x1B7084
|
||||
#define PLL_ENB 0x1B7088
|
||||
|
||||
/* SRC Registers */
|
||||
#define SRC_CTL 0x1A0000 /* 0x1A0000 + (256 * Chn) */
|
||||
#define SRC_CCR 0x1A0004 /* 0x1A0004 + (256 * Chn) */
|
||||
#define SRC_IMAP 0x1A0008 /* 0x1A0008 + (256 * Chn) */
|
||||
#define SRC_CA 0x1A0010 /* 0x1A0010 + (256 * Chn) */
|
||||
#define SRC_CF 0x1A0014 /* 0x1A0014 + (256 * Chn) */
|
||||
#define SRC_SA 0x1A0018 /* 0x1A0018 + (256 * Chn) */
|
||||
#define SRC_LA 0x1A001C /* 0x1A001C + (256 * Chn) */
|
||||
#define SRC_CTLSWR 0x1A0020 /* 0x1A0020 + (256 * Chn) */
|
||||
#define SRC_CD 0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */
|
||||
#define SRC_MCTL 0x1A012C
|
||||
#define SRC_IP 0x1A102C /* 0x1A102C + (256 * Regn) */
|
||||
#define SRC_ENB 0x1A282C /* 0x1A282C + (256 * Regn) */
|
||||
#define SRC_ENBSTAT 0x1A202C
|
||||
#define SRC_ENBSA 0x1A232C
|
||||
#define SRC_DN0Z 0x1A0030
|
||||
#define SRC_DN1Z 0x1A0040
|
||||
#define SRC_UPZ 0x1A0060
|
||||
|
||||
/* GPIO Registers */
|
||||
#define GPIO_DATA 0x1B7020
|
||||
#define GPIO_CTRL 0x1B7024
|
||||
|
||||
/* Virtual memory registers */
|
||||
#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */
|
||||
#define VMEM_PTPAH 0x1C6304 /* 0x1C6304 + (16 * Chn) */
|
||||
#define VMEM_CTL 0x1C7000
|
||||
|
||||
/* Transport Registers */
|
||||
#define TRANSPORT_ENB 0x1B6000
|
||||
#define TRANSPORT_CTL 0x1B6004
|
||||
#define TRANSPORT_INT 0x1B6008
|
||||
|
||||
/* Audio IO */
|
||||
#define AUDIO_IO_AIM 0x1B5000 /* 0x1B5000 + (0x04 * Chn) */
|
||||
#define AUDIO_IO_TX_CTL 0x1B5400 /* 0x1B5400 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */
|
||||
#define AUDIO_IO_RX_CTL 0x1B5410 /* 0x1B5410 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_MCLK 0x1B5600
|
||||
#define AUDIO_IO_TX_BLRCLK 0x1B5604
|
||||
#define AUDIO_IO_RX_BLRCLK 0x1B5608
|
||||
|
||||
/* Mixer */
|
||||
#define MIXER_AMOPLO 0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_AMOPHI 0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_PRING_LO_HI 0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */
|
||||
#define MIXER_PMOPLO 0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_PMOPHI 0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_AR_ENABLE 0x19000C
|
||||
|
||||
#endif
|
|
@ -0,0 +1,488 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctamixer.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of the Audio Mixer
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 21 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctamixer.h"
|
||||
#include "cthardware.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define AMIXER_RESOURCE_NUM 256
|
||||
#define SUM_RESOURCE_NUM 256
|
||||
|
||||
#define AMIXER_Y_IMMEDIATE 1
|
||||
|
||||
#define BLANK_SLOT 4094
|
||||
|
||||
static int amixer_master(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj = 0;
|
||||
return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
|
||||
}
|
||||
|
||||
static int amixer_next_conj(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj++;
|
||||
return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int amixer_index(const struct rsc *rsc)
|
||||
{
|
||||
return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int amixer_output_slot(const struct rsc *rsc)
|
||||
{
|
||||
return (amixer_index(rsc) << 4) + 0x4;
|
||||
}
|
||||
|
||||
static struct rsc_ops amixer_basic_rsc_ops = {
|
||||
.master = amixer_master,
|
||||
.next_conj = amixer_next_conj,
|
||||
.index = amixer_index,
|
||||
.output_slot = amixer_output_slot,
|
||||
};
|
||||
|
||||
static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
|
||||
amixer->input = rsc;
|
||||
if (NULL == rsc)
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
|
||||
else
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk,
|
||||
rsc->ops->output_slot(rsc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* y is a 14-bit immediate constant */
|
||||
static int amixer_set_y(struct amixer *amixer, unsigned int y)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_y(amixer->rsc.ctrl_blk, y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
amixer->sum = sum;
|
||||
if (NULL == sum) {
|
||||
hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
|
||||
} else {
|
||||
hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
|
||||
hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
|
||||
sum->rsc.ops->index(&sum->rsc));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_commit_write(struct amixer *amixer)
|
||||
{
|
||||
struct hw *hw;
|
||||
unsigned int index;
|
||||
int i;
|
||||
struct rsc *input;
|
||||
struct sum *sum;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
input = amixer->input;
|
||||
sum = amixer->sum;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
amixer->rsc.ops->master(&amixer->rsc);
|
||||
if (NULL != input)
|
||||
input->ops->master(input);
|
||||
|
||||
if (NULL != sum)
|
||||
sum->rsc.ops->master(&sum->rsc);
|
||||
|
||||
for (i = 0; i < amixer->rsc.msr; i++) {
|
||||
hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
|
||||
if (NULL != input) {
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk,
|
||||
input->ops->output_slot(input));
|
||||
input->ops->next_conj(input);
|
||||
}
|
||||
if (NULL != sum) {
|
||||
hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
|
||||
sum->rsc.ops->index(&sum->rsc));
|
||||
sum->rsc.ops->next_conj(&sum->rsc);
|
||||
}
|
||||
index = amixer->rsc.ops->output_slot(&amixer->rsc);
|
||||
hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
|
||||
amixer->rsc.ops->next_conj(&amixer->rsc);
|
||||
}
|
||||
amixer->rsc.ops->master(&amixer->rsc);
|
||||
if (NULL != input)
|
||||
input->ops->master(input);
|
||||
|
||||
if (NULL != sum)
|
||||
sum->rsc.ops->master(&sum->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_commit_raw_write(struct amixer *amixer)
|
||||
{
|
||||
struct hw *hw;
|
||||
unsigned int index;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
index = amixer->rsc.ops->output_slot(&amixer->rsc);
|
||||
hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_get_y(struct amixer *amixer)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
return hw->amixer_get_y(amixer->rsc.ctrl_blk);
|
||||
}
|
||||
|
||||
static int amixer_setup(struct amixer *amixer, struct rsc *input,
|
||||
unsigned int scale, struct sum *sum)
|
||||
{
|
||||
amixer_set_input(amixer, input);
|
||||
amixer_set_y(amixer, scale);
|
||||
amixer_set_sum(amixer, sum);
|
||||
amixer_commit_write(amixer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amixer_rsc_ops amixer_ops = {
|
||||
.set_input = amixer_set_input,
|
||||
.set_invalid_squash = amixer_set_invalid_squash,
|
||||
.set_scale = amixer_set_y,
|
||||
.set_sum = amixer_set_sum,
|
||||
.commit_write = amixer_commit_write,
|
||||
.commit_raw_write = amixer_commit_raw_write,
|
||||
.setup = amixer_setup,
|
||||
.get_scale = amixer_get_y,
|
||||
};
|
||||
|
||||
static int amixer_rsc_init(struct amixer *amixer,
|
||||
const struct amixer_desc *desc,
|
||||
struct amixer_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rsc_init(&amixer->rsc, amixer->idx[0],
|
||||
AMIXER, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set amixer specific operations */
|
||||
amixer->rsc.ops = &amixer_basic_rsc_ops;
|
||||
amixer->ops = &amixer_ops;
|
||||
amixer->input = NULL;
|
||||
amixer->sum = NULL;
|
||||
|
||||
amixer_setup(amixer, NULL, 0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_rsc_uninit(struct amixer *amixer)
|
||||
{
|
||||
amixer_setup(amixer, NULL, 0, NULL);
|
||||
rsc_uninit(&amixer->rsc);
|
||||
amixer->ops = NULL;
|
||||
amixer->input = NULL;
|
||||
amixer->sum = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_amixer_rsc(struct amixer_mgr *mgr,
|
||||
const struct amixer_desc *desc,
|
||||
struct amixer **ramixer)
|
||||
{
|
||||
int err, i;
|
||||
unsigned int idx;
|
||||
struct amixer *amixer;
|
||||
unsigned long flags;
|
||||
|
||||
*ramixer = NULL;
|
||||
|
||||
/* Allocate mem for amixer resource */
|
||||
amixer = kzalloc(sizeof(*amixer), GFP_KERNEL);
|
||||
if (NULL == amixer) {
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check whether there are sufficient
|
||||
* amixer resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < desc->msr; i++) {
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
amixer->idx[i] = idx;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = amixer_rsc_init(amixer, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*ramixer = amixer;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i--; i >= 0; i--)
|
||||
mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
kfree(amixer);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < amixer->rsc.msr; i++)
|
||||
mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
amixer_rsc_uninit(amixer);
|
||||
kfree(amixer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
|
||||
{
|
||||
int err;
|
||||
struct amixer_mgr *amixer_mgr;
|
||||
|
||||
*ramixer_mgr = NULL;
|
||||
amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
|
||||
if (NULL == amixer_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
spin_lock_init(&amixer_mgr->mgr_lock);
|
||||
|
||||
amixer_mgr->get_amixer = get_amixer_rsc;
|
||||
amixer_mgr->put_amixer = put_amixer_rsc;
|
||||
|
||||
*ramixer_mgr = amixer_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(amixer_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
|
||||
{
|
||||
rsc_mgr_uninit(&amixer_mgr->mgr);
|
||||
kfree(amixer_mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SUM resource management */
|
||||
|
||||
static int sum_master(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj = 0;
|
||||
return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
|
||||
}
|
||||
|
||||
static int sum_next_conj(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj++;
|
||||
return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int sum_index(const struct rsc *rsc)
|
||||
{
|
||||
return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int sum_output_slot(const struct rsc *rsc)
|
||||
{
|
||||
return (sum_index(rsc) << 4) + 0xc;
|
||||
}
|
||||
|
||||
static struct rsc_ops sum_basic_rsc_ops = {
|
||||
.master = sum_master,
|
||||
.next_conj = sum_next_conj,
|
||||
.index = sum_index,
|
||||
.output_slot = sum_output_slot,
|
||||
};
|
||||
|
||||
static int sum_rsc_init(struct sum *sum,
|
||||
const struct sum_desc *desc,
|
||||
struct sum_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sum->rsc.ops = &sum_basic_rsc_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sum_rsc_uninit(struct sum *sum)
|
||||
{
|
||||
rsc_uninit(&sum->rsc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_sum_rsc(struct sum_mgr *mgr,
|
||||
const struct sum_desc *desc,
|
||||
struct sum **rsum)
|
||||
{
|
||||
int err, i;
|
||||
unsigned int idx;
|
||||
struct sum *sum;
|
||||
unsigned long flags;
|
||||
|
||||
*rsum = NULL;
|
||||
|
||||
/* Allocate mem for sum resource */
|
||||
sum = kzalloc(sizeof(*sum), GFP_KERNEL);
|
||||
if (NULL == sum) {
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check whether there are sufficient sum resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < desc->msr; i++) {
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
sum->idx[i] = idx;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = sum_rsc_init(sum, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*rsum = sum;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i--; i >= 0; i--)
|
||||
mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
kfree(sum);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < sum->rsc.msr; i++)
|
||||
mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
sum_rsc_uninit(sum);
|
||||
kfree(sum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
|
||||
{
|
||||
int err;
|
||||
struct sum_mgr *sum_mgr;
|
||||
|
||||
*rsum_mgr = NULL;
|
||||
sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
|
||||
if (NULL == sum_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
spin_lock_init(&sum_mgr->mgr_lock);
|
||||
|
||||
sum_mgr->get_sum = get_sum_rsc;
|
||||
sum_mgr->put_sum = put_sum_rsc;
|
||||
|
||||
*rsum_mgr = sum_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(sum_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int sum_mgr_destroy(struct sum_mgr *sum_mgr)
|
||||
{
|
||||
rsc_mgr_uninit(&sum_mgr->mgr);
|
||||
kfree(sum_mgr);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctamixer.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the Audio Mixer
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 21 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTAMIXER_H
|
||||
#define CTAMIXER_H
|
||||
|
||||
#include "ctresource.h"
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* Define the descriptor of a summation node resource */
|
||||
struct sum {
|
||||
struct rsc rsc; /* Basic resource info */
|
||||
unsigned char idx[8];
|
||||
};
|
||||
|
||||
/* Define sum resource request description info */
|
||||
struct sum_desc {
|
||||
unsigned int msr;
|
||||
};
|
||||
|
||||
struct sum_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
|
||||
/* request one sum resource */
|
||||
int (*get_sum)(struct sum_mgr *mgr,
|
||||
const struct sum_desc *desc, struct sum **rsum);
|
||||
/* return one sum resource */
|
||||
int (*put_sum)(struct sum_mgr *mgr, struct sum *sum);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of daio resource manager */
|
||||
int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
|
||||
int sum_mgr_destroy(struct sum_mgr *sum_mgr);
|
||||
|
||||
/* Define the descriptor of a amixer resource */
|
||||
struct amixer_rsc_ops;
|
||||
|
||||
struct amixer {
|
||||
struct rsc rsc; /* Basic resource info */
|
||||
unsigned char idx[8];
|
||||
struct rsc *input; /* pointer to a resource acting as source */
|
||||
struct sum *sum; /* Put amixer output to this summation node */
|
||||
struct amixer_rsc_ops *ops; /* AMixer specific operations */
|
||||
};
|
||||
|
||||
struct amixer_rsc_ops {
|
||||
int (*set_input)(struct amixer *amixer, struct rsc *rsc);
|
||||
int (*set_scale)(struct amixer *amixer, unsigned int scale);
|
||||
int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv);
|
||||
int (*set_sum)(struct amixer *amixer, struct sum *sum);
|
||||
int (*commit_write)(struct amixer *amixer);
|
||||
/* Only for interleaved recording */
|
||||
int (*commit_raw_write)(struct amixer *amixer);
|
||||
int (*setup)(struct amixer *amixer, struct rsc *input,
|
||||
unsigned int scale, struct sum *sum);
|
||||
int (*get_scale)(struct amixer *amixer);
|
||||
};
|
||||
|
||||
/* Define amixer resource request description info */
|
||||
struct amixer_desc {
|
||||
unsigned int msr;
|
||||
};
|
||||
|
||||
struct amixer_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
|
||||
/* request one amixer resource */
|
||||
int (*get_amixer)(struct amixer_mgr *mgr,
|
||||
const struct amixer_desc *desc,
|
||||
struct amixer **ramixer);
|
||||
/* return one amixer resource */
|
||||
int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of amixer resource manager */
|
||||
int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
|
||||
int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
|
||||
|
||||
#endif /* CTAMIXER_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctatc.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the device resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTATC_H
|
||||
#define CTATC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/timer.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "ctvmem.h"
|
||||
#include "ctresource.h"
|
||||
|
||||
enum CTALSADEVS { /* Types of alsa devices */
|
||||
FRONT,
|
||||
SURROUND,
|
||||
CLFE,
|
||||
SIDE,
|
||||
IEC958,
|
||||
MIXER,
|
||||
NUM_CTALSADEVS /* This should always be the last */
|
||||
};
|
||||
|
||||
struct ct_atc_chip_sub_details {
|
||||
u16 subsys;
|
||||
const char *nm_model;
|
||||
};
|
||||
|
||||
struct ct_atc_chip_details {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
const struct ct_atc_chip_sub_details *sub_details;
|
||||
const char *nm_card;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
/* Only mono and interleaved modes are supported now. */
|
||||
struct ct_vm_block *vm_block;
|
||||
void *src; /* SRC for interacting with host memory */
|
||||
void **srccs; /* SRCs for sample rate conversion */
|
||||
void **srcimps; /* SRC Input Mappers */
|
||||
void **amixers; /* AMIXERs for routing converted data */
|
||||
void *mono; /* A SUM resource for mixing chs to one */
|
||||
unsigned char n_srcc; /* Number of converting SRCs */
|
||||
unsigned char n_srcimp; /* Number of SRC Input Mappers */
|
||||
unsigned char n_amixer; /* Number of AMIXERs */
|
||||
};
|
||||
|
||||
/* Chip resource management object */
|
||||
struct ct_atc {
|
||||
struct pci_dev *pci;
|
||||
struct snd_card *card;
|
||||
unsigned int rsr; /* reference sample rate in Hz */
|
||||
unsigned int msr; /* master sample rate in rsr */
|
||||
unsigned int pll_rate; /* current rate of Phase Lock Loop */
|
||||
|
||||
int chip_type;
|
||||
int model;
|
||||
const char *chip_name;
|
||||
const char *model_name;
|
||||
|
||||
struct ct_vm *vm; /* device virtual memory manager for this card */
|
||||
int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index);
|
||||
|
||||
spinlock_t atc_lock;
|
||||
|
||||
int (*pcm_playback_prepare)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_playback_position)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*spdif_passthru_playback_prepare)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_position)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*pcm_release_resources)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*select_line_in)(struct ct_atc *atc);
|
||||
int (*select_mic_in)(struct ct_atc *atc);
|
||||
int (*select_digit_io)(struct ct_atc *atc);
|
||||
int (*line_front_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
|
||||
int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
|
||||
int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
|
||||
int (*have_digit_io_switch)(struct ct_atc *atc);
|
||||
|
||||
/* Don't touch! Used for internal object. */
|
||||
void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
|
||||
void *mixer; /* internal mixer object */
|
||||
void *hw; /* chip specific hardware access object */
|
||||
void **daios; /* digital audio io resources */
|
||||
void **pcm; /* SUMs for collecting all pcm stream */
|
||||
void **srcs; /* Sample Rate Converters for input signal */
|
||||
void **srcimps; /* input mappers for SRCs */
|
||||
unsigned char n_daio;
|
||||
unsigned char n_src;
|
||||
unsigned char n_srcimp;
|
||||
unsigned char n_pcm;
|
||||
|
||||
struct ct_timer *timer;
|
||||
};
|
||||
|
||||
|
||||
int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
|
||||
unsigned int rsr, unsigned int msr, int chip_type,
|
||||
struct ct_atc **ratc);
|
||||
int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
|
||||
|
||||
#endif /* CTATC_H */
|
|
@ -0,0 +1,769 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctdaio.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of Digital Audio Input Output
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctdaio.h"
|
||||
#include "cthardware.h"
|
||||
#include "ctimap.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define DAIO_RESOURCE_NUM NUM_DAIOTYP
|
||||
#define DAIO_OUT_MAX SPDIFOO
|
||||
|
||||
union daio_usage {
|
||||
struct {
|
||||
unsigned short lineo1:1;
|
||||
unsigned short lineo2:1;
|
||||
unsigned short lineo3:1;
|
||||
unsigned short lineo4:1;
|
||||
unsigned short spdifoo:1;
|
||||
unsigned short lineim:1;
|
||||
unsigned short spdifio:1;
|
||||
unsigned short spdifi1:1;
|
||||
} bf;
|
||||
unsigned short data;
|
||||
};
|
||||
|
||||
struct daio_rsc_idx {
|
||||
unsigned short left;
|
||||
unsigned short right;
|
||||
};
|
||||
|
||||
struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
|
||||
[LINEO1] = {.left = 0x00, .right = 0x01},
|
||||
[LINEO2] = {.left = 0x18, .right = 0x19},
|
||||
[LINEO3] = {.left = 0x08, .right = 0x09},
|
||||
[LINEO4] = {.left = 0x10, .right = 0x11},
|
||||
[LINEIM] = {.left = 0x1b5, .right = 0x1bd},
|
||||
[SPDIFOO] = {.left = 0x20, .right = 0x21},
|
||||
[SPDIFIO] = {.left = 0x15, .right = 0x1d},
|
||||
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
|
||||
};
|
||||
|
||||
struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
|
||||
[LINEO1] = {.left = 0x40, .right = 0x41},
|
||||
[LINEO2] = {.left = 0x70, .right = 0x71},
|
||||
[LINEO3] = {.left = 0x50, .right = 0x51},
|
||||
[LINEO4] = {.left = 0x60, .right = 0x61},
|
||||
[LINEIM] = {.left = 0x45, .right = 0xc5},
|
||||
[SPDIFOO] = {.left = 0x00, .right = 0x01},
|
||||
[SPDIFIO] = {.left = 0x05, .right = 0x85},
|
||||
};
|
||||
|
||||
static int daio_master(struct rsc *rsc)
|
||||
{
|
||||
/* Actually, this is not the resource index of DAIO.
|
||||
* For DAO, it is the input mapper index. And, for DAI,
|
||||
* it is the output time-slot index. */
|
||||
return rsc->conj = rsc->idx;
|
||||
}
|
||||
|
||||
static int daio_index(const struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj;
|
||||
}
|
||||
|
||||
static int daio_out_next_conj(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj += 2;
|
||||
}
|
||||
|
||||
static int daio_in_next_conj_20k1(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj += 0x200;
|
||||
}
|
||||
|
||||
static int daio_in_next_conj_20k2(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj += 0x100;
|
||||
}
|
||||
|
||||
static struct rsc_ops daio_out_rsc_ops = {
|
||||
.master = daio_master,
|
||||
.next_conj = daio_out_next_conj,
|
||||
.index = daio_index,
|
||||
.output_slot = NULL,
|
||||
};
|
||||
|
||||
static struct rsc_ops daio_in_rsc_ops_20k1 = {
|
||||
.master = daio_master,
|
||||
.next_conj = daio_in_next_conj_20k1,
|
||||
.index = NULL,
|
||||
.output_slot = daio_index,
|
||||
};
|
||||
|
||||
static struct rsc_ops daio_in_rsc_ops_20k2 = {
|
||||
.master = daio_master,
|
||||
.next_conj = daio_in_next_conj_20k2,
|
||||
.index = NULL,
|
||||
.output_slot = daio_index,
|
||||
};
|
||||
|
||||
static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
|
||||
{
|
||||
switch (hw->chip_type) {
|
||||
case ATC20K1:
|
||||
switch (type) {
|
||||
case SPDIFOO: return 0;
|
||||
case SPDIFIO: return 0;
|
||||
case SPDIFI1: return 1;
|
||||
case LINEO1: return 4;
|
||||
case LINEO2: return 7;
|
||||
case LINEO3: return 5;
|
||||
case LINEO4: return 6;
|
||||
case LINEIM: return 7;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
case ATC20K2:
|
||||
switch (type) {
|
||||
case SPDIFOO: return 0;
|
||||
case SPDIFIO: return 0;
|
||||
case LINEO1: return 4;
|
||||
case LINEO2: return 7;
|
||||
case LINEO3: return 5;
|
||||
case LINEO4: return 6;
|
||||
case LINEIM: return 4;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
|
||||
|
||||
static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
|
||||
{
|
||||
((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
|
||||
{
|
||||
((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_commit_write(struct dao *dao)
|
||||
{
|
||||
((struct hw *)dao->hw)->dao_commit_write(dao->hw,
|
||||
daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_set_left_input(struct dao *dao, struct rsc *input)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
|
||||
if (NULL == entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
input->ops->master(input);
|
||||
daio->rscl.ops->master(&daio->rscl);
|
||||
for (i = 0; i < daio->rscl.msr; i++, entry++) {
|
||||
entry->slot = input->ops->output_slot(input);
|
||||
entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl);
|
||||
dao->mgr->imap_add(dao->mgr, entry);
|
||||
dao->imappers[i] = entry;
|
||||
|
||||
input->ops->next_conj(input);
|
||||
daio->rscl.ops->next_conj(&daio->rscl);
|
||||
}
|
||||
input->ops->master(input);
|
||||
daio->rscl.ops->master(&daio->rscl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_set_right_input(struct dao *dao, struct rsc *input)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
|
||||
if (NULL == entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
input->ops->master(input);
|
||||
daio->rscr.ops->master(&daio->rscr);
|
||||
for (i = 0; i < daio->rscr.msr; i++, entry++) {
|
||||
entry->slot = input->ops->output_slot(input);
|
||||
entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr);
|
||||
dao->mgr->imap_add(dao->mgr, entry);
|
||||
dao->imappers[daio->rscl.msr + i] = entry;
|
||||
|
||||
input->ops->next_conj(input);
|
||||
daio->rscr.ops->next_conj(&daio->rscr);
|
||||
}
|
||||
input->ops->master(input);
|
||||
daio->rscr.ops->master(&daio->rscr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_clear_left_input(struct dao *dao)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (NULL == dao->imappers[0])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[0];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
/* Program conjugate resources */
|
||||
for (i = 1; i < daio->rscl.msr; i++) {
|
||||
entry = dao->imappers[i];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
dao->imappers[i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dao->imappers[0]);
|
||||
dao->imappers[0] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_clear_right_input(struct dao *dao)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (NULL == dao->imappers[daio->rscl.msr])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[daio->rscl.msr];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
/* Program conjugate resources */
|
||||
for (i = 1; i < daio->rscr.msr; i++) {
|
||||
entry = dao->imappers[daio->rscl.msr + i];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
dao->imappers[daio->rscl.msr + i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dao->imappers[daio->rscl.msr]);
|
||||
dao->imappers[daio->rscl.msr] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dao_rsc_ops dao_ops = {
|
||||
.set_spos = dao_spdif_set_spos,
|
||||
.commit_write = dao_commit_write,
|
||||
.get_spos = dao_spdif_get_spos,
|
||||
.reinit = dao_rsc_reinit,
|
||||
.set_left_input = dao_set_left_input,
|
||||
.set_right_input = dao_set_right_input,
|
||||
.clear_left_input = dao_clear_left_input,
|
||||
.clear_right_input = dao_clear_right_input,
|
||||
};
|
||||
|
||||
static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
|
||||
{
|
||||
src->ops->master(src);
|
||||
((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
|
||||
src->ops->index(src));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
|
||||
{
|
||||
src->ops->master(src);
|
||||
((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
|
||||
src->ops->index(src));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
|
||||
{
|
||||
unsigned int rsr;
|
||||
|
||||
for (rsr = 0; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_enb_src(struct dai *dai, unsigned int enb)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_commit_write(struct dai *dai)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_commit_write(dai->hw,
|
||||
daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dai_rsc_ops dai_ops = {
|
||||
.set_srt_srcl = dai_set_srt_srcl,
|
||||
.set_srt_srcr = dai_set_srt_srcr,
|
||||
.set_srt_msr = dai_set_srt_msr,
|
||||
.set_enb_src = dai_set_enb_src,
|
||||
.set_enb_srt = dai_set_enb_srt,
|
||||
.commit_write = dai_commit_write,
|
||||
};
|
||||
|
||||
static int daio_rsc_init(struct daio *daio,
|
||||
const struct daio_desc *desc,
|
||||
void *hw)
|
||||
{
|
||||
int err;
|
||||
unsigned int idx_l, idx_r;
|
||||
|
||||
switch (((struct hw *)hw)->chip_type) {
|
||||
case ATC20K1:
|
||||
idx_l = idx_20k1[desc->type].left;
|
||||
idx_r = idx_20k1[desc->type].right;
|
||||
break;
|
||||
case ATC20K2:
|
||||
idx_l = idx_20k2[desc->type].left;
|
||||
idx_r = idx_20k2[desc->type].right;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
/* Set daio->rscl/r->ops to daio specific ones */
|
||||
if (desc->type <= DAIO_OUT_MAX) {
|
||||
daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
|
||||
} else {
|
||||
switch (((struct hw *)hw)->chip_type) {
|
||||
case ATC20K1:
|
||||
daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
|
||||
break;
|
||||
case ATC20K2:
|
||||
daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
daio->type = desc->type;
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
rsc_uninit(&daio->rscl);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daio_rsc_uninit(struct daio *daio)
|
||||
{
|
||||
rsc_uninit(&daio->rscl);
|
||||
rsc_uninit(&daio->rscr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_rsc_init(struct dao *dao,
|
||||
const struct daio_desc *desc,
|
||||
struct daio_mgr *mgr)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
unsigned int conf;
|
||||
int err;
|
||||
|
||||
err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
|
||||
if (NULL == dao->imappers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
dao->ops = &dao_ops;
|
||||
dao->mgr = mgr;
|
||||
dao->hw = hw;
|
||||
err = hw->dao_get_ctrl_blk(&dao->ctrl_blk);
|
||||
if (err)
|
||||
goto error2;
|
||||
|
||||
hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(dao->daio.type, hw));
|
||||
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
|
||||
conf = (desc->msr & 0x7) | (desc->passthru << 3);
|
||||
hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(dao->daio.type, hw), conf);
|
||||
hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(dao->daio.type, hw));
|
||||
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
kfree(dao->imappers);
|
||||
dao->imappers = NULL;
|
||||
error1:
|
||||
daio_rsc_uninit(&dao->daio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dao_rsc_uninit(struct dao *dao)
|
||||
{
|
||||
if (NULL != dao->imappers) {
|
||||
if (NULL != dao->imappers[0])
|
||||
dao_clear_left_input(dao);
|
||||
|
||||
if (NULL != dao->imappers[dao->daio.rscl.msr])
|
||||
dao_clear_right_input(dao);
|
||||
|
||||
kfree(dao->imappers);
|
||||
dao->imappers = NULL;
|
||||
}
|
||||
((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
|
||||
dao->hw = dao->ctrl_blk = NULL;
|
||||
daio_rsc_uninit(&dao->daio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc)
|
||||
{
|
||||
struct daio_mgr *mgr = dao->mgr;
|
||||
struct daio_desc dsc = {0};
|
||||
|
||||
dsc.type = dao->daio.type;
|
||||
dsc.msr = desc->msr;
|
||||
dsc.passthru = desc->passthru;
|
||||
dao_rsc_uninit(dao);
|
||||
return dao_rsc_init(dao, &dsc, mgr);
|
||||
}
|
||||
|
||||
static int dai_rsc_init(struct dai *dai,
|
||||
const struct daio_desc *desc,
|
||||
struct daio_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
unsigned int rsr, msr;
|
||||
|
||||
err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dai->ops = &dai_ops;
|
||||
dai->hw = mgr->mgr.hw;
|
||||
err = hw->dai_get_ctrl_blk(&dai->ctrl_blk);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
|
||||
hw->dai_srt_set_drat(dai->ctrl_blk, 0);
|
||||
/* default to disabling control of a SRC */
|
||||
hw->dai_srt_set_ec(dai->ctrl_blk, 0);
|
||||
hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */
|
||||
hw->dai_commit_write(hw,
|
||||
daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
daio_rsc_uninit(&dai->daio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dai_rsc_uninit(struct dai *dai)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
|
||||
dai->hw = dai->ctrl_blk = NULL;
|
||||
daio_rsc_uninit(&dai->daio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
|
||||
{
|
||||
if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
|
||||
return -ENOENT;
|
||||
|
||||
((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
|
||||
{
|
||||
((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_daio_rsc(struct daio_mgr *mgr,
|
||||
const struct daio_desc *desc,
|
||||
struct daio **rdaio)
|
||||
{
|
||||
int err;
|
||||
struct dai *dai = NULL;
|
||||
struct dao *dao = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
*rdaio = NULL;
|
||||
|
||||
/* Check whether there are sufficient daio resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "Can't meet DAIO resource request!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Allocate mem for daio resource */
|
||||
if (desc->type <= DAIO_OUT_MAX) {
|
||||
dao = kzalloc(sizeof(*dao), GFP_KERNEL);
|
||||
if (NULL == dao) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
err = dao_rsc_init(dao, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*rdaio = &dao->daio;
|
||||
} else {
|
||||
dai = kzalloc(sizeof(*dai), GFP_KERNEL);
|
||||
if (NULL == dai) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
err = dai_rsc_init(dai, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*rdaio = &dai->daio;
|
||||
}
|
||||
|
||||
mgr->daio_enable(mgr, *rdaio);
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (NULL != dao)
|
||||
kfree(dao);
|
||||
else if (NULL != dai)
|
||||
kfree(dai);
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
daio_mgr_put_rsc(&mgr->mgr, desc->type);
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
mgr->daio_disable(mgr, daio);
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
daio_mgr_put_rsc(&mgr->mgr, daio->type);
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
|
||||
if (daio->type <= DAIO_OUT_MAX) {
|
||||
dao_rsc_uninit(container_of(daio, struct dao, daio));
|
||||
kfree(container_of(daio, struct dao, daio));
|
||||
} else {
|
||||
dai_rsc_uninit(container_of(daio, struct dai, daio));
|
||||
kfree(container_of(daio, struct dai, daio));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
if (DAIO_OUT_MAX >= daio->type) {
|
||||
hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
} else {
|
||||
hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
if (DAIO_OUT_MAX >= daio->type) {
|
||||
hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
} else {
|
||||
hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_map_op(void *data, struct imapper *entry)
|
||||
{
|
||||
struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr;
|
||||
struct hw *hw = mgr->hw;
|
||||
|
||||
hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
|
||||
hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
|
||||
hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
|
||||
hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
if ((0 == entry->addr) && (mgr->init_imap_added)) {
|
||||
input_mapper_delete(&mgr->imappers, mgr->init_imap,
|
||||
daio_map_op, mgr);
|
||||
mgr->init_imap_added = 0;
|
||||
}
|
||||
err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
|
||||
if (list_empty(&mgr->imappers)) {
|
||||
input_mapper_add(&mgr->imappers, mgr->init_imap,
|
||||
daio_map_op, mgr);
|
||||
mgr->init_imap_added = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daio_mgr_commit_write(struct daio_mgr *mgr)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
|
||||
{
|
||||
int err, i;
|
||||
struct daio_mgr *daio_mgr;
|
||||
struct imapper *entry;
|
||||
|
||||
*rdaio_mgr = NULL;
|
||||
daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
|
||||
if (NULL == daio_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
spin_lock_init(&daio_mgr->mgr_lock);
|
||||
spin_lock_init(&daio_mgr->imap_lock);
|
||||
INIT_LIST_HEAD(&daio_mgr->imappers);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (NULL == entry) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
entry->slot = entry->addr = entry->next = entry->user = 0;
|
||||
list_add(&entry->list, &daio_mgr->imappers);
|
||||
daio_mgr->init_imap = entry;
|
||||
daio_mgr->init_imap_added = 1;
|
||||
|
||||
daio_mgr->get_daio = get_daio_rsc;
|
||||
daio_mgr->put_daio = put_daio_rsc;
|
||||
daio_mgr->daio_enable = daio_mgr_enb_daio;
|
||||
daio_mgr->daio_disable = daio_mgr_dsb_daio;
|
||||
daio_mgr->imap_add = daio_imap_add;
|
||||
daio_mgr->imap_delete = daio_imap_delete;
|
||||
daio_mgr->commit_write = daio_mgr_commit_write;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
|
||||
((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
|
||||
}
|
||||
((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
|
||||
|
||||
*rdaio_mgr = daio_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
rsc_mgr_uninit(&daio_mgr->mgr);
|
||||
error1:
|
||||
kfree(daio_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int daio_mgr_destroy(struct daio_mgr *daio_mgr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* free daio input mapper list */
|
||||
spin_lock_irqsave(&daio_mgr->imap_lock, flags);
|
||||
free_input_mapper_list(&daio_mgr->imappers);
|
||||
spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
|
||||
|
||||
rsc_mgr_uninit(&daio_mgr->mgr);
|
||||
kfree(daio_mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctdaio.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of Digital Audio Input Output
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTDAIO_H
|
||||
#define CTDAIO_H
|
||||
|
||||
#include "ctresource.h"
|
||||
#include "ctimap.h"
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* Define the descriptor of a daio resource */
|
||||
enum DAIOTYP {
|
||||
LINEO1,
|
||||
LINEO2,
|
||||
LINEO3,
|
||||
LINEO4,
|
||||
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
|
||||
LINEIM,
|
||||
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
|
||||
SPDIFI1, /* S/PDIF In on internal Drive Bay */
|
||||
NUM_DAIOTYP
|
||||
};
|
||||
|
||||
struct dao_rsc_ops;
|
||||
struct dai_rsc_ops;
|
||||
struct daio_mgr;
|
||||
|
||||
struct daio {
|
||||
struct rsc rscl; /* Basic resource info for left TX/RX */
|
||||
struct rsc rscr; /* Basic resource info for right TX/RX */
|
||||
enum DAIOTYP type;
|
||||
};
|
||||
|
||||
struct dao {
|
||||
struct daio daio;
|
||||
struct dao_rsc_ops *ops; /* DAO specific operations */
|
||||
struct imapper **imappers;
|
||||
struct daio_mgr *mgr;
|
||||
void *hw;
|
||||
void *ctrl_blk;
|
||||
};
|
||||
|
||||
struct dai {
|
||||
struct daio daio;
|
||||
struct dai_rsc_ops *ops; /* DAI specific operations */
|
||||
void *hw;
|
||||
void *ctrl_blk;
|
||||
};
|
||||
|
||||
struct dao_desc {
|
||||
unsigned int msr:4;
|
||||
unsigned int passthru:1;
|
||||
};
|
||||
|
||||
struct dao_rsc_ops {
|
||||
int (*set_spos)(struct dao *dao, unsigned int spos);
|
||||
int (*commit_write)(struct dao *dao);
|
||||
int (*get_spos)(struct dao *dao, unsigned int *spos);
|
||||
int (*reinit)(struct dao *dao, const struct dao_desc *desc);
|
||||
int (*set_left_input)(struct dao *dao, struct rsc *input);
|
||||
int (*set_right_input)(struct dao *dao, struct rsc *input);
|
||||
int (*clear_left_input)(struct dao *dao);
|
||||
int (*clear_right_input)(struct dao *dao);
|
||||
};
|
||||
|
||||
struct dai_rsc_ops {
|
||||
int (*set_srt_srcl)(struct dai *dai, struct rsc *src);
|
||||
int (*set_srt_srcr)(struct dai *dai, struct rsc *src);
|
||||
int (*set_srt_msr)(struct dai *dai, unsigned int msr);
|
||||
int (*set_enb_src)(struct dai *dai, unsigned int enb);
|
||||
int (*set_enb_srt)(struct dai *dai, unsigned int enb);
|
||||
int (*commit_write)(struct dai *dai);
|
||||
};
|
||||
|
||||
/* Define daio resource request description info */
|
||||
struct daio_desc {
|
||||
unsigned int type:4;
|
||||
unsigned int msr:4;
|
||||
unsigned int passthru:1;
|
||||
};
|
||||
|
||||
struct daio_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
spinlock_t imap_lock;
|
||||
struct list_head imappers;
|
||||
struct imapper *init_imap;
|
||||
unsigned int init_imap_added;
|
||||
|
||||
/* request one daio resource */
|
||||
int (*get_daio)(struct daio_mgr *mgr,
|
||||
const struct daio_desc *desc, struct daio **rdaio);
|
||||
/* return one daio resource */
|
||||
int (*put_daio)(struct daio_mgr *mgr, struct daio *daio);
|
||||
int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio);
|
||||
int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio);
|
||||
int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry);
|
||||
int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry);
|
||||
int (*commit_write)(struct daio_mgr *mgr);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of daio resource manager */
|
||||
int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
|
||||
int daio_mgr_destroy(struct daio_mgr *daio_mgr);
|
||||
|
||||
#endif /* CTDAIO_H */
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File cthardware.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Jun 26 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cthardware.h"
|
||||
#include "cthw20k1.h"
|
||||
#include "cthw20k2.h"
|
||||
#include <linux/bug.h>
|
||||
|
||||
int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
|
||||
enum CTCARDS model, struct hw **rhw)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (chip_type) {
|
||||
case ATC20K1:
|
||||
err = create_20k1_hw_obj(rhw);
|
||||
break;
|
||||
case ATC20K2:
|
||||
err = create_20k2_hw_obj(rhw);
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
break;
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
(*rhw)->pci = pci;
|
||||
(*rhw)->chip_type = chip_type;
|
||||
(*rhw)->model = model;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int destroy_hw_obj(struct hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (hw->pci->device) {
|
||||
case 0x0005: /* 20k1 device */
|
||||
err = destroy_20k1_hw_obj(hw);
|
||||
break;
|
||||
case 0x000B: /* 20k2 device */
|
||||
err = destroy_20k2_hw_obj(hw);
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
unsigned int get_field(unsigned int data, unsigned int field)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(!field);
|
||||
/* @field should always be greater than 0 */
|
||||
for (i = 0; !(field & (1 << i)); )
|
||||
i++;
|
||||
|
||||
return (data & field) >> i;
|
||||
}
|
||||
|
||||
void set_field(unsigned int *data, unsigned int field, unsigned int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(!field);
|
||||
/* @field should always be greater than 0 */
|
||||
for (i = 0; !(field & (1 << i)); )
|
||||
i++;
|
||||
|
||||
*data = (*data & (~field)) | ((value << i) & field);
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File cthardware.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTHARDWARE_H
|
||||
#define CTHARDWARE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
enum CHIPTYP {
|
||||
ATC20K1,
|
||||
ATC20K2,
|
||||
ATCNONE
|
||||
};
|
||||
|
||||
enum CTCARDS {
|
||||
/* 20k1 models */
|
||||
CTSB055X,
|
||||
CTSB073X,
|
||||
CTUAA,
|
||||
CT20K1_UNKNOWN,
|
||||
/* 20k2 models */
|
||||
CTSB0760,
|
||||
CTHENDRIX,
|
||||
CTSB0880,
|
||||
NUM_CTCARDS /* This should always be the last */
|
||||
};
|
||||
|
||||
/* Type of input source for ADC */
|
||||
enum ADCSRC{
|
||||
ADC_MICIN,
|
||||
ADC_LINEIN,
|
||||
ADC_VIDEO,
|
||||
ADC_AUX,
|
||||
ADC_NONE /* Switch to digital input */
|
||||
};
|
||||
|
||||
struct card_conf {
|
||||
/* device virtual mem page table page physical addr
|
||||
* (supporting one page table page now) */
|
||||
unsigned long vm_pgt_phys;
|
||||
unsigned int rsr; /* reference sample rate in Hzs*/
|
||||
unsigned int msr; /* master sample rate in rsrs */
|
||||
};
|
||||
|
||||
struct hw {
|
||||
int (*card_init)(struct hw *hw, struct card_conf *info);
|
||||
int (*card_stop)(struct hw *hw);
|
||||
int (*pll_init)(struct hw *hw, unsigned int rsr);
|
||||
int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
|
||||
int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
|
||||
int (*have_digit_io_switch)(struct hw *hw);
|
||||
|
||||
/* SRC operations */
|
||||
int (*src_rsc_get_ctrl_blk)(void **rblk);
|
||||
int (*src_rsc_put_ctrl_blk)(void *blk);
|
||||
int (*src_set_state)(void *blk, unsigned int state);
|
||||
int (*src_set_bm)(void *blk, unsigned int bm);
|
||||
int (*src_set_rsr)(void *blk, unsigned int rsr);
|
||||
int (*src_set_sf)(void *blk, unsigned int sf);
|
||||
int (*src_set_wr)(void *blk, unsigned int wr);
|
||||
int (*src_set_pm)(void *blk, unsigned int pm);
|
||||
int (*src_set_rom)(void *blk, unsigned int rom);
|
||||
int (*src_set_vo)(void *blk, unsigned int vo);
|
||||
int (*src_set_st)(void *blk, unsigned int st);
|
||||
int (*src_set_ie)(void *blk, unsigned int ie);
|
||||
int (*src_set_ilsz)(void *blk, unsigned int ilsz);
|
||||
int (*src_set_bp)(void *blk, unsigned int bp);
|
||||
int (*src_set_cisz)(void *blk, unsigned int cisz);
|
||||
int (*src_set_ca)(void *blk, unsigned int ca);
|
||||
int (*src_set_sa)(void *blk, unsigned int sa);
|
||||
int (*src_set_la)(void *blk, unsigned int la);
|
||||
int (*src_set_pitch)(void *blk, unsigned int pitch);
|
||||
int (*src_set_clear_zbufs)(void *blk, unsigned int clear);
|
||||
int (*src_set_dirty)(void *blk, unsigned int flags);
|
||||
int (*src_set_dirty_all)(void *blk);
|
||||
int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk);
|
||||
unsigned int (*src_get_dirty)(void *blk);
|
||||
unsigned int (*src_dirty_conj_mask)(void);
|
||||
int (*src_mgr_get_ctrl_blk)(void **rblk);
|
||||
int (*src_mgr_put_ctrl_blk)(void *blk);
|
||||
/* syncly enable src @idx */
|
||||
int (*src_mgr_enbs_src)(void *blk, unsigned int idx);
|
||||
/* enable src @idx */
|
||||
int (*src_mgr_enb_src)(void *blk, unsigned int idx);
|
||||
/* disable src @idx */
|
||||
int (*src_mgr_dsb_src)(void *blk, unsigned int idx);
|
||||
int (*src_mgr_commit_write)(struct hw *hw, void *blk);
|
||||
|
||||
/* SRC Input Mapper operations */
|
||||
int (*srcimp_mgr_get_ctrl_blk)(void **rblk);
|
||||
int (*srcimp_mgr_put_ctrl_blk)(void *blk);
|
||||
int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot);
|
||||
int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user);
|
||||
int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next);
|
||||
int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr);
|
||||
int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk);
|
||||
|
||||
/* AMIXER operations */
|
||||
int (*amixer_rsc_get_ctrl_blk)(void **rblk);
|
||||
int (*amixer_rsc_put_ctrl_blk)(void *blk);
|
||||
int (*amixer_mgr_get_ctrl_blk)(void **rblk);
|
||||
int (*amixer_mgr_put_ctrl_blk)(void *blk);
|
||||
int (*amixer_set_mode)(void *blk, unsigned int mode);
|
||||
int (*amixer_set_iv)(void *blk, unsigned int iv);
|
||||
int (*amixer_set_x)(void *blk, unsigned int x);
|
||||
int (*amixer_set_y)(void *blk, unsigned int y);
|
||||
int (*amixer_set_sadr)(void *blk, unsigned int sadr);
|
||||
int (*amixer_set_se)(void *blk, unsigned int se);
|
||||
int (*amixer_set_dirty)(void *blk, unsigned int flags);
|
||||
int (*amixer_set_dirty_all)(void *blk);
|
||||
int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*amixer_get_y)(void *blk);
|
||||
unsigned int (*amixer_get_dirty)(void *blk);
|
||||
|
||||
/* DAIO operations */
|
||||
int (*dai_get_ctrl_blk)(void **rblk);
|
||||
int (*dai_put_ctrl_blk)(void *blk);
|
||||
int (*dai_srt_set_srco)(void *blk, unsigned int src);
|
||||
int (*dai_srt_set_srcm)(void *blk, unsigned int src);
|
||||
int (*dai_srt_set_rsr)(void *blk, unsigned int rsr);
|
||||
int (*dai_srt_set_drat)(void *blk, unsigned int drat);
|
||||
int (*dai_srt_set_ec)(void *blk, unsigned int ec);
|
||||
int (*dai_srt_set_et)(void *blk, unsigned int et);
|
||||
int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*dao_get_ctrl_blk)(void **rblk);
|
||||
int (*dao_put_ctrl_blk)(void *blk);
|
||||
int (*dao_set_spos)(void *blk, unsigned int spos);
|
||||
int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*dao_get_spos)(void *blk, unsigned int *spos);
|
||||
|
||||
int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk);
|
||||
int (*daio_mgr_put_ctrl_blk)(void *blk);
|
||||
int (*daio_mgr_enb_dai)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_enb_dao)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_dao_init)(void *blk, unsigned int idx,
|
||||
unsigned int conf);
|
||||
int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot);
|
||||
int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next);
|
||||
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);
|
||||
unsigned int (*get_wc)(struct hw *hw);
|
||||
|
||||
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;
|
||||
unsigned long mem_base;
|
||||
|
||||
enum CHIPTYP chip_type;
|
||||
enum CTCARDS model;
|
||||
};
|
||||
|
||||
int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
|
||||
enum CTCARDS model, struct hw **rhw);
|
||||
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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File cthw20k1.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTHW20K1_H
|
||||
#define CTHW20K1_H
|
||||
|
||||
#include "cthardware.h"
|
||||
|
||||
int create_20k1_hw_obj(struct hw **rhw);
|
||||
int destroy_20k1_hw_obj(struct hw *hw);
|
||||
|
||||
#endif /* CTHW20K1_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File cthw20k2.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTHW20K2_H
|
||||
#define CTHW20K2_H
|
||||
|
||||
#include "cthardware.h"
|
||||
|
||||
int create_20k2_hw_obj(struct hw **rhw);
|
||||
int destroy_20k2_hw_obj(struct hw *hw);
|
||||
|
||||
#endif /* CTHW20K2_H */
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctimap.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of generic input mapper operations
|
||||
* for input mapper management.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctimap.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
int input_mapper_add(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data)
|
||||
{
|
||||
struct list_head *pos, *pre, *head;
|
||||
struct imapper *pre_ent, *pos_ent;
|
||||
|
||||
head = mappers;
|
||||
|
||||
if (list_empty(head)) {
|
||||
entry->next = entry->addr;
|
||||
map_op(data, entry);
|
||||
list_add(&entry->list, head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each(pos, head) {
|
||||
pos_ent = list_entry(pos, struct imapper, list);
|
||||
if (pos_ent->slot > entry->slot) {
|
||||
/* found a position in list */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != head) {
|
||||
pre = pos->prev;
|
||||
if (pre == head)
|
||||
pre = head->prev;
|
||||
|
||||
__list_add(&entry->list, pos->prev, pos);
|
||||
} else {
|
||||
pre = head->prev;
|
||||
pos = head->next;
|
||||
list_add_tail(&entry->list, head);
|
||||
}
|
||||
|
||||
pre_ent = list_entry(pre, struct imapper, list);
|
||||
pos_ent = list_entry(pos, struct imapper, list);
|
||||
|
||||
entry->next = pos_ent->addr;
|
||||
map_op(data, entry);
|
||||
pre_ent->next = entry->addr;
|
||||
map_op(data, pre_ent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data)
|
||||
{
|
||||
struct list_head *next, *pre, *head;
|
||||
struct imapper *pre_ent, *next_ent;
|
||||
|
||||
head = mappers;
|
||||
|
||||
if (list_empty(head))
|
||||
return 0;
|
||||
|
||||
pre = (entry->list.prev == head) ? head->prev : entry->list.prev;
|
||||
next = (entry->list.next == head) ? head->next : entry->list.next;
|
||||
|
||||
if (pre == &entry->list) {
|
||||
/* entry is the only one node in mappers list */
|
||||
entry->next = entry->addr = entry->user = entry->slot = 0;
|
||||
map_op(data, entry);
|
||||
list_del(&entry->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pre_ent = list_entry(pre, struct imapper, list);
|
||||
next_ent = list_entry(next, struct imapper, list);
|
||||
|
||||
pre_ent->next = next_ent->addr;
|
||||
map_op(data, pre_ent);
|
||||
list_del(&entry->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_input_mapper_list(struct list_head *head)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct list_head *pos;
|
||||
|
||||
while (!list_empty(head)) {
|
||||
pos = head->next;
|
||||
list_del(pos);
|
||||
entry = list_entry(pos, struct imapper, list);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctimap.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of generic input mapper operations
|
||||
* for input mapper management.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTIMAP_H
|
||||
#define CTIMAP_H
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
struct imapper {
|
||||
unsigned short slot; /* the id of the slot containing input data */
|
||||
unsigned short user; /* the id of the user resource consuming data */
|
||||
unsigned short addr; /* the input mapper ram id */
|
||||
unsigned short next; /* the next input mapper ram id */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int input_mapper_add(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data);
|
||||
|
||||
int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data);
|
||||
|
||||
void free_input_mapper_list(struct list_head *mappers);
|
||||
|
||||
#endif /* CTIMAP_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctmixer.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the mixer device functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTMIXER_H
|
||||
#define CTMIXER_H
|
||||
|
||||
#include "ctatc.h"
|
||||
#include "ctresource.h"
|
||||
|
||||
#define INIT_VOL 0x1c00
|
||||
|
||||
enum MIXER_PORT_T {
|
||||
MIX_WAVE_FRONT,
|
||||
MIX_WAVE_REAR,
|
||||
MIX_WAVE_CENTLFE,
|
||||
MIX_WAVE_SURROUND,
|
||||
MIX_SPDIF_OUT,
|
||||
MIX_PCMO_FRONT,
|
||||
MIX_MIC_IN,
|
||||
MIX_LINE_IN,
|
||||
MIX_SPDIF_IN,
|
||||
MIX_PCMI_FRONT,
|
||||
MIX_PCMI_REAR,
|
||||
MIX_PCMI_CENTLFE,
|
||||
MIX_PCMI_SURROUND,
|
||||
|
||||
NUM_MIX_PORTS
|
||||
};
|
||||
|
||||
/* alsa mixer descriptor */
|
||||
struct ct_mixer {
|
||||
struct ct_atc *atc;
|
||||
|
||||
void **amixers; /* amixer resources for volume control */
|
||||
void **sums; /* sum resources for signal collection */
|
||||
unsigned int switch_state; /* A bit-map to indicate state of switches */
|
||||
|
||||
int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type,
|
||||
struct rsc **rleft, struct rsc **rright);
|
||||
|
||||
int (*set_input_left)(struct ct_mixer *mixer,
|
||||
enum MIXER_PORT_T type, struct rsc *rsc);
|
||||
int (*set_input_right)(struct ct_mixer *mixer,
|
||||
enum MIXER_PORT_T type, struct rsc *rsc);
|
||||
};
|
||||
|
||||
int ct_alsa_mix_create(struct ct_atc *atc,
|
||||
enum CTALSADEVS device,
|
||||
const char *device_name);
|
||||
int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer);
|
||||
int ct_mixer_destroy(struct ct_mixer *mixer);
|
||||
|
||||
#endif /* CTMIXER_H */
|
|
@ -0,0 +1,426 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctpcm.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the pcm device functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Apr 2 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctpcm.h"
|
||||
#include "cttimer.h"
|
||||
#include <sound/pcm.h>
|
||||
|
||||
/* Hardware descriptions for playback */
|
||||
static struct snd_pcm_hardware ct_pcm_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.formats = (SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_3LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_FLOAT_LE),
|
||||
.rates = (SNDRV_PCM_RATE_CONTINUOUS |
|
||||
SNDRV_PCM_RATE_8000_192000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = (64),
|
||||
.period_bytes_max = (128*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_32000),
|
||||
.rate_min = 32000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = (64),
|
||||
.period_bytes_max = (128*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Hardware descriptions for capture */
|
||||
static struct snd_pcm_hardware ct_pcm_capture_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = (SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_3LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_FLOAT_LE),
|
||||
.rates = (SNDRV_PCM_RATE_CONTINUOUS |
|
||||
SNDRV_PCM_RATE_8000_96000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = (384),
|
||||
.period_bytes_max = (64*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
|
||||
{
|
||||
struct ct_atc_pcm *apcm = atc_pcm;
|
||||
|
||||
if (NULL == apcm->substream)
|
||||
return;
|
||||
|
||||
snd_pcm_period_elapsed(apcm->substream);
|
||||
}
|
||||
|
||||
static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
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;
|
||||
}
|
||||
|
||||
/* pcm playback operations */
|
||||
static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm;
|
||||
int err;
|
||||
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (NULL == apcm)
|
||||
return -ENOMEM;
|
||||
|
||||
apcm->substream = substream;
|
||||
apcm->interrupt = ct_atc_pcm_interrupt;
|
||||
runtime->private_data = apcm;
|
||||
runtime->private_free = ct_atc_pcm_free_substream;
|
||||
if (IEC958 == substream->pcm->device) {
|
||||
runtime->hw = ct_spdif_passthru_playback_hw;
|
||||
atc->spdif_out_passthru(atc, 1);
|
||||
} else {
|
||||
runtime->hw = ct_pcm_playback_hw;
|
||||
if (FRONT == substream->pcm->device)
|
||||
runtime->hw.channels_max = 8;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
1024, UINT_MAX);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
|
||||
if (!apcm->timer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
|
||||
/* TODO: Notify mixer inactive. */
|
||||
if (IEC958 == substream->pcm->device)
|
||||
atc->spdif_out_passthru(atc, 0);
|
||||
|
||||
/* The ct_atc_pcm object will be freed by runtime->private_free */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct ct_atc_pcm *apcm = substream->runtime->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* clear previous resources */
|
||||
atc->pcm_release_resources(atc, apcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct ct_atc_pcm *apcm = substream->runtime->private_data;
|
||||
|
||||
/* clear previous resources */
|
||||
atc->pcm_release_resources(atc, apcm);
|
||||
/* Free snd-allocated pages */
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
|
||||
static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int err;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
if (IEC958 == substream->pcm->device)
|
||||
err = atc->spdif_passthru_playback_prepare(atc, apcm);
|
||||
else
|
||||
err = atc->pcm_playback_prepare(atc, apcm);
|
||||
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
atc->pcm_playback_start(atc, apcm);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
atc->pcm_playback_stop(atc, apcm);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
unsigned long position;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
/* Read out playback position */
|
||||
position = atc->pcm_playback_position(atc, apcm);
|
||||
position = bytes_to_frames(runtime, position);
|
||||
if (position >= runtime->buffer_size)
|
||||
position = 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
/* pcm capture operations */
|
||||
static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm;
|
||||
int err;
|
||||
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (NULL == apcm)
|
||||
return -ENOMEM;
|
||||
|
||||
apcm->started = 0;
|
||||
apcm->substream = substream;
|
||||
apcm->interrupt = ct_atc_pcm_interrupt;
|
||||
runtime->private_data = apcm;
|
||||
runtime->private_free = ct_atc_pcm_free_substream;
|
||||
runtime->hw = ct_pcm_capture_hw;
|
||||
runtime->hw.rate_max = atc->rsr * atc->msr;
|
||||
|
||||
err = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
1024, UINT_MAX);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
|
||||
if (!apcm->timer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/* The ct_atc_pcm object will be freed by runtime->private_free */
|
||||
/* TODO: Notify mixer inactive. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int err;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
err = atc->pcm_capture_prepare(atc, apcm);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
atc->pcm_capture_start(atc, apcm);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
atc->pcm_capture_stop(atc, apcm);
|
||||
break;
|
||||
default:
|
||||
atc->pcm_capture_stop(atc, apcm);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
unsigned long position;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
/* Read out playback position */
|
||||
position = atc->pcm_capture_position(atc, apcm);
|
||||
position = bytes_to_frames(runtime, position);
|
||||
if (position >= runtime->buffer_size)
|
||||
position = 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
/* PCM operators for playback */
|
||||
static struct snd_pcm_ops ct_pcm_playback_ops = {
|
||||
.open = ct_pcm_playback_open,
|
||||
.close = ct_pcm_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = ct_pcm_hw_params,
|
||||
.hw_free = ct_pcm_hw_free,
|
||||
.prepare = ct_pcm_playback_prepare,
|
||||
.trigger = ct_pcm_playback_trigger,
|
||||
.pointer = ct_pcm_playback_pointer,
|
||||
.page = snd_pcm_sgbuf_ops_page,
|
||||
};
|
||||
|
||||
/* PCM operators for capture */
|
||||
static struct snd_pcm_ops ct_pcm_capture_ops = {
|
||||
.open = ct_pcm_capture_open,
|
||||
.close = ct_pcm_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = ct_pcm_hw_params,
|
||||
.hw_free = ct_pcm_hw_free,
|
||||
.prepare = ct_pcm_capture_prepare,
|
||||
.trigger = ct_pcm_capture_trigger,
|
||||
.pointer = ct_pcm_capture_pointer,
|
||||
.page = snd_pcm_sgbuf_ops_page,
|
||||
};
|
||||
|
||||
/* Create ALSA pcm device */
|
||||
int ct_alsa_pcm_create(struct ct_atc *atc,
|
||||
enum CTALSADEVS device,
|
||||
const char *device_name)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
int playback_count, capture_count;
|
||||
|
||||
playback_count = (IEC958 == device) ? 1 : 8;
|
||||
capture_count = (FRONT == device) ? 1 : 0;
|
||||
err = snd_pcm_new(atc->card, "ctxfi", device,
|
||||
playback_count, capture_count, &pcm);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
pcm->private_data = atc;
|
||||
pcm->info_flags = 0;
|
||||
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
|
||||
strlcpy(pcm->name, device_name, sizeof(pcm->name));
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
|
||||
|
||||
if (FRONT == device)
|
||||
snd_pcm_set_ops(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
|
||||
snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctpcm.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the pcm device functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTPCM_H
|
||||
#define CTPCM_H
|
||||
|
||||
#include "ctatc.h"
|
||||
|
||||
int ct_alsa_pcm_create(struct ct_atc *atc,
|
||||
enum CTALSADEVS device,
|
||||
const char *device_name);
|
||||
|
||||
#endif /* CTPCM_H */
|
|
@ -0,0 +1,301 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctresource.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of some generic helper functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 15 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctresource.h"
|
||||
#include "cthardware.h"
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define AUDIO_SLOT_BLOCK_NUM 256
|
||||
|
||||
/* Resource allocation based on bit-map management mechanism */
|
||||
static int
|
||||
get_resource(u8 *rscs, unsigned int amount,
|
||||
unsigned int multi, unsigned int *ridx)
|
||||
{
|
||||
int i, j, k, n;
|
||||
|
||||
/* Check whether there are sufficient resources to meet request. */
|
||||
for (i = 0, n = multi; i < amount; i++) {
|
||||
j = i / 8;
|
||||
k = i % 8;
|
||||
if (rscs[j] & ((u8)1 << k)) {
|
||||
n = multi;
|
||||
continue;
|
||||
}
|
||||
if (!(--n))
|
||||
break; /* found sufficient contiguous resources */
|
||||
}
|
||||
|
||||
if (i >= amount) {
|
||||
/* Can not find sufficient contiguous resources */
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Mark the contiguous bits in resource bit-map as used */
|
||||
for (n = multi; n > 0; n--) {
|
||||
j = i / 8;
|
||||
k = i % 8;
|
||||
rscs[j] |= ((u8)1 << k);
|
||||
i--;
|
||||
}
|
||||
|
||||
*ridx = i + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx)
|
||||
{
|
||||
unsigned int i, j, k, n;
|
||||
|
||||
/* Mark the contiguous bits in resource bit-map as used */
|
||||
for (n = multi, i = idx; n > 0; n--) {
|
||||
j = i / 8;
|
||||
k = i % 8;
|
||||
rscs[j] &= ~((u8)1 << k);
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (n > mgr->avail)
|
||||
return -ENOENT;
|
||||
|
||||
err = get_resource(mgr->rscs, mgr->amount, n, ridx);
|
||||
if (!err)
|
||||
mgr->avail -= n;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
|
||||
{
|
||||
put_resource(mgr->rscs, n, idx);
|
||||
mgr->avail += n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
|
||||
/* SRC channel is at Audio Ring slot 1 every 16 slots. */
|
||||
[SRC] = 0x1,
|
||||
[AMIXER] = 0x4,
|
||||
[SUM] = 0xc,
|
||||
};
|
||||
|
||||
static int rsc_index(const struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj;
|
||||
}
|
||||
|
||||
static int audio_ring_slot(const struct rsc *rsc)
|
||||
{
|
||||
return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
|
||||
}
|
||||
|
||||
static int rsc_next_conj(struct rsc *rsc)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
|
||||
i++;
|
||||
rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
|
||||
return rsc->conj;
|
||||
}
|
||||
|
||||
static int rsc_master(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj = rsc->idx;
|
||||
}
|
||||
|
||||
static struct rsc_ops rsc_generic_ops = {
|
||||
.index = rsc_index,
|
||||
.output_slot = audio_ring_slot,
|
||||
.master = rsc_master,
|
||||
.next_conj = rsc_next_conj,
|
||||
};
|
||||
|
||||
int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
rsc->idx = idx;
|
||||
rsc->conj = idx;
|
||||
rsc->type = type;
|
||||
rsc->msr = msr;
|
||||
rsc->hw = hw;
|
||||
rsc->ops = &rsc_generic_ops;
|
||||
if (NULL == hw) {
|
||||
rsc->ctrl_blk = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SRC:
|
||||
err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
err = ((struct hw *)hw)->
|
||||
amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
|
||||
break;
|
||||
case SRCIMP:
|
||||
case SUM:
|
||||
case DAIO:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Invalid resource type value %d!\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Failed to get resource control block!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsc_uninit(struct rsc *rsc)
|
||||
{
|
||||
if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
|
||||
switch (rsc->type) {
|
||||
case SRC:
|
||||
((struct hw *)rsc->hw)->
|
||||
src_rsc_put_ctrl_blk(rsc->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
((struct hw *)rsc->hw)->
|
||||
amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
|
||||
break;
|
||||
case SUM:
|
||||
case DAIO:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "ctxfi: "
|
||||
"Invalid resource type value %d!\n", rsc->type);
|
||||
break;
|
||||
}
|
||||
|
||||
rsc->hw = rsc->ctrl_blk = NULL;
|
||||
}
|
||||
|
||||
rsc->idx = rsc->conj = 0;
|
||||
rsc->type = NUM_RSCTYP;
|
||||
rsc->msr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
|
||||
unsigned int amount, void *hw_obj)
|
||||
{
|
||||
int err = 0;
|
||||
struct hw *hw = hw_obj;
|
||||
|
||||
mgr->type = NUM_RSCTYP;
|
||||
|
||||
mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
|
||||
if (NULL == mgr->rscs)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (type) {
|
||||
case SRC:
|
||||
err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk);
|
||||
break;
|
||||
case SRCIMP:
|
||||
err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk);
|
||||
break;
|
||||
case DAIO:
|
||||
err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk);
|
||||
break;
|
||||
case SUM:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Invalid resource type value %d!\n", type);
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Failed to get manager control block!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mgr->type = type;
|
||||
mgr->avail = mgr->amount = amount;
|
||||
mgr->hw = hw;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(mgr->rscs);
|
||||
return err;
|
||||
}
|
||||
|
||||
int rsc_mgr_uninit(struct rsc_mgr *mgr)
|
||||
{
|
||||
if (NULL != mgr->rscs) {
|
||||
kfree(mgr->rscs);
|
||||
mgr->rscs = NULL;
|
||||
}
|
||||
|
||||
if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
|
||||
switch (mgr->type) {
|
||||
case SRC:
|
||||
((struct hw *)mgr->hw)->
|
||||
src_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case SRCIMP:
|
||||
((struct hw *)mgr->hw)->
|
||||
srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
((struct hw *)mgr->hw)->
|
||||
amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case DAIO:
|
||||
((struct hw *)mgr->hw)->
|
||||
daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case SUM:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "ctxfi: "
|
||||
"Invalid resource type value %d!\n", mgr->type);
|
||||
break;
|
||||
}
|
||||
|
||||
mgr->hw = mgr->ctrl_blk = NULL;
|
||||
}
|
||||
|
||||
mgr->type = NUM_RSCTYP;
|
||||
mgr->avail = mgr->amount = 0;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctresource.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of generic hardware resources for
|
||||
* resource management.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTRESOURCE_H
|
||||
#define CTRESOURCE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum RSCTYP {
|
||||
SRC,
|
||||
SRCIMP,
|
||||
AMIXER,
|
||||
SUM,
|
||||
DAIO,
|
||||
NUM_RSCTYP /* This must be the last one and less than 16 */
|
||||
};
|
||||
|
||||
struct rsc_ops;
|
||||
|
||||
struct rsc {
|
||||
u32 idx:12; /* The index of a resource */
|
||||
u32 type:4; /* The type (RSCTYP) of a resource */
|
||||
u32 conj:12; /* Current conjugate index */
|
||||
u32 msr:4; /* The Master Sample Rate a resource working on */
|
||||
void *ctrl_blk; /* Chip specific control info block for a resource */
|
||||
void *hw; /* Chip specific object for hardware access means */
|
||||
struct rsc_ops *ops; /* Generic resource operations */
|
||||
};
|
||||
|
||||
struct rsc_ops {
|
||||
int (*master)(struct rsc *rsc); /* Move to master resource */
|
||||
int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
|
||||
int (*index)(const struct rsc *rsc); /* Return the index of resource */
|
||||
/* Return the output slot number */
|
||||
int (*output_slot)(const struct rsc *rsc);
|
||||
};
|
||||
|
||||
int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
|
||||
int rsc_uninit(struct rsc *rsc);
|
||||
|
||||
struct rsc_mgr {
|
||||
enum RSCTYP type; /* The type (RSCTYP) of resource to manage */
|
||||
unsigned int amount; /* The total amount of a kind of resource */
|
||||
unsigned int avail; /* The amount of currently available resources */
|
||||
unsigned char *rscs; /* The bit-map for resource allocation */
|
||||
void *ctrl_blk; /* Chip specific control info block */
|
||||
void *hw; /* Chip specific object for hardware access */
|
||||
};
|
||||
|
||||
/* Resource management is based on bit-map mechanism */
|
||||
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
|
||||
unsigned int amount, void *hw);
|
||||
int rsc_mgr_uninit(struct rsc_mgr *mgr);
|
||||
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
|
||||
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
|
||||
|
||||
#endif /* CTRESOURCE_H */
|
|
@ -0,0 +1,886 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctsrc.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of the Sample Rate Convertor
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctsrc.h"
|
||||
#include "cthardware.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define SRC_RESOURCE_NUM 64
|
||||
#define SRCIMP_RESOURCE_NUM 256
|
||||
|
||||
static unsigned int conj_mask;
|
||||
|
||||
static int src_default_config_memrd(struct src *src);
|
||||
static int src_default_config_memwr(struct src *src);
|
||||
static int src_default_config_arcrw(struct src *src);
|
||||
|
||||
static int (*src_default_config[3])(struct src *) = {
|
||||
[MEMRD] = src_default_config_memrd,
|
||||
[MEMWR] = src_default_config_memwr,
|
||||
[ARCRW] = src_default_config_arcrw
|
||||
};
|
||||
|
||||
static int src_set_state(struct src *src, unsigned int state)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_state(src->rsc.ctrl_blk, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_bm(struct src *src, unsigned int bm)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, bm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_sf(struct src *src, unsigned int sf)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, sf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_pm(struct src *src, unsigned int pm)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, pm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_rom(struct src *src, unsigned int rom)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, rom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_vo(struct src *src, unsigned int vo)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, vo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_st(struct src *src, unsigned int st)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_st(src->rsc.ctrl_blk, st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_bp(struct src *src, unsigned int bp)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_bp(src->rsc.ctrl_blk, bp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_cisz(struct src *src, unsigned int cisz)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, cisz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_ca(struct src *src, unsigned int ca)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, ca);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_sa(struct src *src, unsigned int sa)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, sa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_la(struct src *src, unsigned int la)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_la(src->rsc.ctrl_blk, la);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_pitch(struct src *src, unsigned int pitch)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, pitch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_clear_zbufs(struct src *src)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_commit_write(struct src *src)
|
||||
{
|
||||
struct hw *hw;
|
||||
int i;
|
||||
unsigned int dirty = 0;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
if (src->rsc.msr > 1) {
|
||||
/* Save dirty flags for conjugate resource programming */
|
||||
dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask;
|
||||
}
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
|
||||
/* Program conjugate parameter mixer resources */
|
||||
if (MEMWR == src->mode)
|
||||
return 0;
|
||||
|
||||
for (i = 1; i < src->rsc.msr; i++) {
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_get_ca(struct src *src)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
}
|
||||
|
||||
static int src_init(struct src *src)
|
||||
{
|
||||
src_default_config[src->mode](src);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct src *src_next_interleave(struct src *src)
|
||||
{
|
||||
return src->intlv;
|
||||
}
|
||||
|
||||
static int src_default_config_memrd(struct src *src)
|
||||
{
|
||||
struct hw *hw = src->rsc.hw;
|
||||
unsigned int rsr, msr;
|
||||
|
||||
hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, 1);
|
||||
for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
|
||||
hw->src_set_wr(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_st(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1);
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
|
||||
hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
|
||||
for (msr = 1; msr < src->rsc.msr; msr++) {
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_default_config_memwr(struct src *src)
|
||||
{
|
||||
struct hw *hw = src->rsc.hw;
|
||||
|
||||
hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, 1);
|
||||
hw->src_set_rsr(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
|
||||
hw->src_set_wr(src->rsc.ctrl_blk, 1);
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_st(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
|
||||
hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_default_config_arcrw(struct src *src)
|
||||
{
|
||||
struct hw *hw = src->rsc.hw;
|
||||
unsigned int rsr, msr;
|
||||
unsigned int dirty;
|
||||
|
||||
hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, 0);
|
||||
for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32);
|
||||
hw->src_set_wr(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_st(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
|
||||
/*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/
|
||||
hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
|
||||
/*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
dirty = hw->src_get_dirty(src->rsc.ctrl_blk);
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (msr = 0; msr < src->rsc.msr; msr++) {
|
||||
hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct src_rsc_ops src_rsc_ops = {
|
||||
.set_state = src_set_state,
|
||||
.set_bm = src_set_bm,
|
||||
.set_sf = src_set_sf,
|
||||
.set_pm = src_set_pm,
|
||||
.set_rom = src_set_rom,
|
||||
.set_vo = src_set_vo,
|
||||
.set_st = src_set_st,
|
||||
.set_bp = src_set_bp,
|
||||
.set_cisz = src_set_cisz,
|
||||
.set_ca = src_set_ca,
|
||||
.set_sa = src_set_sa,
|
||||
.set_la = src_set_la,
|
||||
.set_pitch = src_set_pitch,
|
||||
.set_clr_zbufs = src_set_clear_zbufs,
|
||||
.commit_write = src_commit_write,
|
||||
.get_ca = src_get_ca,
|
||||
.init = src_init,
|
||||
.next_interleave = src_next_interleave,
|
||||
};
|
||||
|
||||
static int
|
||||
src_rsc_init(struct src *src, u32 idx,
|
||||
const struct src_desc *desc, struct src_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
int i, n;
|
||||
struct src *p;
|
||||
|
||||
n = (MEMRD == desc->mode) ? desc->multi : 1;
|
||||
for (i = 0, p = src; i < n; i++, p++) {
|
||||
err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
/* Initialize src specific rsc operations */
|
||||
p->ops = &src_rsc_ops;
|
||||
p->multi = (0 == i) ? desc->multi : 1;
|
||||
p->mode = desc->mode;
|
||||
src_default_config[desc->mode](p);
|
||||
mgr->src_enable(mgr, p);
|
||||
p->intlv = p + 1;
|
||||
}
|
||||
(--p)->intlv = NULL; /* Set @intlv of the last SRC to NULL */
|
||||
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
for (i--, p--; i >= 0; i--, p--) {
|
||||
mgr->src_disable(mgr, p);
|
||||
rsc_uninit(&p->rsc);
|
||||
}
|
||||
mgr->commit_write(mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int src_rsc_uninit(struct src *src, struct src_mgr *mgr)
|
||||
{
|
||||
int i, n;
|
||||
struct src *p;
|
||||
|
||||
n = (MEMRD == src->mode) ? src->multi : 1;
|
||||
for (i = 0, p = src; i < n; i++, p++) {
|
||||
mgr->src_disable(mgr, p);
|
||||
rsc_uninit(&p->rsc);
|
||||
p->multi = 0;
|
||||
p->ops = NULL;
|
||||
p->mode = NUM_SRCMODES;
|
||||
p->intlv = NULL;
|
||||
}
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
|
||||
{
|
||||
unsigned int idx = SRC_RESOURCE_NUM;
|
||||
int err;
|
||||
struct src *src;
|
||||
unsigned long flags;
|
||||
|
||||
*rsrc = NULL;
|
||||
|
||||
/* Check whether there are sufficient src resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
if (MEMRD == desc->mode)
|
||||
err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
|
||||
else
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Allocate mem for master src resource */
|
||||
if (MEMRD == desc->mode)
|
||||
src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
|
||||
else
|
||||
src = kzalloc(sizeof(*src), GFP_KERNEL);
|
||||
|
||||
if (NULL == src) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
err = src_rsc_init(src, idx, desc, mgr);
|
||||
if (err)
|
||||
goto error2;
|
||||
|
||||
*rsrc = src;
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
kfree(src);
|
||||
error1:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
if (MEMRD == desc->mode)
|
||||
mgr_put_resource(&mgr->mgr, desc->multi, idx);
|
||||
else
|
||||
mgr_put_resource(&mgr->mgr, 1, idx);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_src_rsc(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
if (MEMRD == src->mode)
|
||||
mgr_put_resource(&mgr->mgr, src->multi,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
else
|
||||
mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
src_rsc_uninit(src, mgr);
|
||||
kfree(src);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_enable_s(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
int i;
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (i = 0; i < src->rsc.msr; i++) {
|
||||
hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_enable(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
int i;
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (i = 0; i < src->rsc.msr; i++) {
|
||||
hw->src_mgr_enb_src(mgr->mgr.ctrl_blk,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_disable(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
int i;
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (i = 0; i < src->rsc.msr; i++) {
|
||||
hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_mgr_commit_write(struct src_mgr *mgr)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
|
||||
{
|
||||
int err, i;
|
||||
struct src_mgr *src_mgr;
|
||||
|
||||
*rsrc_mgr = NULL;
|
||||
src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
|
||||
if (NULL == src_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
spin_lock_init(&src_mgr->mgr_lock);
|
||||
conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
|
||||
|
||||
src_mgr->get_src = get_src_rsc;
|
||||
src_mgr->put_src = put_src_rsc;
|
||||
src_mgr->src_enable_s = src_enable_s;
|
||||
src_mgr->src_enable = src_enable;
|
||||
src_mgr->src_disable = src_disable;
|
||||
src_mgr->commit_write = src_mgr_commit_write;
|
||||
|
||||
/* Disable all SRC resources. */
|
||||
for (i = 0; i < 256; i++)
|
||||
((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
|
||||
|
||||
((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
|
||||
|
||||
*rsrc_mgr = src_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
kfree(src_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int src_mgr_destroy(struct src_mgr *src_mgr)
|
||||
{
|
||||
rsc_mgr_uninit(&src_mgr->mgr);
|
||||
kfree(src_mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SRCIMP resource manager operations */
|
||||
|
||||
static int srcimp_master(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj = 0;
|
||||
return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
|
||||
}
|
||||
|
||||
static int srcimp_next_conj(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj++;
|
||||
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int srcimp_index(const struct rsc *rsc)
|
||||
{
|
||||
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static struct rsc_ops srcimp_basic_rsc_ops = {
|
||||
.master = srcimp_master,
|
||||
.next_conj = srcimp_next_conj,
|
||||
.index = srcimp_index,
|
||||
.output_slot = NULL,
|
||||
};
|
||||
|
||||
static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input)
|
||||
{
|
||||
struct imapper *entry;
|
||||
int i;
|
||||
|
||||
srcimp->rsc.ops->master(&srcimp->rsc);
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
input->ops->master(input);
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
for (i = 0; i < srcimp->rsc.msr; i++) {
|
||||
entry = &srcimp->imappers[i];
|
||||
entry->slot = input->ops->output_slot(input);
|
||||
entry->user = src->rsc.ops->index(&src->rsc);
|
||||
entry->addr = srcimp->rsc.ops->index(&srcimp->rsc);
|
||||
srcimp->mgr->imap_add(srcimp->mgr, entry);
|
||||
srcimp->mapped |= (0x1 << i);
|
||||
|
||||
srcimp->rsc.ops->next_conj(&srcimp->rsc);
|
||||
input->ops->next_conj(input);
|
||||
}
|
||||
|
||||
srcimp->rsc.ops->master(&srcimp->rsc);
|
||||
input->ops->master(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int srcimp_unmap(struct srcimp *srcimp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
for (i = 0; i < srcimp->rsc.msr; i++) {
|
||||
if (srcimp->mapped & (0x1 << i)) {
|
||||
srcimp->mgr->imap_delete(srcimp->mgr,
|
||||
&srcimp->imappers[i]);
|
||||
srcimp->mapped &= ~(0x1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct srcimp_rsc_ops srcimp_ops = {
|
||||
.map = srcimp_map,
|
||||
.unmap = srcimp_unmap
|
||||
};
|
||||
|
||||
static int srcimp_rsc_init(struct srcimp *srcimp,
|
||||
const struct srcimp_desc *desc,
|
||||
struct srcimp_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rsc_init(&srcimp->rsc, srcimp->idx[0],
|
||||
SRCIMP, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Reserve memory for imapper nodes */
|
||||
srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
|
||||
GFP_KERNEL);
|
||||
if (NULL == srcimp->imappers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
/* Set srcimp specific operations */
|
||||
srcimp->rsc.ops = &srcimp_basic_rsc_ops;
|
||||
srcimp->ops = &srcimp_ops;
|
||||
srcimp->mgr = mgr;
|
||||
|
||||
srcimp->rsc.ops->master(&srcimp->rsc);
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
rsc_uninit(&srcimp->rsc);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int srcimp_rsc_uninit(struct srcimp *srcimp)
|
||||
{
|
||||
if (NULL != srcimp->imappers) {
|
||||
kfree(srcimp->imappers);
|
||||
srcimp->imappers = NULL;
|
||||
}
|
||||
srcimp->ops = NULL;
|
||||
srcimp->mgr = NULL;
|
||||
rsc_uninit(&srcimp->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_srcimp_rsc(struct srcimp_mgr *mgr,
|
||||
const struct srcimp_desc *desc,
|
||||
struct srcimp **rsrcimp)
|
||||
{
|
||||
int err, i;
|
||||
unsigned int idx;
|
||||
struct srcimp *srcimp;
|
||||
unsigned long flags;
|
||||
|
||||
*rsrcimp = NULL;
|
||||
|
||||
/* Allocate mem for SRCIMP resource */
|
||||
srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL);
|
||||
if (NULL == srcimp) {
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check whether there are sufficient SRCIMP resources. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < desc->msr; i++) {
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
srcimp->idx[i] = idx;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
|
||||
goto error1;
|
||||
}
|
||||
|
||||
err = srcimp_rsc_init(srcimp, desc, mgr);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
*rsrcimp = srcimp;
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i--; i >= 0; i--)
|
||||
mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
kfree(srcimp);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < srcimp->rsc.msr; i++)
|
||||
mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
srcimp_rsc_uninit(srcimp);
|
||||
kfree(srcimp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int srcimp_map_op(void *data, struct imapper *entry)
|
||||
{
|
||||
struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr;
|
||||
struct hw *hw = mgr->hw;
|
||||
|
||||
hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
|
||||
hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user);
|
||||
hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
|
||||
hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
|
||||
hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
if ((0 == entry->addr) && (mgr->init_imap_added)) {
|
||||
input_mapper_delete(&mgr->imappers,
|
||||
mgr->init_imap, srcimp_map_op, mgr);
|
||||
mgr->init_imap_added = 0;
|
||||
}
|
||||
err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
|
||||
if (list_empty(&mgr->imappers)) {
|
||||
input_mapper_add(&mgr->imappers, mgr->init_imap,
|
||||
srcimp_map_op, mgr);
|
||||
mgr->init_imap_added = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
|
||||
{
|
||||
int err;
|
||||
struct srcimp_mgr *srcimp_mgr;
|
||||
struct imapper *entry;
|
||||
|
||||
*rsrcimp_mgr = NULL;
|
||||
srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
|
||||
if (NULL == srcimp_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
spin_lock_init(&srcimp_mgr->mgr_lock);
|
||||
spin_lock_init(&srcimp_mgr->imap_lock);
|
||||
INIT_LIST_HEAD(&srcimp_mgr->imappers);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (NULL == entry) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
entry->slot = entry->addr = entry->next = entry->user = 0;
|
||||
list_add(&entry->list, &srcimp_mgr->imappers);
|
||||
srcimp_mgr->init_imap = entry;
|
||||
srcimp_mgr->init_imap_added = 1;
|
||||
|
||||
srcimp_mgr->get_srcimp = get_srcimp_rsc;
|
||||
srcimp_mgr->put_srcimp = put_srcimp_rsc;
|
||||
srcimp_mgr->imap_add = srcimp_imap_add;
|
||||
srcimp_mgr->imap_delete = srcimp_imap_delete;
|
||||
|
||||
*rsrcimp_mgr = srcimp_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
rsc_mgr_uninit(&srcimp_mgr->mgr);
|
||||
error1:
|
||||
kfree(srcimp_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* free src input mapper list */
|
||||
spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
|
||||
free_input_mapper_list(&srcimp_mgr->imappers);
|
||||
spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
|
||||
|
||||
rsc_mgr_uninit(&srcimp_mgr->mgr);
|
||||
kfree(srcimp_mgr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctsrc.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the Sample Rate Convertor
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTSRC_H
|
||||
#define CTSRC_H
|
||||
|
||||
#include "ctresource.h"
|
||||
#include "ctimap.h"
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define SRC_STATE_OFF 0x0
|
||||
#define SRC_STATE_INIT 0x4
|
||||
#define SRC_STATE_RUN 0x5
|
||||
|
||||
#define SRC_SF_U8 0x0
|
||||
#define SRC_SF_S16 0x1
|
||||
#define SRC_SF_S24 0x2
|
||||
#define SRC_SF_S32 0x3
|
||||
#define SRC_SF_F32 0x4
|
||||
|
||||
/* Define the descriptor of a src resource */
|
||||
enum SRCMODE {
|
||||
MEMRD, /* Read data from host memory */
|
||||
MEMWR, /* Write data to host memory */
|
||||
ARCRW, /* Read from and write to audio ring channel */
|
||||
NUM_SRCMODES
|
||||
};
|
||||
|
||||
struct src_rsc_ops;
|
||||
|
||||
struct src {
|
||||
struct rsc rsc; /* Basic resource info */
|
||||
struct src *intlv; /* Pointer to next interleaved SRC in a series */
|
||||
struct src_rsc_ops *ops; /* SRC specific operations */
|
||||
/* Number of contiguous srcs for interleaved usage */
|
||||
unsigned char multi;
|
||||
unsigned char mode; /* Working mode of this SRC resource */
|
||||
};
|
||||
|
||||
struct src_rsc_ops {
|
||||
int (*set_state)(struct src *src, unsigned int state);
|
||||
int (*set_bm)(struct src *src, unsigned int bm);
|
||||
int (*set_sf)(struct src *src, unsigned int sf);
|
||||
int (*set_pm)(struct src *src, unsigned int pm);
|
||||
int (*set_rom)(struct src *src, unsigned int rom);
|
||||
int (*set_vo)(struct src *src, unsigned int vo);
|
||||
int (*set_st)(struct src *src, unsigned int st);
|
||||
int (*set_bp)(struct src *src, unsigned int bp);
|
||||
int (*set_cisz)(struct src *src, unsigned int cisz);
|
||||
int (*set_ca)(struct src *src, unsigned int ca);
|
||||
int (*set_sa)(struct src *src, unsigned int sa);
|
||||
int (*set_la)(struct src *src, unsigned int la);
|
||||
int (*set_pitch)(struct src *src, unsigned int pitch);
|
||||
int (*set_clr_zbufs)(struct src *src);
|
||||
int (*commit_write)(struct src *src);
|
||||
int (*get_ca)(struct src *src);
|
||||
int (*init)(struct src *src);
|
||||
struct src* (*next_interleave)(struct src *src);
|
||||
};
|
||||
|
||||
/* Define src resource request description info */
|
||||
struct src_desc {
|
||||
/* Number of contiguous master srcs for interleaved usage */
|
||||
unsigned char multi;
|
||||
unsigned char msr;
|
||||
unsigned char mode; /* Working mode of the requested srcs */
|
||||
};
|
||||
|
||||
/* Define src manager object */
|
||||
struct src_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
|
||||
/* request src resource */
|
||||
int (*get_src)(struct src_mgr *mgr,
|
||||
const struct src_desc *desc, struct src **rsrc);
|
||||
/* return src resource */
|
||||
int (*put_src)(struct src_mgr *mgr, struct src *src);
|
||||
int (*src_enable_s)(struct src_mgr *mgr, struct src *src);
|
||||
int (*src_enable)(struct src_mgr *mgr, struct src *src);
|
||||
int (*src_disable)(struct src_mgr *mgr, struct src *src);
|
||||
int (*commit_write)(struct src_mgr *mgr);
|
||||
};
|
||||
|
||||
/* Define the descriptor of a SRC Input Mapper resource */
|
||||
struct srcimp_mgr;
|
||||
struct srcimp_rsc_ops;
|
||||
|
||||
struct srcimp {
|
||||
struct rsc rsc;
|
||||
unsigned char idx[8];
|
||||
struct imapper *imappers;
|
||||
unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
|
||||
struct srcimp_mgr *mgr;
|
||||
struct srcimp_rsc_ops *ops;
|
||||
};
|
||||
|
||||
struct srcimp_rsc_ops {
|
||||
int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input);
|
||||
int (*unmap)(struct srcimp *srcimp);
|
||||
};
|
||||
|
||||
/* Define SRCIMP resource request description info */
|
||||
struct srcimp_desc {
|
||||
unsigned int msr;
|
||||
};
|
||||
|
||||
struct srcimp_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
spinlock_t imap_lock;
|
||||
struct list_head imappers;
|
||||
struct imapper *init_imap;
|
||||
unsigned int init_imap_added;
|
||||
|
||||
/* request srcimp resource */
|
||||
int (*get_srcimp)(struct srcimp_mgr *mgr,
|
||||
const struct srcimp_desc *desc,
|
||||
struct srcimp **rsrcimp);
|
||||
/* return srcimp resource */
|
||||
int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp);
|
||||
int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry);
|
||||
int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of SRC resource manager */
|
||||
int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
|
||||
int src_mgr_destroy(struct src_mgr *src_mgr);
|
||||
/* Constructor and destructor of SRCIMP resource manager */
|
||||
int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
|
||||
int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
|
||||
|
||||
#endif /* CTSRC_H */
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* 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 <linux/slab.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "ctatc.h"
|
||||
#include "cthardware.h"
|
||||
#include "cttimer.h"
|
||||
|
||||
static int use_system_timer;
|
||||
MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer");
|
||||
module_param(use_system_timer, bool, S_IRUGO);
|
||||
|
||||
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 wc; /* current wallclock */
|
||||
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 MIN_TICKS 1
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
|
||||
{
|
||||
struct hw *hw = atimer->atc->hw;
|
||||
return hw->get_wc(hw);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
unsigned int min_intr = (unsigned int)-1;
|
||||
int updates = 0;
|
||||
unsigned int wc, diff;
|
||||
|
||||
if (list_empty(&atimer->running_head)) {
|
||||
ct_xfitimer_irq_stop(atimer);
|
||||
atimer->reprogram = 0; /* clear flag */
|
||||
return 0;
|
||||
}
|
||||
|
||||
wc = ct_xfitimer_get_wc(atimer);
|
||||
diff = wc - atimer->wc;
|
||||
atimer->wc = wc;
|
||||
list_for_each_entry(ti, &atimer->running_head, running_list) {
|
||||
if (ti->frag_count > diff)
|
||||
ti->frag_count -= diff;
|
||||
else {
|
||||
unsigned int pos;
|
||||
unsigned int period_size, rate;
|
||||
|
||||
period_size = ti->substream->runtime->period_size;
|
||||
rate = ti->substream->runtime->rate;
|
||||
pos = ti->substream->ops->pointer(ti->substream);
|
||||
if (pos / period_size != ti->position / period_size) {
|
||||
ti->need_update = 1;
|
||||
ti->position = pos;
|
||||
updates++;
|
||||
}
|
||||
pos %= period_size;
|
||||
pos = period_size - pos;
|
||||
ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
|
||||
rate - 1, rate);
|
||||
}
|
||||
if (ti->frag_count < min_intr)
|
||||
min_intr = ti->frag_count;
|
||||
}
|
||||
|
||||
if (min_intr < MIN_TICKS)
|
||||
min_intr = MIN_TICKS;
|
||||
ct_xfitimer_irq_rearm(atimer, min_intr);
|
||||
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;
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
if (atimer->irq_handling) {
|
||||
/* reached from IRQ handler; let it handle later */
|
||||
atimer->reprogram = 1;
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (list_empty(&ti->running_list))
|
||||
atimer->wc = ct_xfitimer_get_wc(atimer);
|
||||
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
|
||||
*/
|
||||
|
||||
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) {
|
||||
snd_printd(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 {
|
||||
snd_printd(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);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Timer handling
|
||||
*/
|
||||
|
||||
#ifndef __CTTIMER_H
|
||||
#define __CTTIMER_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
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 */
|
|
@ -0,0 +1,250 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctvmem.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of virtual memory management object
|
||||
* for card device.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Apr 1 2008
|
||||
*/
|
||||
|
||||
#include "ctvmem.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/io.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *))
|
||||
#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE)
|
||||
|
||||
/* *
|
||||
* Find or create vm block based on requested @size.
|
||||
* @size must be page aligned.
|
||||
* */
|
||||
static struct ct_vm_block *
|
||||
get_vm_block(struct ct_vm *vm, unsigned int size)
|
||||
{
|
||||
struct ct_vm_block *block = NULL, *entry;
|
||||
struct list_head *pos;
|
||||
|
||||
size = CT_PAGE_ALIGN(size);
|
||||
if (size > vm->size) {
|
||||
printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
|
||||
"memory space available!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&vm->lock);
|
||||
list_for_each(pos, &vm->unused) {
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
if (entry->size >= size)
|
||||
break; /* found a block that is big enough */
|
||||
}
|
||||
if (pos == &vm->unused)
|
||||
goto out;
|
||||
|
||||
if (entry->size == size) {
|
||||
/* Move the vm node from unused list to used list directly */
|
||||
list_del(&entry->list);
|
||||
list_add(&entry->list, &vm->used);
|
||||
vm->size -= size;
|
||||
block = entry;
|
||||
goto out;
|
||||
}
|
||||
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (NULL == block)
|
||||
goto out;
|
||||
|
||||
block->addr = entry->addr;
|
||||
block->size = size;
|
||||
list_add(&block->list, &vm->used);
|
||||
entry->addr += size;
|
||||
entry->size -= size;
|
||||
vm->size -= size;
|
||||
|
||||
out:
|
||||
mutex_unlock(&vm->lock);
|
||||
return block;
|
||||
}
|
||||
|
||||
static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
|
||||
{
|
||||
struct ct_vm_block *entry, *pre_ent;
|
||||
struct list_head *pos, *pre;
|
||||
|
||||
block->size = CT_PAGE_ALIGN(block->size);
|
||||
|
||||
mutex_lock(&vm->lock);
|
||||
list_del(&block->list);
|
||||
vm->size += block->size;
|
||||
|
||||
list_for_each(pos, &vm->unused) {
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
if (entry->addr >= (block->addr + block->size))
|
||||
break; /* found a position */
|
||||
}
|
||||
if (pos == &vm->unused) {
|
||||
list_add_tail(&block->list, &vm->unused);
|
||||
entry = block;
|
||||
} else {
|
||||
if ((block->addr + block->size) == entry->addr) {
|
||||
entry->addr = block->addr;
|
||||
entry->size += block->size;
|
||||
kfree(block);
|
||||
} else {
|
||||
__list_add(&block->list, pos->prev, pos);
|
||||
entry = block;
|
||||
}
|
||||
}
|
||||
|
||||
pos = &entry->list;
|
||||
pre = pos->prev;
|
||||
while (pre != &vm->unused) {
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
pre_ent = list_entry(pre, struct ct_vm_block, list);
|
||||
if ((pre_ent->addr + pre_ent->size) > entry->addr)
|
||||
break;
|
||||
|
||||
pre_ent->size += entry->size;
|
||||
list_del(pos);
|
||||
kfree(entry);
|
||||
pos = pre;
|
||||
pre = pos->prev;
|
||||
}
|
||||
mutex_unlock(&vm->lock);
|
||||
}
|
||||
|
||||
/* Map host addr (kmalloced/vmalloced) to device logical addr. */
|
||||
static struct ct_vm_block *
|
||||
ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
|
||||
{
|
||||
struct ct_vm_block *block;
|
||||
unsigned int pte_start;
|
||||
unsigned i, pages;
|
||||
unsigned long *ptp;
|
||||
|
||||
block = get_vm_block(vm, size);
|
||||
if (block == NULL) {
|
||||
printk(KERN_ERR "ctxfi: No virtual memory block that is big "
|
||||
"enough to allocate!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptp = vm->ptp[0];
|
||||
pte_start = (block->addr >> CT_PAGE_SHIFT);
|
||||
pages = block->size >> CT_PAGE_SHIFT;
|
||||
for (i = 0; i < pages; i++) {
|
||||
unsigned long addr;
|
||||
addr = snd_pcm_sgbuf_get_addr(substream, i << CT_PAGE_SHIFT);
|
||||
ptp[pte_start + i] = addr;
|
||||
}
|
||||
|
||||
block->size = size;
|
||||
return block;
|
||||
}
|
||||
|
||||
static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
|
||||
{
|
||||
/* do unmapping */
|
||||
put_vm_block(vm, block);
|
||||
}
|
||||
|
||||
/* *
|
||||
* return the host (kmalloced) addr of the @index-th device
|
||||
* page talbe page on success, or NULL on failure.
|
||||
* The first returned NULL indicates the termination.
|
||||
* */
|
||||
static void *
|
||||
ct_get_ptp_virt(struct ct_vm *vm, int index)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index];
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
int ct_vm_create(struct ct_vm **rvm)
|
||||
{
|
||||
struct ct_vm *vm;
|
||||
struct ct_vm_block *block;
|
||||
int i;
|
||||
|
||||
*rvm = NULL;
|
||||
|
||||
vm = kzalloc(sizeof(*vm), GFP_KERNEL);
|
||||
if (NULL == vm)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&vm->lock);
|
||||
|
||||
/* Allocate page table pages */
|
||||
for (i = 0; i < CT_PTP_NUM; i++) {
|
||||
vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (NULL == vm->ptp[i])
|
||||
break;
|
||||
}
|
||||
if (!i) {
|
||||
/* no page table pages are allocated */
|
||||
kfree(vm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
vm->size = CT_ADDRS_PER_PAGE * i;
|
||||
/* Initialise remaining ptps */
|
||||
for (; i < CT_PTP_NUM; i++)
|
||||
vm->ptp[i] = NULL;
|
||||
|
||||
vm->map = ct_vm_map;
|
||||
vm->unmap = ct_vm_unmap;
|
||||
vm->get_ptp_virt = ct_get_ptp_virt;
|
||||
INIT_LIST_HEAD(&vm->unused);
|
||||
INIT_LIST_HEAD(&vm->used);
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (NULL != block) {
|
||||
block->addr = 0;
|
||||
block->size = vm->size;
|
||||
list_add(&block->list, &vm->unused);
|
||||
}
|
||||
|
||||
*rvm = vm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The caller must ensure no mapping pages are being used
|
||||
* by hardware before calling this function */
|
||||
void ct_vm_destroy(struct ct_vm *vm)
|
||||
{
|
||||
int i;
|
||||
struct list_head *pos;
|
||||
struct ct_vm_block *entry;
|
||||
|
||||
/* free used and unused list nodes */
|
||||
while (!list_empty(&vm->used)) {
|
||||
pos = vm->used.next;
|
||||
list_del(pos);
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
kfree(entry);
|
||||
}
|
||||
while (!list_empty(&vm->unused)) {
|
||||
pos = vm->unused.next;
|
||||
list_del(pos);
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
/* free allocated page table pages */
|
||||
for (i = 0; i < CT_PTP_NUM; i++)
|
||||
kfree(vm->ptp[i]);
|
||||
|
||||
vm->size = 0;
|
||||
|
||||
kfree(vm);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @File ctvmem.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of virtual memory management object
|
||||
* for card device.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*/
|
||||
|
||||
#ifndef CTVMEM_H
|
||||
#define CTVMEM_H
|
||||
|
||||
#define CT_PTP_NUM 1 /* num of device page table pages */
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* The chip can handle the page table of 4k pages
|
||||
* (emu20k1 can handle even 8k pages, but we don't use it right now)
|
||||
*/
|
||||
#define CT_PAGE_SIZE 4096
|
||||
#define CT_PAGE_SHIFT 12
|
||||
#define CT_PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
#define CT_PAGE_ALIGN(addr) ALIGN(addr, CT_PAGE_SIZE)
|
||||
|
||||
struct ct_vm_block {
|
||||
unsigned int addr; /* starting logical addr of this block */
|
||||
unsigned int size; /* size of this device virtual mem block */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct snd_pcm_substream;
|
||||
|
||||
/* Virtual memory management object for card device */
|
||||
struct ct_vm {
|
||||
void *ptp[CT_PTP_NUM]; /* Device page table pages */
|
||||
unsigned int size; /* Available addr space in bytes */
|
||||
struct list_head unused; /* List of unused blocks */
|
||||
struct list_head used; /* List of used blocks */
|
||||
struct mutex lock;
|
||||
|
||||
/* Map host addr (kmalloced/vmalloced) to device logical addr. */
|
||||
struct ct_vm_block *(*map)(struct ct_vm *, struct snd_pcm_substream *,
|
||||
int size);
|
||||
/* Unmap device logical addr area. */
|
||||
void (*unmap)(struct ct_vm *, struct ct_vm_block *block);
|
||||
void *(*get_ptp_virt)(struct ct_vm *vm, int index);
|
||||
};
|
||||
|
||||
int ct_vm_create(struct ct_vm **rvm);
|
||||
void ct_vm_destroy(struct ct_vm *vm);
|
||||
|
||||
#endif /* CTVMEM_H */
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* xfi linux driver.
|
||||
*
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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 <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include "ctatc.h"
|
||||
#include "cthardware.h"
|
||||
|
||||
MODULE_AUTHOR("Creative Technology Ltd");
|
||||
MODULE_DESCRIPTION("X-Fi driver version 1.03");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
|
||||
|
||||
static unsigned int reference_rate = 48000;
|
||||
static unsigned int multiple = 2;
|
||||
MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
|
||||
module_param(reference_rate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
|
||||
module_param(multiple, uint, S_IRUGO);
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
|
||||
|
||||
static struct pci_device_id ct_pci_dev_ids[] = {
|
||||
/* only X-Fi is supported, so... */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
|
||||
.driver_data = ATC20K1,
|
||||
},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2),
|
||||
.driver_data = ATC20K2,
|
||||
},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
|
||||
|
||||
static int __devinit
|
||||
ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
||||
{
|
||||
static int dev;
|
||||
struct snd_card *card;
|
||||
struct ct_atc *atc;
|
||||
int err;
|
||||
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
|
||||
if (!enable[dev]) {
|
||||
dev++;
|
||||
return -ENOENT;
|
||||
}
|
||||
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
|
||||
if (err)
|
||||
return err;
|
||||
if ((reference_rate != 48000) && (reference_rate != 44100)) {
|
||||
printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
|
||||
reference_rate);
|
||||
printk(KERN_ERR "ctxfi: The valid values for reference_rate "
|
||||
"are 48000 and 44100, Value 48000 is assumed.\n");
|
||||
reference_rate = 48000;
|
||||
}
|
||||
if ((multiple != 1) && (multiple != 2)) {
|
||||
printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
|
||||
multiple);
|
||||
printk(KERN_ERR "ctxfi: The valid values for multiple are "
|
||||
"1 and 2, Value 2 is assumed.\n");
|
||||
multiple = 2;
|
||||
}
|
||||
err = ct_atc_create(card, pci, reference_rate, multiple,
|
||||
pci_id->driver_data, &atc);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
card->private_data = atc;
|
||||
|
||||
/* Create alsa devices supported by this card */
|
||||
err = ct_atc_create_alsa_devs(atc);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
strcpy(card->driver, "SB-XFi");
|
||||
strcpy(card->shortname, "Creative X-Fi");
|
||||
snprintf(card->longname, sizeof(card->longname), "%s %s %s",
|
||||
card->shortname, atc->chip_name, atc->model_name);
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
dev++;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit ct_card_remove(struct pci_dev *pci)
|
||||
{
|
||||
snd_card_free(pci_get_drvdata(pci));
|
||||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
static struct pci_driver ct_driver = {
|
||||
.name = "SB-XFi",
|
||||
.id_table = ct_pci_dev_ids,
|
||||
.probe = ct_card_probe,
|
||||
.remove = __devexit_p(ct_card_remove),
|
||||
};
|
||||
|
||||
static int __init ct_card_init(void)
|
||||
{
|
||||
return pci_register_driver(&ct_driver);
|
||||
}
|
||||
|
||||
static void __exit ct_card_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ct_driver);
|
||||
}
|
||||
|
||||
module_init(ct_card_init)
|
||||
module_exit(ct_card_exit)
|
Loading…
Reference in New Issue