From 8cc72361481f00253f1e468ade5795427386d593 Mon Sep 17 00:00:00 2001 From: Wai Yew CHAY Date: Thu, 14 May 2009 08:05:58 +0200 Subject: ALSA: SB X-Fi driver merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Sound Blaster X-Fi driver supports Creative solutions based on 20K1 and 20K2 chipsets. Supported hardware : 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 Current release features: * ALSA PCM Playback * ALSA Record * ALSA Mixer Note: * External I/O modules detection not included. Signed-off-by: Wai Yew CHAY Singed-off-by: Ryan RICHARDS Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 10 + sound/pci/Makefile | 1 + sound/pci/ctxfi/Makefile | 5 + sound/pci/ctxfi/ct20k1reg.h | 634 ++++++++++++ sound/pci/ctxfi/ct20k2reg.h | 85 ++ sound/pci/ctxfi/ctamixer.c | 488 +++++++++ sound/pci/ctxfi/ctamixer.h | 96 ++ sound/pci/ctxfi/ctatc.c | 1605 ++++++++++++++++++++++++++++++ sound/pci/ctxfi/ctatc.h | 155 +++ sound/pci/ctxfi/ctdaio.c | 769 +++++++++++++++ sound/pci/ctxfi/ctdaio.h | 122 +++ sound/pci/ctxfi/ctdrv.h | 30 + sound/pci/ctxfi/cthardware.c | 108 ++ sound/pci/ctxfi/cthardware.h | 160 +++ sound/pci/ctxfi/cthw20k1.c | 2230 ++++++++++++++++++++++++++++++++++++++++++ sound/pci/ctxfi/cthw20k1.h | 26 + sound/pci/ctxfi/cthw20k2.c | 2133 ++++++++++++++++++++++++++++++++++++++++ sound/pci/ctxfi/cthw20k2.h | 26 + sound/pci/ctxfi/ctimap.c | 112 +++ sound/pci/ctxfi/ctimap.h | 40 + sound/pci/ctxfi/ctmixer.c | 1108 +++++++++++++++++++++ sound/pci/ctxfi/ctmixer.h | 67 ++ sound/pci/ctxfi/ctpcm.c | 499 ++++++++++ sound/pci/ctxfi/ctpcm.h | 27 + sound/pci/ctxfi/ctresource.c | 297 ++++++ sound/pci/ctxfi/ctresource.h | 72 ++ sound/pci/ctxfi/ctsrc.c | 886 +++++++++++++++++ sound/pci/ctxfi/ctsrc.h | 149 +++ sound/pci/ctxfi/ctvmem.c | 254 +++++ sound/pci/ctxfi/ctvmem.h | 49 + sound/pci/ctxfi/xfi.c | 125 +++ 31 files changed, 12368 insertions(+) create mode 100644 sound/pci/ctxfi/Makefile create mode 100644 sound/pci/ctxfi/ct20k1reg.h create mode 100644 sound/pci/ctxfi/ct20k2reg.h create mode 100644 sound/pci/ctxfi/ctamixer.c create mode 100644 sound/pci/ctxfi/ctamixer.h create mode 100644 sound/pci/ctxfi/ctatc.c create mode 100644 sound/pci/ctxfi/ctatc.h create mode 100644 sound/pci/ctxfi/ctdaio.c create mode 100644 sound/pci/ctxfi/ctdaio.h create mode 100644 sound/pci/ctxfi/ctdrv.h create mode 100644 sound/pci/ctxfi/cthardware.c create mode 100644 sound/pci/ctxfi/cthardware.h create mode 100644 sound/pci/ctxfi/cthw20k1.c create mode 100644 sound/pci/ctxfi/cthw20k1.h create mode 100644 sound/pci/ctxfi/cthw20k2.c create mode 100644 sound/pci/ctxfi/cthw20k2.h create mode 100644 sound/pci/ctxfi/ctimap.c create mode 100644 sound/pci/ctxfi/ctimap.h create mode 100644 sound/pci/ctxfi/ctmixer.c create mode 100644 sound/pci/ctxfi/ctmixer.h create mode 100644 sound/pci/ctxfi/ctpcm.c create mode 100644 sound/pci/ctxfi/ctpcm.h create mode 100644 sound/pci/ctxfi/ctresource.c create mode 100644 sound/pci/ctxfi/ctresource.h create mode 100644 sound/pci/ctxfi/ctsrc.c create mode 100644 sound/pci/ctxfi/ctsrc.h create mode 100644 sound/pci/ctxfi/ctvmem.c create mode 100644 sound/pci/ctxfi/ctvmem.h create mode 100644 sound/pci/ctxfi/xfi.c (limited to 'sound') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93422e3a3f0..3a7640feaf9 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -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 diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 65b25d221cd..6a1281ec01e 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_SND) += \ ali5451/ \ au88x0/ \ aw2/ \ + ctxfi/ \ ca0106/ \ cs46xx/ \ cs5535audio/ \ diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile new file mode 100644 index 00000000000..29043237f9f --- /dev/null +++ b/sound/pci/ctxfi/Makefile @@ -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 \ + cthw20k2.o cthw20k1.o + +obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h new file mode 100644 index 00000000000..c62e6775dab --- /dev/null +++ b/sound/pci/ctxfi/ct20k1reg.h @@ -0,0 +1,634 @@ +/** + * 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 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 */ + + diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h new file mode 100644 index 00000000000..2d07986f57c --- /dev/null +++ b/sound/pci/ctxfi/ct20k2reg.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 diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c new file mode 100644 index 00000000000..119791aa70e --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.c @@ -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 + +#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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + unsigned int index = 0; + int i = 0; + struct rsc *input = NULL; + struct sum *sum = NULL; + + hw = (struct 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 = NULL; + unsigned int index = 0; + + hw = (struct 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 = NULL; + + hw = (struct 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 = 0; + + 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 = 0, i = 0; + unsigned int idx = 0; + struct amixer *amixer = NULL; + 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 "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 = 0; + + 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 = 0; + 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 = 0; + + 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 = 0, i = 0; + unsigned int idx = 0; + struct sum *sum = NULL; + 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 "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 = 0; + + 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 = 0; + 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; +} + diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h new file mode 100644 index 00000000000..cc49e5ab475 --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.h @@ -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 + +/* 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 */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c new file mode 100644 index 00000000000..5f35374863f --- /dev/null +++ b/sound/pci/ctxfi/ctatc.c @@ -0,0 +1,1605 @@ +/** + * 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.c + * + * @Brief + * This file contains the implementation of the device resource management + * object. + * + * @Author Liu Chun + * @Date Mar 28 2008 + */ + +#include "ctatc.h" +#include "ctpcm.h" +#include "ctmixer.h" +#include "ctdrv.h" +#include "cthardware.h" +#include "ctsrc.h" +#include "ctamixer.h" +#include "ctdaio.h" +#include +#include +#include +#include + +#define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ +#define DAIONUM 7 +#define MAX_MULTI_CHN 8 + +#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \ + | IEC958_AES0_CON_NOT_COPYRIGHT) \ + | ((IEC958_AES1_CON_MIXER \ + | IEC958_AES1_CON_ORIGINAL) << 8) \ + | (0x10 << 16) \ + | ((IEC958_AES3_CON_FS_48000) << 24)) + +static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { + [CTSB0760] = {.subsys = PCI_SUBSYS_CREATIVE_SB0760, + .nm_model = "SB076x"}, + [CTHENDRIX] = {.subsys = PCI_SUBSYS_CREATIVE_HENDRIX, + .nm_model = "Hendrix"}, + [CTSB08801] = {.subsys = PCI_SUBSYS_CREATIVE_SB08801, + .nm_model = "SB0880"}, + [CTSB08802] = {.subsys = PCI_SUBSYS_CREATIVE_SB08802, + .nm_model = "SB0880"}, + [CTSB08803] = {.subsys = PCI_SUBSYS_CREATIVE_SB08803, + .nm_model = "SB0880"} +}; + +static struct ct_atc_chip_details atc_chip_details[] = { + {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K1, + .sub_details = NULL, + .nm_card = "X-Fi 20k1"}, + {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K2, + .sub_details = atc_sub_details, + .nm_card = "X-Fi 20k2"}, + {} /* terminator */ +}; + +static struct { + int (*create)(struct ct_atc *atc, + enum CTALSADEVS device, const char *device_name); + int (*destroy)(void *alsa_dev); + const char *public_name; +} alsa_dev_funcs[NUM_CTALSADEVS] = { + [FRONT] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Front/WaveIn"}, + [REAR] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Rear"}, + [CLFE] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Center/LFE"}, + [SURROUND] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Surround"}, + [IEC958] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "IEC958 Non-audio"}, + + [MIXER] = { .create = ct_alsa_mix_create, + .destroy = NULL, + .public_name = "Mixer"} +}; + +typedef int (*create_t)(void *, void **); +typedef int (*destroy_t)(void *); + +static struct { + int (*create)(void *hw, void **rmgr); + int (*destroy)(void *mgr); +} rsc_mgr_funcs[NUM_RSCTYP] = { + [SRC] = { .create = (create_t)src_mgr_create, + .destroy = (destroy_t)src_mgr_destroy }, + [SRCIMP] = { .create = (create_t)srcimp_mgr_create, + .destroy = (destroy_t)srcimp_mgr_destroy }, + [AMIXER] = { .create = (create_t)amixer_mgr_create, + .destroy = (destroy_t)amixer_mgr_destroy }, + [SUM] = { .create = (create_t)sum_mgr_create, + .destroy = (destroy_t)sum_mgr_destroy }, + [DAIO] = { .create = (create_t)daio_mgr_create, + .destroy = (destroy_t)daio_mgr_destroy } +}; + +static int +atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm); + +/* * + * Only mono and interleaved modes are supported now. + * Always allocates a contiguous channel block. + * */ + +static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + unsigned long flags; + struct snd_pcm_runtime *runtime; + struct ct_vm *vm; + + if (NULL == apcm->substream) + return 0; + + runtime = apcm->substream->runtime; + vm = atc->vm; + + spin_lock_irqsave(&atc->vm_lock, flags); + apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); + spin_unlock_irqrestore(&atc->vm_lock, flags); + + if (NULL == apcm->vm_block) + return -ENOENT; + + return 0; +} + +static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + unsigned long flags; + struct ct_vm *vm; + + if (NULL == apcm->vm_block) + return; + + vm = atc->vm; + + spin_lock_irqsave(&atc->vm_lock, flags); + vm->unmap(vm, apcm->vm_block); + spin_unlock_irqrestore(&atc->vm_lock, flags); + + apcm->vm_block = NULL; +} + +static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) +{ + struct ct_vm *vm; + void *kvirt_addr; + unsigned long phys_addr; + unsigned long flags; + + spin_lock_irqsave(&atc->vm_lock, flags); + vm = atc->vm; + kvirt_addr = vm->get_ptp_virt(vm, index); + if (kvirt_addr == NULL) + phys_addr = (~0UL); + else + phys_addr = virt_to_phys(kvirt_addr); + + spin_unlock_irqrestore(&atc->vm_lock, flags); + + return phys_addr; +} + +static unsigned int convert_format(snd_pcm_format_t snd_format) +{ + switch (snd_format) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + return SRC_SF_U8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + return SRC_SF_S16; + case SNDRV_PCM_FORMAT_S24_3LE: + return SRC_SF_S24; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + return SRC_SF_S32; + default: + printk(KERN_ERR "not recognized snd format is %d \n", + snd_format); + return SRC_SF_S16; + } +} + +static unsigned int +atc_get_pitch(unsigned int input_rate, unsigned int output_rate) +{ + unsigned int pitch = 0; + int b = 0; + + /* get pitch and convert to fixed-point 8.24 format. */ + pitch = (input_rate / output_rate) << 24; + input_rate %= output_rate; + input_rate /= 100; + output_rate /= 100; + for (b = 31; ((b >= 0) && !(input_rate >> b)); ) + b--; + + if (b >= 0) { + input_rate <<= (31 - b); + input_rate /= output_rate; + b = 24 - (31 - b); + if (b >= 0) + input_rate <<= b; + else + input_rate >>= -b; + + pitch |= input_rate; + } + + return pitch; +} + +static int select_rom(unsigned int pitch) +{ + if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) { + /* 0.26 <= pitch <= 1.72 */ + return 1; + } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) { + /* pitch == 1.8375 */ + return 2; + } else if (0x02000000 == pitch) { + /* pitch == 2 */ + return 3; + } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) { + /* 0 <= pitch <= 8 */ + return 0; + } else { + return -ENOENT; + } +} + +static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct src_desc desc = {0}; + struct amixer_desc mix_dsc = {0}; + struct src *src = NULL; + struct amixer *amixer = NULL; + int err = 0; + int n_amixer = apcm->substream->runtime->channels, i = 0; + int device = apcm->substream->pcm->device; + unsigned int pitch = 0; + unsigned long flags; + + if (NULL != apcm->src) { + /* Prepared pcm playback */ + return 0; + } + + /* Get SRC resource */ + desc.multi = apcm->substream->runtime->channels; + desc.msr = atc->msr; + desc.mode = MEMRD; + err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); + if (err) + goto error1; + + pitch = atc_get_pitch(apcm->substream->runtime->rate, + (atc->rsr * atc->msr)); + src = apcm->src; + src->ops->set_pitch(src, pitch); + src->ops->set_rom(src, select_rom(pitch)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); + + /* Get AMIXER resource */ + n_amixer = (n_amixer < 2) ? 2 : n_amixer; + apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + if (NULL == apcm->amixers) { + err = -ENOMEM; + goto error1; + } + mix_dsc.msr = atc->msr; + for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { + err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, + (struct amixer **)&apcm->amixers[i]); + if (err) + goto error1; + + apcm->n_amixer++; + } + + /* Set up device virtual mem map */ + err = ct_map_audio_buffer(atc, apcm); + if (err < 0) + goto error1; + + /* Connect resources */ + src = apcm->src; + for (i = 0; i < n_amixer; i++) { + amixer = apcm->amixers[i]; + spin_lock_irqsave(&atc->atc_lock, flags); + amixer->ops->setup(amixer, &src->rsc, + INIT_VOL, atc->pcm[i+device*2]); + spin_unlock_irqrestore(&atc->atc_lock, flags); + src = src->ops->next_interleave(src); + if (NULL == src) + src = apcm->src; + } + + return 0; + +error1: + atc_pcm_release_resources(atc, apcm); + return err; +} + +static int +atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; + struct srcimp *srcimp = NULL; + int i = 0; + + if (NULL != apcm->srcimps) { + for (i = 0; i < apcm->n_srcimp; i++) { + srcimp = apcm->srcimps[i]; + srcimp->ops->unmap(srcimp); + srcimp_mgr->put_srcimp(srcimp_mgr, srcimp); + apcm->srcimps[i] = NULL; + } + kfree(apcm->srcimps); + apcm->srcimps = NULL; + } + + if (NULL != apcm->srccs) { + for (i = 0; i < apcm->n_srcc; i++) { + src_mgr->put_src(src_mgr, apcm->srccs[i]); + apcm->srccs[i] = NULL; + } + kfree(apcm->srccs); + apcm->srccs = NULL; + } + + if (NULL != apcm->amixers) { + for (i = 0; i < apcm->n_amixer; i++) { + amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]); + apcm->amixers[i] = NULL; + } + kfree(apcm->amixers); + apcm->amixers = NULL; + } + + if (NULL != apcm->mono) { + sum_mgr->put_sum(sum_mgr, apcm->mono); + apcm->mono = NULL; + } + + if (NULL != apcm->src) { + src_mgr->put_src(src_mgr, apcm->src); + apcm->src = NULL; + } + + if (NULL != apcm->vm_block) { + /* Undo device virtual mem map */ + ct_unmap_audio_buffer(atc, apcm); + apcm->vm_block = NULL; + } + + return 0; +} + +static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + unsigned int max_cisz = 0; + struct src *src = apcm->src; + + max_cisz = src->multi * src->rsc.msr; + max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8); + + src->ops->set_sa(src, apcm->vm_block->addr); + src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); + src->ops->set_ca(src, apcm->vm_block->addr + max_cisz); + src->ops->set_cisz(src, max_cisz); + + src->ops->set_bm(src, 1); + src->ops->set_state(src, SRC_STATE_INIT); + src->ops->commit_write(src); + + return 0; +} + +static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + int i = 0; + + src = apcm->src; + src->ops->set_bm(src, 0); + src->ops->set_state(src, SRC_STATE_OFF); + src->ops->commit_write(src); + + if (NULL != apcm->srccs) { + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + src->ops->set_bm(src, 0); + src->ops->set_state(src, SRC_STATE_OFF); + src->ops->commit_write(src); + } + } + + apcm->started = 0; + + return 0; +} + +static int +atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = apcm->src; + u32 size = 0, max_cisz = 0; + int position = 0; + + position = src->ops->get_ca(src); + + size = apcm->vm_block->size; + max_cisz = src->multi * src->rsc.msr; + max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8); + + return (position + size - max_cisz - apcm->vm_block->addr) % size; +} + +struct src_node_conf_t { + unsigned int pitch; + unsigned int msr:8; + unsigned int mix_msr:8; + unsigned int imp_msr:8; + unsigned int vo:1; +}; + +static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, + struct src_node_conf_t *conf, int *n_srcc) +{ + unsigned int pitch = 0; + + /* get pitch and convert to fixed-point 8.24 format. */ + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + *n_srcc = 0; + + if (1 == atc->msr) { + *n_srcc = apcm->substream->runtime->channels; + conf[0].pitch = pitch; + conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; + conf[0].vo = 1; + } else if (2 == atc->msr) { + if (0x8000000 < pitch) { + /* Need two-stage SRCs, SRCIMPs and + * AMIXERs for converting format */ + conf[0].pitch = (atc->msr << 24); + conf[0].msr = conf[0].mix_msr = 1; + conf[0].imp_msr = atc->msr; + conf[0].vo = 0; + conf[1].pitch = atc_get_pitch(atc->rsr, + apcm->substream->runtime->rate); + conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1; + conf[1].vo = 1; + *n_srcc = apcm->substream->runtime->channels * 2; + } else if (0x1000000 < pitch) { + /* Need one-stage SRCs, SRCIMPs and + * AMIXERs for converting format */ + conf[0].pitch = pitch; + conf[0].msr = conf[0].mix_msr + = conf[0].imp_msr = atc->msr; + conf[0].vo = 1; + *n_srcc = apcm->substream->runtime->channels; + } + } +} + +static int +atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; + struct src_desc src_dsc = {0}; + struct src *src = NULL; + struct srcimp_desc srcimp_dsc = {0}; + struct srcimp *srcimp = NULL; + struct amixer_desc mix_dsc = {0}; + struct sum_desc sum_dsc = {0}; + unsigned int pitch = 0; + int multi = 0, err = 0, i = 0; + int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; + struct src_node_conf_t src_node_conf[2] = {{0} }; + + /* The numbers of converting SRCs and SRCIMPs should be determined + * by pitch value. */ + + multi = apcm->substream->runtime->channels; + + /* get pitch and convert to fixed-point 8.24 format. */ + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + + setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc); + n_sum = (1 == multi) ? 1 : 0; + n_amixer += n_sum * 2 + n_srcc; + n_srcimp += n_srcc; + if ((multi > 1) && (0x8000000 >= pitch)) { + /* Need extra AMIXERs and SRCIMPs for special treatment + * of interleaved recording of conjugate channels */ + n_amixer += multi * atc->msr; + n_srcimp += multi * atc->msr; + } else { + n_srcimp += multi; + } + + if (n_srcc) { + apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); + if (NULL == apcm->srccs) + return -ENOMEM; + } + if (n_amixer) { + apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + if (NULL == apcm->amixers) { + err = -ENOMEM; + goto error1; + } + } + apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); + if (NULL == apcm->srcimps) { + err = -ENOMEM; + goto error1; + } + + /* Allocate SRCs for sample rate conversion if needed */ + src_dsc.multi = 1; + src_dsc.mode = ARCRW; + for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) { + src_dsc.msr = src_node_conf[i/multi].msr; + err = src_mgr->get_src(src_mgr, &src_dsc, + (struct src **)&apcm->srccs[i]); + if (err) + goto error1; + + src = apcm->srccs[i]; + pitch = src_node_conf[i/multi].pitch; + src->ops->set_pitch(src, pitch); + src->ops->set_rom(src, select_rom(pitch)); + src->ops->set_vo(src, src_node_conf[i/multi].vo); + + apcm->n_srcc++; + } + + /* Allocate AMIXERs for routing SRCs of conversion if needed */ + for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { + if (i < (n_sum*2)) + mix_dsc.msr = atc->msr; + else if (i < (n_sum*2+n_srcc)) + mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr; + else + mix_dsc.msr = 1; + + err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, + (struct amixer **)&apcm->amixers[i]); + if (err) + goto error1; + + apcm->n_amixer++; + } + + /* Allocate a SUM resource to mix all input channels together */ + sum_dsc.msr = atc->msr; + err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono); + if (err) + goto error1; + + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + /* Allocate SRCIMP resources */ + for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) { + if (i < (n_srcc)) + srcimp_dsc.msr = src_node_conf[i/multi].imp_msr; + else if (1 == multi) + srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1; + else + srcimp_dsc.msr = 1; + + err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp); + if (err) + goto error1; + + apcm->srcimps[i] = srcimp; + apcm->n_srcimp++; + } + + /* Allocate a SRC for writing data to host memory */ + src_dsc.multi = apcm->substream->runtime->channels; + src_dsc.msr = 1; + src_dsc.mode = MEMWR; + err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src); + if (err) + goto error1; + + src = apcm->src; + src->ops->set_pitch(src, pitch); + + /* Set up device virtual mem map */ + err = ct_map_audio_buffer(atc, apcm); + if (err < 0) + goto error1; + + return 0; + +error1: + atc_pcm_release_resources(atc, apcm); + return err; +} + +static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + struct amixer *amixer = NULL; + struct srcimp *srcimp = NULL; + struct ct_mixer *mixer = atc->mixer; + struct sum *mono = NULL; + struct rsc *out_ports[8] = {NULL}; + int err = 0, i = 0, j = 0, n_sum = 0, multi = 0; + unsigned int pitch = 0; + int mix_base = 0, imp_base = 0; + + if (NULL != apcm->src) { + /* Prepared pcm capture */ + return 0; + } + + /* Get needed resources. */ + err = atc_pcm_capture_get_resources(atc, apcm); + if (err) + return err; + + /* Connect resources */ + mixer->get_output_ports(mixer, MIX_PCMO_FRONT, + &out_ports[0], &out_ports[1]); + + multi = apcm->substream->runtime->channels; + if (1 == multi) { + mono = apcm->mono; + for (i = 0; i < 2; i++) { + amixer = apcm->amixers[i]; + amixer->ops->setup(amixer, out_ports[i], + MONO_SUM_SCALE, mono); + } + out_ports[0] = &mono->rsc; + n_sum = 1; + mix_base = n_sum * 2; + } + + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + srcimp = apcm->srcimps[imp_base+i]; + amixer = apcm->amixers[mix_base+i]; + srcimp->ops->map(srcimp, src, out_ports[i%multi]); + amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); + out_ports[i%multi] = &amixer->rsc; + } + + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + + if ((multi > 1) && (pitch <= 0x8000000)) { + /* Special connection for interleaved + * recording with conjugate channels */ + for (i = 0; i < multi; i++) { + out_ports[i]->ops->master(out_ports[i]); + for (j = 0; j < atc->msr; j++) { + amixer = apcm->amixers[apcm->n_srcc+j*multi+i]; + amixer->ops->set_input(amixer, out_ports[i]); + amixer->ops->set_scale(amixer, INIT_VOL); + amixer->ops->set_sum(amixer, NULL); + amixer->ops->commit_raw_write(amixer); + out_ports[i]->ops->next_conj(out_ports[i]); + + srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i]; + srcimp->ops->map(srcimp, apcm->src, + &amixer->rsc); + } + } + } else { + for (i = 0; i < multi; i++) { + srcimp = apcm->srcimps[apcm->n_srcc+i]; + srcimp->ops->map(srcimp, apcm->src, out_ports[i]); + } + } + + return 0; +} + +static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + int i = 0, multi = 0; + + if (apcm->started) + return 0; + + apcm->started = 1; + multi = apcm->substream->runtime->channels; + /* Set up converting SRCs */ + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + src->ops->set_pm(src, ((i%multi) != (multi-1))); + src_mgr->src_disable(src_mgr, src); + } + + /* Set up recording SRC */ + src = apcm->src; + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_sa(src, apcm->vm_block->addr); + src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); + src->ops->set_ca(src, apcm->vm_block->addr); + src_mgr->src_disable(src_mgr, src); + + /* Disable relevant SRCs firstly */ + src_mgr->commit_write(src_mgr); + + /* Enable SRCs respectively */ + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + src->ops->set_state(src, SRC_STATE_RUN); + src->ops->commit_write(src); + src_mgr->src_enable_s(src_mgr, src); + } + src = apcm->src; + src->ops->set_bm(src, 1); + src->ops->set_state(src, SRC_STATE_RUN); + src->ops->commit_write(src); + src_mgr->src_enable_s(src_mgr, src); + + /* Enable relevant SRCs synchronously */ + src_mgr->commit_write(src_mgr); + + return 0; +} + +static int +atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = apcm->src; + + return src->ops->get_ca(src) - apcm->vm_block->addr; +} + +static int spdif_passthru_playback_get_resources(struct ct_atc *atc, + struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct src_desc desc = {0}; + struct amixer_desc mix_dsc = {0}; + struct src *src = NULL; + int err = 0; + int n_amixer = apcm->substream->runtime->channels, i = 0; + unsigned int pitch = 0, rsr = atc->pll_rate; + + /* Get SRC resource */ + desc.multi = apcm->substream->runtime->channels; + desc.msr = 1; + while (apcm->substream->runtime->rate > (rsr * desc.msr)) + desc.msr <<= 1; + + desc.mode = MEMRD; + err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); + if (err) + goto error1; + + pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr)); + src = apcm->src; + src->ops->set_pitch(src, pitch); + src->ops->set_rom(src, select_rom(pitch)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); + src->ops->set_bp(src, 1); + + /* Get AMIXER resource */ + n_amixer = (n_amixer < 2) ? 2 : n_amixer; + apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + if (NULL == apcm->amixers) { + err = -ENOMEM; + goto error1; + } + mix_dsc.msr = desc.msr; + for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { + err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, + (struct amixer **)&apcm->amixers[i]); + if (err) + goto error1; + + apcm->n_amixer++; + } + + /* Set up device virtual mem map */ + err = ct_map_audio_buffer(atc, apcm); + if (err < 0) + goto error1; + + return 0; + +error1: + atc_pcm_release_resources(atc, apcm); + return err; +} + +static int +spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio); + unsigned long flags; + unsigned int rate = apcm->substream->runtime->rate; + unsigned int status = 0; + int err = 0; + unsigned char iec958_con_fs = 0; + + switch (rate) { + case 48000: + iec958_con_fs = IEC958_AES3_CON_FS_48000; + break; + case 44100: + iec958_con_fs = IEC958_AES3_CON_FS_44100; + break; + case 32000: + iec958_con_fs = IEC958_AES3_CON_FS_32000; + break; + default: + return -ENOENT; + } + + spin_lock_irqsave(&atc->atc_lock, flags); + dao->ops->get_spos(dao, &status); + if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { + status &= ((~IEC958_AES3_CON_FS) << 24); + status |= (iec958_con_fs << 24); + dao->ops->set_spos(dao, status); + dao->ops->commit_write(dao); + } + if ((rate != atc->pll_rate) && (32000 != rate)) { + err = ((struct hw *)atc->hw)->pll_init(atc->hw, rate); + atc->pll_rate = err ? 0 : rate; + } + spin_unlock_irqrestore(&atc->atc_lock, flags); + + return err; +} + +static int +spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + struct amixer *amixer = NULL; + struct dao *dao = NULL; + int err = 0; + int i = 0; + unsigned long flags; + + if (NULL != apcm->src) + return 0; + + /* Configure SPDIFOO and PLL to passthrough mode; + * determine pll_rate. */ + err = spdif_passthru_playback_setup(atc, apcm); + if (err) + return err; + + /* Get needed resources. */ + err = spdif_passthru_playback_get_resources(atc, apcm); + if (err) + return err; + + /* Connect resources */ + src = apcm->src; + for (i = 0; i < apcm->n_amixer; i++) { + amixer = apcm->amixers[i]; + amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); + src = src->ops->next_interleave(src); + if (NULL == src) + src = apcm->src; + } + /* Connect to SPDIFOO */ + spin_lock_irqsave(&atc->atc_lock, flags); + dao = container_of(atc->daios[SPDIFOO], struct dao, daio); + amixer = apcm->amixers[0]; + dao->ops->set_left_input(dao, &amixer->rsc); + amixer = apcm->amixers[1]; + dao->ops->set_right_input(dao, &amixer->rsc); + spin_unlock_irqrestore(&atc->atc_lock, flags); + + return 0; +} + +static int atc_select_line_in(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + struct ct_mixer *mixer = atc->mixer; + struct src *src = NULL; + + if (hw->is_adc_source_selected(hw, ADC_LINEIN)) + return 0; + + mixer->set_input_left(mixer, MIX_MIC_IN, NULL); + mixer->set_input_right(mixer, MIX_MIC_IN, NULL); + + hw->select_adc_source(hw, ADC_LINEIN); + + src = atc->srcs[2]; + mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); + src = atc->srcs[3]; + mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); + + return 0; +} + +static int atc_select_mic_in(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + struct ct_mixer *mixer = atc->mixer; + struct src *src = NULL; + + if (hw->is_adc_source_selected(hw, ADC_MICIN)) + return 0; + + mixer->set_input_left(mixer, MIX_LINE_IN, NULL); + mixer->set_input_right(mixer, MIX_LINE_IN, NULL); + + hw->select_adc_source(hw, ADC_MICIN); + + src = atc->srcs[2]; + mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc); + src = atc->srcs[3]; + mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); + + return 0; +} + +static int atc_have_digit_io_switch(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->have_digit_io_switch(hw); +} + +static int atc_select_digit_io(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + if (hw->is_adc_source_selected(hw, ADC_NONE)) + return 0; + + hw->select_adc_source(hw, ADC_NONE); + + return 0; +} + +static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type) +{ + struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO]; + + if (state) + daio_mgr->daio_enable(daio_mgr, atc->daios[type]); + else + daio_mgr->daio_disable(daio_mgr, atc->daios[type]); + + daio_mgr->commit_write(daio_mgr); + + return 0; +} + +static int +atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type) +{ + struct dao *dao = container_of(atc->daios[type], struct dao, daio); + return dao->ops->get_spos(dao, status); +} + +static int +atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type) +{ + struct dao *dao = container_of(atc->daios[type], struct dao, daio); + + dao->ops->set_spos(dao, status); + dao->ops->commit_write(dao); + return 0; +} + +static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO1); +} + +static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO4); +} + +static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO3); +} + +static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO2); +} + +static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEIM); +} + +static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, SPDIFOO); +} + +static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, SPDIFIO); +} + +static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status) +{ + return atc_dao_get_status(atc, status, SPDIFOO); +} + +static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status) +{ + return atc_dao_set_status(atc, status, SPDIFOO); +} + +static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) +{ + unsigned long flags; + struct dao_desc da_dsc = {0}; + struct dao *dao = NULL; + int err = 0; + struct ct_mixer *mixer = atc->mixer; + struct rsc *rscs[2] = {NULL}; + unsigned int spos = 0; + + spin_lock_irqsave(&atc->atc_lock, flags); + dao = container_of(atc->daios[SPDIFOO], struct dao, daio); + da_dsc.msr = state ? 1 : atc->msr; + da_dsc.passthru = state ? 1 : 0; + err = dao->ops->reinit(dao, &da_dsc); + if (state) { + spos = IEC958_DEFAULT_CON; + } else { + mixer->get_output_ports(mixer, MIX_SPDIF_OUT, + &rscs[0], &rscs[1]); + dao->ops->set_left_input(dao, rscs[0]); + dao->ops->set_right_input(dao, rscs[1]); + /* Restore PLL to atc->rsr if needed. */ + if (atc->pll_rate != atc->rsr) { + err = ((struct hw *)atc->hw)->pll_init(atc->hw, + atc->rsr); + atc->pll_rate = err ? 0 : atc->rsr; + } + } + dao->ops->set_spos(dao, spos); + dao->ops->commit_write(dao); + spin_unlock_irqrestore(&atc->atc_lock, flags); + + return err; +} + +static int ct_atc_destroy(struct ct_atc *atc) +{ + struct daio_mgr *daio_mgr = NULL; + struct dao *dao = NULL; + struct dai *dai = NULL; + struct daio *daio = NULL; + struct sum_mgr *sum_mgr = NULL; + struct src_mgr *src_mgr = NULL; + struct srcimp_mgr *srcimp_mgr = NULL; + struct srcimp *srcimp = NULL; + struct ct_mixer *mixer = NULL; + int i = 0; + + if (NULL == atc) + return 0; + + /* Stop hardware and disable all interrupts */ + if (NULL != atc->hw) + ((struct hw *)atc->hw)->card_stop(atc->hw); + + /* Destroy internal mixer objects */ + if (NULL != atc->mixer) { + mixer = atc->mixer; + mixer->set_input_left(mixer, MIX_LINE_IN, NULL); + mixer->set_input_right(mixer, MIX_LINE_IN, NULL); + mixer->set_input_left(mixer, MIX_MIC_IN, NULL); + mixer->set_input_right(mixer, MIX_MIC_IN, NULL); + mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL); + mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL); + ct_mixer_destroy(atc->mixer); + } + + if (NULL != atc->daios) { + daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; + for (i = 0; i < atc->n_daio; i++) { + daio = atc->daios[i]; + if (daio->type < LINEIM) { + dao = container_of(daio, struct dao, daio); + dao->ops->clear_left_input(dao); + dao->ops->clear_right_input(dao); + } else { + dai = container_of(daio, struct dai, daio); + /* some thing to do for dai ... */ + } + daio_mgr->put_daio(daio_mgr, daio); + } + kfree(atc->daios); + } + + if (NULL != atc->pcm) { + sum_mgr = atc->rsc_mgrs[SUM]; + for (i = 0; i < atc->n_pcm; i++) + sum_mgr->put_sum(sum_mgr, atc->pcm[i]); + + kfree(atc->pcm); + } + + if (NULL != atc->srcs) { + src_mgr = atc->rsc_mgrs[SRC]; + for (i = 0; i < atc->n_src; i++) + src_mgr->put_src(src_mgr, atc->srcs[i]); + + kfree(atc->srcs); + } + + if (NULL != atc->srcimps) { + srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + for (i = 0; i < atc->n_srcimp; i++) { + srcimp = atc->srcimps[i]; + srcimp->ops->unmap(srcimp); + srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]); + } + kfree(atc->srcimps); + } + + for (i = 0; i < NUM_RSCTYP; i++) { + if ((NULL != rsc_mgr_funcs[i].destroy) && + (NULL != atc->rsc_mgrs[i])) + rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]); + + } + + if (NULL != atc->hw) + destroy_hw_obj((struct hw *)atc->hw); + + /* Destroy device virtual memory manager object */ + if (NULL != atc->vm) { + ct_vm_destroy(atc->vm); + atc->vm = NULL; + } + + kfree(atc); + + return 0; +} + +static int atc_dev_free(struct snd_device *dev) +{ + struct ct_atc *atc = dev->device_data; + return ct_atc_destroy(atc); +} + +static int atc_identify_card(struct ct_atc *atc) +{ + u16 subsys = 0; + u8 revision = 0; + struct pci_dev *pci = atc->pci; + const struct ct_atc_chip_details *d; + enum CTCARDS i; + + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys); + pci_read_config_byte(pci, PCI_REVISION_ID, &revision); + atc->chip_details = NULL; + atc->model = NUM_CTCARDS; + for (d = atc_chip_details; d->vendor; d++) { + if (d->vendor != pci->vendor || d->device != pci->device) + continue; + + if (NULL == d->sub_details) { + atc->chip_details = d; + break; + } + for (i = 0; i < NUM_CTCARDS; i++) { + if ((d->sub_details[i].subsys == subsys) || + (((subsys & 0x6000) == 0x6000) && + ((d->sub_details[i].subsys & 0x6000) == 0x6000))) { + atc->model = i; + break; + } + } + if (i >= NUM_CTCARDS) + continue; + + atc->chip_details = d; + break; + /* not take revision into consideration now */ + } + if (!d->vendor) + return -ENOENT; + + return 0; +} + +static int ct_create_alsa_devs(struct ct_atc *atc) +{ + enum CTALSADEVS i; + struct hw *hw = atc->hw; + int err; + + switch (hw->get_chip_type(hw)) { + case ATC20K1: + alsa_dev_funcs[MIXER].public_name = "20K1"; + break; + case ATC20K2: + alsa_dev_funcs[MIXER].public_name = "20K2"; + break; + default: + alsa_dev_funcs[MIXER].public_name = "Unknown"; + break; + } + + for (i = 0; i < NUM_CTALSADEVS; i++) { + if (NULL == alsa_dev_funcs[i].create) + continue; + + err = alsa_dev_funcs[i].create(atc, i, + alsa_dev_funcs[i].public_name); + if (err) { + printk(KERN_ERR "Creating alsa device %d failed!\n", i); + return err; + } + } + + return 0; +} + +static int atc_create_hw_devs(struct ct_atc *atc) +{ + struct hw *hw = NULL; + struct card_conf info = {0}; + int i = 0, err = 0; + + err = create_hw_obj(atc->pci, &hw); + if (err) { + printk(KERN_ERR "Failed to create hw obj!!!\n"); + return err; + } + atc->hw = hw; + + /* Initialize card hardware. */ + info.rsr = atc->rsr; + info.msr = atc->msr; + info.vm_pgt_phys = atc_get_ptp_phys(atc, 0); + err = hw->card_init(hw, &info); + if (err < 0) + return err; + + for (i = 0; i < NUM_RSCTYP; i++) { + if (NULL == rsc_mgr_funcs[i].create) + continue; + + err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); + if (err) { + printk(KERN_ERR "Failed to create rsc_mgr %d!!!\n", i); + return err; + } + } + + return 0; +} + +static int atc_get_resources(struct ct_atc *atc) +{ + struct daio_desc da_desc = {0}; + struct daio_mgr *daio_mgr = NULL; + struct src_desc src_dsc = {0}; + struct src_mgr *src_mgr = NULL; + struct srcimp_desc srcimp_dsc = {0}; + struct srcimp_mgr *srcimp_mgr = NULL; + struct sum_desc sum_dsc = {0}; + struct sum_mgr *sum_mgr = NULL; + int err = 0, i = 0; + unsigned short subsys_id = 0; + + atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); + if (NULL == atc->daios) + return -ENOMEM; + + atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + if (NULL == atc->srcs) + return -ENOMEM; + + atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + if (NULL == atc->srcimps) + return -ENOMEM; + + atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); + if (NULL == atc->pcm) + return -ENOMEM; + + daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; + da_desc.msr = atc->msr; + for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { + da_desc.type = i; + err = daio_mgr->get_daio(daio_mgr, &da_desc, + (struct daio **)&atc->daios[i]); + if (err) { + printk(KERN_ERR "Failed to get DAIO " + "resource %d!!!\n", i); + return err; + } + atc->n_daio++; + } + pci_read_config_word(atc->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + da_desc.type = SPDIFI1; + } else { + da_desc.type = SPDIFIO; + } + err = daio_mgr->get_daio(daio_mgr, &da_desc, + (struct daio **)&atc->daios[i]); + if (err) { + printk(KERN_ERR "Failed to get S/PDIF-in resource!!!\n"); + return err; + } + atc->n_daio++; + + src_mgr = atc->rsc_mgrs[SRC]; + src_dsc.multi = 1; + src_dsc.msr = atc->msr; + src_dsc.mode = ARCRW; + for (i = 0, atc->n_src = 0; i < (2*2); i++) { + err = src_mgr->get_src(src_mgr, &src_dsc, + (struct src **)&atc->srcs[i]); + if (err) + return err; + + atc->n_src++; + } + + srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ + for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { + err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, + (struct srcimp **)&atc->srcimps[i]); + if (err) + return err; + + atc->n_srcimp++; + } + srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */ + for (i = 0; i < (2*1); i++) { + err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, + (struct srcimp **)&atc->srcimps[2*1+i]); + if (err) + return err; + + atc->n_srcimp++; + } + + sum_mgr = atc->rsc_mgrs[SUM]; + sum_dsc.msr = atc->msr; + for (i = 0, atc->n_pcm = 0; i < (2*4); i++) { + err = sum_mgr->get_sum(sum_mgr, &sum_dsc, + (struct sum **)&atc->pcm[i]); + if (err) + return err; + + atc->n_pcm++; + } + + err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); + if (err) { + printk(KERN_ERR "Failed to create mixer obj!!!\n"); + return err; + } + + return 0; +} + +static void +atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, + struct src **srcs, struct srcimp **srcimps) +{ + struct rsc *rscs[2] = {NULL}; + struct src *src = NULL; + struct srcimp *srcimp = NULL; + int i = 0; + + rscs[0] = &dai->daio.rscl; + rscs[1] = &dai->daio.rscr; + for (i = 0; i < 2; i++) { + src = srcs[i]; + srcimp = srcimps[i]; + srcimp->ops->map(srcimp, src, rscs[i]); + src_mgr->src_disable(src_mgr, src); + } + + src_mgr->commit_write(src_mgr); /* Actually disable SRCs */ + + src = srcs[0]; + src->ops->set_pm(src, 1); + for (i = 0; i < 2; i++) { + src = srcs[i]; + src->ops->set_state(src, SRC_STATE_RUN); + src->ops->commit_write(src); + src_mgr->src_enable_s(src_mgr, src); + } + + dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc)); + dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc)); + + dai->ops->set_enb_src(dai, 1); + dai->ops->set_enb_srt(dai, 1); + dai->ops->commit_write(dai); + + src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */ +} + +static void atc_connect_resources(struct ct_atc *atc) +{ + struct dai *dai = NULL; + struct dao *dao = NULL; + struct src *src = NULL; + struct sum *sum = NULL; + struct ct_mixer *mixer = NULL; + struct rsc *rscs[2] = {NULL}; + int i = 0, j = 0; + + mixer = atc->mixer; + + for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) { + mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]); + dao = container_of(atc->daios[j], struct dao, daio); + dao->ops->set_left_input(dao, rscs[0]); + dao->ops->set_right_input(dao, rscs[1]); + } + + dai = container_of(atc->daios[LINEIM], struct dai, daio); + atc_connect_dai(atc->rsc_mgrs[SRC], dai, + (struct src **)&atc->srcs[2], + (struct srcimp **)&atc->srcimps[2]); + src = atc->srcs[2]; + mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); + src = atc->srcs[3]; + mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); + + dai = container_of(atc->daios[SPDIFIO], struct dai, daio); + atc_connect_dai(atc->rsc_mgrs[SRC], dai, + (struct src **)&atc->srcs[0], + (struct srcimp **)&atc->srcimps[0]); + + src = atc->srcs[0]; + mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc); + src = atc->srcs[1]; + mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc); + + for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) { + sum = atc->pcm[j]; + mixer->set_input_left(mixer, i, &sum->rsc); + sum = atc->pcm[j+1]; + mixer->set_input_right(mixer, i, &sum->rsc); + } +} + +static void atc_set_ops(struct ct_atc *atc) +{ + /* Set operations */ + atc->map_audio_buffer = ct_map_audio_buffer; + atc->unmap_audio_buffer = ct_unmap_audio_buffer; + atc->pcm_playback_prepare = atc_pcm_playback_prepare; + atc->pcm_release_resources = atc_pcm_release_resources; + atc->pcm_playback_start = atc_pcm_playback_start; + atc->pcm_playback_stop = atc_pcm_stop; + atc->pcm_playback_position = atc_pcm_playback_position; + atc->pcm_capture_prepare = atc_pcm_capture_prepare; + atc->pcm_capture_start = atc_pcm_capture_start; + atc->pcm_capture_stop = atc_pcm_stop; + atc->pcm_capture_position = atc_pcm_capture_position; + atc->spdif_passthru_playback_prepare = spdif_passthru_playback_prepare; + atc->get_ptp_phys = atc_get_ptp_phys; + atc->select_line_in = atc_select_line_in; + atc->select_mic_in = atc_select_mic_in; + atc->select_digit_io = atc_select_digit_io; + atc->line_front_unmute = atc_line_front_unmute; + atc->line_surround_unmute = atc_line_surround_unmute; + atc->line_clfe_unmute = atc_line_clfe_unmute; + atc->line_rear_unmute = atc_line_rear_unmute; + atc->line_in_unmute = atc_line_in_unmute; + atc->spdif_out_unmute = atc_spdif_out_unmute; + atc->spdif_in_unmute = atc_spdif_in_unmute; + atc->spdif_out_get_status = atc_spdif_out_get_status; + atc->spdif_out_set_status = atc_spdif_out_set_status; + atc->spdif_out_passthru = atc_spdif_out_passthru; + atc->have_digit_io_switch = atc_have_digit_io_switch; +} + +/** + * ct_atc_create - create and initialize a hardware manager + * @card: corresponding alsa card object + * @pci: corresponding kernel pci device object + * @ratc: return created object address in it + * + * Creates and initializes a hardware manager. + * + * Creates kmallocated ct_atc structure. Initializes hardware. + * Returns 0 if suceeds, or negative error code if fails. + */ + +int ct_atc_create(struct snd_card *card, struct pci_dev *pci, + unsigned int rsr, unsigned int msr, struct ct_atc **ratc) +{ + struct ct_atc *atc = NULL; + static struct snd_device_ops ops = { + .dev_free = atc_dev_free, + }; + int err = 0; + + *ratc = NULL; + + atc = kzalloc(sizeof(*atc), GFP_KERNEL); + if (NULL == atc) + return -ENOMEM; + + atc->card = card; + atc->pci = pci; + atc->rsr = rsr; + atc->msr = msr; + + /* Set operations */ + atc_set_ops(atc); + + spin_lock_init(&atc->atc_lock); + spin_lock_init(&atc->vm_lock); + + /* Find card model */ + err = atc_identify_card(atc); + if (err < 0) { + printk(KERN_ERR "ctatc: Card not recognised\n"); + goto error1; + } + + /* Set up device virtual memory management object */ + err = ct_vm_create(&atc->vm); + if (err < 0) + goto error1; + + /* Create all atc hw devices */ + err = atc_create_hw_devs(atc); + if (err < 0) + goto error1; + + /* Get resources */ + err = atc_get_resources(atc); + if (err < 0) + goto error1; + + /* Build topology */ + atc_connect_resources(atc); + + atc->create_alsa_devs = ct_create_alsa_devs; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); + if (err < 0) + goto error1; + + snd_card_set_dev(card, &pci->dev); + + *ratc = atc; + return 0; + +error1: + ct_atc_destroy(atc); + printk(KERN_ERR "Something wrong!!!\n"); + return err; +} + diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h new file mode 100644 index 00000000000..286c993d461 --- /dev/null +++ b/sound/pci/ctxfi/ctatc.h @@ -0,0 +1,155 @@ +/** + * 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 +#include +#include +#include +#include + +#include "ctvmem.h" +#include "ctresource.h" + +enum CTALSADEVS { /* Types of alsa devices */ + FRONT, + REAR, + CLFE, + SURROUND, + IEC958, + MIXER, + NUM_CTALSADEVS /* This should always be the last */ +}; + +enum CTCARDS { + CTSB0760, + CTHENDRIX, + CTSB08801, + CTSB08802, + CTSB08803, + NUM_CTCARDS /* 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; + +/* alsa pcm stream descriptor */ +struct ct_atc_pcm { + struct snd_pcm_substream *substream; + void (*interrupt)(struct ct_atc_pcm *apcm); + unsigned int started:1; + unsigned int stop_timer:1; + struct timer_list timer; + spinlock_t timer_lock; + unsigned int position; + + /* Only mono and interleaved modes are supported now. */ + struct ct_vm_block *vm_block; + 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 */ + + const struct ct_atc_chip_details *chip_details; + enum CTCARDS model; + /* Create all alsa devices */ + int (*create_alsa_devs)(struct ct_atc *atc); + + 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; + spinlock_t vm_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; +}; + + +int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, + unsigned int rsr, unsigned int msr, + struct ct_atc **ratc); + +#endif /* CTATC_H */ diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c new file mode 100644 index 00000000000..a2aea399eba --- /dev/null +++ b/sound/pci/ctxfi/ctdaio.c @@ -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 +#include + +#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->get_chip_type(hw)) { + 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 = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + 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 = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + 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 = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + 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 = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + 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 = 0; + + 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 = 0; + unsigned int idx_l = 0, idx_r = 0; + + switch (((struct hw *)hw)->get_chip_type(hw)) { + 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)->get_chip_type(hw)) { + 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 = 0; + int err = 0; + + 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 = 0; + struct hw *hw = mgr->mgr.hw; + unsigned int rsr = 0, msr = 0; + + 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 = 0; + 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 = 0; + + 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 = 0; + + 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 = 0, i = 0; + 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; +} + diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h new file mode 100644 index 00000000000..0f52ce571ee --- /dev/null +++ b/sound/pci/ctxfi/ctdaio.h @@ -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 +#include + +/* 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 */ diff --git a/sound/pci/ctxfi/ctdrv.h b/sound/pci/ctxfi/ctdrv.h new file mode 100644 index 00000000000..f776a44f52c --- /dev/null +++ b/sound/pci/ctxfi/ctdrv.h @@ -0,0 +1,30 @@ +/** + * 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 ctdrv.h + * + * @breaf + * This file contains the definition of card IDs supported by this driver. + * + * @author Liu Chun + * + */ + +#ifndef CTDRV_H +#define CTDRV_H + +#define PCI_VENDOR_CREATIVE 0x1102 +#define PCI_DEVICE_CREATIVE_20K1 0x0005 +#define PCI_DEVICE_CREATIVE_20K2 0x000B +#define PCI_SUBVENDOR_CREATIVE 0x1102 +#define PCI_SUBSYS_CREATIVE_SB0760 0x0024 +#define PCI_SUBSYS_CREATIVE_SB08801 0x0041 +#define PCI_SUBSYS_CREATIVE_SB08802 0x0042 +#define PCI_SUBSYS_CREATIVE_SB08803 0x0043 +#define PCI_SUBSYS_CREATIVE_HENDRIX 0x6000 + +#endif /* CTDRV_H */ diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c new file mode 100644 index 00000000000..8e58860f641 --- /dev/null +++ b/sound/pci/ctxfi/cthardware.c @@ -0,0 +1,108 @@ +/** + * 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 + +static enum CHIPTYP get_chip_type(struct hw *hw) +{ + enum CHIPTYP type = ATCNONE; + + switch (hw->pci->device) { + case 0x0005: /* 20k1 device */ + type = ATC20K1; + break; + case 0x000B: /* 20k2 device */ + type = ATC20K2; + break; + default: + type = ATCNONE; + break; + } + + return type; +} + +int create_hw_obj(struct pci_dev *pci, struct hw **rhw) +{ + int err = 0; + + switch (pci->device) { + case 0x0005: /* 20k1 device */ + err = create_20k1_hw_obj(rhw); + break; + case 0x000B: /* 20k2 device */ + err = create_20k2_hw_obj(rhw); + break; + default: + err = -ENODEV; + break; + } + if (err) + return err; + + (*rhw)->pci = pci; + (*rhw)->get_chip_type = get_chip_type; + + return 0; +} + +int destroy_hw_obj(struct hw *hw) +{ + int err = 0; + + 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); +} + diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h new file mode 100644 index 00000000000..b0512df8b33 --- /dev/null +++ b/sound/pci/ctxfi/cthardware.h @@ -0,0 +1,160 @@ +/** + * 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 +#include + +enum CHIPTYP { + ATC20K1, + ATC20K2, + ATCNONE +}; + +/* 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); + enum CHIPTYP (*get_chip_type)(struct hw *hw); + 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); + + struct pci_dev *pci; /* the pci kernel structure of this card */ + int irq; + unsigned long io_base; + unsigned long mem_base; +}; + +int create_hw_obj(struct pci_dev *pci, 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); + +#endif /* CTHARDWARE_H */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c new file mode 100644 index 00000000000..53572d92ef5 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.c @@ -0,0 +1,2230 @@ +/** + * 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.c + * + * @Brief + * This file contains the implementation of hardware access methord for 20k1. + * + * @Author Liu Chun + * @Date Jun 24 2008 + * + */ + +#include "cthw20k1.h" +#include "ct20k1reg.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ + +struct hw20k1 { + struct hw hw; + spinlock_t reg_20k1_lock; + spinlock_t reg_pci_lock; +}; + +static u32 hw_read_20kx(struct hw *hw, u32 reg); +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); +static u32 hw_read_pci(struct hw *hw, u32 reg); +static void hw_write_pci(struct hw *hw, u32 reg, u32 data); + +/* + * Type definition block. + * The layout of control structures can be directly applied on 20k2 chip. + */ + +/* + * SRC control block definitions. + */ + +/* SRC resource control block */ +#define SRCCTL_STATE 0x00000007 +#define SRCCTL_BM 0x00000008 +#define SRCCTL_RSR 0x00000030 +#define SRCCTL_SF 0x000001C0 +#define SRCCTL_WR 0x00000200 +#define SRCCTL_PM 0x00000400 +#define SRCCTL_ROM 0x00001800 +#define SRCCTL_VO 0x00002000 +#define SRCCTL_ST 0x00004000 +#define SRCCTL_IE 0x00008000 +#define SRCCTL_ILSZ 0x000F0000 +#define SRCCTL_BP 0x00100000 + +#define SRCCCR_CISZ 0x000007FF +#define SRCCCR_CWA 0x001FF800 +#define SRCCCR_D 0x00200000 +#define SRCCCR_RS 0x01C00000 +#define SRCCCR_NAL 0x3E000000 +#define SRCCCR_RA 0xC0000000 + +#define SRCCA_CA 0x03FFFFFF +#define SRCCA_RS 0x1C000000 +#define SRCCA_NAL 0xE0000000 + +#define SRCSA_SA 0x03FFFFFF + +#define SRCLA_LA 0x03FFFFFF + +/* Mixer Parameter Ring ram Low and Hight register. + * Fixed-point value in 8.24 format for parameter channel */ +#define MPRLH_PITCH 0xFFFFFFFF + +/* SRC resource register dirty flags */ +union src_dirty { + struct { + u16 ctl:1; + u16 ccr:1; + u16 sa:1; + u16 la:1; + u16 ca:1; + u16 mpr:1; + u16 czbfs:1; /* Clear Z-Buffers */ + u16 rsv:9; + } bf; + u16 data; +}; + +struct src_rsc_ctrl_blk { + unsigned int ctl; + unsigned int ccr; + unsigned int ca; + unsigned int sa; + unsigned int la; + unsigned int mpr; + union src_dirty dirty; +}; + +/* SRC manager control block */ +union src_mgr_dirty { + struct { + u16 enb0:1; + u16 enb1:1; + u16 enb2:1; + u16 enb3:1; + u16 enb4:1; + u16 enb5:1; + u16 enb6:1; + u16 enb7:1; + u16 enbsa:1; + u16 rsv:7; + } bf; + u16 data; +}; + +struct src_mgr_ctrl_blk { + unsigned int enbsa; + unsigned int enb[8]; + union src_mgr_dirty dirty; +}; + +/* SRCIMP manager control block */ +#define SRCAIM_ARC 0x00000FFF +#define SRCAIM_NXT 0x00FF0000 +#define SRCAIM_SRC 0xFF000000 + +struct srcimap { + unsigned int srcaim; + unsigned int idx; +}; + +/* SRCIMP manager register dirty flags */ +union srcimp_mgr_dirty { + struct { + u16 srcimap:1; + u16 rsv:15; + } bf; + u16 data; +}; + +struct srcimp_mgr_ctrl_blk { + struct srcimap srcimap; + union srcimp_mgr_dirty dirty; +}; + +/* + * Function implementation block. + */ + +static int src_get_rsc_ctrl_blk(void **rblk) +{ + struct src_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_put_rsc_ctrl_blk(void *blk) +{ + kfree((struct src_rsc_ctrl_blk *)blk); + + return 0; +} + +static int src_set_state(void *blk, unsigned int state) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_STATE, state); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bm(void *blk, unsigned int bm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BM, bm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rsr(void *blk, unsigned int rsr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_RSR, rsr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_sf(void *blk, unsigned int sf) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_SF, sf); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_wr(void *blk, unsigned int wr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_WR, wr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_pm(void *blk, unsigned int pm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_PM, pm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rom(void *blk, unsigned int rom) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ROM, rom); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_vo(void *blk, unsigned int vo) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_VO, vo); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_st(void *blk, unsigned int st) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ST, st); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ie(void *blk, unsigned int ie) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_IE, ie); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ilsz(void *blk, unsigned int ilsz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bp(void *blk, unsigned int bp) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BP, bp); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_cisz(void *blk, unsigned int cisz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ccr, SRCCCR_CISZ, cisz); + ctl->dirty.bf.ccr = 1; + return 0; +} + +static int src_set_ca(void *blk, unsigned int ca) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ca, SRCCA_CA, ca); + ctl->dirty.bf.ca = 1; + return 0; +} + +static int src_set_sa(void *blk, unsigned int sa) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->sa, SRCSA_SA, sa); + ctl->dirty.bf.sa = 1; + return 0; +} + +static int src_set_la(void *blk, unsigned int la) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->la, SRCLA_LA, la); + ctl->dirty.bf.la = 1; + return 0; +} + +static int src_set_pitch(void *blk, unsigned int pitch) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->mpr, MPRLH_PITCH, pitch); + ctl->dirty.bf.mpr = 1; + return 0; +} + +static int src_set_clear_zbufs(void *blk, unsigned int clear) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); + return 0; +} + +static int src_set_dirty(void *blk, unsigned int flags) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int src_set_dirty_all(void *blk) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +#define AR_SLOT_SIZE 4096 +#define AR_SLOT_BLOCK_SIZE 16 +#define AR_PTS_PITCH 6 +#define AR_PARAM_SRC_OFFSET 0x60 + +static unsigned int src_param_pitch_mixer(unsigned int src_idx) +{ + return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE + - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; + +} + +static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + int i = 0; + + if (ctl->dirty.bf.czbfs) { + /* Clear Z-Buffer registers */ + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0); + + for (i = 0; i < 4; i++) + hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0); + + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0); + + ctl->dirty.bf.czbfs = 0; + } + if (ctl->dirty.bf.mpr) { + /* Take the parameter mixer resource in the same group as that + * the idx src is in for simplicity. Unlike src, all conjugate + * parameter mixer resources must be programmed for + * corresponding conjugate src resources. */ + unsigned int pm_idx = src_param_pitch_mixer(idx); + hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr); + hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3); + hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0); + ctl->dirty.bf.mpr = 0; + } + if (ctl->dirty.bf.sa) { + hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa); + ctl->dirty.bf.sa = 0; + } + if (ctl->dirty.bf.la) { + hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la); + ctl->dirty.bf.la = 0; + } + if (ctl->dirty.bf.ca) { + hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca); + ctl->dirty.bf.ca = 0; + } + + /* Write srccf register */ + hw_write_20kx(hw, SRCCF+idx*0x100, 0x0); + + if (ctl->dirty.bf.ccr) { + hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr); + ctl->dirty.bf.ccr = 0; + } + if (ctl->dirty.bf.ctl) { + hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl); + ctl->dirty.bf.ctl = 0; + } + + return 0; +} + +static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100); + ctl->dirty.bf.ca = 0; + + return get_field(ctl->ca, SRCCA_CA); +} + +static unsigned int src_get_dirty(void *blk) +{ + return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; +} + +static unsigned int src_dirty_conj_mask(void) +{ + return 0x20; +} + +static int src_mgr_enbs_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0); + ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + return 0; +} + +static int src_mgr_enb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_dsb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_commit_write(struct hw *hw, void *blk) +{ + struct src_mgr_ctrl_blk *ctl = blk; + int i = 0; + unsigned int ret = 0; + + if (ctl->dirty.bf.enbsa) { + do { + ret = hw_read_20kx(hw, SRCENBSTAT); + } while (ret & 0x1); + hw_write_20kx(hw, SRCENBS, ctl->enbsa); + ctl->dirty.bf.enbsa = 0; + } + for (i = 0; i < 8; i++) { + if ((ctl->dirty.data & (0x1 << i))) { + hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]); + ctl->dirty.data &= ~(0x1 << i); + } + } + + return 0; +} + +static int src_mgr_get_ctrl_blk(void **rblk) +{ + struct src_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct src_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_get_ctrl_blk(void **rblk) +{ + struct srcimp_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int srcimp_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct srcimp_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + ctl->srcimap.idx = addr; + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_commit_write(struct hw *hw, void *blk) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srcimap) { + hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100, + ctl->srcimap.srcaim); + ctl->dirty.bf.srcimap = 0; + } + + return 0; +} + +/* + * AMIXER control block definitions. + */ + +#define AMOPLO_M 0x00000003 +#define AMOPLO_X 0x0003FFF0 +#define AMOPLO_Y 0xFFFC0000 + +#define AMOPHI_SADR 0x000000FF +#define AMOPHI_SE 0x80000000 + +/* AMIXER resource register dirty flags */ +union amixer_dirty { + struct { + u16 amoplo:1; + u16 amophi:1; + u16 rsv:14; + } bf; + u16 data; +}; + +/* AMIXER resource control block */ +struct amixer_rsc_ctrl_blk { + unsigned int amoplo; + unsigned int amophi; + union amixer_dirty dirty; +}; + +static int amixer_set_mode(void *blk, unsigned int mode) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_M, mode); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_iv(void *blk, unsigned int iv) +{ + /* 20k1 amixer does not have this field */ + return 0; +} + +static int amixer_set_x(void *blk, unsigned int x) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_X, x); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_y(void *blk, unsigned int y) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_Y, y); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_sadr(void *blk, unsigned int sadr) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SADR, sadr); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_se(void *blk, unsigned int se) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SE, se); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_dirty(void *blk, unsigned int flags) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int amixer_set_dirty_all(void *blk) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { + hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo); + ctl->dirty.bf.amoplo = 0; + hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi); + ctl->dirty.bf.amophi = 0; + } + + return 0; +} + +static int amixer_get_y(void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + return get_field(ctl->amoplo, AMOPLO_Y); +} + +static unsigned int amixer_get_dirty(void *blk) +{ + return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; +} + +static int amixer_rsc_get_ctrl_blk(void **rblk) +{ + struct amixer_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int amixer_rsc_put_ctrl_blk(void *blk) +{ + kfree((struct amixer_rsc_ctrl_blk *)blk); + + return 0; +} + +static int amixer_mgr_get_ctrl_blk(void **rblk) +{ + /*amixer_mgr_ctrl_blk_t *blk;*/ + + *rblk = NULL; + /*blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk;*/ + + return 0; +} + +static int amixer_mgr_put_ctrl_blk(void *blk) +{ + /*kfree((amixer_mgr_ctrl_blk_t *)blk);*/ + + return 0; +} + +/* + * DAIO control block definitions. + */ + +/* Receiver Sample Rate Tracker Control register */ +#define SRTCTL_SRCR 0x000000FF +#define SRTCTL_SRCL 0x0000FF00 +#define SRTCTL_RSR 0x00030000 +#define SRTCTL_DRAT 0x000C0000 +#define SRTCTL_RLE 0x10000000 +#define SRTCTL_RLP 0x20000000 +#define SRTCTL_EC 0x40000000 +#define SRTCTL_ET 0x80000000 + +/* DAIO Receiver register dirty flags */ +union dai_dirty { + struct { + u16 srtctl:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* DAIO Receiver control block */ +struct dai_ctrl_blk { + unsigned int srtctl; + union dai_dirty dirty; +}; + +/* S/PDIF Transmitter register dirty flags */ +union dao_dirty { + struct { + u16 spos:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* S/PDIF Transmitter control block */ +struct dao_ctrl_blk { + unsigned int spos; /* S/PDIF Output Channel Status Register */ + union dao_dirty dirty; +}; + +/* Audio Input Mapper RAM */ +#define AIM_ARC 0x00000FFF +#define AIM_NXT 0x007F0000 + +struct daoimap { + unsigned int aim; + unsigned int idx; +}; + +/* I2S Transmitter/Receiver Control register */ +#define I2SCTL_EA 0x00000004 +#define I2SCTL_EI 0x00000010 + +/* S/PDIF Transmitter Control register */ +#define SPOCTL_OE 0x00000001 +#define SPOCTL_OS 0x0000000E +#define SPOCTL_RIV 0x00000010 +#define SPOCTL_LIV 0x00000020 +#define SPOCTL_SR 0x000000C0 + +/* S/PDIF Receiver Control register */ +#define SPICTL_EN 0x00000001 +#define SPICTL_I24 0x00000002 +#define SPICTL_IB 0x00000004 +#define SPICTL_SM 0x00000008 +#define SPICTL_VM 0x00000010 + +/* DAIO manager register dirty flags */ +union daio_mgr_dirty { + struct { + u32 i2soctl:4; + u32 i2sictl:4; + u32 spoctl:4; + u32 spictl:4; + u32 daoimap:1; + u32 rsv:15; + } bf; + u32 data; +}; + +/* DAIO manager control block */ +struct daio_mgr_ctrl_blk { + unsigned int i2sctl; + unsigned int spoctl; + unsigned int spictl; + struct daoimap daoimap; + union daio_mgr_dirty dirty; +}; + +static int dai_srt_set_srcr(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_SRCR, src); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_srcl(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_SRCL, src); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_rsr(void *blk, unsigned int rsr) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_RSR, rsr); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_drat(void *blk, unsigned int drat) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_DRAT, drat); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_ec(void *blk, unsigned int ec) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_et(void *blk, unsigned int et) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dai_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srtctl) { + if (idx < 4) { + /* S/PDIF SRTs */ + hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl); + } else { + /* I2S SRT */ + hw_write_20kx(hw, SRTICTL, ctl->srtctl); + } + ctl->dirty.bf.srtctl = 0; + } + + return 0; +} + +static int dai_get_ctrl_blk(void **rblk) +{ + struct dai_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dai_put_ctrl_blk(void *blk) +{ + kfree((struct dai_ctrl_blk *)blk); + + return 0; +} + +static int dao_set_spos(void *blk, unsigned int spos) +{ + ((struct dao_ctrl_blk *)blk)->spos = spos; + ((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1; + return 0; +} + +static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dao_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.spos) { + if (idx < 4) { + /* S/PDIF SPOSx */ + hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos); + } + ctl->dirty.bf.spos = 0; + } + + return 0; +} + +static int dao_get_spos(void *blk, unsigned int *spos) +{ + *spos = ((struct dao_ctrl_blk *)blk)->spos; + return 0; +} + +static int dao_get_ctrl_blk(void **rblk) +{ + struct dao_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dao_put_ctrl_blk(void *blk) +{ + kfree((struct dao_ctrl_blk *)blk); + + return 0; +} + +static int daio_mgr_enb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF input */ + set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1); + ctl->dirty.bf.spictl |= (0x1 << idx); + } else { + /* I2S input */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1); + ctl->dirty.bf.i2sictl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_dsb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF input */ + set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0); + ctl->dirty.bf.spictl |= (0x1 << idx); + } else { + /* I2S input */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0); + ctl->dirty.bf.i2sictl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_enb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1); + ctl->dirty.bf.spoctl |= (0x1 << idx); + } else { + /* I2S output */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1); + ctl->dirty.bf.i2soctl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_dsb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0); + ctl->dirty.bf.spoctl |= (0x1 << idx); + } else { + /* I2S output */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0); + ctl->dirty.bf.i2soctl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + switch ((conf & 0x7)) { + case 0: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3); + break; /* CDIF */ + case 1: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0); + break; + case 2: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1); + break; + case 4: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2); + break; + default: + break; + } + set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8), + (conf >> 4) & 0x1); /* Non-audio */ + set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8), + (conf >> 4) & 0x1); /* Non-audio */ + set_field(&ctl->spoctl, SPOCTL_OS << (idx*8), + ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */ + + ctl->dirty.bf.spoctl |= (0x1 << idx); + } else { + /* I2S output */ + /*idx %= 4; */ + } + return 0; +} + +static int daio_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_ARC, slot); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_NXT, next); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + ctl->daoimap.idx = addr; + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_commit_write(struct hw *hw, void *blk) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + int i = 0; + + if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) { + for (i = 0; i < 4; i++) { + if ((ctl->dirty.bf.i2sictl & (0x1 << i))) + ctl->dirty.bf.i2sictl &= ~(0x1 << i); + + if ((ctl->dirty.bf.i2soctl & (0x1 << i))) + ctl->dirty.bf.i2soctl &= ~(0x1 << i); + } + hw_write_20kx(hw, I2SCTL, ctl->i2sctl); + mdelay(1); + } + if (ctl->dirty.bf.spoctl) { + for (i = 0; i < 4; i++) { + if ((ctl->dirty.bf.spoctl & (0x1 << i))) + ctl->dirty.bf.spoctl &= ~(0x1 << i); + } + hw_write_20kx(hw, SPOCTL, ctl->spoctl); + mdelay(1); + } + if (ctl->dirty.bf.spictl) { + for (i = 0; i < 4; i++) { + if ((ctl->dirty.bf.spictl & (0x1 << i))) + ctl->dirty.bf.spictl &= ~(0x1 << i); + } + hw_write_20kx(hw, SPICTL, ctl->spictl); + mdelay(1); + } + if (ctl->dirty.bf.daoimap) { + hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4, + ctl->daoimap.aim); + ctl->dirty.bf.daoimap = 0; + } + + return 0; +} + +static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) +{ + struct daio_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + blk->i2sctl = hw_read_20kx(hw, I2SCTL); + blk->spoctl = hw_read_20kx(hw, SPOCTL); + blk->spictl = hw_read_20kx(hw, SPICTL); + + *rblk = blk; + + return 0; +} + +static int daio_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct daio_mgr_ctrl_blk *)blk); + + return 0; +} + +/* Card hardware initialization block */ +struct dac_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct adc_conf { + unsigned int msr; /* master sample rate in rsrs */ + unsigned char input; /* the input source of ADC */ + unsigned char mic20db; /* boost mic by 20db if input is microphone */ +}; + +struct daio_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct trn_conf { + unsigned long vm_pgt_phys; +}; + +static int hw_daio_init(struct hw *hw, const struct daio_conf *info) +{ + u32 i2sorg = 0; + u32 spdorg = 0; + + /* Read I2S CTL. Keep original value. */ + /*i2sorg = hw_read_20kx(hw, I2SCTL);*/ + i2sorg = 0x94040404; /* enable all audio out and I2S-D input */ + /* Program I2S with proper master sample rate and enable + * the correct I2S channel. */ + i2sorg &= 0xfffffffc; + + /* Enable S/PDIF-out-A in fixed 24-bit data + * format and default to 48kHz. */ + /* Disable all before doing any changes. */ + hw_write_20kx(hw, SPOCTL, 0x0); + spdorg = 0x05; + + switch (info->msr) { + case 1: + i2sorg |= 1; + spdorg |= (0x0 << 6); + break; + case 2: + i2sorg |= 2; + spdorg |= (0x1 << 6); + break; + case 4: + i2sorg |= 3; + spdorg |= (0x2 << 6); + break; + default: + i2sorg |= 1; + break; + } + + hw_write_20kx(hw, I2SCTL, i2sorg); + hw_write_20kx(hw, SPOCTL, spdorg); + + /* Enable S/PDIF-in-A in fixed 24-bit data format. */ + /* Disable all before doing any changes. */ + hw_write_20kx(hw, SPICTL, 0x0); + mdelay(1); + spdorg = 0x0a0a0a0a; + hw_write_20kx(hw, SPICTL, spdorg); + mdelay(1); + + return 0; +} + +/* TRANSPORT operations */ +static int hw_trn_init(struct hw *hw, const struct trn_conf *info) +{ + u32 trnctl = 0; + unsigned long ptp_phys_low = 0, ptp_phys_high = 0; + + /* Set up device page table */ + if ((~0UL) == info->vm_pgt_phys) { + printk(KERN_ERR "Wrong device page table page address!\n"); + return -1; + } + + trnctl = 0x13; /* 32-bit, 4k-size page */ +#if BITS_PER_LONG == 64 + ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); + ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); + trnctl |= (1<<2); +#elif BITS_PER_LONG == 32 + ptp_phys_low = info->vm_pgt_phys & (~0UL); + ptp_phys_high = 0; +#else +# error "Unknown BITS_PER_LONG!" +#endif +#if PAGE_SIZE == 8192 + trnctl |= (1<<5); +#endif + hw_write_20kx(hw, PTPALX, ptp_phys_low); + hw_write_20kx(hw, PTPAHX, ptp_phys_high); + hw_write_20kx(hw, TRNCTL, trnctl); + hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */ + + return 0; +} + +/* Card initialization */ +#define GCTL_EAC 0x00000001 +#define GCTL_EAI 0x00000002 +#define GCTL_BEP 0x00000004 +#define GCTL_BES 0x00000008 +#define GCTL_DSP 0x00000010 +#define GCTL_DBP 0x00000020 +#define GCTL_ABP 0x00000040 +#define GCTL_TBP 0x00000080 +#define GCTL_SBP 0x00000100 +#define GCTL_FBP 0x00000200 +#define GCTL_XA 0x00000400 +#define GCTL_ET 0x00000800 +#define GCTL_PR 0x00001000 +#define GCTL_MRL 0x00002000 +#define GCTL_SDE 0x00004000 +#define GCTL_SDI 0x00008000 +#define GCTL_SM 0x00010000 +#define GCTL_SR 0x00020000 +#define GCTL_SD 0x00040000 +#define GCTL_SE 0x00080000 +#define GCTL_AID 0x00100000 + +static int hw_pll_init(struct hw *hw, unsigned int rsr) +{ + unsigned int pllctl; + int i = 0; + + pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731; + for (i = 0; i < 3; i++) { + if (hw_read_20kx(hw, PLLCTL) == pllctl) + break; + + hw_write_20kx(hw, PLLCTL, pllctl); + mdelay(40); + } + if (i >= 3) { + printk(KERN_ALERT "PLL initialization failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +static int hw_auto_init(struct hw *hw) +{ + unsigned int gctl; + int i; + + gctl = hw_read_20kx(hw, GCTL); + set_field(&gctl, GCTL_EAI, 0); + hw_write_20kx(hw, GCTL, gctl); + set_field(&gctl, GCTL_EAI, 1); + hw_write_20kx(hw, GCTL, gctl); + mdelay(10); + for (i = 0; i < 400000; i++) { + gctl = hw_read_20kx(hw, GCTL); + if (get_field(gctl, GCTL_AID)) + break; + } + if (!get_field(gctl, GCTL_AID)) { + printk(KERN_ALERT "Card Auto-init failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +static int i2c_unlock(struct hw *hw) +{ + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + return 0; + + hw_write_pci(hw, 0xcc, 0x8c); + hw_write_pci(hw, 0xcc, 0x0e); + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + return 0; + + hw_write_pci(hw, 0xcc, 0xee); + hw_write_pci(hw, 0xcc, 0xaa); + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + return 0; + + return -1; +} + +static void i2c_lock(struct hw *hw) +{ + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + hw_write_pci(hw, 0xcc, 0x00); +} + +static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) +{ + unsigned int ret = 0; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); + hw_write_pci(hw, 0xE0, device); + hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff)); +} + +/* DAC operations */ + +static int hw_reset_dac(struct hw *hw) +{ + u32 i = 0; + u16 gpioorg = 0; + unsigned int ret = 0; + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); + hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ + + /* To be effective, need to reset the DAC twice. */ + for (i = 0; i < 2; i++) { + /* set gpio */ + mdelay(100); + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xfffd; + hw_write_20kx(hw, GPIO, gpioorg); + mdelay(1); + hw_write_20kx(hw, GPIO, gpioorg | 0x2); + } + + i2c_write(hw, 0x00180080, 0x01, 0x80); + i2c_write(hw, 0x00180080, 0x02, 0x10); + + i2c_lock(hw); + + return 0; +} + +static int hw_dac_init(struct hw *hw, const struct dac_conf *info) +{ + u32 data = 0; + u16 gpioorg = 0; + u16 subsys_id = 0; + unsigned int ret = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x, unmute outputs */ + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xffbf; /* set GPIO6 to low */ + gpioorg |= 2; /* set GPIO1 to high */ + hw_write_20kx(hw, GPIO, gpioorg); + return 0; + } + + /* mute outputs */ + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xffbf; + hw_write_20kx(hw, GPIO, gpioorg); + + hw_reset_dac(hw); + + if (i2c_unlock(hw)) + return -1; + + hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); + + switch (info->msr) { + case 1: + data = 0x24; + break; + case 2: + data = 0x25; + break; + case 4: + data = 0x26; + break; + default: + data = 0x24; + break; + } + + i2c_write(hw, 0x00180080, 0x06, data); + i2c_write(hw, 0x00180080, 0x09, data); + i2c_write(hw, 0x00180080, 0x0c, data); + i2c_write(hw, 0x00180080, 0x0f, data); + + i2c_lock(hw); + + /* unmute outputs */ + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg = gpioorg | 0x40; + hw_write_20kx(hw, GPIO, gpioorg); + + return 0; +} + +/* ADC operations */ + +static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + return data; +} + +static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data = ((data & (0x1<<7)) && (data & (0x1<<8))); + break; + case ADC_LINEIN: + data = (!(data & (0x1<<7)) && (data & (0x1<<8))); + break; + case ADC_NONE: /* Digital I/O */ + data = (!(data & (0x1<<8))); + break; + default: + data = 0; + } + return data; +} + +static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data = (data & (0x1 << 7)) ? 1 : 0; + break; + case ADC_LINEIN: + data = (data & (0x1 << 7)) ? 0 : 1; + break; + default: + data = 0; + } + return data; +} + +static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) +{ + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x cards */ + return is_adc_input_selected_SB055x(hw, type); + } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + return is_adc_input_selected_hendrix(hw, type); + } else if ((subsys_id & 0xf000) == 0x6000) { + /* Vista compatible cards */ + return is_adc_input_selected_hendrix(hw, type); + } else { + return is_adc_input_selected_SBx(hw, type); + } +} + +static int +adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) +{ + u32 data = 0; + + /* + * check and set the following GPIO bits accordingly + * ADC_Gain = GPIO2 + * DRM_off = GPIO3 + * Mic_Pwr_on = GPIO7 + * Digital_IO_Sel = GPIO8 + * Mic_Sw = GPIO9 + * Aux/MicLine_Sw = GPIO12 + */ + data = hw_read_20kx(hw, GPIO); + data &= 0xec73; + switch (type) { + case ADC_MICIN: + data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ; + data |= boost ? (0x1<<2) : 0; + break; + case ADC_LINEIN: + data |= (0x1<<8); + break; + case ADC_AUX: + data |= (0x1<<8) | (0x1<<12); + break; + case ADC_NONE: + data |= (0x1<<12); /* set to digital */ + break; + default: + return -1; + } + + hw_write_20kx(hw, GPIO, data); + + return 0; +} + + +static int +adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) +{ + u32 data = 0; + u32 i2c_data = 0; + unsigned int ret = 0; + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); /* i2c ready poll */ + /* set i2c access mode as Direct Control */ + hw_write_pci(hw, 0xEC, 0x05); + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data |= ((0x1 << 7) | (0x1 << 8)); + i2c_data = 0x1; /* Mic-in */ + break; + case ADC_LINEIN: + data &= ~(0x1 << 7); + data |= (0x1 << 8); + i2c_data = 0x2; /* Line-in */ + break; + case ADC_NONE: + data &= ~(0x1 << 8); + i2c_data = 0x0; /* set to Digital */ + break; + default: + i2c_lock(hw); + return -1; + } + hw_write_20kx(hw, GPIO, data); + i2c_write(hw, 0x001a0080, 0x2a, i2c_data); + if (boost) { + i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ + } else { + i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ + } + + i2c_lock(hw); + + return 0; +} + +static int +adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) +{ + u32 data = 0; + u32 i2c_data = 0; + unsigned int ret = 0; + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); /* i2c ready poll */ + /* set i2c access mode as Direct Control */ + hw_write_pci(hw, 0xEC, 0x05); + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data |= (0x1 << 7); + i2c_data = 0x1; /* Mic-in */ + break; + case ADC_LINEIN: + data &= ~(0x1 << 7); + i2c_data = 0x2; /* Line-in */ + break; + default: + i2c_lock(hw); + return -1; + } + hw_write_20kx(hw, GPIO, data); + i2c_write(hw, 0x001a0080, 0x2a, i2c_data); + if (boost) { + i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ + } else { + i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ + } + + i2c_lock(hw); + + return 0; +} + +static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) +{ + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x cards */ + return adc_input_select_SB055x(hw, type, (ADC_MICIN == type)); + } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); + } else if ((subsys_id & 0xf000) == 0x6000) { + /* Vista compatible cards */ + return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); + } else { + return adc_input_select_SBx(hw, type, (ADC_MICIN == type)); + } +} + +static int adc_init_SB055x(struct hw *hw, int input, int mic20db) +{ + return adc_input_select_SB055x(hw, input, mic20db); +} + +static int adc_init_SBx(struct hw *hw, int input, int mic20db) +{ + u16 gpioorg; + u16 input_source; + u32 adcdata = 0; + unsigned int ret = 0; + + input_source = 0x100; /* default to analog */ + switch (input) { + case ADC_MICIN: + adcdata = 0x1; + input_source = 0x180; /* set GPIO7 to select Mic */ + break; + case ADC_LINEIN: + adcdata = 0x2; + break; + case ADC_VIDEO: + adcdata = 0x4; + break; + case ADC_AUX: + adcdata = 0x8; + break; + case ADC_NONE: + adcdata = 0x0; + input_source = 0x0; /* set to Digital */ + break; + default: + break; + } + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); /* i2c ready poll */ + hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ + + i2c_write(hw, 0x001a0080, 0x0e, 0x08); + i2c_write(hw, 0x001a0080, 0x18, 0x0a); + i2c_write(hw, 0x001a0080, 0x28, 0x86); + i2c_write(hw, 0x001a0080, 0x2a, adcdata); + + if (mic20db) { + i2c_write(hw, 0x001a0080, 0x1c, 0xf7); + i2c_write(hw, 0x001a0080, 0x1e, 0xf7); + } else { + i2c_write(hw, 0x001a0080, 0x1c, 0xcf); + i2c_write(hw, 0x001a0080, 0x1e, 0xcf); + } + + if (!(hw_read_20kx(hw, ID0) & 0x100)) + i2c_write(hw, 0x001a0080, 0x16, 0x26); + + i2c_lock(hw); + + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xfe7f; + gpioorg |= input_source; + hw_write_20kx(hw, GPIO, gpioorg); + + return 0; +} + +static int hw_adc_init(struct hw *hw, const struct adc_conf *info) +{ + int err = 0; + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* Sb055x card */ + err = adc_init_SB055x(hw, info->input, info->mic20db); + } else { + err = adc_init_SBx(hw, info->input, info->mic20db); + } + + return err; +} + +static int hw_have_digit_io_switch(struct hw *hw) +{ + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + /* SB073x and Vista compatible cards have no digit IO switch */ + return !((subsys_id == 0x0029) || (subsys_id == 0x0031) + || ((subsys_id & 0xf000) == 0x6000)); +} + +#define UAA_CFG_PWRSTATUS 0x44 +#define UAA_CFG_SPACE_FLAG 0xA0 +#define UAA_CORE_CHANGE 0x3FFC +static int uaa_to_xfi(struct pci_dev *pci) +{ + unsigned int bar0, bar1, bar2, bar3, bar4, bar5; + unsigned int cmd, irq, cl_size, l_timer, pwr; + unsigned int CTLA, CTLZ, CTLL, CTLX, CTL_, CTLF, CTLi; + unsigned int is_uaa = 0; + unsigned int data[4] = {0}; + unsigned int io_base; + void *mem_base; + int i = 0; + + /* By default, Hendrix card UAA Bar0 should be using memory... */ + io_base = pci_resource_start(pci, 0); + mem_base = ioremap(io_base, pci_resource_len(pci, 0)); + if (NULL == mem_base) + return -ENOENT; + + CTLX = ___constant_swab32(*((unsigned int *)"CTLX")); + CTL_ = ___constant_swab32(*((unsigned int *)"CTL-")); + CTLF = ___constant_swab32(*((unsigned int *)"CTLF")); + CTLi = ___constant_swab32(*((unsigned int *)"CTLi")); + CTLA = ___constant_swab32(*((unsigned int *)"CTLA")); + CTLZ = ___constant_swab32(*((unsigned int *)"CTLZ")); + CTLL = ___constant_swab32(*((unsigned int *)"CTLL")); + + /* Read current mode from Mode Change Register */ + for (i = 0; i < 4; i++) + data[i] = readl(mem_base + UAA_CORE_CHANGE); + + /* Determine current mode... */ + if (data[0] == CTLA) { + is_uaa = ((data[1] == CTLZ && data[2] == CTLL + && data[3] == CTLA) || (data[1] == CTLA + && data[2] == CTLZ && data[3] == CTLL)); + } else if (data[0] == CTLZ) { + is_uaa = (data[1] == CTLL + && data[2] == CTLA && data[3] == CTLA); + } else if (data[0] == CTLL) { + is_uaa = (data[1] == CTLA + && data[2] == CTLA && data[3] == CTLZ); + } else { + is_uaa = 0; + } + + if (!is_uaa) { + /* Not in UAA mode currently. Return directly. */ + iounmap(mem_base); + return 0; + } + + pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5); + pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq); + pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size); + pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer); + pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr); + pci_read_config_dword(pci, PCI_COMMAND, &cmd); + + /* Set up X-Fi core PCI configuration space. */ + /* Switch to X-Fi config space with BAR0 exposed. */ + pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321); + /* Copy UAA's BAR5 into X-Fi BAR0 */ + pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5); + /* Switch to X-Fi config space without BAR0 exposed. */ + pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4); + pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq); + pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size); + pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer); + pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr); + pci_write_config_dword(pci, PCI_COMMAND, cmd); + + /* Switch to X-Fi mode */ + writel(CTLX, (mem_base + UAA_CORE_CHANGE)); + writel(CTL_, (mem_base + UAA_CORE_CHANGE)); + writel(CTLF, (mem_base + UAA_CORE_CHANGE)); + writel(CTLi, (mem_base + UAA_CORE_CHANGE)); + + iounmap(mem_base); + + return 0; +} + +static int hw_card_start(struct hw *hw) +{ + int err = 0; + struct pci_dev *pci = hw->pci; + u16 subsys_id = 0; + unsigned int dma_mask = 0; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + /* Set DMA transfer mask */ + dma_mask = CT_XFI_DMA_MASK; + if (pci_set_dma_mask(pci, dma_mask) < 0 || + pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + printk(KERN_ERR "architecture does not support PCI " + "busmaster DMA with mask 0x%x\n", dma_mask); + err = -ENXIO; + goto error1; + } + + err = pci_request_regions(pci, "XFi"); + if (err < 0) + goto error1; + + /* Switch to X-Fi mode from UAA mode if neeeded */ + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) { + err = uaa_to_xfi(pci); + if (err) + goto error2; + + hw->io_base = pci_resource_start(pci, 5); + } else { + hw->io_base = pci_resource_start(pci, 0); + } + + /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, + atc->chip_details->nm_card, hw))) { + goto error2; + } + hw->irq = pci->irq; + */ + + pci_set_master(pci); + + return 0; + +error2: + pci_release_regions(pci); + hw->io_base = 0; +error1: + pci_disable_device(pci); + return err; +} + +static int hw_card_stop(struct hw *hw) +{ + /* TODO: Disable interrupt and so on... */ + return 0; +} + +static int hw_card_shutdown(struct hw *hw) +{ + if (hw->irq >= 0) + free_irq(hw->irq, hw); + + hw->irq = -1; + + if (NULL != ((void *)hw->mem_base)) + iounmap((void *)hw->mem_base); + + hw->mem_base = (unsigned long)NULL; + + if (hw->io_base) + pci_release_regions(hw->pci); + + hw->io_base = 0; + + pci_disable_device(hw->pci); + + return 0; +} + +static int hw_card_init(struct hw *hw, struct card_conf *info) +{ + int err; + unsigned int gctl; + u16 subsys_id = 0; + u32 data = 0; + struct dac_conf dac_info = {0}; + struct adc_conf adc_info = {0}; + struct daio_conf daio_info = {0}; + struct trn_conf trn_info = {0}; + + /* Get PCI io port base address and do Hendrix switch if needed. */ + if (!hw->io_base) { + err = hw_card_start(hw); + if (err) + return err; + } + + /* PLL init */ + err = hw_pll_init(hw, info->rsr); + if (err < 0) + return err; + + /* kick off auto-init */ + err = hw_auto_init(hw); + if (err < 0) + return err; + + /* Enable audio ring */ + gctl = hw_read_20kx(hw, GCTL); + set_field(&gctl, GCTL_EAC, 1); + set_field(&gctl, GCTL_DBP, 1); + set_field(&gctl, GCTL_TBP, 1); + set_field(&gctl, GCTL_FBP, 1); + set_field(&gctl, GCTL_ET, 1); + hw_write_20kx(hw, GCTL, gctl); + mdelay(10); + + /* Reset all global pending interrupts */ + hw_write_20kx(hw, GIE, 0); + /* Reset all SRC pending interrupts */ + hw_write_20kx(hw, SRCIP, 0); + mdelay(30); + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + /* Detect the card ID and configure GPIO accordingly. */ + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x cards */ + hw_write_20kx(hw, GPIOCTL, 0x13fe); + } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + hw_write_20kx(hw, GPIOCTL, 0x00e6); + } else if ((subsys_id & 0xf000) == 0x6000) { + /* Vista compatible cards */ + hw_write_20kx(hw, GPIOCTL, 0x00c2); + } else { + hw_write_20kx(hw, GPIOCTL, 0x01e6); + } + + trn_info.vm_pgt_phys = info->vm_pgt_phys; + err = hw_trn_init(hw, &trn_info); + if (err < 0) + return err; + + daio_info.msr = info->msr; + err = hw_daio_init(hw, &daio_info); + if (err < 0) + return err; + + dac_info.msr = info->msr; + err = hw_dac_init(hw, &dac_info); + if (err < 0) + return err; + + adc_info.msr = info->msr; + adc_info.input = ADC_LINEIN; + adc_info.mic20db = 0; + err = hw_adc_init(hw, &adc_info); + if (err < 0) + return err; + + data = hw_read_20kx(hw, SRCMCTL); + data |= 0x1; /* Enables input from the audio ring */ + hw_write_20kx(hw, SRCMCTL, data); + + return 0; +} + +static u32 hw_read_20kx(struct hw *hw, u32 reg) +{ + u32 value; + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + outl(reg, hw->io_base + 0x0); + value = inl(hw->io_base + 0x4); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + + return value; +} + +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + outl(reg, hw->io_base + 0x0); + outl(data, hw->io_base + 0x4); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + +} + +static u32 hw_read_pci(struct hw *hw, u32 reg) +{ + u32 value; + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); + outl(reg, hw->io_base + 0x10); + value = inl(hw->io_base + 0x14); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); + + return value; +} + +static void hw_write_pci(struct hw *hw, u32 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); + outl(reg, hw->io_base + 0x10); + outl(data, hw->io_base + 0x14); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); +} + +int create_20k1_hw_obj(struct hw **rhw) +{ + struct hw *hw; + struct hw20k1 *hw20k1; + + *rhw = NULL; + hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL); + if (NULL == hw20k1) + return -ENOMEM; + + spin_lock_init(&hw20k1->reg_20k1_lock); + spin_lock_init(&hw20k1->reg_pci_lock); + + hw = &hw20k1->hw; + + hw->io_base = 0; + hw->mem_base = (unsigned long)NULL; + hw->irq = -1; + + hw->card_init = hw_card_init; + hw->card_stop = hw_card_stop; + hw->pll_init = hw_pll_init; + hw->is_adc_source_selected = hw_is_adc_input_selected; + hw->select_adc_source = hw_adc_input_select; + hw->have_digit_io_switch = hw_have_digit_io_switch; + + hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; + hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; + hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; + hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; + hw->src_set_state = src_set_state; + hw->src_set_bm = src_set_bm; + hw->src_set_rsr = src_set_rsr; + hw->src_set_sf = src_set_sf; + hw->src_set_wr = src_set_wr; + hw->src_set_pm = src_set_pm; + hw->src_set_rom = src_set_rom; + hw->src_set_vo = src_set_vo; + hw->src_set_st = src_set_st; + hw->src_set_ie = src_set_ie; + hw->src_set_ilsz = src_set_ilsz; + hw->src_set_bp = src_set_bp; + hw->src_set_cisz = src_set_cisz; + hw->src_set_ca = src_set_ca; + hw->src_set_sa = src_set_sa; + hw->src_set_la = src_set_la; + hw->src_set_pitch = src_set_pitch; + hw->src_set_dirty = src_set_dirty; + hw->src_set_clear_zbufs = src_set_clear_zbufs; + hw->src_set_dirty_all = src_set_dirty_all; + hw->src_commit_write = src_commit_write; + hw->src_get_ca = src_get_ca; + hw->src_get_dirty = src_get_dirty; + hw->src_dirty_conj_mask = src_dirty_conj_mask; + hw->src_mgr_enbs_src = src_mgr_enbs_src; + hw->src_mgr_enb_src = src_mgr_enb_src; + hw->src_mgr_dsb_src = src_mgr_dsb_src; + hw->src_mgr_commit_write = src_mgr_commit_write; + + hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; + hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; + hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; + hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; + hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; + hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; + hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; + + hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; + hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; + hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; + hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; + hw->amixer_set_mode = amixer_set_mode; + hw->amixer_set_iv = amixer_set_iv; + hw->amixer_set_x = amixer_set_x; + hw->amixer_set_y = amixer_set_y; + hw->amixer_set_sadr = amixer_set_sadr; + hw->amixer_set_se = amixer_set_se; + hw->amixer_set_dirty = amixer_set_dirty; + hw->amixer_set_dirty_all = amixer_set_dirty_all; + hw->amixer_commit_write = amixer_commit_write; + hw->amixer_get_y = amixer_get_y; + hw->amixer_get_dirty = amixer_get_dirty; + + hw->dai_get_ctrl_blk = dai_get_ctrl_blk; + hw->dai_put_ctrl_blk = dai_put_ctrl_blk; + hw->dai_srt_set_srco = dai_srt_set_srcr; + hw->dai_srt_set_srcm = dai_srt_set_srcl; + hw->dai_srt_set_rsr = dai_srt_set_rsr; + hw->dai_srt_set_drat = dai_srt_set_drat; + hw->dai_srt_set_ec = dai_srt_set_ec; + hw->dai_srt_set_et = dai_srt_set_et; + hw->dai_commit_write = dai_commit_write; + + hw->dao_get_ctrl_blk = dao_get_ctrl_blk; + hw->dao_put_ctrl_blk = dao_put_ctrl_blk; + hw->dao_set_spos = dao_set_spos; + hw->dao_commit_write = dao_commit_write; + hw->dao_get_spos = dao_get_spos; + + hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; + hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; + hw->daio_mgr_enb_dai = daio_mgr_enb_dai; + hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; + hw->daio_mgr_enb_dao = daio_mgr_enb_dao; + hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; + hw->daio_mgr_dao_init = daio_mgr_dao_init; + hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; + hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; + hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; + hw->daio_mgr_commit_write = daio_mgr_commit_write; + + *rhw = hw; + + return 0; +} + +int destroy_20k1_hw_obj(struct hw *hw) +{ + if (hw->io_base) + hw_card_shutdown(hw); + + kfree(container_of(hw, struct hw20k1, hw)); + return 0; +} diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h new file mode 100644 index 00000000000..02f72fb448a --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.h @@ -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 */ diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c new file mode 100644 index 00000000000..cdcb75cc33b --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.c @@ -0,0 +1,2133 @@ +/** + * 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.c + * + * @Brief + * This file contains the implementation of hardware access methord for 20k2. + * + * @Author Liu Chun + * @Date May 14 2008 + * + */ + +#include "cthw20k2.h" +#include "ct20k2reg.h" +#include +#include +#include +#include +#include +#include +#include + +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ + +static u32 hw_read_20kx(struct hw *hw, u32 reg); +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); + +/* + * Type definition block. + * The layout of control structures can be directly applied on 20k2 chip. + */ + +/* + * SRC control block definitions. + */ + +/* SRC resource control block */ +#define SRCCTL_STATE 0x00000007 +#define SRCCTL_BM 0x00000008 +#define SRCCTL_RSR 0x00000030 +#define SRCCTL_SF 0x000001C0 +#define SRCCTL_WR 0x00000200 +#define SRCCTL_PM 0x00000400 +#define SRCCTL_ROM 0x00001800 +#define SRCCTL_VO 0x00002000 +#define SRCCTL_ST 0x00004000 +#define SRCCTL_IE 0x00008000 +#define SRCCTL_ILSZ 0x000F0000 +#define SRCCTL_BP 0x00100000 + +#define SRCCCR_CISZ 0x000007FF +#define SRCCCR_CWA 0x001FF800 +#define SRCCCR_D 0x00200000 +#define SRCCCR_RS 0x01C00000 +#define SRCCCR_NAL 0x3E000000 +#define SRCCCR_RA 0xC0000000 + +#define SRCCA_CA 0x0FFFFFFF +#define SRCCA_RS 0xE0000000 + +#define SRCSA_SA 0x0FFFFFFF + +#define SRCLA_LA 0x0FFFFFFF + +/* Mixer Parameter Ring ram Low and Hight register. + * Fixed-point value in 8.24 format for parameter channel */ +#define MPRLH_PITCH 0xFFFFFFFF + +/* SRC resource register dirty flags */ +union src_dirty { + struct { + u16 ctl:1; + u16 ccr:1; + u16 sa:1; + u16 la:1; + u16 ca:1; + u16 mpr:1; + u16 czbfs:1; /* Clear Z-Buffers */ + u16 rsv:9; + } bf; + u16 data; +}; + +struct src_rsc_ctrl_blk { + unsigned int ctl; + unsigned int ccr; + unsigned int ca; + unsigned int sa; + unsigned int la; + unsigned int mpr; + union src_dirty dirty; +}; + +/* SRC manager control block */ +union src_mgr_dirty { + struct { + u16 enb0:1; + u16 enb1:1; + u16 enb2:1; + u16 enb3:1; + u16 enb4:1; + u16 enb5:1; + u16 enb6:1; + u16 enb7:1; + u16 enbsa:1; + u16 rsv:7; + } bf; + u16 data; +}; + +struct src_mgr_ctrl_blk { + unsigned int enbsa; + unsigned int enb[8]; + union src_mgr_dirty dirty; +}; + +/* SRCIMP manager control block */ +#define SRCAIM_ARC 0x00000FFF +#define SRCAIM_NXT 0x00FF0000 +#define SRCAIM_SRC 0xFF000000 + +struct srcimap { + unsigned int srcaim; + unsigned int idx; +}; + +/* SRCIMP manager register dirty flags */ +union srcimp_mgr_dirty { + struct { + u16 srcimap:1; + u16 rsv:15; + } bf; + u16 data; +}; + +struct srcimp_mgr_ctrl_blk { + struct srcimap srcimap; + union srcimp_mgr_dirty dirty; +}; + +/* + * Function implementation block. + */ + +static int src_get_rsc_ctrl_blk(void **rblk) +{ + struct src_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_put_rsc_ctrl_blk(void *blk) +{ + kfree((struct src_rsc_ctrl_blk *)blk); + + return 0; +} + +static int src_set_state(void *blk, unsigned int state) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_STATE, state); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bm(void *blk, unsigned int bm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BM, bm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rsr(void *blk, unsigned int rsr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_RSR, rsr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_sf(void *blk, unsigned int sf) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_SF, sf); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_wr(void *blk, unsigned int wr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_WR, wr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_pm(void *blk, unsigned int pm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_PM, pm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rom(void *blk, unsigned int rom) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ROM, rom); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_vo(void *blk, unsigned int vo) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_VO, vo); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_st(void *blk, unsigned int st) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ST, st); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ie(void *blk, unsigned int ie) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_IE, ie); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ilsz(void *blk, unsigned int ilsz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bp(void *blk, unsigned int bp) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BP, bp); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_cisz(void *blk, unsigned int cisz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ccr, SRCCCR_CISZ, cisz); + ctl->dirty.bf.ccr = 1; + return 0; +} + +static int src_set_ca(void *blk, unsigned int ca) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ca, SRCCA_CA, ca); + ctl->dirty.bf.ca = 1; + return 0; +} + +static int src_set_sa(void *blk, unsigned int sa) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->sa, SRCSA_SA, sa); + ctl->dirty.bf.sa = 1; + return 0; +} + +static int src_set_la(void *blk, unsigned int la) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->la, SRCLA_LA, la); + ctl->dirty.bf.la = 1; + return 0; +} + +static int src_set_pitch(void *blk, unsigned int pitch) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->mpr, MPRLH_PITCH, pitch); + ctl->dirty.bf.mpr = 1; + return 0; +} + +static int src_set_clear_zbufs(void *blk, unsigned int clear) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); + return 0; +} + +static int src_set_dirty(void *blk, unsigned int flags) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int src_set_dirty_all(void *blk) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +#define AR_SLOT_SIZE 4096 +#define AR_SLOT_BLOCK_SIZE 16 +#define AR_PTS_PITCH 6 +#define AR_PARAM_SRC_OFFSET 0x60 + +static unsigned int src_param_pitch_mixer(unsigned int src_idx) +{ + return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE + - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; + +} + +static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + int i = 0; + + if (ctl->dirty.bf.czbfs) { + /* Clear Z-Buffer registers */ + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0); + + for (i = 0; i < 4; i++) + hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0); + + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0); + + ctl->dirty.bf.czbfs = 0; + } + if (ctl->dirty.bf.mpr) { + /* Take the parameter mixer resource in the same group as that + * the idx src is in for simplicity. Unlike src, all conjugate + * parameter mixer resources must be programmed for + * corresponding conjugate src resources. */ + unsigned int pm_idx = src_param_pitch_mixer(idx); + hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr); + hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3); + hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0); + ctl->dirty.bf.mpr = 0; + } + if (ctl->dirty.bf.sa) { + hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa); + ctl->dirty.bf.sa = 0; + } + if (ctl->dirty.bf.la) { + hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la); + ctl->dirty.bf.la = 0; + } + if (ctl->dirty.bf.ca) { + hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca); + ctl->dirty.bf.ca = 0; + } + + /* Write srccf register */ + hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0); + + if (ctl->dirty.bf.ccr) { + hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr); + ctl->dirty.bf.ccr = 0; + } + if (ctl->dirty.bf.ctl) { + hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl); + ctl->dirty.bf.ctl = 0; + } + + return 0; +} + +static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100); + ctl->dirty.bf.ca = 0; + + return get_field(ctl->ca, SRCCA_CA); +} + +static unsigned int src_get_dirty(void *blk) +{ + return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; +} + +static unsigned int src_dirty_conj_mask(void) +{ + return 0x20; +} + +static int src_mgr_enbs_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + return 0; +} + +static int src_mgr_enb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_dsb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_commit_write(struct hw *hw, void *blk) +{ + struct src_mgr_ctrl_blk *ctl = blk; + int i = 0; + unsigned int ret = 0; + + if (ctl->dirty.bf.enbsa) { + do { + ret = hw_read_20kx(hw, SRC_ENBSTAT); + } while (ret & 0x1); + hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa); + ctl->dirty.bf.enbsa = 0; + } + for (i = 0; i < 8; i++) { + if ((ctl->dirty.data & (0x1 << i))) { + hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]); + ctl->dirty.data &= ~(0x1 << i); + } + } + + return 0; +} + +static int src_mgr_get_ctrl_blk(void **rblk) +{ + struct src_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct src_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_get_ctrl_blk(void **rblk) +{ + struct srcimp_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int srcimp_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct srcimp_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + ((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr; + ((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_commit_write(struct hw *hw, void *blk) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srcimap) { + hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100, + ctl->srcimap.srcaim); + ctl->dirty.bf.srcimap = 0; + } + + return 0; +} + +/* + * AMIXER control block definitions. + */ + +#define AMOPLO_M 0x00000003 +#define AMOPLO_IV 0x00000004 +#define AMOPLO_X 0x0003FFF0 +#define AMOPLO_Y 0xFFFC0000 + +#define AMOPHI_SADR 0x000000FF +#define AMOPHI_SE 0x80000000 + +/* AMIXER resource register dirty flags */ +union amixer_dirty { + struct { + u16 amoplo:1; + u16 amophi:1; + u16 rsv:14; + } bf; + u16 data; +}; + +/* AMIXER resource control block */ +struct amixer_rsc_ctrl_blk { + unsigned int amoplo; + unsigned int amophi; + union amixer_dirty dirty; +}; + +static int amixer_set_mode(void *blk, unsigned int mode) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_M, mode); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_iv(void *blk, unsigned int iv) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_IV, iv); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_x(void *blk, unsigned int x) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_X, x); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_y(void *blk, unsigned int y) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_Y, y); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_sadr(void *blk, unsigned int sadr) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SADR, sadr); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_se(void *blk, unsigned int se) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SE, se); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_dirty(void *blk, unsigned int flags) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int amixer_set_dirty_all(void *blk) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { + hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo); + ctl->dirty.bf.amoplo = 0; + hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi); + ctl->dirty.bf.amophi = 0; + } + + return 0; +} + +static int amixer_get_y(void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + return get_field(ctl->amoplo, AMOPLO_Y); +} + +static unsigned int amixer_get_dirty(void *blk) +{ + return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; +} + +static int amixer_rsc_get_ctrl_blk(void **rblk) +{ + struct amixer_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int amixer_rsc_put_ctrl_blk(void *blk) +{ + kfree((struct amixer_rsc_ctrl_blk *)blk); + + return 0; +} + +static int amixer_mgr_get_ctrl_blk(void **rblk) +{ + *rblk = NULL; + + return 0; +} + +static int amixer_mgr_put_ctrl_blk(void *blk) +{ + return 0; +} + +/* + * DAIO control block definitions. + */ + +/* Receiver Sample Rate Tracker Control register */ +#define SRTCTL_SRCO 0x000000FF +#define SRTCTL_SRCM 0x0000FF00 +#define SRTCTL_RSR 0x00030000 +#define SRTCTL_DRAT 0x00300000 +#define SRTCTL_EC 0x01000000 +#define SRTCTL_ET 0x10000000 + +/* DAIO Receiver register dirty flags */ +union dai_dirty { + struct { + u16 srt:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* DAIO Receiver control block */ +struct dai_ctrl_blk { + unsigned int srt; + union dai_dirty dirty; +}; + +/* Audio Input Mapper RAM */ +#define AIM_ARC 0x00000FFF +#define AIM_NXT 0x007F0000 + +struct daoimap { + unsigned int aim; + unsigned int idx; +}; + +/* Audio Transmitter Control and Status register */ +#define ATXCTL_EN 0x00000001 +#define ATXCTL_MODE 0x00000010 +#define ATXCTL_CD 0x00000020 +#define ATXCTL_RAW 0x00000100 +#define ATXCTL_MT 0x00000200 +#define ATXCTL_NUC 0x00003000 +#define ATXCTL_BEN 0x00010000 +#define ATXCTL_BMUX 0x00700000 +#define ATXCTL_B24 0x01000000 +#define ATXCTL_CPF 0x02000000 +#define ATXCTL_RIV 0x10000000 +#define ATXCTL_LIV 0x20000000 +#define ATXCTL_RSAT 0x40000000 +#define ATXCTL_LSAT 0x80000000 + +/* XDIF Transmitter register dirty flags */ +union dao_dirty { + struct { + u16 atxcsl:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* XDIF Transmitter control block */ +struct dao_ctrl_blk { + /* XDIF Transmitter Channel Status Low Register */ + unsigned int atxcsl; + union dao_dirty dirty; +}; + +/* Audio Receiver Control register */ +#define ARXCTL_EN 0x00000001 + +/* DAIO manager register dirty flags */ +union daio_mgr_dirty { + struct { + u32 atxctl:8; + u32 arxctl:8; + u32 daoimap:1; + u32 rsv:15; + } bf; + u32 data; +}; + +/* DAIO manager control block */ +struct daio_mgr_ctrl_blk { + struct daoimap daoimap; + unsigned int txctl[8]; + unsigned int rxctl[8]; + union daio_mgr_dirty dirty; +}; + +static int dai_srt_set_srco(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_SRCO, src); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_srcm(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_SRCM, src); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_rsr(void *blk, unsigned int rsr) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_RSR, rsr); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_drat(void *blk, unsigned int drat) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_DRAT, drat); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_ec(void *blk, unsigned int ec) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_et(void *blk, unsigned int et) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dai_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srt) { + hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt); + ctl->dirty.bf.srt = 0; + } + + return 0; +} + +static int dai_get_ctrl_blk(void **rblk) +{ + struct dai_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dai_put_ctrl_blk(void *blk) +{ + kfree((struct dai_ctrl_blk *)blk); + + return 0; +} + +static int dao_set_spos(void *blk, unsigned int spos) +{ + ((struct dao_ctrl_blk *)blk)->atxcsl = spos; + ((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1; + return 0; +} + +static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dao_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.atxcsl) { + if (idx < 4) { + /* S/PDIF SPOSx */ + hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx, + ctl->atxcsl); + } + ctl->dirty.bf.atxcsl = 0; + } + + return 0; +} + +static int dao_get_spos(void *blk, unsigned int *spos) +{ + *spos = ((struct dao_ctrl_blk *)blk)->atxcsl; + return 0; +} + +static int dao_get_ctrl_blk(void **rblk) +{ + struct dao_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dao_put_ctrl_blk(void *blk) +{ + kfree((struct dao_ctrl_blk *)blk); + + return 0; +} + +static int daio_mgr_enb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->rxctl[idx], ARXCTL_EN, 1); + ctl->dirty.bf.arxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_dsb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->rxctl[idx], ARXCTL_EN, 0); + + ctl->dirty.bf.arxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_enb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->txctl[idx], ATXCTL_EN, 1); + ctl->dirty.bf.atxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_dsb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->txctl[idx], ATXCTL_EN, 0); + ctl->dirty.bf.atxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + switch ((conf & 0x7)) { + case 1: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 0); + break; + case 2: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 1); + break; + case 4: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 2); + break; + case 8: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 3); + break; + default: + break; + } + /* CDIF */ + set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7))); + /* Non-audio */ + set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1); + /* Non-audio */ + set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1); + set_field(&ctl->txctl[idx], ATXCTL_RAW, + ((conf >> 3) & 0x1) ? 0 : 0); + ctl->dirty.bf.atxctl |= (0x1 << idx); + } else { + /* I2S output */ + /*idx %= 4; */ + } + return 0; +} + +static int daio_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_ARC, slot); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_NXT, next); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + ((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr; + ((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_commit_write(struct hw *hw, void *blk) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + unsigned int data = 0; + int i = 0; + + for (i = 0; i < 8; i++) { + if ((ctl->dirty.bf.atxctl & (0x1 << i))) { + data = ctl->txctl[i]; + hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); + ctl->dirty.bf.atxctl &= ~(0x1 << i); + mdelay(1); + } + if ((ctl->dirty.bf.arxctl & (0x1 << i))) { + data = ctl->rxctl[i]; + hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); + ctl->dirty.bf.arxctl &= ~(0x1 << i); + mdelay(1); + } + } + if (ctl->dirty.bf.daoimap) { + hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4, + ctl->daoimap.aim); + ctl->dirty.bf.daoimap = 0; + } + + return 0; +} + +static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) +{ + struct daio_mgr_ctrl_blk *blk; + int i = 0; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + for (i = 0; i < 8; i++) { + blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i)); + blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i)); + } + + *rblk = blk; + + return 0; +} + +static int daio_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct daio_mgr_ctrl_blk *)blk); + + return 0; +} + +/* Card hardware initialization block */ +struct dac_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct adc_conf { + unsigned int msr; /* master sample rate in rsrs */ + unsigned char input; /* the input source of ADC */ + unsigned char mic20db; /* boost mic by 20db if input is microphone */ +}; + +struct daio_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct trn_conf { + unsigned long vm_pgt_phys; +}; + +static int hw_daio_init(struct hw *hw, const struct daio_conf *info) +{ + u32 dwData = 0; + int i; + + /* Program I2S with proper sample rate and enable the correct I2S + * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */ + if (1 == info->msr) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101); + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else if (2 == info->msr) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); + /* Specify all playing 96khz + * EA [0] - Enabled + * RTA [4:5] - 96kHz + * EB [8] - Enabled + * RTB [12:13] - 96kHz + * EC [16] - Enabled + * RTC [20:21] - 96kHz + * ED [24] - Enabled + * RTD [28:29] - 96kHz */ + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else { + printk(KERN_ALERT "ERROR!!! Invalid sampling rate!!!\n"); + return -EINVAL; + } + + for (i = 0; i < 8; i++) { + if (i <= 3) { + /* 1st 3 channels are SPDIFs (SB0960) */ + if (i == 3) + dwData = 0x1001001; + else + dwData = 0x1000001; + + hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), dwData); + hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), dwData); + + /* Initialize the SPDIF Out Channel status registers. + * The value specified here is based on the typical + * values provided in the specification, namely: Clock + * Accuracy of 1000ppm, Sample Rate of 48KHz, + * unspecified source number, Generation status = 1, + * Category code = 0x12 (Digital Signal Mixer), + * Mode = 0, Emph = 0, Copy Permitted, AN = 0 + * (indicating that we're transmitting digital audio, + * and the Professional Use bit is 0. */ + + hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i), + 0x02109204); /* Default to 48kHz */ + + hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); + } else { + /* Next 5 channels are I2S (SB0960) */ + dwData = 0x11; + hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), dwData); + if (2 == info->msr) { + /* Four channels per sample period */ + dwData |= 0x1000; + } + hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), dwData); + } + } + + return 0; +} + +/* TRANSPORT operations */ +static int hw_trn_init(struct hw *hw, const struct trn_conf *info) +{ + u32 vmctl = 0, data = 0; + unsigned long ptp_phys_low = 0, ptp_phys_high = 0; + int i = 0; + + /* Set up device page table */ + if ((~0UL) == info->vm_pgt_phys) { + printk(KERN_ALERT "Wrong device page table page address!!!\n"); + return -1; + } + + vmctl = 0x80000C0F; /* 32-bit, 4k-size page */ +#if BITS_PER_LONG == 64 + ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); + ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); + vmctl |= (3<<8); +#elif BITS_PER_LONG == 32 + ptp_phys_low = info->vm_pgt_phys & (~0UL); + ptp_phys_high = 0; +#else +# error "Unknown BITS_PER_LONG!" +#endif +#if PAGE_SIZE == 8192 +# error "Don't support 8k-page!" +#endif + /* Write page table physical address to all PTPAL registers */ + for (i = 0; i < 64; i++) { + hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low); + hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high); + } + /* Enable virtual memory transfer */ + hw_write_20kx(hw, VMEM_CTL, vmctl); + /* Enable transport bus master and queueing of request */ + hw_write_20kx(hw, TRANSPORT_CTL, 0x03); + hw_write_20kx(hw, TRANSPORT_INT, 0x200c01); + /* Enable transport ring */ + data = hw_read_20kx(hw, TRANSPORT_ENB); + hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03)); + + return 0; +} + +/* Card initialization */ +#define GCTL_AIE 0x00000001 +#define GCTL_UAA 0x00000002 +#define GCTL_DPC 0x00000004 +#define GCTL_DBP 0x00000008 +#define GCTL_ABP 0x00000010 +#define GCTL_TBP 0x00000020 +#define GCTL_SBP 0x00000040 +#define GCTL_FBP 0x00000080 +#define GCTL_ME 0x00000100 +#define GCTL_AID 0x00001000 + +#define PLLCTL_SRC 0x00000007 +#define PLLCTL_SPE 0x00000008 +#define PLLCTL_RD 0x000000F0 +#define PLLCTL_FD 0x0001FF00 +#define PLLCTL_OD 0x00060000 +#define PLLCTL_B 0x00080000 +#define PLLCTL_AS 0x00100000 +#define PLLCTL_LF 0x03E00000 +#define PLLCTL_SPS 0x1C000000 +#define PLLCTL_AD 0x60000000 + +#define PLLSTAT_CCS 0x00000007 +#define PLLSTAT_SPL 0x00000008 +#define PLLSTAT_CRD 0x000000F0 +#define PLLSTAT_CFD 0x0001FF00 +#define PLLSTAT_SL 0x00020000 +#define PLLSTAT_FAS 0x00040000 +#define PLLSTAT_B 0x00080000 +#define PLLSTAT_PD 0x00100000 +#define PLLSTAT_OCA 0x00200000 +#define PLLSTAT_NCA 0x00400000 + +static int hw_pll_init(struct hw *hw, unsigned int rsr) +{ + unsigned int pllenb; + unsigned int pllctl; + unsigned int pllstat; + int i; + + pllenb = 0xB; + hw_write_20kx(hw, PLL_ENB, pllenb); + pllctl = 0x20D00000; + set_field(&pllctl, PLLCTL_FD, 16 - 4); + hw_write_20kx(hw, PLL_CTL, pllctl); + mdelay(40); + pllctl = hw_read_20kx(hw, PLL_CTL); + set_field(&pllctl, PLLCTL_B, 0); + if (48000 == rsr) { + set_field(&pllctl, PLLCTL_FD, 16 - 2); + set_field(&pllctl, PLLCTL_RD, 1 - 1); + } else { /* 44100 */ + set_field(&pllctl, PLLCTL_FD, 147 - 2); + set_field(&pllctl, PLLCTL_RD, 10 - 1); + } + hw_write_20kx(hw, PLL_CTL, pllctl); + mdelay(40); + for (i = 0; i < 1000; i++) { + pllstat = hw_read_20kx(hw, PLL_STAT); + if (get_field(pllstat, PLLSTAT_PD)) + continue; + + if (get_field(pllstat, PLLSTAT_B) != + get_field(pllctl, PLLCTL_B)) + continue; + + if (get_field(pllstat, PLLSTAT_CCS) != + get_field(pllctl, PLLCTL_SRC)) + continue; + + if (get_field(pllstat, PLLSTAT_CRD) != + get_field(pllctl, PLLCTL_RD)) + continue; + + if (get_field(pllstat, PLLSTAT_CFD) != + get_field(pllctl, PLLCTL_FD)) + continue; + + break; + } + if (i >= 1000) { + printk(KERN_ALERT "PLL initialization failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +static int hw_auto_init(struct hw *hw) +{ + unsigned int gctl; + int i; + + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + set_field(&gctl, GCTL_AIE, 0); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + set_field(&gctl, GCTL_AIE, 1); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + mdelay(10); + for (i = 0; i < 400000; i++) { + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + if (get_field(gctl, GCTL_AID)) + break; + } + if (!get_field(gctl, GCTL_AID)) { + printk(KERN_ALERT "Card Auto-init failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +/* DAC operations */ + +#define CS4382_MC1 0x1 +#define CS4382_MC2 0x2 +#define CS4382_MC3 0x3 +#define CS4382_FC 0x4 +#define CS4382_IC 0x5 +#define CS4382_XC1 0x6 +#define CS4382_VCA1 0x7 +#define CS4382_VCB1 0x8 +#define CS4382_XC2 0x9 +#define CS4382_VCA2 0xA +#define CS4382_VCB2 0xB +#define CS4382_XC3 0xC +#define CS4382_VCA3 0xD +#define CS4382_VCB3 0xE +#define CS4382_XC4 0xF +#define CS4382_VCA4 0x10 +#define CS4382_VCB4 0x11 +#define CS4382_CREV 0x12 + +/* I2C status */ +#define STATE_LOCKED 0x00 +#define STATE_UNLOCKED 0xAA +#define DATA_READY 0x800000 /* Used with I2C_IF_STATUS */ +#define DATA_ABORT 0x10000 /* Used with I2C_IF_STATUS */ + +#define I2C_STATUS_DCM 0x00000001 +#define I2C_STATUS_BC 0x00000006 +#define I2C_STATUS_APD 0x00000008 +#define I2C_STATUS_AB 0x00010000 +#define I2C_STATUS_DR 0x00800000 + +#define I2C_ADDRESS_PTAD 0x0000FFFF +#define I2C_ADDRESS_SLAD 0x007F0000 + +struct REGS_CS4382 { + u32 dwModeControl_1; + u32 dwModeControl_2; + u32 dwModeControl_3; + + u32 dwFilterControl; + u32 dwInvertControl; + + u32 dwMixControl_P1; + u32 dwVolControl_A1; + u32 dwVolControl_B1; + + u32 dwMixControl_P2; + u32 dwVolControl_A2; + u32 dwVolControl_B2; + + u32 dwMixControl_P3; + u32 dwVolControl_A3; + u32 dwVolControl_B3; + + u32 dwMixControl_P4; + u32 dwVolControl_A4; + u32 dwVolControl_B4; +}; + +static u8 m_bAddressSize, m_bDataSize, m_bDeviceID; + +static int I2CUnlockFullAccess(struct hw *hw) +{ + u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] = {0xB3, 0xD4}; + + /* Send keys for forced BIOS mode */ + hw_write_20kx(hw, I2C_IF_WLOCK, + UnlockKeySequence_FLASH_FULLACCESS_MODE[0]); + hw_write_20kx(hw, I2C_IF_WLOCK, + UnlockKeySequence_FLASH_FULLACCESS_MODE[1]); + /* Check whether the chip is unlocked */ + if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED) + return 0; + + return -1; +} + +static int I2CLockChip(struct hw *hw) +{ + /* Write twice */ + hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); + hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); + if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED) + return 0; + + return -1; +} + +static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) +{ + int err = 0; + unsigned int RegI2CStatus; + unsigned int RegI2CAddress; + + err = I2CUnlockFullAccess(hw); + if (err < 0) + return err; + + m_bAddressSize = bAddressSize; + m_bDataSize = bDataSize; + m_bDeviceID = bDeviceID; + + RegI2CAddress = 0; + set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, bDeviceID); + + hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + + set_field(&RegI2CStatus, I2C_STATUS_DCM, 1); /* Direct control mode */ + + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + + return 0; +} + +static int I2CUninit(struct hw *hw) +{ + unsigned int RegI2CStatus; + unsigned int RegI2CAddress; + + RegI2CAddress = 0; + set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ + + hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + + set_field(&RegI2CStatus, I2C_STATUS_DCM, 0); /* I2C mode */ + + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + + return I2CLockChip(hw); +} + +static int I2CWaitDataReady(struct hw *hw) +{ + int i = 0x400000; + unsigned int ret = 0; + + do { + ret = hw_read_20kx(hw, I2C_IF_STATUS); + } while ((!(ret & DATA_READY)) && --i); + + return i; +} + +static int I2CRead(struct hw *hw, u16 wAddress, u32 *pdwData) +{ + unsigned int RegI2CStatus; + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + set_field(&RegI2CStatus, I2C_STATUS_BC, + (4 == m_bAddressSize) ? 0 : m_bAddressSize); + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + if (!I2CWaitDataReady(hw)) + return -1; + + hw_write_20kx(hw, I2C_IF_WDATA, (u32)wAddress); + if (!I2CWaitDataReady(hw)) + return -1; + + /* Force a read operation */ + hw_write_20kx(hw, I2C_IF_RDATA, 0); + if (!I2CWaitDataReady(hw)) + return -1; + + *pdwData = hw_read_20kx(hw, I2C_IF_RDATA); + + return 0; +} + +static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) +{ + unsigned int dwI2CData = (dwData << (m_bAddressSize * 8)) | wAddress; + unsigned int RegI2CStatus; + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + + set_field(&RegI2CStatus, I2C_STATUS_BC, + (4 == (m_bAddressSize + m_bDataSize)) ? + 0 : (m_bAddressSize + m_bDataSize)); + + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + I2CWaitDataReady(hw); + /* Dummy write to trigger the write oprtation */ + hw_write_20kx(hw, I2C_IF_WDATA, 0); + I2CWaitDataReady(hw); + + /* This is the real data */ + hw_write_20kx(hw, I2C_IF_WDATA, dwI2CData); + I2CWaitDataReady(hw); + + return 0; +} + +static int hw_dac_init(struct hw *hw, const struct dac_conf *info) +{ + int err = 0; + u32 dwData = 0; + int i = 0; + struct REGS_CS4382 cs4382_Read = {0}; + struct REGS_CS4382 cs4382_Def = { + 0x00000001, /* Mode Control 1 */ + 0x00000000, /* Mode Control 2 */ + 0x00000084, /* Mode Control 3 */ + 0x00000000, /* Filter Control */ + 0x00000000, /* Invert Control */ + 0x00000024, /* Mixing Control Pair 1 */ + 0x00000000, /* Vol Control A1 */ + 0x00000000, /* Vol Control B1 */ + 0x00000024, /* Mixing Control Pair 2 */ + 0x00000000, /* Vol Control A2 */ + 0x00000000, /* Vol Control B2 */ + 0x00000024, /* Mixing Control Pair 3 */ + 0x00000000, /* Vol Control A3 */ + 0x00000000, /* Vol Control B3 */ + 0x00000024, /* Mixing Control Pair 4 */ + 0x00000000, /* Vol Control A4 */ + 0x00000000 /* Vol Control B4 */ + }; + + /* Set DAC reset bit as output */ + dwData = hw_read_20kx(hw, GPIO_CTRL); + dwData |= 0x02; + hw_write_20kx(hw, GPIO_CTRL, dwData); + + err = I2CInit(hw, 0x18, 1, 1); + if (err < 0) + goto End; + + for (i = 0; i < 2; i++) { + /* Reset DAC twice just in-case the chip + * didn't initialized properly */ + dwData = hw_read_20kx(hw, GPIO_DATA); + /* GPIO data bit 1 */ + dwData &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(10); + dwData |= 0x2; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(50); + + /* Reset the 2nd time */ + dwData &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(10); + dwData |= 0x2; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(50); + + if (I2CRead(hw, CS4382_MC1, &cs4382_Read.dwModeControl_1)) + continue; + + if (I2CRead(hw, CS4382_MC2, &cs4382_Read.dwModeControl_2)) + continue; + + if (I2CRead(hw, CS4382_MC3, &cs4382_Read.dwModeControl_3)) + continue; + + if (I2CRead(hw, CS4382_FC, &cs4382_Read.dwFilterControl)) + continue; + + if (I2CRead(hw, CS4382_IC, &cs4382_Read.dwInvertControl)) + continue; + + if (I2CRead(hw, CS4382_XC1, &cs4382_Read.dwMixControl_P1)) + continue; + + if (I2CRead(hw, CS4382_VCA1, &cs4382_Read.dwVolControl_A1)) + continue; + + if (I2CRead(hw, CS4382_VCB1, &cs4382_Read.dwVolControl_B1)) + continue; + + if (I2CRead(hw, CS4382_XC2, &cs4382_Read.dwMixControl_P2)) + continue; + + if (I2CRead(hw, CS4382_VCA2, &cs4382_Read.dwVolControl_A2)) + continue; + + if (I2CRead(hw, CS4382_VCB2, &cs4382_Read.dwVolControl_B2)) + continue; + + if (I2CRead(hw, CS4382_XC3, &cs4382_Read.dwMixControl_P3)) + continue; + + if (I2CRead(hw, CS4382_VCA3, &cs4382_Read.dwVolControl_A3)) + continue; + + if (I2CRead(hw, CS4382_VCB3, &cs4382_Read.dwVolControl_B3)) + continue; + + if (I2CRead(hw, CS4382_XC4, &cs4382_Read.dwMixControl_P4)) + continue; + + if (I2CRead(hw, CS4382_VCA4, &cs4382_Read.dwVolControl_A4)) + continue; + + if (I2CRead(hw, CS4382_VCB4, &cs4382_Read.dwVolControl_B4)) + continue; + + if (memcmp(&cs4382_Read, &cs4382_Def, + sizeof(struct REGS_CS4382))) + continue; + else + break; + } + + if (i >= 2) + goto End; + + /* Note: Every I2C write must have some delay. + * This is not a requirement but the delay works here... */ + I2CWrite(hw, CS4382_MC1, 0x80); + I2CWrite(hw, CS4382_MC2, 0x10); + if (1 == info->msr) { + I2CWrite(hw, CS4382_XC1, 0x24); + I2CWrite(hw, CS4382_XC2, 0x24); + I2CWrite(hw, CS4382_XC3, 0x24); + I2CWrite(hw, CS4382_XC4, 0x24); + } else if (2 == info->msr) { + I2CWrite(hw, CS4382_XC1, 0x25); + I2CWrite(hw, CS4382_XC2, 0x25); + I2CWrite(hw, CS4382_XC3, 0x25); + I2CWrite(hw, CS4382_XC4, 0x25); + } else { + I2CWrite(hw, CS4382_XC1, 0x26); + I2CWrite(hw, CS4382_XC2, 0x26); + I2CWrite(hw, CS4382_XC3, 0x26); + I2CWrite(hw, CS4382_XC4, 0x26); + } + + return 0; +End: + + I2CUninit(hw); + return -1; +} + +/* ADC operations */ +#define MAKE_WM8775_ADDR(addr, data) (u32)(((addr<<1)&0xFE)|((data>>8)&0x1)) +#define MAKE_WM8775_DATA(data) (u32)(data&0xFF) + +#define WM8775_IC 0x0B +#define WM8775_MMC 0x0C +#define WM8775_AADCL 0x0E +#define WM8775_AADCR 0x0F +#define WM8775_ADCMC 0x15 +#define WM8775_RESET 0x17 + +static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO_DATA); + switch (type) { + case ADC_MICIN: + data = (data & (0x1 << 14)) ? 1 : 0; + break; + case ADC_LINEIN: + data = (data & (0x1 << 14)) ? 0 : 1; + break; + default: + data = 0; + } + return data; +} + +static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO_DATA); + switch (type) { + case ADC_MICIN: + data |= (0x1 << 14); + hw_write_20kx(hw, GPIO_DATA, data); + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + MAKE_WM8775_DATA(0x101)); /* Mic-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + break; + case ADC_LINEIN: + data &= ~(0x1 << 14); + hw_write_20kx(hw, GPIO_DATA, data); + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + MAKE_WM8775_DATA(0x102)); /* Line-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + break; + default: + break; + } + + return 0; +} + +static int hw_adc_init(struct hw *hw, const struct adc_conf *info) +{ + int err = 0; + u32 dwMux = 2, dwData = 0, dwCtl = 0; + + /* Set ADC reset bit as output */ + dwData = hw_read_20kx(hw, GPIO_CTRL); + dwData |= (0x1 << 15); + hw_write_20kx(hw, GPIO_CTRL, dwData); + + /* Initialize I2C */ + err = I2CInit(hw, 0x1A, 1, 1); + if (err < 0) { + printk(KERN_ALERT "Failure to acquire I2C!!!\n"); + goto error; + } + + /* Make ADC in normal operation */ + dwData = hw_read_20kx(hw, GPIO_DATA); + dwData &= ~(0x1 << 15); + mdelay(10); + dwData |= (0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(50); + + /* Set the master mode (256fs) */ + if (1 == info->msr) { + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), + MAKE_WM8775_DATA(0x02)); + } else if (2 == info->msr) { + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), + MAKE_WM8775_DATA(0x0A)); + } else { + printk(KERN_ALERT "Invalid master sampling " + "rate (msr %d)!!!\n", info->msr); + err = -EINVAL; + goto error; + } + + /* Configure GPIO bit 14 change to line-in/mic-in */ + dwCtl = hw_read_20kx(hw, GPIO_CTRL); + dwCtl |= 0x1<<14; + hw_write_20kx(hw, GPIO_CTRL, dwCtl); + + /* Check using Mic-in or Line-in */ + dwData = hw_read_20kx(hw, GPIO_DATA); + + if (dwMux == 1) { + /* Configures GPIO data to select Mic-in */ + dwData |= 0x1<<14; + hw_write_20kx(hw, GPIO_DATA, dwData); + + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + MAKE_WM8775_DATA(0x101)); /* Mic-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + } else if (dwMux == 2) { + /* Configures GPIO data to select Line-in */ + dwData &= ~(0x1<<14); + hw_write_20kx(hw, GPIO_DATA, dwData); + + /* Setup ADC */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + MAKE_WM8775_DATA(0x102)); /* Line-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + } else { + printk(KERN_ALERT "ERROR!!! Invalid input mux!!!\n"); + err = -EINVAL; + goto error; + } + + return 0; + +error: + I2CUninit(hw); + return err; +} + +static int hw_have_digit_io_switch(struct hw *hw) +{ + return 0; +} + +static int hw_card_start(struct hw *hw) +{ + int err = 0; + struct pci_dev *pci = hw->pci; + unsigned int gctl; + unsigned int dma_mask = 0; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + /* Set DMA transfer mask */ + dma_mask = CT_XFI_DMA_MASK; + if (pci_set_dma_mask(pci, dma_mask) < 0 || + pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + printk(KERN_ERR "architecture does not support PCI " + "busmaster DMA with mask 0x%x\n", dma_mask); + err = -ENXIO; + goto error1; + } + + err = pci_request_regions(pci, "XFi"); + if (err < 0) + goto error1; + + hw->io_base = pci_resource_start(hw->pci, 2); + hw->mem_base = (unsigned long)ioremap(hw->io_base, + pci_resource_len(hw->pci, 2)); + if (NULL == (void *)hw->mem_base) { + err = -ENOENT; + goto error2; + } + + /* Switch to 20k2 mode from UAA mode. */ + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + set_field(&gctl, GCTL_UAA, 0); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + + /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, + atc->chip_details->nm_card, hw))) { + goto error3; + } + hw->irq = pci->irq; + */ + + pci_set_master(pci); + + return 0; + +/*error3: + iounmap((void *)hw->mem_base); + hw->mem_base = (unsigned long)NULL;*/ +error2: + pci_release_regions(pci); + hw->io_base = 0; +error1: + pci_disable_device(pci); + return err; +} + +static int hw_card_stop(struct hw *hw) +{ + /* TODO: Disable interrupt and so on... */ + return 0; +} + +static int hw_card_shutdown(struct hw *hw) +{ + if (hw->irq >= 0) + free_irq(hw->irq, hw); + + hw->irq = -1; + + if (NULL != ((void *)hw->mem_base)) + iounmap((void *)hw->mem_base); + + hw->mem_base = (unsigned long)NULL; + + if (hw->io_base) + pci_release_regions(hw->pci); + + hw->io_base = 0; + + pci_disable_device(hw->pci); + + return 0; +} + +static int hw_card_init(struct hw *hw, struct card_conf *info) +{ + int err; + unsigned int gctl; + u32 data = 0; + struct dac_conf dac_info = {0}; + struct adc_conf adc_info = {0}; + struct daio_conf daio_info = {0}; + struct trn_conf trn_info = {0}; + + /* Get PCI io port/memory base address and + * do 20kx core switch if needed. */ + if (!hw->io_base) { + err = hw_card_start(hw); + if (err) + return err; + } + + /* PLL init */ + err = hw_pll_init(hw, info->rsr); + if (err < 0) + return err; + + /* kick off auto-init */ + err = hw_auto_init(hw); + if (err < 0) + return err; + + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + set_field(&gctl, GCTL_DBP, 1); + set_field(&gctl, GCTL_TBP, 1); + set_field(&gctl, GCTL_FBP, 1); + set_field(&gctl, GCTL_DPC, 0); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + + /* Reset all global pending interrupts */ + hw_write_20kx(hw, INTERRUPT_GIE, 0); + /* Reset all SRC pending interrupts */ + hw_write_20kx(hw, SRC_IP, 0); + + /* TODO: detect the card ID and configure GPIO accordingly. */ + /* Configures GPIO (0xD802 0x98028) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ + /* Configures GPIO (SB0880) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ + hw_write_20kx(hw, GPIO_CTRL, 0xD802); + + /* Enable audio ring */ + hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); + + trn_info.vm_pgt_phys = info->vm_pgt_phys; + err = hw_trn_init(hw, &trn_info); + if (err < 0) + return err; + + daio_info.msr = info->msr; + err = hw_daio_init(hw, &daio_info); + if (err < 0) + return err; + + dac_info.msr = info->msr; + err = hw_dac_init(hw, &dac_info); + if (err < 0) + return err; + + adc_info.msr = info->msr; + adc_info.input = ADC_LINEIN; + adc_info.mic20db = 0; + err = hw_adc_init(hw, &adc_info); + if (err < 0) + return err; + + data = hw_read_20kx(hw, SRC_MCTL); + data |= 0x1; /* Enables input from the audio ring */ + hw_write_20kx(hw, SRC_MCTL, data); + + return 0; +} + +static u32 hw_read_20kx(struct hw *hw, u32 reg) +{ + return readl((void *)(hw->mem_base + reg)); +} + +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) +{ + writel(data, (void *)(hw->mem_base + reg)); +} + +int create_20k2_hw_obj(struct hw **rhw) +{ + struct hw *hw; + + *rhw = NULL; + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (NULL == hw) + return -ENOMEM; + + hw->io_base = 0; + hw->mem_base = (unsigned long)NULL; + hw->irq = -1; + + hw->card_init = hw_card_init; + hw->card_stop = hw_card_stop; + hw->pll_init = hw_pll_init; + hw->is_adc_source_selected = hw_is_adc_input_selected; + hw->select_adc_source = hw_adc_input_select; + hw->have_digit_io_switch = hw_have_digit_io_switch; + + hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; + hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; + hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; + hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; + hw->src_set_state = src_set_state; + hw->src_set_bm = src_set_bm; + hw->src_set_rsr = src_set_rsr; + hw->src_set_sf = src_set_sf; + hw->src_set_wr = src_set_wr; + hw->src_set_pm = src_set_pm; + hw->src_set_rom = src_set_rom; + hw->src_set_vo = src_set_vo; + hw->src_set_st = src_set_st; + hw->src_set_ie = src_set_ie; + hw->src_set_ilsz = src_set_ilsz; + hw->src_set_bp = src_set_bp; + hw->src_set_cisz = src_set_cisz; + hw->src_set_ca = src_set_ca; + hw->src_set_sa = src_set_sa; + hw->src_set_la = src_set_la; + hw->src_set_pitch = src_set_pitch; + hw->src_set_dirty = src_set_dirty; + hw->src_set_clear_zbufs = src_set_clear_zbufs; + hw->src_set_dirty_all = src_set_dirty_all; + hw->src_commit_write = src_commit_write; + hw->src_get_ca = src_get_ca; + hw->src_get_dirty = src_get_dirty; + hw->src_dirty_conj_mask = src_dirty_conj_mask; + hw->src_mgr_enbs_src = src_mgr_enbs_src; + hw->src_mgr_enb_src = src_mgr_enb_src; + hw->src_mgr_dsb_src = src_mgr_dsb_src; + hw->src_mgr_commit_write = src_mgr_commit_write; + + hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; + hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; + hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; + hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; + hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; + hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; + hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; + + hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; + hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; + hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; + hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; + hw->amixer_set_mode = amixer_set_mode; + hw->amixer_set_iv = amixer_set_iv; + hw->amixer_set_x = amixer_set_x; + hw->amixer_set_y = amixer_set_y; + hw->amixer_set_sadr = amixer_set_sadr; + hw->amixer_set_se = amixer_set_se; + hw->amixer_set_dirty = amixer_set_dirty; + hw->amixer_set_dirty_all = amixer_set_dirty_all; + hw->amixer_commit_write = amixer_commit_write; + hw->amixer_get_y = amixer_get_y; + hw->amixer_get_dirty = amixer_get_dirty; + + hw->dai_get_ctrl_blk = dai_get_ctrl_blk; + hw->dai_put_ctrl_blk = dai_put_ctrl_blk; + hw->dai_srt_set_srco = dai_srt_set_srco; + hw->dai_srt_set_srcm = dai_srt_set_srcm; + hw->dai_srt_set_rsr = dai_srt_set_rsr; + hw->dai_srt_set_drat = dai_srt_set_drat; + hw->dai_srt_set_ec = dai_srt_set_ec; + hw->dai_srt_set_et = dai_srt_set_et; + hw->dai_commit_write = dai_commit_write; + + hw->dao_get_ctrl_blk = dao_get_ctrl_blk; + hw->dao_put_ctrl_blk = dao_put_ctrl_blk; + hw->dao_set_spos = dao_set_spos; + hw->dao_commit_write = dao_commit_write; + hw->dao_get_spos = dao_get_spos; + + hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; + hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; + hw->daio_mgr_enb_dai = daio_mgr_enb_dai; + hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; + hw->daio_mgr_enb_dao = daio_mgr_enb_dao; + hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; + hw->daio_mgr_dao_init = daio_mgr_dao_init; + hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; + hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; + hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; + hw->daio_mgr_commit_write = daio_mgr_commit_write; + + *rhw = hw; + + return 0; +} + +int destroy_20k2_hw_obj(struct hw *hw) +{ + if (hw->io_base) + hw_card_shutdown(hw); + + kfree(hw); + return 0; +} diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h new file mode 100644 index 00000000000..d2b7daab681 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.h @@ -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 */ diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c new file mode 100644 index 00000000000..d34eacd902c --- /dev/null +++ b/sound/pci/ctxfi/ctimap.c @@ -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 + +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 = NULL; + struct list_head *pos = NULL; + + while (!list_empty(head)) { + pos = head->next; + list_del(pos); + entry = list_entry(pos, struct imapper, list); + kfree(entry); + } +} + diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h new file mode 100644 index 00000000000..53ccf9be8b6 --- /dev/null +++ b/sound/pci/ctxfi/ctimap.h @@ -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 + +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 */ diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c new file mode 100644 index 00000000000..c80d692952f --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.c @@ -0,0 +1,1108 @@ +/** + * 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.c + * + * @Brief + * This file contains the implementation of alsa mixer device functions. + * + * @Author Liu Chun + * @Date May 28 2008 + * + */ + + +#include "ctmixer.h" +#include "ctamixer.h" +#include +#include +#include +#include +#include + +enum CT_SUM_CTL { + SUM_IN_F, + SUM_IN_R, + SUM_IN_C, + SUM_IN_S, + SUM_IN_F_C, + + NUM_CT_SUMS +}; + +enum CT_AMIXER_CTL { + /* volume control mixers */ + AMIXER_MASTER_F, + AMIXER_MASTER_R, + AMIXER_MASTER_C, + AMIXER_MASTER_S, + AMIXER_PCM_F, + AMIXER_PCM_R, + AMIXER_PCM_C, + AMIXER_PCM_S, + AMIXER_SPDIFI, + AMIXER_LINEIN, + AMIXER_MIC, + AMIXER_SPDIFO, + AMIXER_WAVE_F, + AMIXER_WAVE_R, + AMIXER_WAVE_C, + AMIXER_WAVE_S, + AMIXER_MASTER_F_C, + AMIXER_PCM_F_C, + AMIXER_SPDIFI_C, + AMIXER_LINEIN_C, + AMIXER_MIC_C, + + /* this should always be the last one */ + NUM_CT_AMIXERS +}; + +enum CTALSA_MIXER_CTL { + /* volume control mixers */ + MIXER_MASTER_P, + MIXER_PCM_P, + MIXER_LINEIN_P, + MIXER_MIC_P, + MIXER_SPDIFI_P, + MIXER_SPDIFO_P, + MIXER_WAVEF_P, + MIXER_WAVER_P, + MIXER_WAVEC_P, + MIXER_WAVES_P, + MIXER_MASTER_C, + MIXER_PCM_C, + MIXER_LINEIN_C, + MIXER_MIC_C, + MIXER_SPDIFI_C, + + /* switch control mixers */ + MIXER_PCM_C_S, + MIXER_LINEIN_C_S, + MIXER_MIC_C_S, + MIXER_SPDIFI_C_S, + MIXER_LINEIN_P_S, + MIXER_SPDIFO_P_S, + MIXER_SPDIFI_P_S, + MIXER_WAVEF_P_S, + MIXER_WAVER_P_S, + MIXER_WAVEC_P_S, + MIXER_WAVES_P_S, + MIXER_DIGITAL_IO_S, + MIXER_IEC958_MASK, + MIXER_IEC958_DEFAULT, + MIXER_IEC958_STREAM, + + /* this should always be the last one */ + NUM_CTALSA_MIXERS +}; + +#define VOL_MIXER_START MIXER_MASTER_P +#define VOL_MIXER_END MIXER_SPDIFI_C +#define VOL_MIXER_NUM (VOL_MIXER_END - VOL_MIXER_START + 1) +#define SWH_MIXER_START MIXER_PCM_C_S +#define SWH_MIXER_END MIXER_DIGITAL_IO_S +#define SWH_CAPTURE_START MIXER_PCM_C_S +#define SWH_CAPTURE_END MIXER_SPDIFI_C_S + +#define CHN_NUM 2 + +struct ct_kcontrol_init { + unsigned char ctl; + char *name; +}; + +static struct ct_kcontrol_init +ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { + [MIXER_MASTER_P] = { + .ctl = 1, + .name = "Master Playback Volume", + }, + [MIXER_MASTER_C] = { + .ctl = 1, + .name = "Master Capture Volume", + }, + [MIXER_PCM_P] = { + .ctl = 1, + .name = "PCM Playback Volume", + }, + [MIXER_PCM_C] = { + .ctl = 1, + .name = "PCM Capture Volume", + }, + [MIXER_LINEIN_P] = { + .ctl = 1, + .name = "Line-in Playback Volume", + }, + [MIXER_LINEIN_C] = { + .ctl = 1, + .name = "Line-in Capture Volume", + }, + [MIXER_MIC_P] = { + .ctl = 1, + .name = "Mic Playback Volume", + }, + [MIXER_MIC_C] = { + .ctl = 1, + .name = "Mic Capture Volume", + }, + [MIXER_SPDIFI_P] = { + .ctl = 1, + .name = "S/PDIF-in Playback Volume", + }, + [MIXER_SPDIFI_C] = { + .ctl = 1, + .name = "S/PDIF-in Capture Volume", + }, + [MIXER_SPDIFO_P] = { + .ctl = 1, + .name = "S/PDIF-out Playback Volume", + }, + [MIXER_WAVEF_P] = { + .ctl = 1, + .name = "Front Playback Volume", + }, + [MIXER_WAVES_P] = { + .ctl = 1, + .name = "Surround Playback Volume", + }, + [MIXER_WAVEC_P] = { + .ctl = 1, + .name = "Center/LFE Playback Volume", + }, + [MIXER_WAVER_P] = { + .ctl = 1, + .name = "Rear Playback Volume", + }, + + [MIXER_PCM_C_S] = { + .ctl = 1, + .name = "PCM Capture Switch", + }, + [MIXER_LINEIN_C_S] = { + .ctl = 1, + .name = "Line-in Capture Switch", + }, + [MIXER_MIC_C_S] = { + .ctl = 1, + .name = "Mic Capture Switch", + }, + [MIXER_SPDIFI_C_S] = { + .ctl = 1, + .name = "S/PDIF-in Capture Switch", + }, + [MIXER_LINEIN_P_S] = { + .ctl = 1, + .name = "Line-in Playback Switch", + }, + [MIXER_SPDIFO_P_S] = { + .ctl = 1, + .name = "S/PDIF-out Playback Switch", + }, + [MIXER_SPDIFI_P_S] = { + .ctl = 1, + .name = "S/PDIF-in Playback Switch", + }, + [MIXER_WAVEF_P_S] = { + .ctl = 1, + .name = "Front Playback Switch", + }, + [MIXER_WAVES_P_S] = { + .ctl = 1, + .name = "Surround Playback Switch", + }, + [MIXER_WAVEC_P_S] = { + .ctl = 1, + .name = "Center/LFE Playback Switch", + }, + [MIXER_WAVER_P_S] = { + .ctl = 1, + .name = "Rear Playback Switch", + }, + [MIXER_DIGITAL_IO_S] = { + .ctl = 0, + .name = "Digit-IO Playback Switch", + }, +}; + +static void +ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); + +static void +ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); + +static struct snd_kcontrol *kctls[2] = {NULL}; + +static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) +{ + switch (alsa_index) { + case MIXER_MASTER_P: return AMIXER_MASTER_F; + case MIXER_MASTER_C: return AMIXER_MASTER_F_C; + case MIXER_PCM_P: return AMIXER_PCM_F; + case MIXER_PCM_C: + case MIXER_PCM_C_S: return AMIXER_PCM_F_C; + case MIXER_LINEIN_P: return AMIXER_LINEIN; + case MIXER_LINEIN_C: + case MIXER_LINEIN_C_S: return AMIXER_LINEIN_C; + case MIXER_MIC_P: return AMIXER_MIC; + case MIXER_MIC_C: + case MIXER_MIC_C_S: return AMIXER_MIC_C; + case MIXER_SPDIFI_P: return AMIXER_SPDIFI; + case MIXER_SPDIFI_C: + case MIXER_SPDIFI_C_S: return AMIXER_SPDIFI_C; + case MIXER_SPDIFO_P: return AMIXER_SPDIFO; + case MIXER_WAVEF_P: return AMIXER_WAVE_F; + case MIXER_WAVES_P: return AMIXER_WAVE_S; + case MIXER_WAVEC_P: return AMIXER_WAVE_C; + case MIXER_WAVER_P: return AMIXER_WAVE_R; + default: return NUM_CT_AMIXERS; + } +} + +static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index) +{ + switch (index) { + case AMIXER_MASTER_F: return AMIXER_MASTER_F_C; + case AMIXER_PCM_F: return AMIXER_PCM_F_C; + case AMIXER_SPDIFI: return AMIXER_SPDIFI_C; + case AMIXER_LINEIN: return AMIXER_LINEIN_C; + case AMIXER_MIC: return AMIXER_MIC_C; + default: return NUM_CT_AMIXERS; + } +} + +static unsigned char +get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type) +{ + return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START))) + ? 1 : 0; +} + +static void +set_switch_state(struct ct_mixer *mixer, + enum CTALSA_MIXER_CTL type, unsigned char state) +{ + if (state) + mixer->switch_state |= (0x1 << (type - SWH_MIXER_START)); + else + mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START)); +} + +/* Map integer value ranging from 0 to 65535 to 14-bit float value ranging + * from 2^-6 to (1+1023/1024) */ +static unsigned int uint16_to_float14(unsigned int x) +{ + unsigned int i = 0; + + if (x < 17) + return 0; + + x *= 2031; + x /= 65535; + x += 16; + + /* i <= 6 */ + for (i = 0; !(x & 0x400); i++) + x <<= 1; + + x = (((7 - i) & 0x7) << 10) | (x & 0x3ff); + + return x; +} + +static unsigned int float14_to_uint16(unsigned int x) +{ + unsigned int e = 0; + + if (!x) + return x; + + e = (x >> 10) & 0x7; + x &= 0x3ff; + x += 1024; + x >>= (7 - e); + x -= 16; + x *= 65535; + x /= 2031; + + return x; +} + +static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 43690; + uinfo->value.integer.step = 128; + + return 0; +} + +static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); + struct amixer *amixer = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + amixer = ((struct ct_mixer *)atc->mixer)-> + amixers[type*CHN_NUM+i]; + /* Convert 14-bit float-point scale to 16-bit integer volume */ + ucontrol->value.integer.value[i] = + (float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xffff); + } + + return 0; +} + +static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + struct ct_mixer *mixer = atc->mixer; + enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); + struct amixer *amixer = NULL; + int i = 0, j = 0, change = 0, val = 0; + + for (i = 0; i < 2; i++) { + /* Convert 16-bit integer volume to 14-bit float-point scale */ + val = (ucontrol->value.integer.value[i] & 0xffff); + amixer = mixer->amixers[type*CHN_NUM+i]; + if ((float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xff80) + != (val & 0xff80)) { + val = uint16_to_float14(val); + amixer->ops->set_scale(amixer, val); + amixer->ops->commit_write(amixer); + change = 1; + /* Synchronize Master/PCM playback AMIXERs. */ + if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) { + for (j = 1; j < 4; j++) { + amixer = mixer-> + amixers[(type+j)*CHN_NUM+i]; + amixer->ops->set_scale(amixer, val); + amixer->ops->commit_write(amixer); + } + } + } + } + + return change; +} + +static struct snd_kcontrol_new vol_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = ct_alsa_mix_volume_info, + .get = ct_alsa_mix_volume_get, + .put = ct_alsa_mix_volume_put +}; + +static void +do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) +{ + + if (MIXER_LINEIN_C_S == type) { + atc->select_line_in(atc); + set_switch_state(atc->mixer, MIXER_MIC_C_S, 0); + snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctls[1]->id); + } else if (MIXER_MIC_C_S == type) { + atc->select_mic_in(atc); + set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0); + snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctls[0]->id); + } +} + +static void +do_digit_io_switch(struct ct_atc *atc, int state) +{ + struct ct_mixer *mixer = atc->mixer; + + if (state) { + atc->select_digit_io(atc); + atc->spdif_out_unmute(atc, + get_switch_state(mixer, MIXER_SPDIFO_P_S)); + atc->spdif_in_unmute(atc, 1); + atc->line_in_unmute(atc, 0); + return; + } + + if (get_switch_state(mixer, MIXER_LINEIN_C_S)) + atc->select_line_in(atc); + else if (get_switch_state(mixer, MIXER_MIC_C_S)) + atc->select_mic_in(atc); + + atc->spdif_out_unmute(atc, 0); + atc->spdif_in_unmute(atc, 0); + atc->line_in_unmute(atc, 1); + return; +} + +static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->value.integer.step = 1; + + return 0; +} + +static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_mixer *mixer = + ((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer; + enum CTALSA_MIXER_CTL type = kcontrol->private_value; + + ucontrol->value.integer.value[0] = get_switch_state(mixer, type); + return 0; +} + +static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + struct ct_mixer *mixer = atc->mixer; + enum CTALSA_MIXER_CTL type = kcontrol->private_value; + int state = 0; + + state = ucontrol->value.integer.value[0]; + if (get_switch_state(mixer, type) == state) + return 0; + + set_switch_state(mixer, type, state); + /* Do changes in mixer. */ + if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { + if (state) { + ct_mixer_recording_select(mixer, + get_amixer_index(type)); + } else { + ct_mixer_recording_unselect(mixer, + get_amixer_index(type)); + } + } + /* Do changes out of mixer. */ + if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) + do_line_mic_switch(atc, type); + else if (MIXER_WAVEF_P_S == type) + atc->line_front_unmute(atc, state); + else if (MIXER_WAVES_P_S == type) + atc->line_surround_unmute(atc, state); + else if (MIXER_WAVEC_P_S == type) + atc->line_clfe_unmute(atc, state); + else if (MIXER_WAVER_P_S == type) + atc->line_rear_unmute(atc, state); + else if (MIXER_LINEIN_P_S == type) + atc->line_in_unmute(atc, state); + else if (MIXER_SPDIFO_P_S == type) + atc->spdif_out_unmute(atc, state); + else if (MIXER_SPDIFI_P_S == type) + atc->spdif_in_unmute(atc, state); + else if (MIXER_DIGITAL_IO_S == type) + do_digit_io_switch(atc, state); + + return 1; +} + +static struct snd_kcontrol_new swh_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = ct_alsa_mix_switch_info, + .get = ct_alsa_mix_switch_get, + .put = ct_alsa_mix_switch_put +}; + +static int ct_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; + + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; + + return 0; +} + +static int ct_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + unsigned int status = 0; + + atc->spdif_out_get_status(atc, &status); + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; + + return 0; +} + +static int ct_spdif_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + int change = 1; + unsigned int status = 0, old_status = 0; + + status = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + + atc->spdif_out_get_status(atc, &old_status); + change = (old_status != status); + if (change) + atc->spdif_out_set_status(atc, status); + + return change; +} + +static struct snd_kcontrol_new iec958_mask_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .count = 1, + .info = ct_spdif_info, + .get = ct_spdif_get_mask, + .private_value = MIXER_IEC958_MASK +}; + +static struct snd_kcontrol_new iec958_default_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .count = 1, + .info = ct_spdif_info, + .get = ct_spdif_default_get, + .put = ct_spdif_put, + .private_value = MIXER_IEC958_DEFAULT +}; + +static struct snd_kcontrol_new iec958_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .count = 1, + .info = ct_spdif_info, + .get = ct_spdif_get, + .put = ct_spdif_put, + .private_value = MIXER_IEC958_STREAM +}; + +#define NUM_IEC958_CTL 3 + +static int +ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) +{ + struct snd_kcontrol *kctl = NULL; + int err = 0; + + kctl = snd_ctl_new1(new, mixer->atc); + if (NULL == kctl) + return -ENOMEM; + + if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface) + kctl->id.device = IEC958; + + err = snd_ctl_add(mixer->atc->card, kctl); + if (err) + return err; + + switch (new->private_value) { + case MIXER_LINEIN_C_S: + kctls[0] = kctl; break; + case MIXER_MIC_C_S: + kctls[1] = kctl; break; + default: + break; + } + + return 0; +} + +static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) +{ + enum CTALSA_MIXER_CTL type = 0; + struct ct_atc *atc = mixer->atc; + int err = 0; + + /* Create snd kcontrol instances on demand */ + for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) { + if (ct_kcontrol_init_table[type].ctl) { + vol_ctl.name = ct_kcontrol_init_table[type].name; + vol_ctl.private_value = (unsigned long)type; + err = ct_mixer_kcontrol_new(mixer, &vol_ctl); + if (err) + return err; + } + } + + ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = + atc->have_digit_io_switch(atc); + for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { + if (ct_kcontrol_init_table[type].ctl) { + swh_ctl.name = ct_kcontrol_init_table[type].name; + swh_ctl.private_value = (unsigned long)type; + err = ct_mixer_kcontrol_new(mixer, &swh_ctl); + if (err) + return err; + } + } + + err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl); + if (err) + return err; + + err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl); + if (err) + return err; + + err = ct_mixer_kcontrol_new(mixer, &iec958_ctl); + if (err) + return err; + + atc->line_front_unmute(atc, 1); + set_switch_state(mixer, MIXER_WAVEF_P_S, 1); + atc->line_surround_unmute(atc, 0); + set_switch_state(mixer, MIXER_WAVES_P_S, 0); + atc->line_clfe_unmute(atc, 0); + set_switch_state(mixer, MIXER_WAVEC_P_S, 0); + atc->line_rear_unmute(atc, 0); + set_switch_state(mixer, MIXER_WAVER_P_S, 0); + atc->spdif_out_unmute(atc, 0); + set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); + atc->line_in_unmute(atc, 0); + set_switch_state(mixer, MIXER_LINEIN_P_S, 0); + atc->spdif_in_unmute(atc, 0); + set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); + + set_switch_state(mixer, MIXER_PCM_C_S, 1); + set_switch_state(mixer, MIXER_LINEIN_C_S, 1); + set_switch_state(mixer, MIXER_SPDIFI_C_S, 1); + + return 0; +} + +static void +ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) +{ + struct amixer *amix_d = NULL; + struct sum *sum_c = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + amix_d = mixer->amixers[type*CHN_NUM+i]; + sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i]; + amix_d->ops->set_sum(amix_d, sum_c); + amix_d->ops->commit_write(amix_d); + } +} + +static void +ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) +{ + struct amixer *amix_d = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + amix_d = mixer->amixers[type*CHN_NUM+i]; + amix_d->ops->set_sum(amix_d, NULL); + amix_d->ops->commit_write(amix_d); + } +} + +static int ct_mixer_get_resources(struct ct_mixer *mixer) +{ + struct sum_mgr *sum_mgr = NULL; + struct sum *sum = NULL; + struct sum_desc sum_desc = {0}; + struct amixer_mgr *amixer_mgr = NULL; + struct amixer *amixer = NULL; + struct amixer_desc am_desc = {0}; + int err = 0; + int i = 0; + + /* Allocate sum resources for mixer obj */ + sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; + sum_desc.msr = mixer->atc->msr; + for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { + err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); + if (err) { + printk(KERN_ERR "Failed to get sum resources for " + "front output!\n"); + break; + } + mixer->sums[i] = sum; + } + if (err) + goto error1; + + /* Allocate amixer resources for mixer obj */ + amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; + am_desc.msr = mixer->atc->msr; + for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { + err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); + if (err) { + printk(KERN_ERR "Failed to get amixer resources for " + "mixer obj!\n"); + break; + } + mixer->amixers[i] = amixer; + } + if (err) + goto error2; + + return 0; + +error2: + for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { + if (NULL != mixer->amixers[i]) { + amixer = mixer->amixers[i]; + amixer_mgr->put_amixer(amixer_mgr, amixer); + mixer->amixers[i] = NULL; + } + } +error1: + for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { + if (NULL != mixer->sums[i]) { + sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); + mixer->sums[i] = NULL; + } + } + + return err; +} + +static int ct_mixer_get_mem(struct ct_mixer **rmixer) +{ + struct ct_mixer *mixer = NULL; + int err = 0; + + *rmixer = NULL; + /* Allocate mem for mixer obj */ + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + if (NULL == mixer) + return -ENOMEM; + + mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), + GFP_KERNEL); + if (NULL == mixer->amixers) { + err = -ENOMEM; + goto error1; + } + mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); + if (NULL == mixer->sums) { + err = -ENOMEM; + goto error2; + } + + *rmixer = mixer; + return 0; + +error2: + kfree(mixer->amixers); +error1: + kfree(mixer); + return err; +} + +static int ct_mixer_topology_build(struct ct_mixer *mixer) +{ + struct sum *sum = NULL; + struct amixer *amix_d = NULL, *amix_s = NULL; + enum CT_AMIXER_CTL i = 0, j = 0; + + /* Build topology from destination to source */ + + /* Set up Master mixer */ + for (i = AMIXER_MASTER_F, j = SUM_IN_F; + i <= AMIXER_MASTER_S; i++, j++) { + amix_d = mixer->amixers[i*CHN_NUM]; + sum = mixer->sums[j*CHN_NUM]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[i*CHN_NUM+1]; + sum = mixer->sums[j*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + } + + /* Set up Wave-out mixer */ + for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F; + i <= AMIXER_WAVE_S; i++, j++) { + amix_d = mixer->amixers[i*CHN_NUM]; + amix_s = mixer->amixers[j*CHN_NUM]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[i*CHN_NUM+1]; + amix_s = mixer->amixers[j*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + } + + /* Set up S/PDIF-out mixer */ + amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM]; + amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1]; + amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + + /* Set up PCM-in mixer */ + for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) { + amix_d = mixer->amixers[i*CHN_NUM]; + sum = mixer->sums[j*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[i*CHN_NUM+1]; + sum = mixer->sums[j*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + } + + /* Set up Line-in mixer */ + amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM]; + sum = mixer->sums[SUM_IN_F*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Mic-in mixer */ + amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM]; + sum = mixer->sums[SUM_IN_F*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up S/PDIF-in mixer */ + amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM]; + sum = mixer->sums[SUM_IN_F*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Master recording mixer */ + amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + + /* Set up PCM-in recording mixer */ + amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Line-in recording mixer */ + amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Mic-in recording mixer */ + amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up S/PDIF-in recording mixer */ + amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + return 0; +} + +static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc) +{ + amixer->ops->set_input(amixer, rsc); + amixer->ops->commit_write(amixer); + + return 0; +} + +static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type) +{ + switch (type) { + case MIX_WAVE_FRONT: return AMIXER_WAVE_F; + case MIX_WAVE_SURROUND: return AMIXER_WAVE_S; + case MIX_WAVE_CENTLFE: return AMIXER_WAVE_C; + case MIX_WAVE_REAR: return AMIXER_WAVE_R; + case MIX_PCMO_FRONT: return AMIXER_MASTER_F_C; + case MIX_SPDIF_OUT: return AMIXER_SPDIFO; + case MIX_LINE_IN: return AMIXER_LINEIN; + case MIX_MIC_IN: return AMIXER_MIC; + case MIX_SPDIF_IN: return AMIXER_SPDIFI; + case MIX_PCMI_FRONT: return AMIXER_PCM_F; + case MIX_PCMI_SURROUND: return AMIXER_PCM_S; + case MIX_PCMI_CENTLFE: return AMIXER_PCM_C; + case MIX_PCMI_REAR: return AMIXER_PCM_R; + default: return 0; + } +} + +static int mixer_get_output_ports(struct ct_mixer *mixer, + enum MIXER_PORT_T type, + struct rsc **rleft, struct rsc **rright) +{ + enum CT_AMIXER_CTL amix = port_to_amixer(type); + + if (NULL != rleft) + *rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc; + + if (NULL != rright) + *rright = + &((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc; + + return 0; +} + +static int mixer_set_input_left(struct ct_mixer *mixer, + enum MIXER_PORT_T type, struct rsc *rsc) +{ + enum CT_AMIXER_CTL amix = port_to_amixer(type); + + mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); + amix = get_recording_amixer(amix); + if (amix < NUM_CT_AMIXERS) + mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); + + return 0; +} + +static int +mixer_set_input_right(struct ct_mixer *mixer, + enum MIXER_PORT_T type, struct rsc *rsc) +{ + enum CT_AMIXER_CTL amix = port_to_amixer(type); + + mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); + amix = get_recording_amixer(amix); + if (amix < NUM_CT_AMIXERS) + mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); + + return 0; +} + +int ct_mixer_destroy(struct ct_mixer *mixer) +{ + struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; + struct amixer_mgr *amixer_mgr = + (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; + struct amixer *amixer = NULL; + int i = 0; + + /* Release amixer resources */ + for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { + if (NULL != mixer->amixers[i]) { + amixer = mixer->amixers[i]; + amixer_mgr->put_amixer(amixer_mgr, amixer); + } + } + + /* Release sum resources */ + for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { + if (NULL != mixer->sums[i]) + sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); + } + + /* Release mem assigned to mixer object */ + kfree(mixer->sums); + kfree(mixer->amixers); + kfree(mixer); + + return 0; +} + +int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer) +{ + struct ct_mixer *mixer = NULL; + int err = 0; + + *rmixer = NULL; + + /* Allocate mem for mixer obj */ + err = ct_mixer_get_mem(&mixer); + if (err) + return err; + + mixer->switch_state = 0; + mixer->atc = atc; + /* Set operations */ + mixer->get_output_ports = mixer_get_output_ports; + mixer->set_input_left = mixer_set_input_left; + mixer->set_input_right = mixer_set_input_right; + + /* Allocate chip resources for mixer obj */ + err = ct_mixer_get_resources(mixer); + if (err) + goto error; + + /* Build internal mixer topology */ + ct_mixer_topology_build(mixer); + + *rmixer = mixer; + + return 0; + +error: + ct_mixer_destroy(mixer); + return err; +} + +int ct_alsa_mix_create(struct ct_atc *atc, + enum CTALSADEVS device, + const char *device_name) +{ + int err = 0; + + /* Create snd kcontrol instances on demand */ + vol_ctl.device = swh_ctl.device = device; + err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer); + if (err) + return err; + + strcpy(atc->card->mixername, device_name); + + return 0; +} diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h new file mode 100644 index 00000000000..e2d96ebde74 --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.h @@ -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 */ diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c new file mode 100644 index 00000000000..73d4fdbbb9f --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.c @@ -0,0 +1,499 @@ +/** + * 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 + +/* 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_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_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 = 1, + .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 | + SNDRV_PCM_FMTBIT_U16_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 = 1, + .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_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_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); + 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; + + spin_lock_init(&apcm->timer_lock); + apcm->stop_timer = 0; + 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; + } + + 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) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int ct_pcm_hw_free(struct snd_pcm_substream *substream) +{ + /* Free snd-allocated pages */ + return snd_pcm_lib_free_pages(substream); +} + +static void ct_pcm_timer_callback(unsigned long data) +{ + struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; + struct snd_pcm_substream *substream = apcm->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int period_size = runtime->period_size; + unsigned int buffer_size = runtime->buffer_size; + unsigned long flags; + unsigned int position = 0, dist = 0, interval = 0; + + position = substream->ops->pointer(substream); + dist = (position + buffer_size - apcm->position) % buffer_size; + if ((dist >= period_size) || + (position/period_size != apcm->position/period_size)) { + apcm->interrupt(apcm); + apcm->position = position; + } + /* Add extra HZ*5/1000 to avoid overrun issue when recording + * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ + interval = ((period_size - (position % period_size)) + * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; + spin_lock_irqsave(&apcm->timer_lock, flags); + apcm->timer.expires = jiffies + interval; + if (!apcm->stop_timer) + add_timer(&apcm->timer); + + spin_unlock_irqrestore(&apcm->timer_lock, flags); +} + +static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) +{ + unsigned long flags; + + spin_lock_irqsave(&apcm->timer_lock, flags); + if (timer_pending(&apcm->timer)) { + /* The timer has already been started. */ + spin_unlock_irqrestore(&apcm->timer_lock, flags); + return 0; + } + + init_timer(&apcm->timer); + apcm->timer.data = (unsigned long)apcm; + apcm->timer.function = ct_pcm_timer_callback; + spin_unlock_irqrestore(&apcm->timer_lock, flags); + apcm->position = 0; + + return 0; +} + +static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) +{ + struct snd_pcm_runtime *runtime = apcm->substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&apcm->timer_lock, flags); + if (timer_pending(&apcm->timer)) { + /* The timer has already been started. */ + spin_unlock_irqrestore(&apcm->timer_lock, flags); + return 0; + } + + apcm->timer.expires = jiffies + (runtime->period_size * HZ + + (runtime->rate - 1)) / runtime->rate; + apcm->stop_timer = 0; + add_timer(&apcm->timer); + spin_unlock_irqrestore(&apcm->timer_lock, flags); + + return 0; +} + +static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) +{ + unsigned long flags; + + spin_lock_irqsave(&apcm->timer_lock, flags); + apcm->stop_timer = 1; + del_timer(&apcm->timer); + spin_unlock_irqrestore(&apcm->timer_lock, flags); + + try_to_del_timer_sync(&apcm->timer); + + return 0; +} + +static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + 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 "Preparing pcm playback failed!!!\n"); + return err; + } + + ct_pcm_timer_prepare(apcm); + + 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); + ct_pcm_timer_start(apcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ct_pcm_timer_stop(apcm); + atc->pcm_playback_stop(atc, apcm); + break; + default: + 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); + 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; + + spin_lock_init(&apcm->timer_lock); + apcm->started = 0; + apcm->stop_timer = 0; + apcm->substream = substream; + apcm->interrupt = ct_atc_pcm_interrupt; + runtime->private_data = apcm; + 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; + } + + 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 "Preparing pcm capture failed!!!\n"); + return err; + } + + ct_pcm_timer_prepare(apcm); + + 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); + ct_pcm_timer_start(apcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + ct_pcm_timer_stop(apcm); + atc->pcm_capture_stop(atc, apcm); + break; + default: + ct_pcm_timer_stop(apcm); + atc->pcm_capture_stop(atc, apcm); + break; + } + + 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); + 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, +}; + +/* 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, +}; + +/* 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; + char name[128]; + + strncpy(name, device_name, sizeof(name)); + playback_count = (IEC958 == device) ? 1 : 8; + capture_count = (FRONT == device) ? 1 : 0; + err = snd_pcm_new(atc->card, name, device, + playback_count, capture_count, &pcm); + if (err < 0) { + printk(KERN_ERR "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; + strcpy(pcm->name, device_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, + snd_dma_pci_data(atc->pci), 128*1024, 128*1024); + + return 0; +} diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h new file mode 100644 index 00000000000..178da0dca64 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.h @@ -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 */ diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c new file mode 100644 index 00000000000..414fc23267c --- /dev/null +++ b/sound/pci/ctxfi/ctresource.c @@ -0,0 +1,297 @@ +/** + * 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 +#include + +#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 = 0, j = 0, k = 0, n = 0; + + /* 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 = 0, j = 0, k = 0, n = 0; + + /* 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 = 0; + + 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 "Invalid resource type value %d!\n", type); + return -EINVAL; + } + + if (err) { + printk(KERN_ERR "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 "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 "Invalid resource type value %d!\n", type); + err = -EINVAL; + goto error; + } + + if (err) { + printk(KERN_ERR "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 "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; +} diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h new file mode 100644 index 00000000000..0838c2e84f8 --- /dev/null +++ b/sound/pci/ctxfi/ctresource.h @@ -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 + +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 */ diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c new file mode 100644 index 00000000000..d3e0ad5ed9f --- /dev/null +++ b/sound/pci/ctxfi/ctsrc.c @@ -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 + +#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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + + hw = (struct 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 = NULL; + int i = 0; + unsigned int dirty = 0; + + hw = (struct 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 = NULL; + + hw = (struct 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 = 0, msr = 0; + + 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 = 0, msr = 0; + 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 = 0; + int i = 0, n = 0; + 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 = 0, n = 0; + 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 = 0; + struct src *src = NULL; + 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 "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 = 0; + + 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 = 0; + + 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 = 0; + + 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 = 0, i = 0; + 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 = NULL; + int i = 0; + + 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 = 0; + + /* 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 = 0; + + 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 = 0, i = 0; + unsigned int idx = 0; + struct srcimp *srcimp = NULL; + 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 "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 = 0; + + 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 = 0; + + 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 = 0; + + 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 = 0; + 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; +} diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h new file mode 100644 index 00000000000..259366aabca --- /dev/null +++ b/sound/pci/ctxfi/ctsrc.h @@ -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 +#include + +#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 */ diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c new file mode 100644 index 00000000000..46ca04ce921 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.c @@ -0,0 +1,254 @@ +/** + * 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 +#include +#include /* for PAGE_SIZE macro definition */ +#include +#include + +#define CT_PTES_PER_PAGE (PAGE_SIZE / sizeof(void *)) +#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * 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 = NULL; + struct list_head *pos = NULL; + + 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) + return NULL; + + 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; + return entry; + } + + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (NULL == block) + return NULL; + + block->addr = entry->addr; + block->size = size; + list_add(&block->list, &vm->used); + entry->addr += size; + entry->size -= size; + vm->size -= size; + + return block; +} + +static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) +{ + struct ct_vm_block *entry = NULL, *pre_ent = NULL; + struct list_head *pos = NULL, *pre = NULL; + + 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; + } +} + +/* Map host addr (kmalloced/vmalloced) to device logical addr. */ +static struct ct_vm_block * +ct_vm_map(struct ct_vm *vm, void *host_addr, int size) +{ + struct ct_vm_block *block = NULL; + unsigned long pte_start; + unsigned long i; + unsigned long pages; + unsigned long start_phys; + unsigned long *ptp; + + /* do mapping */ + if ((unsigned long)host_addr >= VMALLOC_START) { + printk(KERN_ERR "Fail! Not support vmalloced addr now!\n"); + return NULL; + } + + if (size > vm->size) { + printk(KERN_ERR "Fail! No sufficient device virtural " + "memory space available!\n"); + return NULL; + } + + start_phys = (virt_to_phys(host_addr) & PAGE_MASK); + pages = (PAGE_ALIGN(virt_to_phys(host_addr) + size) + - start_phys) >> PAGE_SHIFT; + + ptp = vm->ptp[0]; + + block = get_vm_block(vm, (pages << PAGE_SHIFT)); + if (block == NULL) { + printk(KERN_ERR "No virtual memory block that is big " + "enough to allocate!\n"); + return NULL; + } + + pte_start = (block->addr >> PAGE_SHIFT); + for (i = 0; i < pages; i++) + ptp[pte_start+i] = start_phys + (i << PAGE_SHIFT); + + block->addr += (virt_to_phys(host_addr) & (~PAGE_MASK)); + block->size = size; + + return block; +} + +static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) +{ + /* do unmapping */ + block->size = ((block->addr + block->size + PAGE_SIZE - 1) + & PAGE_MASK) - (block->addr & PAGE_MASK); + block->addr &= PAGE_MASK; + 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; + + /* 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 = NULL; + struct ct_vm_block *entry = NULL; + + /* 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); +} diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h new file mode 100644 index 00000000000..4eb5bdd5cad --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.h @@ -0,0 +1,49 @@ +/** + * 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 +#include + +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; +}; + +/* 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 */ + + /* Map host addr (kmalloced/vmalloced) to device logical addr. */ + struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, 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 */ diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c new file mode 100644 index 00000000000..f9114403651 --- /dev/null +++ b/sound/pci/ctxfi/xfi.c @@ -0,0 +1,125 @@ +/* + * 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 +#include +#include +#include +#include +#include "ctatc.h" +#include "ctdrv.h" + +MODULE_AUTHOR("Creative Technology Ltd"); +MODULE_DESCRIPTION("X-Fi driver version 1.03"); +MODULE_LICENSE("GPLv2"); +MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); + +static unsigned int reference_rate = 48000; +static unsigned int multiple = 2; +module_param(reference_rate, uint, S_IRUGO); +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; + +static struct pci_device_id ct_pci_dev_ids[] = { + /* only X-Fi is supported, so... */ + { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K1) }, + { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K2) }, + { 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 "Invalid reference_rate value %u!!!\n" + "The valid values for reference_rate " + "are 48000 and 44100.\nValue 48000 is " + "assumed.\n", reference_rate); + reference_rate = 48000; + } + if ((multiple != 1) && (multiple != 2)) { + printk(KERN_ERR "Invalid multiple value %u!!!\nThe valid " + "values for multiple are 1 and 2.\nValue 2 " + "is assumed.\n", multiple); + multiple = 2; + } + err = ct_atc_create(card, pci, reference_rate, multiple, &atc); + if (err < 0) + goto error; + + card->private_data = atc; + + /* Create alsa devices supported by this card */ + err = atc->create_alsa_devs(atc); + if (err < 0) + goto error; + + strcpy(card->driver, "SB-XFi"); + strcpy(card->shortname, "Creative X-Fi"); + strcpy(card->longname, "Creative ALSA Driver X-Fi"); + + 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) -- cgit v1.2.3 From d0da727e025da8b443a4a614dbb7a031b89857d0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 10:56:04 +0200 Subject: ALSA: ctxfi - Add missing inclusion of linux/delay.h Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 1 + sound/pci/ctxfi/cthw20k2.c | 1 + 2 files changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 53572d92ef5..44283bd7b2d 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -25,6 +25,7 @@ #include #include #include +#include #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index cdcb75cc33b..9c2d38b4faa 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -24,6 +24,7 @@ #include #include #include +#include #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ -- cgit v1.2.3 From 9fc20f030ba457d20eb994e1def7e2ce7d5ae1a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 15:14:18 +0200 Subject: ALSA: ctxfi - Move PCI ID definitions to linux/pci_ids.h Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 17 +++++++++-------- sound/pci/ctxfi/ctdrv.h | 30 ------------------------------ sound/pci/ctxfi/xfi.c | 6 +++--- 3 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 sound/pci/ctxfi/ctdrv.h (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 5f35374863f..073fe2a59da 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -18,7 +18,6 @@ #include "ctatc.h" #include "ctpcm.h" #include "ctmixer.h" -#include "ctdrv.h" #include "cthardware.h" #include "ctsrc.h" #include "ctamixer.h" @@ -40,23 +39,25 @@ | ((IEC958_AES3_CON_FS_48000) << 24)) static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { - [CTSB0760] = {.subsys = PCI_SUBSYS_CREATIVE_SB0760, + [CTSB0760] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB0760, .nm_model = "SB076x"}, - [CTHENDRIX] = {.subsys = PCI_SUBSYS_CREATIVE_HENDRIX, + [CTHENDRIX] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, .nm_model = "Hendrix"}, - [CTSB08801] = {.subsys = PCI_SUBSYS_CREATIVE_SB08801, + [CTSB08801] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08801, .nm_model = "SB0880"}, - [CTSB08802] = {.subsys = PCI_SUBSYS_CREATIVE_SB08802, + [CTSB08802] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08802, .nm_model = "SB0880"}, - [CTSB08803] = {.subsys = PCI_SUBSYS_CREATIVE_SB08803, + [CTSB08803] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08803, .nm_model = "SB0880"} }; static struct ct_atc_chip_details atc_chip_details[] = { - {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K1, + {.vendor = PCI_VENDOR_ID_CREATIVE, + .device = PCI_DEVICE_ID_CREATIVE_20K1, .sub_details = NULL, .nm_card = "X-Fi 20k1"}, - {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K2, + {.vendor = PCI_VENDOR_ID_CREATIVE, + .device = PCI_DEVICE_ID_CREATIVE_20K2, .sub_details = atc_sub_details, .nm_card = "X-Fi 20k2"}, {} /* terminator */ diff --git a/sound/pci/ctxfi/ctdrv.h b/sound/pci/ctxfi/ctdrv.h deleted file mode 100644 index f776a44f52c..00000000000 --- a/sound/pci/ctxfi/ctdrv.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 ctdrv.h - * - * @breaf - * This file contains the definition of card IDs supported by this driver. - * - * @author Liu Chun - * - */ - -#ifndef CTDRV_H -#define CTDRV_H - -#define PCI_VENDOR_CREATIVE 0x1102 -#define PCI_DEVICE_CREATIVE_20K1 0x0005 -#define PCI_DEVICE_CREATIVE_20K2 0x000B -#define PCI_SUBVENDOR_CREATIVE 0x1102 -#define PCI_SUBSYS_CREATIVE_SB0760 0x0024 -#define PCI_SUBSYS_CREATIVE_SB08801 0x0041 -#define PCI_SUBSYS_CREATIVE_SB08802 0x0042 -#define PCI_SUBSYS_CREATIVE_SB08803 0x0043 -#define PCI_SUBSYS_CREATIVE_HENDRIX 0x6000 - -#endif /* CTDRV_H */ diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index f9114403651..b7368fded53 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -11,10 +11,10 @@ #include #include #include +#include #include #include #include "ctatc.h" -#include "ctdrv.h" MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); @@ -32,8 +32,8 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static struct pci_device_id ct_pci_dev_ids[] = { /* only X-Fi is supported, so... */ - { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K1) }, - { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K2) }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1) }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2) }, { 0, } }; MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); -- cgit v1.2.3 From 35b053becb64eba13f3ea5c8c51023997169ff34 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 15:17:16 +0200 Subject: ALSA: ctxfi - Avoid unneeded pci_read_config_*() calls Use struct pci subsystem_device and revision fields instead of unneeded calls of pci_read_config_*(). Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 073fe2a59da..f5234500233 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1190,14 +1190,14 @@ static int atc_dev_free(struct snd_device *dev) static int atc_identify_card(struct ct_atc *atc) { - u16 subsys = 0; - u8 revision = 0; + u16 subsys; + u8 revision; struct pci_dev *pci = atc->pci; const struct ct_atc_chip_details *d; enum CTCARDS i; - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys); - pci_read_config_byte(pci, PCI_REVISION_ID, &revision); + subsys = pci->subsystem_device; + revision = pci->revision; atc->chip_details = NULL; atc->model = NUM_CTCARDS; for (d = atc_chip_details; d->vendor; d++) { @@ -1308,7 +1308,7 @@ static int atc_get_resources(struct ct_atc *atc) struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr = NULL; int err = 0, i = 0; - unsigned short subsys_id = 0; + unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); if (NULL == atc->daios) @@ -1339,7 +1339,7 @@ static int atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - pci_read_config_word(atc->pci, PCI_SUBSYSTEM_ID, &subsys_id); + subsys_id = atc->pci->subsystem_device; if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { /* SB073x cards */ da_desc.type = SPDIFI1; -- cgit v1.2.3 From b3e0afe61e8271a8d082478752a67e5c279c8f23 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 15:19:30 +0200 Subject: ALSA: ctxfi - Add prefix to debug prints Added ctxfi: prefix to each debug print. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctamixer.c | 4 ++-- sound/pci/ctxfi/ctatc.c | 16 +++++++++------- sound/pci/ctxfi/cthw20k2.c | 17 +++++++++-------- sound/pci/ctxfi/ctmixer.c | 6 +++--- sound/pci/ctxfi/ctpcm.c | 6 +++--- sound/pci/ctxfi/ctresource.c | 20 ++++++++++++-------- sound/pci/ctxfi/ctsrc.c | 4 ++-- sound/pci/ctxfi/ctvmem.c | 7 ++++--- sound/pci/ctxfi/xfi.c | 15 ++++++++------- 9 files changed, 52 insertions(+), 43 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index 119791aa70e..859e996ad72 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -259,7 +259,7 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet AMIXER resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n"); goto error; } @@ -413,7 +413,7 @@ static int get_sum_rsc(struct sum_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet SUM resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n"); goto error; } diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index f5234500233..ead104ea1e3 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -191,7 +191,7 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) case SNDRV_PCM_FORMAT_S32_LE: return SRC_SF_S32; default: - printk(KERN_ERR "not recognized snd format is %d \n", + printk(KERN_ERR "ctxfi: not recognized snd format is %d \n", snd_format); return SRC_SF_S16; } @@ -1254,7 +1254,8 @@ static int ct_create_alsa_devs(struct ct_atc *atc) err = alsa_dev_funcs[i].create(atc, i, alsa_dev_funcs[i].public_name); if (err) { - printk(KERN_ERR "Creating alsa device %d failed!\n", i); + printk(KERN_ERR "ctxfi: " + "Creating alsa device %d failed!\n", i); return err; } } @@ -1289,7 +1290,8 @@ static int atc_create_hw_devs(struct ct_atc *atc) err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); if (err) { - printk(KERN_ERR "Failed to create rsc_mgr %d!!!\n", i); + printk(KERN_ERR "ctxfi: " + "Failed to create rsc_mgr %d!!!\n", i); return err; } } @@ -1333,7 +1335,7 @@ static int atc_get_resources(struct ct_atc *atc) err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { - printk(KERN_ERR "Failed to get DAIO " + printk(KERN_ERR "ctxfi: Failed to get DAIO " "resource %d!!!\n", i); return err; } @@ -1349,7 +1351,7 @@ static int atc_get_resources(struct ct_atc *atc) err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { - printk(KERN_ERR "Failed to get S/PDIF-in resource!!!\n"); + printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n"); return err; } atc->n_daio++; @@ -1400,7 +1402,7 @@ static int atc_get_resources(struct ct_atc *atc) err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); if (err) { - printk(KERN_ERR "Failed to create mixer obj!!!\n"); + printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n"); return err; } @@ -1600,7 +1602,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, error1: ct_atc_destroy(atc); - printk(KERN_ERR "Something wrong!!!\n"); + printk(KERN_ERR "ctxfi: Something wrong!!!\n"); return err; } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 9c2d38b4faa..7c24c2ca96b 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1144,7 +1144,7 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else { - printk(KERN_ALERT "ERROR!!! Invalid sampling rate!!!\n"); + printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); return -EINVAL; } @@ -1197,7 +1197,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { - printk(KERN_ALERT "Wrong device page table page address!!!\n"); + printk(KERN_ALERT "ctxfi: " + "Wrong device page table page address!!!\n"); return -1; } @@ -1314,7 +1315,7 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr) break; } if (i >= 1000) { - printk(KERN_ALERT "PLL initialization failed!!!\n"); + printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n"); return -EBUSY; } @@ -1338,7 +1339,7 @@ static int hw_auto_init(struct hw *hw) break; } if (!get_field(gctl, GCTL_AID)) { - printk(KERN_ALERT "Card Auto-init failed!!!\n"); + printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n"); return -EBUSY; } @@ -1762,7 +1763,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) /* Initialize I2C */ err = I2CInit(hw, 0x1A, 1, 1); if (err < 0) { - printk(KERN_ALERT "Failure to acquire I2C!!!\n"); + printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n"); goto error; } @@ -1782,7 +1783,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { - printk(KERN_ALERT "Invalid master sampling " + printk(KERN_ALERT "ctxfi: Invalid master sampling " "rate (msr %d)!!!\n", info->msr); err = -EINVAL; goto error; @@ -1820,7 +1821,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ } else { - printk(KERN_ALERT "ERROR!!! Invalid input mux!!!\n"); + printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); err = -EINVAL; goto error; } @@ -1852,7 +1853,7 @@ static int hw_card_start(struct hw *hw) dma_mask = CT_XFI_DMA_MASK; if (pci_set_dma_mask(pci, dma_mask) < 0 || pci_set_consistent_dma_mask(pci, dma_mask) < 0) { - printk(KERN_ERR "architecture does not support PCI " + printk(KERN_ERR "ctxfi: architecture does not support PCI " "busmaster DMA with mask 0x%x\n", dma_mask); err = -ENXIO; goto error1; diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index c80d692952f..b7950768c3f 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -760,7 +760,7 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer) for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); if (err) { - printk(KERN_ERR "Failed to get sum resources for " + printk(KERN_ERR "ctxfi:Failed to get sum resources for " "front output!\n"); break; } @@ -775,8 +775,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer) for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); if (err) { - printk(KERN_ERR "Failed to get amixer resources for " - "mixer obj!\n"); + printk(KERN_ERR "ctxfi:Failed to get amixer resources " + "for mixer obj!\n"); break; } mixer->amixers[i] = amixer; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 73d4fdbbb9f..a64cb0ed875 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -284,7 +284,7 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) err = atc->pcm_playback_prepare(atc, apcm); if (err < 0) { - printk(KERN_ERR "Preparing pcm playback failed!!!\n"); + printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n"); return err; } @@ -389,7 +389,7 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) err = atc->pcm_capture_prepare(atc, apcm); if (err < 0) { - printk(KERN_ERR "Preparing pcm capture failed!!!\n"); + printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n"); return err; } @@ -477,7 +477,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc, err = snd_pcm_new(atc->card, name, device, playback_count, capture_count, &pcm); if (err < 0) { - printk(KERN_ERR "snd_pcm_new failed!! Err=%d\n", err); + printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err); return err; } diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c index 414fc23267c..da21a717a07 100644 --- a/sound/pci/ctxfi/ctresource.c +++ b/sound/pci/ctxfi/ctresource.c @@ -162,12 +162,14 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) case DAIO: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", type); + printk(KERN_ERR + "ctxfi: Invalid resource type value %d!\n", type); return -EINVAL; } if (err) { - printk(KERN_ERR "Failed to get resource control block!\n"); + printk(KERN_ERR + "ctxfi: Failed to get resource control block!\n"); return err; } @@ -190,8 +192,8 @@ int rsc_uninit(struct rsc *rsc) case DAIO: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", - rsc->type); + printk(KERN_ERR "ctxfi: " + "Invalid resource type value %d!\n", rsc->type); break; } @@ -233,13 +235,15 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, case SUM: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", type); + printk(KERN_ERR + "ctxfi: Invalid resource type value %d!\n", type); err = -EINVAL; goto error; } if (err) { - printk(KERN_ERR "Failed to get manager control block!\n"); + printk(KERN_ERR + "ctxfi: Failed to get manager control block!\n"); goto error; } @@ -282,8 +286,8 @@ int rsc_mgr_uninit(struct rsc_mgr *mgr) case SUM: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", - mgr->type); + printk(KERN_ERR "ctxfi: " + "Invalid resource type value %d!\n", mgr->type); break; } diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index d3e0ad5ed9f..77e118c5bc9 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -431,7 +431,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet SRC resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n"); return err; } @@ -740,7 +740,7 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet SRCIMP resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n"); goto error1; } diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 46ca04ce921..cecf77e3ee8 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -121,12 +121,13 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) /* do mapping */ if ((unsigned long)host_addr >= VMALLOC_START) { - printk(KERN_ERR "Fail! Not support vmalloced addr now!\n"); + printk(KERN_ERR "ctxfi: " + "Fail! Not support vmalloced addr now!\n"); return NULL; } if (size > vm->size) { - printk(KERN_ERR "Fail! No sufficient device virtural " + printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " "memory space available!\n"); return NULL; } @@ -139,7 +140,7 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) block = get_vm_block(vm, (pages << PAGE_SHIFT)); if (block == NULL) { - printk(KERN_ERR "No virtual memory block that is big " + printk(KERN_ERR "ctxfi: No virtual memory block that is big " "enough to allocate!\n"); return NULL; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index b7368fded53..19310ed26e8 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -57,16 +57,17 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err) return err; if ((reference_rate != 48000) && (reference_rate != 44100)) { - printk(KERN_ERR "Invalid reference_rate value %u!!!\n" - "The valid values for reference_rate " - "are 48000 and 44100.\nValue 48000 is " - "assumed.\n", reference_rate); + 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 "Invalid multiple value %u!!!\nThe valid " - "values for multiple are 1 and 2.\nValue 2 " - "is assumed.\n", multiple); + 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, &atc); -- cgit v1.2.3 From 3e3ee6dc949d25423f72bb5ee5f58dfa79ac85c7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 20 May 2009 16:47:59 +0200 Subject: ALSA: ctxfi - Add depends on X86 The ctxfi driver requires explicitly the 4k page size, and gives a build error on architectures with non-4k pages. As a workaround, just add the kconfig dependency on X86, which is the only architecture ever tested. Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 3a7640feaf9..2d7fef5df01 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -277,6 +277,7 @@ config SND_CS5535AUDIO config SND_CTXFI tristate "Creative Sound Blaster X-Fi" + depends on X86 select SND_PCM help If you want to use soundcards based on Creative Sound Blastr X-Fi -- cgit v1.2.3 From aae80dc24aeddec4e2b6e182a43491942f8667d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 18:35:27 +0200 Subject: ALSA: ctxfi - Add missing module parameter definitions Added missing module_param*() and MODULE_PARM*(). Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/xfi.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 19310ed26e8..e31e29e4ec7 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -23,13 +23,22 @@ 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) }, -- cgit v1.2.3 From 8a4259bf89d23bfd58d87e275ef6da29cea6b3c5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 08:40:51 +0200 Subject: ALSA: ctxfi - Fix Oops at mmapping Replace a spinlock with a mutex protecting the vm block list at mmap / munmap calls, which caused Oops like below: BUG: sleeping function called from invalid context at mm/slub.c:1599 in_atomic(): 0, irqs_disabled(): 1, pid: 32065, name: xine Pid: 32065, comm: xine Tainted: P 2.6.29.4-75.fc10.x86_64 #1 Call Trace: [] __might_sleep+0x105/0x10a [] kmem_cache_alloc+0x32/0xe2 [] ct_vm_map+0xfa/0x19e [snd_ctxfi] [] ct_map_audio_buffer+0x4c/0x76 [snd_ctxfi] [] atc_pcm_playback_prepare+0x1d7/0x2a8 [snd_ctxfi] [] ? up_read+0x9/0xb [] ? __up_read+0x7c/0x87 [] ct_pcm_playback_prepare+0x39/0x60 [snd_ctxfi] [] snd_pcm_do_prepare+0x16/0x28 [snd_pcm] [] snd_pcm_action_single+0x2d/0x5b [snd_pcm] [] snd_pcm_action_nonatomic+0x52/0x6a [snd_pcm] [] snd_pcm_common_ioctl1+0x404/0xc79 [snd_pcm] [] ? alloc_pages_current+0xb9/0xc2 [] ? new_slab+0x1a5/0x1cb [] ? vma_prio_tree_insert+0x23/0xc1 [] snd_pcm_playback_ioctl1+0x213/0x230 [snd_pcm] [] ? mmap_region+0x397/0x4c9 [] snd_pcm_playback_ioctl+0x2e/0x36 [snd_pcm] [] vfs_ioctl+0x2a/0x78 [] do_vfs_ioctl+0x462/0x4a2 [] ? default_spin_lock_flags+0x9/0xe [] ? trace_hardirqs_off_thunk+0x3a/0x6c [] sys_ioctl+0x55/0x77 [] system_call_fastpath+0x16/0x1b Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 11 ----------- sound/pci/ctxfi/ctatc.h | 1 - sound/pci/ctxfi/ctvmem.c | 14 +++++++++++--- sound/pci/ctxfi/ctvmem.h | 3 ++- 4 files changed, 13 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index ead104ea1e3..1a4bb357eae 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -119,7 +119,6 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm); static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - unsigned long flags; struct snd_pcm_runtime *runtime; struct ct_vm *vm; @@ -129,9 +128,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) runtime = apcm->substream->runtime; vm = atc->vm; - spin_lock_irqsave(&atc->vm_lock, flags); apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); - spin_unlock_irqrestore(&atc->vm_lock, flags); if (NULL == apcm->vm_block) return -ENOENT; @@ -141,7 +138,6 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - unsigned long flags; struct ct_vm *vm; if (NULL == apcm->vm_block) @@ -149,9 +145,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) vm = atc->vm; - spin_lock_irqsave(&atc->vm_lock, flags); vm->unmap(vm, apcm->vm_block); - spin_unlock_irqrestore(&atc->vm_lock, flags); apcm->vm_block = NULL; } @@ -161,9 +155,7 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) struct ct_vm *vm; void *kvirt_addr; unsigned long phys_addr; - unsigned long flags; - spin_lock_irqsave(&atc->vm_lock, flags); vm = atc->vm; kvirt_addr = vm->get_ptp_virt(vm, index); if (kvirt_addr == NULL) @@ -171,8 +163,6 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) else phys_addr = virt_to_phys(kvirt_addr); - spin_unlock_irqrestore(&atc->vm_lock, flags); - return phys_addr; } @@ -1562,7 +1552,6 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, atc_set_ops(atc); spin_lock_init(&atc->atc_lock); - spin_lock_init(&atc->vm_lock); /* Find card model */ err = atc_identify_card(atc); diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 286c993d461..a7b0ec24cd7 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -101,7 +101,6 @@ struct ct_atc { unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index); spinlock_t atc_lock; - spinlock_t vm_lock; int (*pcm_playback_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm); diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index cecf77e3ee8..363b67e3a9e 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -35,25 +35,27 @@ get_vm_block(struct ct_vm *vm, unsigned int size) struct ct_vm_block *block = NULL, *entry = NULL; struct list_head *pos = 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) - return NULL; + 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; - return entry; + block = entry; + goto out; } block = kzalloc(sizeof(*block), GFP_KERNEL); if (NULL == block) - return NULL; + goto out; block->addr = entry->addr; block->size = size; @@ -62,6 +64,8 @@ get_vm_block(struct ct_vm *vm, unsigned int size) entry->size -= size; vm->size -= size; + out: + mutex_unlock(&vm->lock); return block; } @@ -70,6 +74,7 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) struct ct_vm_block *entry = NULL, *pre_ent = NULL; struct list_head *pos = NULL, *pre = NULL; + mutex_lock(&vm->lock); list_del(&block->list); vm->size += block->size; @@ -106,6 +111,7 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) pos = pre; pre = pos->prev; } + mutex_unlock(&vm->lock); } /* Map host addr (kmalloced/vmalloced) to device logical addr. */ @@ -191,6 +197,8 @@ int ct_vm_create(struct ct_vm **rvm) 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); diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 4eb5bdd5cad..618952efa5b 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -20,7 +20,7 @@ #define CT_PTP_NUM 1 /* num of device page table pages */ -#include +#include #include struct ct_vm_block { @@ -35,6 +35,7 @@ struct ct_vm { 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 *, void *host_addr, int size); -- cgit v1.2.3 From 67fbf880631bb4493ad8d23f25562abdf09dc01d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 09:18:26 +0200 Subject: ALSA: ctxfi - Fix a typo in MODULE_LICENSE A space has to be put between GPL and v2. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/xfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index e31e29e4ec7..bf232e7299a 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -18,7 +18,7 @@ MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); -MODULE_LICENSE("GPLv2"); +MODULE_LICENSE("GPL v2"); MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); static unsigned int reference_rate = 48000; -- cgit v1.2.3 From 822fa19b5c23746577687a0ec48eae0ec1cd22a0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:12:17 +0200 Subject: ALSA: ALSA: ctxfi - Release PCM resources at each prepare call The prepare callback can be called multiple times, thus it needs to release and acquire the resource again by itself at the second or later call. Simply add pcm_release_resources() at the beginning of each prepare callback in ctatc.c. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 1a4bb357eae..e14ed714721 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -254,6 +254,9 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) return 0; } + /* first release old resources */ + atc->pcm_release_resources(atc, apcm); + /* Get SRC resource */ desc.multi = apcm->substream->runtime->channels; desc.msr = atc->msr; @@ -496,6 +499,9 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; struct src_node_conf_t src_node_conf[2] = {{0} }; + /* first release old resources */ + atc->pcm_release_resources(atc, apcm); + /* The numbers of converting SRCs and SRCIMPs should be determined * by pitch value. */ @@ -767,6 +773,9 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, int n_amixer = apcm->substream->runtime->channels, i = 0; unsigned int pitch = 0, rsr = atc->pll_rate; + /* first release old resources */ + atc->pcm_release_resources(atc, apcm); + /* Get SRC resource */ desc.multi = apcm->substream->runtime->channels; desc.msr = 1; -- cgit v1.2.3 From 6585db943aade186d38eaab2720c18887b94c875 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:17:27 +0200 Subject: ALSA: ctxfi - Fix surround mixer names We usually pick up "Surround" mixer for the rear output, and "Side" for the extra surround. Fix the channel mapping to follow it. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctmixer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index b7950768c3f..177c46e248d 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -168,7 +168,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVES_P] = { .ctl = 1, - .name = "Surround Playback Volume", + .name = "Side Playback Volume", }, [MIXER_WAVEC_P] = { .ctl = 1, @@ -176,7 +176,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVER_P] = { .ctl = 1, - .name = "Rear Playback Volume", + .name = "Surround Playback Volume", }, [MIXER_PCM_C_S] = { @@ -213,7 +213,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVES_P_S] = { .ctl = 1, - .name = "Surround Playback Switch", + .name = "Side Playback Switch", }, [MIXER_WAVEC_P_S] = { .ctl = 1, @@ -221,7 +221,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVER_P_S] = { .ctl = 1, - .name = "Rear Playback Switch", + .name = "Surround Playback Switch", }, [MIXER_DIGITAL_IO_S] = { .ctl = 0, -- cgit v1.2.3 From 8372d4980fbc2e403f0dc5457441c8c6b7c04915 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:27:56 +0200 Subject: ALSA: ctxfi - Fix PCM device naming PCM names for surround streams should be also fixed as well as the mixer element names. Also, a bit clean up for PCM name setup. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 8 ++++---- sound/pci/ctxfi/ctatc.h | 4 ++-- sound/pci/ctxfi/ctpcm.c | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index e14ed714721..675dd4c8eae 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -72,15 +72,15 @@ static struct { [FRONT] = { .create = ct_alsa_pcm_create, .destroy = NULL, .public_name = "Front/WaveIn"}, - [REAR] = { .create = ct_alsa_pcm_create, + [SURROUND] = { .create = ct_alsa_pcm_create, .destroy = NULL, - .public_name = "Rear"}, + .public_name = "Surround"}, [CLFE] = { .create = ct_alsa_pcm_create, .destroy = NULL, .public_name = "Center/LFE"}, - [SURROUND] = { .create = ct_alsa_pcm_create, + [SIDE] = { .create = ct_alsa_pcm_create, .destroy = NULL, - .public_name = "Surround"}, + .public_name = "Side"}, [IEC958] = { .create = ct_alsa_pcm_create, .destroy = NULL, .public_name = "IEC958 Non-audio"}, diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index a7b0ec24cd7..b86d12cd4a1 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -29,9 +29,9 @@ enum CTALSADEVS { /* Types of alsa devices */ FRONT, - REAR, - CLFE, SURROUND, + CLFE, + SIDE, IEC958, MIXER, NUM_CTALSADEVS /* This should always be the last */ diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index a64cb0ed875..756d8b2776b 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -469,12 +469,10 @@ int ct_alsa_pcm_create(struct ct_atc *atc, struct snd_pcm *pcm; int err; int playback_count, capture_count; - char name[128]; - strncpy(name, device_name, sizeof(name)); playback_count = (IEC958 == device) ? 1 : 8; capture_count = (FRONT == device) ? 1 : 0; - err = snd_pcm_new(atc->card, name, device, + 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); @@ -484,7 +482,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc, pcm->private_data = atc; pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, device_name); + strlcpy(pcm->name, device_name, sizeof(pcm->name)); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); -- cgit v1.2.3 From d2b9b96c516d4d61663d92ab4ad4f15ca0134ef2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:39:05 +0200 Subject: ALSA: ctxfi - Fix supported PCM formats The device seems supporting only U8, S16, S24_3LE, S32. Other linear formats result in bad outputs. Also, added the support for 32bit float format, which wasn't listed in the original code. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 5 ++--- sound/pci/ctxfi/ctpcm.c | 15 +++++---------- 2 files changed, 7 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 675dd4c8eae..268ecc4c285 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -170,16 +170,15 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) { switch (snd_format) { case SNDRV_PCM_FORMAT_U8: - case SNDRV_PCM_FORMAT_S8: return SRC_SF_U8; case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_U16_LE: return SRC_SF_S16; case SNDRV_PCM_FORMAT_S24_3LE: return SRC_SF_S24; - case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S32_LE: return SRC_SF_S32; + case SNDRV_PCM_FORMAT_FLOAT_LE: + return SRC_SF_F32; default: printk(KERN_ERR "ctxfi: not recognized snd format is %d \n", snd_format); diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 756d8b2776b..26d86dc35e5 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -26,12 +26,10 @@ static struct snd_pcm_hardware ct_pcm_playback_hw = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S24_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_FLOAT_LE), .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000), .rate_min = 8000, @@ -52,8 +50,7 @@ static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE), + .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000), @@ -77,12 +74,10 @@ static struct snd_pcm_hardware ct_pcm_capture_hw = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID), .formats = (SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S24_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_FLOAT_LE), .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000), .rate_min = 8000, -- cgit v1.2.3 From cd391e206f486955e216a61bd9ebcb0e142122e9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 15:04:29 +0200 Subject: ALSA: ctxfi - Remove PAGE_SIZE limitation Remove the limitation of PAGE_SIZE to be 4k by defining the own page size and macros for 4k. 8kb page size could be natively supported, but it's disabled right now for simplicity. Also, clean up using upper_32_bits() macro. Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 1 - sound/pci/ctxfi/cthw20k1.c | 16 ++++++---------- sound/pci/ctxfi/cthw20k2.c | 17 ++++------------- sound/pci/ctxfi/ctvmem.c | 25 ++++++++++++------------- sound/pci/ctxfi/ctvmem.h | 8 ++++++++ 5 files changed, 30 insertions(+), 37 deletions(-) (limited to 'sound') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 2d7fef5df01..3a7640feaf9 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -277,7 +277,6 @@ config SND_CS5535AUDIO config SND_CTXFI tristate "Creative Sound Blaster X-Fi" - depends on X86 select SND_PCM help If you want to use soundcards based on Creative Sound Blastr X-Fi diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 44283bd7b2d..b7b8e6f41d0 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1249,18 +1249,14 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) } trnctl = 0x13; /* 32-bit, 4k-size page */ -#if BITS_PER_LONG == 64 - ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); - ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); - trnctl |= (1<<2); -#elif BITS_PER_LONG == 32 - ptp_phys_low = info->vm_pgt_phys & (~0UL); - ptp_phys_high = 0; -#else -# error "Unknown BITS_PER_LONG!" -#endif + ptp_phys_low = (u32)info->vm_pgt_phys; + ptp_phys_high = upper_32_bits(info->vm_pgt_phys); + if (sizeof(void *) == 8) /* 64bit address */ + trnctl |= (1 << 2); +#if 0 /* Only 4k h/w pages for simplicitiy */ #if PAGE_SIZE == 8192 trnctl |= (1<<5); +#endif #endif hw_write_20kx(hw, PTPALX, ptp_phys_low); hw_write_20kx(hw, PTPAHX, ptp_phys_high); diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 7c24c2ca96b..349728765f2 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1203,19 +1203,10 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) } vmctl = 0x80000C0F; /* 32-bit, 4k-size page */ -#if BITS_PER_LONG == 64 - ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); - ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); - vmctl |= (3<<8); -#elif BITS_PER_LONG == 32 - ptp_phys_low = info->vm_pgt_phys & (~0UL); - ptp_phys_high = 0; -#else -# error "Unknown BITS_PER_LONG!" -#endif -#if PAGE_SIZE == 8192 -# error "Don't support 8k-page!" -#endif + ptp_phys_low = (u32)info->vm_pgt_phys; + ptp_phys_high = upper_32_bits(info->vm_pgt_phys); + if (sizeof(void *) == 8) /* 64bit address */ + vmctl |= (3 << 8); /* Write page table physical address to all PTPAL registers */ for (i = 0; i < 64; i++) { hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low); diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 363b67e3a9e..74a03623d04 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -18,12 +18,11 @@ #include "ctvmem.h" #include #include -#include /* for PAGE_SIZE macro definition */ #include #include -#define CT_PTES_PER_PAGE (PAGE_SIZE / sizeof(void *)) -#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * PAGE_SIZE) +#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. @@ -138,24 +137,24 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) return NULL; } - start_phys = (virt_to_phys(host_addr) & PAGE_MASK); - pages = (PAGE_ALIGN(virt_to_phys(host_addr) + size) - - start_phys) >> PAGE_SHIFT; + start_phys = (virt_to_phys(host_addr) & CT_PAGE_MASK); + pages = (CT_PAGE_ALIGN(virt_to_phys(host_addr) + size) + - start_phys) >> CT_PAGE_SHIFT; ptp = vm->ptp[0]; - block = get_vm_block(vm, (pages << PAGE_SHIFT)); + block = get_vm_block(vm, (pages << CT_PAGE_SHIFT)); if (block == NULL) { printk(KERN_ERR "ctxfi: No virtual memory block that is big " "enough to allocate!\n"); return NULL; } - pte_start = (block->addr >> PAGE_SHIFT); + pte_start = (block->addr >> CT_PAGE_SHIFT); for (i = 0; i < pages; i++) - ptp[pte_start+i] = start_phys + (i << PAGE_SHIFT); + ptp[pte_start+i] = start_phys + (i << CT_PAGE_SHIFT); - block->addr += (virt_to_phys(host_addr) & (~PAGE_MASK)); + block->addr += (virt_to_phys(host_addr) & (~CT_PAGE_MASK)); block->size = size; return block; @@ -164,9 +163,9 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) { /* do unmapping */ - block->size = ((block->addr + block->size + PAGE_SIZE - 1) - & PAGE_MASK) - (block->addr & PAGE_MASK); - block->addr &= PAGE_MASK; + block->size = ((block->addr + block->size + CT_PAGE_SIZE - 1) + & CT_PAGE_MASK) - (block->addr & CT_PAGE_MASK); + block->addr &= CT_PAGE_MASK; put_vm_block(vm, block); } diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 618952efa5b..17d2d37a9ea 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -23,6 +23,14 @@ #include #include +/* 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 */ -- cgit v1.2.3 From c76157d9286ed598c241c212aa5a3c6e5107bd82 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 15:26:19 +0200 Subject: ALSA: ctxfi - Support SG-buffers Use SG-buffers instead of contiguous pages. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 2 +- sound/pci/ctxfi/ctpcm.c | 4 +++- sound/pci/ctxfi/ctvmem.c | 56 +++++++++++++++++++----------------------------- sound/pci/ctxfi/ctvmem.h | 5 ++++- 4 files changed, 30 insertions(+), 37 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 268ecc4c285..684947546d8 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -128,7 +128,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) runtime = apcm->substream->runtime; vm = atc->vm; - apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); + apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes); if (NULL == apcm->vm_block) return -ENOENT; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 26d86dc35e5..52ddf19d83b 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -442,6 +442,7 @@ static struct snd_pcm_ops ct_pcm_playback_ops = { .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 */ @@ -454,6 +455,7 @@ static struct snd_pcm_ops ct_pcm_capture_ops = { .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 */ @@ -485,7 +487,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc, 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, + 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; diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 74a03623d04..b7f8e58ae07 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *)) #define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE) @@ -34,6 +34,13 @@ get_vm_block(struct ct_vm *vm, unsigned int size) struct ct_vm_block *block = NULL, *entry = NULL; struct list_head *pos = NULL; + 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); @@ -73,6 +80,8 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) struct ct_vm_block *entry = NULL, *pre_ent = NULL; struct list_head *pos = NULL, *pre = NULL; + block->size = CT_PAGE_ALIGN(block->size); + mutex_lock(&vm->lock); list_del(&block->list); vm->size += block->size; @@ -115,57 +124,36 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) /* Map host addr (kmalloced/vmalloced) to device logical addr. */ static struct ct_vm_block * -ct_vm_map(struct ct_vm *vm, void *host_addr, int size) +ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size) { - struct ct_vm_block *block = NULL; - unsigned long pte_start; - unsigned long i; - unsigned long pages; - unsigned long start_phys; + struct ct_vm_block *block; + unsigned int pte_start; + unsigned i, pages; unsigned long *ptp; - /* do mapping */ - if ((unsigned long)host_addr >= VMALLOC_START) { - printk(KERN_ERR "ctxfi: " - "Fail! Not support vmalloced addr now!\n"); - return NULL; - } - - if (size > vm->size) { - printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " - "memory space available!\n"); - return NULL; - } - - start_phys = (virt_to_phys(host_addr) & CT_PAGE_MASK); - pages = (CT_PAGE_ALIGN(virt_to_phys(host_addr) + size) - - start_phys) >> CT_PAGE_SHIFT; - - ptp = vm->ptp[0]; - - block = get_vm_block(vm, (pages << CT_PAGE_SHIFT)); + 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); - for (i = 0; i < pages; i++) - ptp[pte_start+i] = start_phys + (i << 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->addr += (virt_to_phys(host_addr) & (~CT_PAGE_MASK)); block->size = size; - return block; } static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) { /* do unmapping */ - block->size = ((block->addr + block->size + CT_PAGE_SIZE - 1) - & CT_PAGE_MASK) - (block->addr & CT_PAGE_MASK); - block->addr &= CT_PAGE_MASK; put_vm_block(vm, block); } diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 17d2d37a9ea..01e4fd0386a 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -37,6 +37,8 @@ struct ct_vm_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 */ @@ -46,7 +48,8 @@ struct ct_vm { struct mutex lock; /* Map host addr (kmalloced/vmalloced) to device logical addr. */ - struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, int size); + 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); -- cgit v1.2.3 From 6d74b86d3c0f9cfa949566a862aaad840e393249 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 09:26:41 +0200 Subject: ALSA: ctxfi - Allow 64bit DMA emu20kx chips support 64bit address PTE. Allow the DMA bit mask to accept 64bit address, too. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index b7b8e6f41d0..1da1f82fe81 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -15,8 +15,6 @@ * */ -#include "cthw20k1.h" -#include "ct20k1reg.h" #include #include #include @@ -26,8 +24,14 @@ #include #include #include +#include "cthw20k1.h" +#include "ct20k1reg.h" -#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ +#if BITS_PER_LONG == 32 +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ +#else +#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ +#endif struct hw20k1 { struct hw hw; -- cgit v1.2.3 From 42a0b31827e4c555efebda7d347cf4ea6b82913a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 09:29:22 +0200 Subject: ALSA: ctxfi - Fix endian-dependent codes The UAA-mode check in hwct20k1.c is implemented with the endian-dependent codes. Fix to be more portable (and readable). Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 1da1f82fe81..fd5f454c404 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1785,6 +1785,8 @@ static int hw_have_digit_io_switch(struct hw *hw) || ((subsys_id & 0xf000) == 0x6000)); } +#define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + #define UAA_CFG_PWRSTATUS 0x44 #define UAA_CFG_SPACE_FLAG 0xA0 #define UAA_CORE_CHANGE 0x3FFC @@ -1792,12 +1794,18 @@ static int uaa_to_xfi(struct pci_dev *pci) { unsigned int bar0, bar1, bar2, bar3, bar4, bar5; unsigned int cmd, irq, cl_size, l_timer, pwr; - unsigned int CTLA, CTLZ, CTLL, CTLX, CTL_, CTLF, CTLi; unsigned int is_uaa = 0; unsigned int data[4] = {0}; unsigned int io_base; void *mem_base; int i = 0; + const u32 CTLX = CTLBITS('C', 'T', 'L', 'X'); + const u32 CTL_ = CTLBITS('C', 'T', 'L', '-'); + const u32 CTLF = CTLBITS('C', 'T', 'L', 'F'); + const u32 CTLi = CTLBITS('C', 'T', 'L', 'i'); + const u32 CTLA = CTLBITS('C', 'T', 'L', 'A'); + const u32 CTLZ = CTLBITS('C', 'T', 'L', 'Z'); + const u32 CTLL = CTLBITS('C', 'T', 'L', 'L'); /* By default, Hendrix card UAA Bar0 should be using memory... */ io_base = pci_resource_start(pci, 0); @@ -1805,14 +1813,6 @@ static int uaa_to_xfi(struct pci_dev *pci) if (NULL == mem_base) return -ENOENT; - CTLX = ___constant_swab32(*((unsigned int *)"CTLX")); - CTL_ = ___constant_swab32(*((unsigned int *)"CTL-")); - CTLF = ___constant_swab32(*((unsigned int *)"CTLF")); - CTLi = ___constant_swab32(*((unsigned int *)"CTLi")); - CTLA = ___constant_swab32(*((unsigned int *)"CTLA")); - CTLZ = ___constant_swab32(*((unsigned int *)"CTLZ")); - CTLL = ___constant_swab32(*((unsigned int *)"CTLL")); - /* Read current mode from Mode Change Register */ for (i = 0; i < 4; i++) data[i] = readl(mem_base + UAA_CORE_CHANGE); -- cgit v1.2.3 From 6bc5874a1ddf98ac0fe6c4eab7d286c11cb1c748 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 12:15:51 +0200 Subject: ALSA: ctxfi - Fix previous fix for 64bit DMA Remove unneeded substitution to 32bit int to make it really working. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index fd5f454c404..e530a6d6042 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1883,18 +1883,17 @@ static int hw_card_start(struct hw *hw) int err = 0; struct pci_dev *pci = hw->pci; u16 subsys_id = 0; - unsigned int dma_mask = 0; err = pci_enable_device(pci); if (err < 0) return err; /* Set DMA transfer mask */ - dma_mask = CT_XFI_DMA_MASK; - if (pci_set_dma_mask(pci, dma_mask) < 0 || - pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || + pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { printk(KERN_ERR "architecture does not support PCI " - "busmaster DMA with mask 0x%x\n", dma_mask); + "busmaster DMA with mask 0x%llx\n", + CT_XFI_DMA_MASK); err = -ENXIO; goto error1; } -- cgit v1.2.3 From b7bbf876087e0e2c0ba723a8398083c9a9ac1dfd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:11:07 +0200 Subject: ALSA: ctxfi - Use native timer interrupt on emu20k1 emu20k1 has a native timer interrupt based on the audio clock, which is more accurate than the system timer (from the synchronization POV). This patch adds the code to handle this with multiple streams. The system timer is still used on emu20k2, and can be used also for emu20k1 easily by changing USE_SYSTEM_TIMER to 1 in cttimer.c. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/Makefile | 2 +- sound/pci/ctxfi/ct20k1reg.h | 2 + sound/pci/ctxfi/ctatc.c | 21 ++- sound/pci/ctxfi/ctatc.h | 9 +- sound/pci/ctxfi/cthardware.h | 19 ++ sound/pci/ctxfi/cthw20k1.c | 43 ++++- sound/pci/ctxfi/ctpcm.c | 106 ++--------- sound/pci/ctxfi/cttimer.c | 417 +++++++++++++++++++++++++++++++++++++++++++ sound/pci/ctxfi/cttimer.h | 29 +++ 9 files changed, 543 insertions(+), 105 deletions(-) create mode 100644 sound/pci/ctxfi/cttimer.c create mode 100644 sound/pci/ctxfi/cttimer.h (limited to 'sound') diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile index 29043237f9f..15075f89e98 100644 --- a/sound/pci/ctxfi/Makefile +++ b/sound/pci/ctxfi/Makefile @@ -1,5 +1,5 @@ snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ - ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \ + ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \ cthw20k2.o cthw20k1.o obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h index c62e6775dab..f2e34e3f27e 100644 --- a/sound/pci/ctxfi/ct20k1reg.h +++ b/sound/pci/ctxfi/ct20k1reg.h @@ -589,6 +589,8 @@ #define WC 0x1C6000 #define TIMR 0x1C6004 +# define TIMR_IE (1<<15) +# define TIMR_IP (1<<14) #define GIP 0x1C6010 #define GIE 0x1C6014 diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 684947546d8..10b741977dd 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -22,6 +22,7 @@ #include "ctsrc.h" #include "ctamixer.h" #include "ctdaio.h" +#include "cttimer.h" #include #include #include @@ -307,6 +308,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) src = apcm->src; } + ct_timer_prepare(apcm->timer); + return 0; error1: @@ -389,6 +392,7 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) src->ops->set_state(src, SRC_STATE_INIT); src->ops->commit_write(src); + ct_timer_start(apcm->timer); return 0; } @@ -397,6 +401,8 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct src *src = NULL; int i = 0; + ct_timer_stop(apcm->timer); + src = apcm->src; src->ops->set_bm(src, 0); src->ops->set_state(src, SRC_STATE_OFF); @@ -701,6 +707,8 @@ static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) } } + ct_timer_prepare(apcm->timer); + return 0; } @@ -749,6 +757,7 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) /* Enable relevant SRCs synchronously */ src_mgr->commit_write(src_mgr); + ct_timer_start(apcm->timer); return 0; } @@ -906,6 +915,8 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) dao->ops->set_right_input(dao, &amixer->rsc); spin_unlock_irqrestore(&atc->atc_lock, flags); + ct_timer_prepare(apcm->timer); + return 0; } @@ -1100,6 +1111,11 @@ static int ct_atc_destroy(struct ct_atc *atc) if (NULL == atc) return 0; + if (atc->timer) { + ct_timer_free(atc->timer); + atc->timer = NULL; + } + /* Stop hardware and disable all interrupts */ if (NULL != atc->hw) ((struct hw *)atc->hw)->card_stop(atc->hw); @@ -1586,6 +1602,10 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, /* Build topology */ atc_connect_resources(atc); + atc->timer = ct_timer_new(atc); + if (!atc->timer) + goto error1; + atc->create_alsa_devs = ct_create_alsa_devs; err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); @@ -1602,4 +1622,3 @@ error1: printk(KERN_ERR "ctxfi: Something wrong!!!\n"); return err; } - diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index b86d12cd4a1..a3f9b1bc7dc 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -59,16 +59,15 @@ struct ct_atc_chip_details { }; struct ct_atc; +struct ct_timer; +struct ct_timer_instance; /* alsa pcm stream descriptor */ struct ct_atc_pcm { struct snd_pcm_substream *substream; void (*interrupt)(struct ct_atc_pcm *apcm); + struct ct_timer_instance *timer; unsigned int started:1; - unsigned int stop_timer:1; - struct timer_list timer; - spinlock_t timer_lock; - unsigned int position; /* Only mono and interleaved modes are supported now. */ struct ct_vm_block *vm_block; @@ -144,6 +143,8 @@ struct ct_atc { unsigned char n_src; unsigned char n_srcimp; unsigned char n_pcm; + + struct ct_timer *timer; }; diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index b0512df8b33..35350cf9d2f 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -145,6 +145,12 @@ struct hw { int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); int (*daio_mgr_commit_write)(struct hw *hw, void *blk); + int (*set_timer_irq)(struct hw *hw, int enable); + int (*set_timer_tick)(struct hw *hw, unsigned int tick); + + void (*irq_callback)(void *data, unsigned int bit); + void *irq_callback_data; + struct pci_dev *pci; /* the pci kernel structure of this card */ int irq; unsigned long io_base; @@ -157,4 +163,17 @@ int destroy_hw_obj(struct hw *hw); unsigned int get_field(unsigned int data, unsigned int field); void set_field(unsigned int *data, unsigned int field, unsigned int value); +/* IRQ bits */ +#define PLL_INT (1 << 10) /* PLL input-clock out-of-range */ +#define FI_INT (1 << 9) /* forced interrupt */ +#define IT_INT (1 << 8) /* timer interrupt */ +#define PCI_INT (1 << 7) /* PCI bus error pending */ +#define URT_INT (1 << 6) /* UART Tx/Rx */ +#define GPI_INT (1 << 5) /* GPI pin */ +#define MIX_INT (1 << 4) /* mixer parameter segment FIFO channels */ +#define DAI_INT (1 << 3) /* DAI (SR-tracker or SPDIF-receiver) */ +#define TP_INT (1 << 2) /* transport priority queue */ +#define DSP_INT (1 << 1) /* DSP */ +#define SRC_INT (1 << 0) /* SRC channels */ + #endif /* CTHARDWARE_H */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index e530a6d6042..550b30a2bcf 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1171,6 +1171,21 @@ static int daio_mgr_put_ctrl_blk(void *blk) return 0; } +/* Timer interrupt */ +static int set_timer_irq(struct hw *hw, int enable) +{ + hw_write_20kx(hw, GIE, enable ? IT_INT : 0); + return 0; +} + +static int set_timer_tick(struct hw *hw, unsigned int ticks) +{ + if (ticks) + ticks |= TIMR_IE | TIMR_IP; + hw_write_20kx(hw, TIMR, ticks); + return 0; +} + /* Card hardware initialization block */ struct dac_conf { unsigned int msr; /* master sample rate in rsrs */ @@ -1878,6 +1893,22 @@ static int uaa_to_xfi(struct pci_dev *pci) return 0; } +static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id) +{ + struct hw *hw = dev_id; + unsigned int status; + + status = hw_read_20kx(hw, GIP); + if (!status) + return IRQ_NONE; + + if (hw->irq_callback) + hw->irq_callback(hw->irq_callback_data, status); + + hw_write_20kx(hw, GIP, status); + return IRQ_HANDLED; +} + static int hw_card_start(struct hw *hw) { int err = 0; @@ -1914,12 +1945,13 @@ static int hw_card_start(struct hw *hw) hw->io_base = pci_resource_start(pci, 0); } - /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, - atc->chip_details->nm_card, hw))) { + err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED, + "ctxfi", hw); + if (err < 0) { + printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); goto error2; } hw->irq = pci->irq; - */ pci_set_master(pci); @@ -1936,6 +1968,8 @@ error1: static int hw_card_stop(struct hw *hw) { /* TODO: Disable interrupt and so on... */ + if (hw->irq >= 0) + synchronize_irq(hw->irq); return 0; } @@ -2215,6 +2249,9 @@ int create_20k1_hw_obj(struct hw **rhw) hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; hw->daio_mgr_commit_write = daio_mgr_commit_write; + hw->set_timer_irq = set_timer_irq; + hw->set_timer_tick = set_timer_tick; + *rhw = hw; return 0; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 52ddf19d83b..32b742dcd53 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -16,6 +16,7 @@ */ #include "ctpcm.h" +#include "cttimer.h" #include /* Hardware descriptions for playback */ @@ -108,6 +109,7 @@ static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); atc->pcm_release_resources(atc, apcm); + ct_timer_instance_free(apcm->timer); kfree(apcm); runtime->private_data = NULL; } @@ -124,8 +126,6 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) if (NULL == apcm) return -ENOMEM; - spin_lock_init(&apcm->timer_lock); - apcm->stop_timer = 0; apcm->substream = substream; apcm->interrupt = ct_atc_pcm_interrupt; runtime->private_data = apcm; @@ -153,6 +153,10 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) return err; } + apcm->timer = ct_timer_instance_new(atc->timer, apcm); + if (!apcm->timer) + return -ENOMEM; + return 0; } @@ -182,89 +186,6 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } -static void ct_pcm_timer_callback(unsigned long data) -{ - struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; - struct snd_pcm_substream *substream = apcm->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int period_size = runtime->period_size; - unsigned int buffer_size = runtime->buffer_size; - unsigned long flags; - unsigned int position = 0, dist = 0, interval = 0; - - position = substream->ops->pointer(substream); - dist = (position + buffer_size - apcm->position) % buffer_size; - if ((dist >= period_size) || - (position/period_size != apcm->position/period_size)) { - apcm->interrupt(apcm); - apcm->position = position; - } - /* Add extra HZ*5/1000 to avoid overrun issue when recording - * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ - interval = ((period_size - (position % period_size)) - * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; - spin_lock_irqsave(&apcm->timer_lock, flags); - apcm->timer.expires = jiffies + interval; - if (!apcm->stop_timer) - add_timer(&apcm->timer); - - spin_unlock_irqrestore(&apcm->timer_lock, flags); -} - -static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) -{ - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - if (timer_pending(&apcm->timer)) { - /* The timer has already been started. */ - spin_unlock_irqrestore(&apcm->timer_lock, flags); - return 0; - } - - init_timer(&apcm->timer); - apcm->timer.data = (unsigned long)apcm; - apcm->timer.function = ct_pcm_timer_callback; - spin_unlock_irqrestore(&apcm->timer_lock, flags); - apcm->position = 0; - - return 0; -} - -static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) -{ - struct snd_pcm_runtime *runtime = apcm->substream->runtime; - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - if (timer_pending(&apcm->timer)) { - /* The timer has already been started. */ - spin_unlock_irqrestore(&apcm->timer_lock, flags); - return 0; - } - - apcm->timer.expires = jiffies + (runtime->period_size * HZ + - (runtime->rate - 1)) / runtime->rate; - apcm->stop_timer = 0; - add_timer(&apcm->timer); - spin_unlock_irqrestore(&apcm->timer_lock, flags); - - return 0; -} - -static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) -{ - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - apcm->stop_timer = 1; - del_timer(&apcm->timer); - spin_unlock_irqrestore(&apcm->timer_lock, flags); - - try_to_del_timer_sync(&apcm->timer); - - return 0; -} static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) { @@ -283,8 +204,6 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) return err; } - ct_pcm_timer_prepare(apcm); - return 0; } @@ -300,12 +219,10 @@ ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: atc->pcm_playback_start(atc, apcm); - ct_pcm_timer_start(apcm); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ct_pcm_timer_stop(apcm); atc->pcm_playback_stop(atc, apcm); break; default: @@ -341,9 +258,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) if (NULL == apcm) return -ENOMEM; - spin_lock_init(&apcm->timer_lock); apcm->started = 0; - apcm->stop_timer = 0; apcm->substream = substream; apcm->interrupt = ct_atc_pcm_interrupt; runtime->private_data = apcm; @@ -365,6 +280,10 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) return err; } + apcm->timer = ct_timer_instance_new(atc->timer, apcm); + if (!apcm->timer) + return -ENOMEM; + return 0; } @@ -388,8 +307,6 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) return err; } - ct_pcm_timer_prepare(apcm); - return 0; } @@ -403,14 +320,11 @@ ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: atc->pcm_capture_start(atc, apcm); - ct_pcm_timer_start(apcm); break; case SNDRV_PCM_TRIGGER_STOP: - ct_pcm_timer_stop(apcm); atc->pcm_capture_stop(atc, apcm); break; default: - ct_pcm_timer_stop(apcm); atc->pcm_capture_stop(atc, apcm); break; } diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c new file mode 100644 index 00000000000..3acb26d0c4c --- /dev/null +++ b/sound/pci/ctxfi/cttimer.c @@ -0,0 +1,417 @@ +/* + * PCM timer handling on ctxfi + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + */ + +#include +#include +#include +#include "ctatc.h" +#include "cthardware.h" +#include "cttimer.h" + +struct ct_timer_ops { + void (*init)(struct ct_timer_instance *); + void (*prepare)(struct ct_timer_instance *); + void (*start)(struct ct_timer_instance *); + void (*stop)(struct ct_timer_instance *); + void (*free_instance)(struct ct_timer_instance *); + void (*interrupt)(struct ct_timer *); + void (*free_global)(struct ct_timer *); +}; + +/* timer instance -- assigned to each PCM stream */ +struct ct_timer_instance { + spinlock_t lock; + struct ct_timer *timer_base; + struct ct_atc_pcm *apcm; + struct snd_pcm_substream *substream; + struct timer_list timer; + struct list_head instance_list; + struct list_head running_list; + unsigned int position; + unsigned int frag_count; + unsigned int running:1; + unsigned int need_update:1; +}; + +/* timer instance manager */ +struct ct_timer { + spinlock_t lock; /* global timer lock (for xfitimer) */ + spinlock_t list_lock; /* lock for instance list */ + struct ct_atc *atc; + struct ct_timer_ops *ops; + struct list_head instance_head; + struct list_head running_head; + unsigned int irq_handling:1; /* in IRQ handling */ + unsigned int reprogram:1; /* need to reprogram the internval */ + unsigned int running:1; /* global timer running */ +}; + + +/* + * system-timer-based updates + */ + +static void ct_systimer_callback(unsigned long data) +{ + struct ct_timer_instance *ti = (struct ct_timer_instance *)data; + struct snd_pcm_substream *substream = ti->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = ti->apcm; + unsigned int period_size = runtime->period_size; + unsigned int buffer_size = runtime->buffer_size; + unsigned long flags; + unsigned int position, dist, interval; + + position = substream->ops->pointer(substream); + dist = (position + buffer_size - ti->position) % buffer_size; + if (dist >= period_size || + position / period_size != ti->position / period_size) { + apcm->interrupt(apcm); + ti->position = position; + } + /* Add extra HZ*5/1000 to avoid overrun issue when recording + * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ + interval = ((period_size - (position % period_size)) + * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; + spin_lock_irqsave(&ti->lock, flags); + if (ti->running) + mod_timer(&ti->timer, jiffies + interval); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_init(struct ct_timer_instance *ti) +{ + setup_timer(&ti->timer, ct_systimer_callback, + (unsigned long)ti); +} + +static void ct_systimer_start(struct ct_timer_instance *ti) +{ + struct snd_pcm_runtime *runtime = ti->substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&ti->lock, flags); + ti->running = 1; + mod_timer(&ti->timer, + jiffies + (runtime->period_size * HZ + + (runtime->rate - 1)) / runtime->rate); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_stop(struct ct_timer_instance *ti) +{ + unsigned long flags; + + spin_lock_irqsave(&ti->lock, flags); + ti->running = 0; + del_timer(&ti->timer); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_prepare(struct ct_timer_instance *ti) +{ + ct_systimer_stop(ti); + try_to_del_timer_sync(&ti->timer); +} + +#define ct_systimer_free ct_systimer_prepare + +static struct ct_timer_ops ct_systimer_ops = { + .init = ct_systimer_init, + .free_instance = ct_systimer_free, + .prepare = ct_systimer_prepare, + .start = ct_systimer_start, + .stop = ct_systimer_stop, +}; + + +/* + * Handling multiple streams using a global emu20k1 timer irq + */ + +#define CT_TIMER_FREQ 48000 +#define MAX_TICKS ((1 << 13) - 1) + +static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks) +{ + struct hw *hw = atimer->atc->hw; + if (ticks > MAX_TICKS) + ticks = MAX_TICKS; + hw->set_timer_tick(hw, ticks); + if (!atimer->running) + hw->set_timer_irq(hw, 1); + atimer->running = 1; +} + +static void ct_xfitimer_irq_stop(struct ct_timer *atimer) +{ + if (atimer->running) { + struct hw *hw = atimer->atc->hw; + hw->set_timer_irq(hw, 0); + hw->set_timer_tick(hw, 0); + atimer->running = 0; + } +} + +/* + * reprogram the timer interval; + * checks the running instance list and determines the next timer interval. + * also updates the each stream position, returns the number of streams + * to call snd_pcm_period_elapsed() appropriately + * + * call this inside the lock and irq disabled + */ +static int ct_xfitimer_reprogram(struct ct_timer *atimer) +{ + struct ct_timer_instance *ti; + int min_intr = -1; + int updates = 0; + + list_for_each_entry(ti, &atimer->running_head, running_list) { + struct snd_pcm_runtime *runtime; + unsigned int pos, diff; + int intr; + runtime = ti->substream->runtime; + pos = ti->substream->ops->pointer(ti->substream); + if (pos < ti->position) + diff = runtime->buffer_size - ti->position + pos; + else + diff = pos - ti->position; + ti->position = pos; + while (diff >= ti->frag_count) { + ti->frag_count += runtime->period_size; + ti->need_update = 1; + updates++; + } + ti->frag_count -= diff; + intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ, + runtime->rate); + if (min_intr < 0 || intr < min_intr) + min_intr = intr; + } + + if (min_intr > 0) + ct_xfitimer_irq_rearm(atimer, min_intr); + else + ct_xfitimer_irq_stop(atimer); + + atimer->reprogram = 0; /* clear flag */ + return updates; +} + +/* look through the instance list and call period_elapsed if needed */ +static void ct_xfitimer_check_period(struct ct_timer *atimer) +{ + struct ct_timer_instance *ti; + unsigned long flags; + + spin_lock_irqsave(&atimer->list_lock, flags); + list_for_each_entry(ti, &atimer->instance_head, instance_list) { + if (ti->need_update) { + ti->need_update = 0; + ti->apcm->interrupt(ti->apcm); + } + } + spin_unlock_irqrestore(&atimer->list_lock, flags); +} + +/* Handle timer-interrupt */ +static void ct_xfitimer_callback(struct ct_timer *atimer) +{ + int update; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + atimer->irq_handling = 1; + do { + update = ct_xfitimer_reprogram(atimer); + spin_unlock(&atimer->lock); + if (update) + ct_xfitimer_check_period(atimer); + spin_lock(&atimer->lock); + } while (atimer->reprogram); + atimer->irq_handling = 0; + spin_unlock_irqrestore(&atimer->lock, flags); +} + +static void ct_xfitimer_prepare(struct ct_timer_instance *ti) +{ + ti->frag_count = ti->substream->runtime->period_size; + ti->need_update = 0; +} + + +/* start/stop the timer */ +static void ct_xfitimer_update(struct ct_timer *atimer) +{ + unsigned long flags; + int update; + + if (atimer->irq_handling) { + /* reached from IRQ handler; let it handle later */ + atimer->reprogram = 1; + return; + } + + spin_lock_irqsave(&atimer->lock, flags); + ct_xfitimer_irq_stop(atimer); + update = ct_xfitimer_reprogram(atimer); + spin_unlock_irqrestore(&atimer->lock, flags); + if (update) + ct_xfitimer_check_period(atimer); +} + +static void ct_xfitimer_start(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + list_add(&ti->running_list, &atimer->running_head); + spin_unlock_irqrestore(&atimer->lock, flags); + ct_xfitimer_update(atimer); +} + +static void ct_xfitimer_stop(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + list_del_init(&ti->running_list); + ti->need_update = 0; + spin_unlock_irqrestore(&atimer->lock, flags); + ct_xfitimer_update(atimer); +} + +static void ct_xfitimer_free_global(struct ct_timer *atimer) +{ + ct_xfitimer_irq_stop(atimer); +} + +static struct ct_timer_ops ct_xfitimer_ops = { + .prepare = ct_xfitimer_prepare, + .start = ct_xfitimer_start, + .stop = ct_xfitimer_stop, + .interrupt = ct_xfitimer_callback, + .free_global = ct_xfitimer_free_global, +}; + +/* + * timer instance + */ + +struct ct_timer_instance * +ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm) +{ + struct ct_timer_instance *ti; + + ti = kzalloc(sizeof(*ti), GFP_KERNEL); + if (!ti) + return NULL; + spin_lock_init(&ti->lock); + INIT_LIST_HEAD(&ti->instance_list); + INIT_LIST_HEAD(&ti->running_list); + ti->timer_base = atimer; + ti->apcm = apcm; + ti->substream = apcm->substream; + if (atimer->ops->init) + atimer->ops->init(ti); + + spin_lock_irq(&atimer->list_lock); + list_add(&ti->instance_list, &atimer->instance_head); + spin_unlock_irq(&atimer->list_lock); + + return ti; +} + +void ct_timer_prepare(struct ct_timer_instance *ti) +{ + if (ti->timer_base->ops->prepare) + ti->timer_base->ops->prepare(ti); + ti->position = 0; + ti->running = 0; +} + +void ct_timer_start(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + atimer->ops->start(ti); +} + +void ct_timer_stop(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + atimer->ops->stop(ti); +} + +void ct_timer_instance_free(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + + atimer->ops->stop(ti); /* to be sure */ + if (atimer->ops->free_instance) + atimer->ops->free_instance(ti); + + spin_lock_irq(&atimer->list_lock); + list_del(&ti->instance_list); + spin_unlock_irq(&atimer->list_lock); + + kfree(ti); +} + +/* + * timer manager + */ + +#define USE_SYSTEM_TIMER 0 + +static void ct_timer_interrupt(void *data, unsigned int status) +{ + struct ct_timer *timer = data; + + /* Interval timer interrupt */ + if ((status & IT_INT) && timer->ops->interrupt) + timer->ops->interrupt(timer); +} + +struct ct_timer *ct_timer_new(struct ct_atc *atc) +{ + struct ct_timer *atimer; + struct hw *hw; + + atimer = kzalloc(sizeof(*atimer), GFP_KERNEL); + if (!atimer) + return NULL; + spin_lock_init(&atimer->lock); + spin_lock_init(&atimer->list_lock); + INIT_LIST_HEAD(&atimer->instance_head); + INIT_LIST_HEAD(&atimer->running_head); + atimer->atc = atc; + hw = atc->hw; + if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { + printk(KERN_INFO "ctxfi: Use xfi-native timer\n"); + atimer->ops = &ct_xfitimer_ops; + hw->irq_callback_data = atimer; + hw->irq_callback = ct_timer_interrupt; + } else { + printk(KERN_INFO "ctxfi: Use system timer\n"); + atimer->ops = &ct_systimer_ops; + } + return atimer; +} + +void ct_timer_free(struct ct_timer *atimer) +{ + struct hw *hw = atimer->atc->hw; + hw->irq_callback = NULL; + if (atimer->ops->free_global) + atimer->ops->free_global(atimer); + kfree(atimer); +} + diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h new file mode 100644 index 00000000000..97934822929 --- /dev/null +++ b/sound/pci/ctxfi/cttimer.h @@ -0,0 +1,29 @@ +/* + * Timer handling + */ + +#ifndef __CTTIMER_H +#define __CTTIMER_H + +#include +#include +#include + +struct snd_pcm_substream; +struct ct_atc; +struct ct_atc_pcm; + +struct ct_timer; +struct ct_timer_instance; + +struct ct_timer *ct_timer_new(struct ct_atc *atc); +void ct_timer_free(struct ct_timer *atimer); + +struct ct_timer_instance * +ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm); +void ct_timer_instance_free(struct ct_timer_instance *ti); +void ct_timer_start(struct ct_timer_instance *ti); +void ct_timer_stop(struct ct_timer_instance *ti); +void ct_timer_prepare(struct ct_timer_instance *ti); + +#endif /* __CTTIMER_H */ -- cgit v1.2.3 From 775ffa1d3e5a550dd2c9d947d773021c61531b36 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:12:16 +0200 Subject: ALSA: ctxfi - Set periods_min to 2 Set 2 to minimal periods of playback pcm setups, too. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctpcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 32b742dcd53..a0bd31c6090 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -40,7 +40,7 @@ static struct snd_pcm_hardware ct_pcm_playback_hw = { .buffer_bytes_max = (128*1024), .period_bytes_min = (64), .period_bytes_max = (128*1024), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, .fifo_size = 0, }; @@ -62,7 +62,7 @@ static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { .buffer_bytes_max = (128*1024), .period_bytes_min = (64), .period_bytes_max = (128*1024), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, .fifo_size = 0, }; -- cgit v1.2.3 From 2a36f67f8c81f0babda0e811c760b7bfa971010b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:34:10 +0200 Subject: ALSA: ctxfi - Clean up / optimize - Use static tables instead of assigining each funciton pointer - Add __devinit* to appropriate places; pcm, mixer and timer cannot be marked because they are kept in the function table that lives long - Move create_alsa_devs function out of struct ct_atc to mark it __devinit Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 82 ++++++++--------- sound/pci/ctxfi/ctatc.h | 3 +- sound/pci/ctxfi/cthardware.c | 4 +- sound/pci/ctxfi/cthw20k1.c | 205 +++++++++++++++++++++---------------------- sound/pci/ctxfi/cthw20k2.c | 195 ++++++++++++++++++++-------------------- sound/pci/ctxfi/xfi.c | 2 +- 6 files changed, 243 insertions(+), 248 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 10b741977dd..9b1324544db 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1202,7 +1202,7 @@ static int atc_dev_free(struct snd_device *dev) return ct_atc_destroy(atc); } -static int atc_identify_card(struct ct_atc *atc) +static int __devinit atc_identify_card(struct ct_atc *atc) { u16 subsys; u8 revision; @@ -1243,7 +1243,7 @@ static int atc_identify_card(struct ct_atc *atc) return 0; } -static int ct_create_alsa_devs(struct ct_atc *atc) +int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) { enum CTALSADEVS i; struct hw *hw = atc->hw; @@ -1277,7 +1277,7 @@ static int ct_create_alsa_devs(struct ct_atc *atc) return 0; } -static int atc_create_hw_devs(struct ct_atc *atc) +static int __devinit atc_create_hw_devs(struct ct_atc *atc) { struct hw *hw = NULL; struct card_conf info = {0}; @@ -1313,7 +1313,7 @@ static int atc_create_hw_devs(struct ct_atc *atc) return 0; } -static int atc_get_resources(struct ct_atc *atc) +static int __devinit atc_get_resources(struct ct_atc *atc) { struct daio_desc da_desc = {0}; struct daio_mgr *daio_mgr = NULL; @@ -1423,7 +1423,7 @@ static int atc_get_resources(struct ct_atc *atc) return 0; } -static void +static void __devinit atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, struct src **srcs, struct srcimp **srcimps) { @@ -1462,7 +1462,7 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */ } -static void atc_connect_resources(struct ct_atc *atc) +static void __devinit atc_connect_resources(struct ct_atc *atc) { struct dai *dai = NULL; struct dao *dao = NULL; @@ -1508,37 +1508,35 @@ static void atc_connect_resources(struct ct_atc *atc) } } -static void atc_set_ops(struct ct_atc *atc) -{ - /* Set operations */ - atc->map_audio_buffer = ct_map_audio_buffer; - atc->unmap_audio_buffer = ct_unmap_audio_buffer; - atc->pcm_playback_prepare = atc_pcm_playback_prepare; - atc->pcm_release_resources = atc_pcm_release_resources; - atc->pcm_playback_start = atc_pcm_playback_start; - atc->pcm_playback_stop = atc_pcm_stop; - atc->pcm_playback_position = atc_pcm_playback_position; - atc->pcm_capture_prepare = atc_pcm_capture_prepare; - atc->pcm_capture_start = atc_pcm_capture_start; - atc->pcm_capture_stop = atc_pcm_stop; - atc->pcm_capture_position = atc_pcm_capture_position; - atc->spdif_passthru_playback_prepare = spdif_passthru_playback_prepare; - atc->get_ptp_phys = atc_get_ptp_phys; - atc->select_line_in = atc_select_line_in; - atc->select_mic_in = atc_select_mic_in; - atc->select_digit_io = atc_select_digit_io; - atc->line_front_unmute = atc_line_front_unmute; - atc->line_surround_unmute = atc_line_surround_unmute; - atc->line_clfe_unmute = atc_line_clfe_unmute; - atc->line_rear_unmute = atc_line_rear_unmute; - atc->line_in_unmute = atc_line_in_unmute; - atc->spdif_out_unmute = atc_spdif_out_unmute; - atc->spdif_in_unmute = atc_spdif_in_unmute; - atc->spdif_out_get_status = atc_spdif_out_get_status; - atc->spdif_out_set_status = atc_spdif_out_set_status; - atc->spdif_out_passthru = atc_spdif_out_passthru; - atc->have_digit_io_switch = atc_have_digit_io_switch; -} +static struct ct_atc atc_preset __devinitdata = { + .map_audio_buffer = ct_map_audio_buffer, + .unmap_audio_buffer = ct_unmap_audio_buffer, + .pcm_playback_prepare = atc_pcm_playback_prepare, + .pcm_release_resources = atc_pcm_release_resources, + .pcm_playback_start = atc_pcm_playback_start, + .pcm_playback_stop = atc_pcm_stop, + .pcm_playback_position = atc_pcm_playback_position, + .pcm_capture_prepare = atc_pcm_capture_prepare, + .pcm_capture_start = atc_pcm_capture_start, + .pcm_capture_stop = atc_pcm_stop, + .pcm_capture_position = atc_pcm_capture_position, + .spdif_passthru_playback_prepare = spdif_passthru_playback_prepare, + .get_ptp_phys = atc_get_ptp_phys, + .select_line_in = atc_select_line_in, + .select_mic_in = atc_select_mic_in, + .select_digit_io = atc_select_digit_io, + .line_front_unmute = atc_line_front_unmute, + .line_surround_unmute = atc_line_surround_unmute, + .line_clfe_unmute = atc_line_clfe_unmute, + .line_rear_unmute = atc_line_rear_unmute, + .line_in_unmute = atc_line_in_unmute, + .spdif_out_unmute = atc_spdif_out_unmute, + .spdif_in_unmute = atc_spdif_in_unmute, + .spdif_out_get_status = atc_spdif_out_get_status, + .spdif_out_set_status = atc_spdif_out_set_status, + .spdif_out_passthru = atc_spdif_out_passthru, + .have_digit_io_switch = atc_have_digit_io_switch, +}; /** * ct_atc_create - create and initialize a hardware manager @@ -1552,7 +1550,7 @@ static void atc_set_ops(struct ct_atc *atc) * Returns 0 if suceeds, or negative error code if fails. */ -int ct_atc_create(struct snd_card *card, struct pci_dev *pci, +int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, struct ct_atc **ratc) { struct ct_atc *atc = NULL; @@ -1567,14 +1565,14 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, if (NULL == atc) return -ENOMEM; + /* Set operations */ + *atc = atc_preset; + atc->card = card; atc->pci = pci; atc->rsr = rsr; atc->msr = msr; - /* Set operations */ - atc_set_ops(atc); - spin_lock_init(&atc->atc_lock); /* Find card model */ @@ -1606,8 +1604,6 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, if (!atc->timer) goto error1; - atc->create_alsa_devs = ct_create_alsa_devs; - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); if (err < 0) goto error1; diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index a3f9b1bc7dc..04459aa0d4d 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -91,8 +91,6 @@ struct ct_atc { const struct ct_atc_chip_details *chip_details; enum CTCARDS model; - /* Create all alsa devices */ - int (*create_alsa_devs)(struct ct_atc *atc); struct ct_vm *vm; /* device virtual memory manager for this card */ int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); @@ -151,5 +149,6 @@ struct ct_atc { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, struct ct_atc **ratc); +int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); #endif /* CTATC_H */ diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 8e58860f641..53d1acadc0e 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -20,7 +20,7 @@ #include "cthw20k2.h" #include -static enum CHIPTYP get_chip_type(struct hw *hw) +static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) { enum CHIPTYP type = ATCNONE; @@ -39,7 +39,7 @@ static enum CHIPTYP get_chip_type(struct hw *hw) return type; } -int create_hw_obj(struct pci_dev *pci, struct hw **rhw) +int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) { int err = 0; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 550b30a2bcf..df565c11fcc 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -2138,9 +2138,107 @@ static void hw_write_pci(struct hw *hw, u32 reg, u32 data) &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); } -int create_20k1_hw_obj(struct hw **rhw) +static struct hw ct20k1_preset __devinitdata = { + .irq = -1, + + .card_init = hw_card_init, + .card_stop = hw_card_stop, + .pll_init = hw_pll_init, + .is_adc_source_selected = hw_is_adc_input_selected, + .select_adc_source = hw_adc_input_select, + .have_digit_io_switch = hw_have_digit_io_switch, + + .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk, + .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk, + .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk, + .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk, + .src_set_state = src_set_state, + .src_set_bm = src_set_bm, + .src_set_rsr = src_set_rsr, + .src_set_sf = src_set_sf, + .src_set_wr = src_set_wr, + .src_set_pm = src_set_pm, + .src_set_rom = src_set_rom, + .src_set_vo = src_set_vo, + .src_set_st = src_set_st, + .src_set_ie = src_set_ie, + .src_set_ilsz = src_set_ilsz, + .src_set_bp = src_set_bp, + .src_set_cisz = src_set_cisz, + .src_set_ca = src_set_ca, + .src_set_sa = src_set_sa, + .src_set_la = src_set_la, + .src_set_pitch = src_set_pitch, + .src_set_dirty = src_set_dirty, + .src_set_clear_zbufs = src_set_clear_zbufs, + .src_set_dirty_all = src_set_dirty_all, + .src_commit_write = src_commit_write, + .src_get_ca = src_get_ca, + .src_get_dirty = src_get_dirty, + .src_dirty_conj_mask = src_dirty_conj_mask, + .src_mgr_enbs_src = src_mgr_enbs_src, + .src_mgr_enb_src = src_mgr_enb_src, + .src_mgr_dsb_src = src_mgr_dsb_src, + .src_mgr_commit_write = src_mgr_commit_write, + + .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk, + .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk, + .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc, + .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser, + .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt, + .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr, + .srcimp_mgr_commit_write = srcimp_mgr_commit_write, + + .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk, + .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk, + .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk, + .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk, + .amixer_set_mode = amixer_set_mode, + .amixer_set_iv = amixer_set_iv, + .amixer_set_x = amixer_set_x, + .amixer_set_y = amixer_set_y, + .amixer_set_sadr = amixer_set_sadr, + .amixer_set_se = amixer_set_se, + .amixer_set_dirty = amixer_set_dirty, + .amixer_set_dirty_all = amixer_set_dirty_all, + .amixer_commit_write = amixer_commit_write, + .amixer_get_y = amixer_get_y, + .amixer_get_dirty = amixer_get_dirty, + + .dai_get_ctrl_blk = dai_get_ctrl_blk, + .dai_put_ctrl_blk = dai_put_ctrl_blk, + .dai_srt_set_srco = dai_srt_set_srcr, + .dai_srt_set_srcm = dai_srt_set_srcl, + .dai_srt_set_rsr = dai_srt_set_rsr, + .dai_srt_set_drat = dai_srt_set_drat, + .dai_srt_set_ec = dai_srt_set_ec, + .dai_srt_set_et = dai_srt_set_et, + .dai_commit_write = dai_commit_write, + + .dao_get_ctrl_blk = dao_get_ctrl_blk, + .dao_put_ctrl_blk = dao_put_ctrl_blk, + .dao_set_spos = dao_set_spos, + .dao_commit_write = dao_commit_write, + .dao_get_spos = dao_get_spos, + + .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk, + .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk, + .daio_mgr_enb_dai = daio_mgr_enb_dai, + .daio_mgr_dsb_dai = daio_mgr_dsb_dai, + .daio_mgr_enb_dao = daio_mgr_enb_dao, + .daio_mgr_dsb_dao = daio_mgr_dsb_dao, + .daio_mgr_dao_init = daio_mgr_dao_init, + .daio_mgr_set_imaparc = daio_mgr_set_imaparc, + .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, + .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, + .daio_mgr_commit_write = daio_mgr_commit_write, + + .set_timer_irq = set_timer_irq, + .set_timer_tick = set_timer_tick, +}; + +int __devinit create_20k1_hw_obj(struct hw **rhw) { - struct hw *hw; struct hw20k1 *hw20k1; *rhw = NULL; @@ -2151,108 +2249,9 @@ int create_20k1_hw_obj(struct hw **rhw) spin_lock_init(&hw20k1->reg_20k1_lock); spin_lock_init(&hw20k1->reg_pci_lock); - hw = &hw20k1->hw; + hw20k1->hw = ct20k1_preset; - hw->io_base = 0; - hw->mem_base = (unsigned long)NULL; - hw->irq = -1; - - hw->card_init = hw_card_init; - hw->card_stop = hw_card_stop; - hw->pll_init = hw_pll_init; - hw->is_adc_source_selected = hw_is_adc_input_selected; - hw->select_adc_source = hw_adc_input_select; - hw->have_digit_io_switch = hw_have_digit_io_switch; - - hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; - hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; - hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; - hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; - hw->src_set_state = src_set_state; - hw->src_set_bm = src_set_bm; - hw->src_set_rsr = src_set_rsr; - hw->src_set_sf = src_set_sf; - hw->src_set_wr = src_set_wr; - hw->src_set_pm = src_set_pm; - hw->src_set_rom = src_set_rom; - hw->src_set_vo = src_set_vo; - hw->src_set_st = src_set_st; - hw->src_set_ie = src_set_ie; - hw->src_set_ilsz = src_set_ilsz; - hw->src_set_bp = src_set_bp; - hw->src_set_cisz = src_set_cisz; - hw->src_set_ca = src_set_ca; - hw->src_set_sa = src_set_sa; - hw->src_set_la = src_set_la; - hw->src_set_pitch = src_set_pitch; - hw->src_set_dirty = src_set_dirty; - hw->src_set_clear_zbufs = src_set_clear_zbufs; - hw->src_set_dirty_all = src_set_dirty_all; - hw->src_commit_write = src_commit_write; - hw->src_get_ca = src_get_ca; - hw->src_get_dirty = src_get_dirty; - hw->src_dirty_conj_mask = src_dirty_conj_mask; - hw->src_mgr_enbs_src = src_mgr_enbs_src; - hw->src_mgr_enb_src = src_mgr_enb_src; - hw->src_mgr_dsb_src = src_mgr_dsb_src; - hw->src_mgr_commit_write = src_mgr_commit_write; - - hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; - hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; - hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; - hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; - hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; - hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; - hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; - - hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; - hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; - hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; - hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; - hw->amixer_set_mode = amixer_set_mode; - hw->amixer_set_iv = amixer_set_iv; - hw->amixer_set_x = amixer_set_x; - hw->amixer_set_y = amixer_set_y; - hw->amixer_set_sadr = amixer_set_sadr; - hw->amixer_set_se = amixer_set_se; - hw->amixer_set_dirty = amixer_set_dirty; - hw->amixer_set_dirty_all = amixer_set_dirty_all; - hw->amixer_commit_write = amixer_commit_write; - hw->amixer_get_y = amixer_get_y; - hw->amixer_get_dirty = amixer_get_dirty; - - hw->dai_get_ctrl_blk = dai_get_ctrl_blk; - hw->dai_put_ctrl_blk = dai_put_ctrl_blk; - hw->dai_srt_set_srco = dai_srt_set_srcr; - hw->dai_srt_set_srcm = dai_srt_set_srcl; - hw->dai_srt_set_rsr = dai_srt_set_rsr; - hw->dai_srt_set_drat = dai_srt_set_drat; - hw->dai_srt_set_ec = dai_srt_set_ec; - hw->dai_srt_set_et = dai_srt_set_et; - hw->dai_commit_write = dai_commit_write; - - hw->dao_get_ctrl_blk = dao_get_ctrl_blk; - hw->dao_put_ctrl_blk = dao_put_ctrl_blk; - hw->dao_set_spos = dao_set_spos; - hw->dao_commit_write = dao_commit_write; - hw->dao_get_spos = dao_get_spos; - - hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; - hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; - hw->daio_mgr_enb_dai = daio_mgr_enb_dai; - hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; - hw->daio_mgr_enb_dao = daio_mgr_enb_dao; - hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; - hw->daio_mgr_dao_init = daio_mgr_dao_init; - hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; - hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; - hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; - hw->daio_mgr_commit_write = daio_mgr_commit_write; - - hw->set_timer_irq = set_timer_irq; - hw->set_timer_tick = set_timer_tick; - - *rhw = hw; + *rhw = &hw20k1->hw; return 0; } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 349728765f2..041199fbae1 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2006,7 +2006,103 @@ static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) writel(data, (void *)(hw->mem_base + reg)); } -int create_20k2_hw_obj(struct hw **rhw) +static struct hw ct20k2_preset __devinitdata = { + .irq = -1, + + .card_init = hw_card_init, + .card_stop = hw_card_stop, + .pll_init = hw_pll_init, + .is_adc_source_selected = hw_is_adc_input_selected, + .select_adc_source = hw_adc_input_select, + .have_digit_io_switch = hw_have_digit_io_switch, + + .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk, + .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk, + .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk, + .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk, + .src_set_state = src_set_state, + .src_set_bm = src_set_bm, + .src_set_rsr = src_set_rsr, + .src_set_sf = src_set_sf, + .src_set_wr = src_set_wr, + .src_set_pm = src_set_pm, + .src_set_rom = src_set_rom, + .src_set_vo = src_set_vo, + .src_set_st = src_set_st, + .src_set_ie = src_set_ie, + .src_set_ilsz = src_set_ilsz, + .src_set_bp = src_set_bp, + .src_set_cisz = src_set_cisz, + .src_set_ca = src_set_ca, + .src_set_sa = src_set_sa, + .src_set_la = src_set_la, + .src_set_pitch = src_set_pitch, + .src_set_dirty = src_set_dirty, + .src_set_clear_zbufs = src_set_clear_zbufs, + .src_set_dirty_all = src_set_dirty_all, + .src_commit_write = src_commit_write, + .src_get_ca = src_get_ca, + .src_get_dirty = src_get_dirty, + .src_dirty_conj_mask = src_dirty_conj_mask, + .src_mgr_enbs_src = src_mgr_enbs_src, + .src_mgr_enb_src = src_mgr_enb_src, + .src_mgr_dsb_src = src_mgr_dsb_src, + .src_mgr_commit_write = src_mgr_commit_write, + + .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk, + .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk, + .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc, + .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser, + .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt, + .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr, + .srcimp_mgr_commit_write = srcimp_mgr_commit_write, + + .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk, + .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk, + .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk, + .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk, + .amixer_set_mode = amixer_set_mode, + .amixer_set_iv = amixer_set_iv, + .amixer_set_x = amixer_set_x, + .amixer_set_y = amixer_set_y, + .amixer_set_sadr = amixer_set_sadr, + .amixer_set_se = amixer_set_se, + .amixer_set_dirty = amixer_set_dirty, + .amixer_set_dirty_all = amixer_set_dirty_all, + .amixer_commit_write = amixer_commit_write, + .amixer_get_y = amixer_get_y, + .amixer_get_dirty = amixer_get_dirty, + + .dai_get_ctrl_blk = dai_get_ctrl_blk, + .dai_put_ctrl_blk = dai_put_ctrl_blk, + .dai_srt_set_srco = dai_srt_set_srco, + .dai_srt_set_srcm = dai_srt_set_srcm, + .dai_srt_set_rsr = dai_srt_set_rsr, + .dai_srt_set_drat = dai_srt_set_drat, + .dai_srt_set_ec = dai_srt_set_ec, + .dai_srt_set_et = dai_srt_set_et, + .dai_commit_write = dai_commit_write, + + .dao_get_ctrl_blk = dao_get_ctrl_blk, + .dao_put_ctrl_blk = dao_put_ctrl_blk, + .dao_set_spos = dao_set_spos, + .dao_commit_write = dao_commit_write, + .dao_get_spos = dao_get_spos, + + .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk, + .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk, + .daio_mgr_enb_dai = daio_mgr_enb_dai, + .daio_mgr_dsb_dai = daio_mgr_dsb_dai, + .daio_mgr_enb_dao = daio_mgr_enb_dao, + .daio_mgr_dsb_dao = daio_mgr_dsb_dao, + .daio_mgr_dao_init = daio_mgr_dao_init, + .daio_mgr_set_imaparc = daio_mgr_set_imaparc, + .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, + .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, + .daio_mgr_commit_write = daio_mgr_commit_write, +}; + +int __devinit create_20k2_hw_obj(struct hw **rhw) { struct hw *hw; @@ -2015,102 +2111,7 @@ int create_20k2_hw_obj(struct hw **rhw) if (NULL == hw) return -ENOMEM; - hw->io_base = 0; - hw->mem_base = (unsigned long)NULL; - hw->irq = -1; - - hw->card_init = hw_card_init; - hw->card_stop = hw_card_stop; - hw->pll_init = hw_pll_init; - hw->is_adc_source_selected = hw_is_adc_input_selected; - hw->select_adc_source = hw_adc_input_select; - hw->have_digit_io_switch = hw_have_digit_io_switch; - - hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; - hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; - hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; - hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; - hw->src_set_state = src_set_state; - hw->src_set_bm = src_set_bm; - hw->src_set_rsr = src_set_rsr; - hw->src_set_sf = src_set_sf; - hw->src_set_wr = src_set_wr; - hw->src_set_pm = src_set_pm; - hw->src_set_rom = src_set_rom; - hw->src_set_vo = src_set_vo; - hw->src_set_st = src_set_st; - hw->src_set_ie = src_set_ie; - hw->src_set_ilsz = src_set_ilsz; - hw->src_set_bp = src_set_bp; - hw->src_set_cisz = src_set_cisz; - hw->src_set_ca = src_set_ca; - hw->src_set_sa = src_set_sa; - hw->src_set_la = src_set_la; - hw->src_set_pitch = src_set_pitch; - hw->src_set_dirty = src_set_dirty; - hw->src_set_clear_zbufs = src_set_clear_zbufs; - hw->src_set_dirty_all = src_set_dirty_all; - hw->src_commit_write = src_commit_write; - hw->src_get_ca = src_get_ca; - hw->src_get_dirty = src_get_dirty; - hw->src_dirty_conj_mask = src_dirty_conj_mask; - hw->src_mgr_enbs_src = src_mgr_enbs_src; - hw->src_mgr_enb_src = src_mgr_enb_src; - hw->src_mgr_dsb_src = src_mgr_dsb_src; - hw->src_mgr_commit_write = src_mgr_commit_write; - - hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; - hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; - hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; - hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; - hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; - hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; - hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; - - hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; - hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; - hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; - hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; - hw->amixer_set_mode = amixer_set_mode; - hw->amixer_set_iv = amixer_set_iv; - hw->amixer_set_x = amixer_set_x; - hw->amixer_set_y = amixer_set_y; - hw->amixer_set_sadr = amixer_set_sadr; - hw->amixer_set_se = amixer_set_se; - hw->amixer_set_dirty = amixer_set_dirty; - hw->amixer_set_dirty_all = amixer_set_dirty_all; - hw->amixer_commit_write = amixer_commit_write; - hw->amixer_get_y = amixer_get_y; - hw->amixer_get_dirty = amixer_get_dirty; - - hw->dai_get_ctrl_blk = dai_get_ctrl_blk; - hw->dai_put_ctrl_blk = dai_put_ctrl_blk; - hw->dai_srt_set_srco = dai_srt_set_srco; - hw->dai_srt_set_srcm = dai_srt_set_srcm; - hw->dai_srt_set_rsr = dai_srt_set_rsr; - hw->dai_srt_set_drat = dai_srt_set_drat; - hw->dai_srt_set_ec = dai_srt_set_ec; - hw->dai_srt_set_et = dai_srt_set_et; - hw->dai_commit_write = dai_commit_write; - - hw->dao_get_ctrl_blk = dao_get_ctrl_blk; - hw->dao_put_ctrl_blk = dao_put_ctrl_blk; - hw->dao_set_spos = dao_set_spos; - hw->dao_commit_write = dao_commit_write; - hw->dao_get_spos = dao_get_spos; - - hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; - hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; - hw->daio_mgr_enb_dai = daio_mgr_enb_dai; - hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; - hw->daio_mgr_enb_dao = daio_mgr_enb_dao; - hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; - hw->daio_mgr_dao_init = daio_mgr_dao_init; - hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; - hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; - hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; - hw->daio_mgr_commit_write = daio_mgr_commit_write; - + *hw = ct20k2_preset; *rhw = hw; return 0; diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index bf232e7299a..279dac6c34d 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -86,7 +86,7 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) card->private_data = atc; /* Create alsa devices supported by this card */ - err = atc->create_alsa_devs(atc); + err = ct_atc_create_alsa_devs(atc); if (err < 0) goto error; -- cgit v1.2.3 From 032abb519c23000f8a0fae78bb460047f9129270 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:37:19 +0200 Subject: ALSA: ctxfi - Set device 0 for mixer control elements Mixer control elements are usually assigned to device 0. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctmixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 177c46e248d..fac783f585f 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -1097,7 +1097,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, int err = 0; /* Create snd kcontrol instances on demand */ - vol_ctl.device = swh_ctl.device = device; + /* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */ err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer); if (err) return err; -- cgit v1.2.3 From 28cd4aa43de2b6d3b1e3385d450bfb31cbe8d72a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 17:58:00 +0200 Subject: ALSA: ctxfi - Add missing inclusion of linux/math64.h Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cttimer.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 3acb26d0c4c..ceda74e356c 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include "ctatc.h" -- cgit v1.2.3 From 54de6bc8b2437f642844cecb8d183df2368ffceb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 10:21:07 +0200 Subject: ALSA: ctxfi - Optimize the native timer handling using wc counter Optimize the timer update routine to look up wall clock once instead of checking the position of each stream at each timer update. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthardware.h | 1 + sound/pci/ctxfi/cthw20k1.c | 6 ++++ sound/pci/ctxfi/cttimer.c | 76 ++++++++++++++++++++++++++++---------------- 3 files changed, 55 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 35350cf9d2f..8f11644ddc9 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -147,6 +147,7 @@ struct hw { 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; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index df565c11fcc..b165466e1a5 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1186,6 +1186,11 @@ static int set_timer_tick(struct hw *hw, unsigned int ticks) return 0; } +static unsigned int get_wc(struct hw *hw) +{ + return hw_read_20kx(hw, WC); +} + /* Card hardware initialization block */ struct dac_conf { unsigned int msr; /* master sample rate in rsrs */ @@ -2235,6 +2240,7 @@ static struct hw ct20k1_preset __devinitdata = { .set_timer_irq = set_timer_irq, .set_timer_tick = set_timer_tick, + .get_wc = get_wc, }; int __devinit create_20k1_hw_obj(struct hw **rhw) diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index ceda74e356c..ec869a4fe2b 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -47,6 +47,7 @@ struct ct_timer { 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 */ @@ -136,6 +137,7 @@ static struct ct_timer_ops ct_systimer_ops = { */ #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) @@ -159,6 +161,12 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer) } } +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. @@ -170,37 +178,46 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer) static int ct_xfitimer_reprogram(struct ct_timer *atimer) { struct ct_timer_instance *ti; - int min_intr = -1; + 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) { - struct snd_pcm_runtime *runtime; - unsigned int pos, diff; - int intr; - runtime = ti->substream->runtime; - pos = ti->substream->ops->pointer(ti->substream); - if (pos < ti->position) - diff = runtime->buffer_size - ti->position + pos; - else - diff = pos - ti->position; - ti->position = pos; - while (diff >= ti->frag_count) { - ti->frag_count += runtime->period_size; - ti->need_update = 1; - updates++; + 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); } - ti->frag_count -= diff; - intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ, - runtime->rate); - if (min_intr < 0 || intr < min_intr) - min_intr = intr; + if (ti->frag_count < min_intr) + min_intr = ti->frag_count; } - if (min_intr > 0) - ct_xfitimer_irq_rearm(atimer, min_intr); - else - ct_xfitimer_irq_stop(atimer); - + if (min_intr < MIN_TICKS) + min_intr = MIN_TICKS; + ct_xfitimer_irq_rearm(atimer, min_intr); atimer->reprogram = 0; /* clear flag */ return updates; } @@ -253,13 +270,14 @@ 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; } - spin_lock_irqsave(&atimer->lock, flags); ct_xfitimer_irq_stop(atimer); update = ct_xfitimer_reprogram(atimer); spin_unlock_irqrestore(&atimer->lock, flags); @@ -273,6 +291,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti) 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); @@ -396,12 +416,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) atimer->atc = atc; hw = atc->hw; if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { - printk(KERN_INFO "ctxfi: Use xfi-native timer\n"); + 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 { - printk(KERN_INFO "ctxfi: Use system timer\n"); + snd_printd(KERN_INFO "ctxfi: Use system timer\n"); atimer->ops = &ct_systimer_ops; } return atimer; -- cgit v1.2.3 From d436dd063be605dc29f17b2cb0b99a852db89bed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 13:50:18 +0200 Subject: ALSA: ctxfi - Make volume controls more intuitive Change the volume control to dB scale (as the raw data seems so). Also added the TLV dB-scale information. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctmixer.c | 49 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index fac783f585f..796156e4bd3 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -18,11 +18,12 @@ #include "ctmixer.h" #include "ctamixer.h" +#include #include #include #include #include -#include +#include enum CT_SUM_CTL { SUM_IN_F, @@ -292,6 +293,7 @@ set_switch_state(struct ct_mixer *mixer, mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START)); } +#if 0 /* not used */ /* Map integer value ranging from 0 to 65535 to 14-bit float value ranging * from 2^-6 to (1+1023/1024) */ static unsigned int uint16_to_float14(unsigned int x) @@ -331,6 +333,12 @@ static unsigned int float14_to_uint16(unsigned int x) return x; } +#endif /* not used */ + +#define VOL_SCALE 0x1c +#define VOL_MAX 0x100 + +static const DECLARE_TLV_DB_SCALE(ct_vol_db_scale, -6400, 25, 1); static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -338,8 +346,7 @@ static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 43690; - uinfo->value.integer.step = 128; + uinfo->value.integer.max = VOL_MAX; return 0; } @@ -349,15 +356,18 @@ static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol, { struct ct_atc *atc = snd_kcontrol_chip(kcontrol); enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); - struct amixer *amixer = NULL; - int i = 0; + struct amixer *amixer; + int i, val; for (i = 0; i < 2; i++) { amixer = ((struct ct_mixer *)atc->mixer)-> amixers[type*CHN_NUM+i]; - /* Convert 14-bit float-point scale to 16-bit integer volume */ - ucontrol->value.integer.value[i] = - (float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xffff); + val = amixer->ops->get_scale(amixer) / VOL_SCALE; + if (val < 0) + val = 0; + else if (val > VOL_MAX) + val = VOL_MAX; + ucontrol->value.integer.value[i] = val; } return 0; @@ -369,16 +379,19 @@ static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, struct ct_atc *atc = snd_kcontrol_chip(kcontrol); struct ct_mixer *mixer = atc->mixer; enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); - struct amixer *amixer = NULL; - int i = 0, j = 0, change = 0, val = 0; + struct amixer *amixer; + int i, j, val, oval, change = 0; for (i = 0; i < 2; i++) { - /* Convert 16-bit integer volume to 14-bit float-point scale */ - val = (ucontrol->value.integer.value[i] & 0xffff); + val = ucontrol->value.integer.value[i]; + if (val < 0) + val = 0; + else if (val > VOL_MAX) + val = VOL_MAX; + val *= VOL_SCALE; amixer = mixer->amixers[type*CHN_NUM+i]; - if ((float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xff80) - != (val & 0xff80)) { - val = uint16_to_float14(val); + oval = amixer->ops->get_scale(amixer); + if (val != oval) { amixer->ops->set_scale(amixer, val); amixer->ops->commit_write(amixer); change = 1; @@ -398,11 +411,13 @@ static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new vol_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = ct_alsa_mix_volume_info, .get = ct_alsa_mix_volume_get, - .put = ct_alsa_mix_volume_put + .put = ct_alsa_mix_volume_put, + .tlv = { .p = ct_vol_db_scale }, }; static void -- cgit v1.2.3 From 4836ac655410e7f126d316b0be062b38746f7529 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 14:49:26 +0200 Subject: ALSA: ctxfi - Fix DMA mask for emu20k2 chip Allow 64bit DMA mask for emu20k2 chip, too. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k2.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 041199fbae1..edbfb482746 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -26,7 +26,11 @@ #include #include -#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ +#if BITS_PER_LONG == 32 +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ +#else +#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ +#endif static u32 hw_read_20kx(struct hw *hw, u32 reg); static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); @@ -1834,18 +1838,16 @@ static int hw_card_start(struct hw *hw) int err = 0; struct pci_dev *pci = hw->pci; unsigned int gctl; - unsigned int dma_mask = 0; err = pci_enable_device(pci); if (err < 0) return err; /* Set DMA transfer mask */ - dma_mask = CT_XFI_DMA_MASK; - if (pci_set_dma_mask(pci, dma_mask) < 0 || - pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || + pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { printk(KERN_ERR "ctxfi: architecture does not support PCI " - "busmaster DMA with mask 0x%x\n", dma_mask); + "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK); err = -ENXIO; goto error1; } -- cgit v1.2.3 From 514eef9c2a711b4c24b97bb456d39695a6fe1775 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 14:57:57 +0200 Subject: ALSA: ctxfi - Remove useless initializations and cast Remove useless variable initializations and cast at the beginning of functions. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctamixer.c | 62 ++++++++-------- sound/pci/ctxfi/ctatc.c | 164 ++++++++++++++++++++++--------------------- sound/pci/ctxfi/ctdaio.c | 40 +++++------ sound/pci/ctxfi/cthardware.c | 6 +- sound/pci/ctxfi/cthw20k1.c | 82 +++++++++++----------- sound/pci/ctxfi/cthw20k2.c | 52 +++++++------- sound/pci/ctxfi/ctimap.c | 4 +- sound/pci/ctxfi/ctmixer.c | 60 ++++++++-------- sound/pci/ctxfi/ctresource.c | 6 +- sound/pci/ctxfi/ctsrc.c | 110 ++++++++++++++--------------- sound/pci/ctxfi/ctvmem.c | 12 ++-- 11 files changed, 301 insertions(+), 297 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index 859e996ad72..a1db51b3ead 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -58,9 +58,9 @@ static struct rsc_ops amixer_basic_rsc_ops = { static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); amixer->input = rsc; if (NULL == rsc) @@ -75,9 +75,9 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) /* y is a 14-bit immediate constant */ static int amixer_set_y(struct amixer *amixer, unsigned int y) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; hw->amixer_set_y(amixer->rsc.ctrl_blk, y); return 0; @@ -85,9 +85,9 @@ static int amixer_set_y(struct amixer *amixer, unsigned int y) static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv); return 0; @@ -95,9 +95,9 @@ static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) static int amixer_set_sum(struct amixer *amixer, struct sum *sum) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; amixer->sum = sum; if (NULL == sum) { hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); @@ -112,13 +112,13 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum) static int amixer_commit_write(struct amixer *amixer) { - struct hw *hw = NULL; - unsigned int index = 0; - int i = 0; - struct rsc *input = NULL; - struct sum *sum = NULL; + struct hw *hw; + unsigned int index; + int i; + struct rsc *input; + struct sum *sum; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; input = amixer->input; sum = amixer->sum; @@ -158,10 +158,10 @@ static int amixer_commit_write(struct amixer *amixer) static int amixer_commit_raw_write(struct amixer *amixer) { - struct hw *hw = NULL; - unsigned int index = 0; + struct hw *hw; + unsigned int index; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; index = amixer->rsc.ops->output_slot(&amixer->rsc); hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); @@ -170,9 +170,9 @@ static int amixer_commit_raw_write(struct amixer *amixer) static int amixer_get_y(struct amixer *amixer) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; return hw->amixer_get_y(amixer->rsc.ctrl_blk); } @@ -201,7 +201,7 @@ static int amixer_rsc_init(struct amixer *amixer, const struct amixer_desc *desc, struct amixer_mgr *mgr) { - int err = 0; + int err; err = rsc_init(&amixer->rsc, amixer->idx[0], AMIXER, desc->msr, mgr->mgr.hw); @@ -233,9 +233,9 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, const struct amixer_desc *desc, struct amixer **ramixer) { - int err = 0, i = 0; - unsigned int idx = 0; - struct amixer *amixer = NULL; + int err, i; + unsigned int idx; + struct amixer *amixer; unsigned long flags; *ramixer = NULL; @@ -284,7 +284,7 @@ error: static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) { unsigned long flags; - int i = 0; + int i; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < amixer->rsc.msr; i++) @@ -299,7 +299,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) { - int err = 0; + int err; struct amixer_mgr *amixer_mgr; *ramixer_mgr = NULL; @@ -367,7 +367,7 @@ static int sum_rsc_init(struct sum *sum, const struct sum_desc *desc, struct sum_mgr *mgr) { - int err = 0; + int err; err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); if (err) @@ -388,9 +388,9 @@ static int get_sum_rsc(struct sum_mgr *mgr, const struct sum_desc *desc, struct sum **rsum) { - int err = 0, i = 0; - unsigned int idx = 0; - struct sum *sum = NULL; + int err, i; + unsigned int idx; + struct sum *sum; unsigned long flags; *rsum = NULL; @@ -438,7 +438,7 @@ error: static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) { unsigned long flags; - int i = 0; + int i; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < sum->rsc.msr; i++) @@ -453,7 +453,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) { - int err = 0; + int err; struct sum_mgr *sum_mgr; *rsum_mgr = NULL; diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 9b1324544db..7898a375df0 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -190,8 +190,8 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) static unsigned int atc_get_pitch(unsigned int input_rate, unsigned int output_rate) { - unsigned int pitch = 0; - int b = 0; + unsigned int pitch; + int b; /* get pitch and convert to fixed-point 8.24 format. */ pitch = (input_rate / output_rate) << 24; @@ -241,12 +241,12 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct src_desc desc = {0}; struct amixer_desc mix_dsc = {0}; - struct src *src = NULL; - struct amixer *amixer = NULL; - int err = 0; + struct src *src; + struct amixer *amixer; + int err; int n_amixer = apcm->substream->runtime->channels, i = 0; int device = apcm->substream->pcm->device; - unsigned int pitch = 0; + unsigned int pitch; unsigned long flags; if (NULL != apcm->src) { @@ -324,8 +324,8 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; - struct srcimp *srcimp = NULL; - int i = 0; + struct srcimp *srcimp; + int i; if (NULL != apcm->srcimps) { for (i = 0; i < apcm->n_srcimp; i++) { @@ -377,7 +377,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - unsigned int max_cisz = 0; + unsigned int max_cisz; struct src *src = apcm->src; max_cisz = src->multi * src->rsc.msr; @@ -398,8 +398,8 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; - int i = 0; + struct src *src; + int i; ct_timer_stop(apcm->timer); @@ -426,8 +426,8 @@ static int atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct src *src = apcm->src; - u32 size = 0, max_cisz = 0; - int position = 0; + u32 size, max_cisz; + int position; position = src->ops->get_ca(src); @@ -449,7 +449,7 @@ struct src_node_conf_t { static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, struct src_node_conf_t *conf, int *n_srcc) { - unsigned int pitch = 0; + unsigned int pitch; /* get pitch and convert to fixed-point 8.24 format. */ pitch = atc_get_pitch((atc->rsr * atc->msr), @@ -494,14 +494,14 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; struct src_desc src_dsc = {0}; - struct src *src = NULL; + struct src *src; struct srcimp_desc srcimp_dsc = {0}; - struct srcimp *srcimp = NULL; + struct srcimp *srcimp; struct amixer_desc mix_dsc = {0}; struct sum_desc sum_dsc = {0}; - unsigned int pitch = 0; - int multi = 0, err = 0, i = 0; - int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; + unsigned int pitch; + int multi, err, i; + int n_srcimp, n_amixer, n_srcc, n_sum; struct src_node_conf_t src_node_conf[2] = {{0} }; /* first release old resources */ @@ -518,8 +518,8 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc); n_sum = (1 == multi) ? 1 : 0; - n_amixer += n_sum * 2 + n_srcc; - n_srcimp += n_srcc; + n_amixer = n_sum * 2 + n_srcc; + n_srcimp = n_srcc; if ((multi > 1) && (0x8000000 >= pitch)) { /* Need extra AMIXERs and SRCIMPs for special treatment * of interleaved recording of conjugate channels */ @@ -633,14 +633,14 @@ error1: static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; - struct amixer *amixer = NULL; - struct srcimp *srcimp = NULL; + struct src *src; + struct amixer *amixer; + struct srcimp *srcimp; struct ct_mixer *mixer = atc->mixer; - struct sum *mono = NULL; + struct sum *mono; struct rsc *out_ports[8] = {NULL}; - int err = 0, i = 0, j = 0, n_sum = 0, multi = 0; - unsigned int pitch = 0; + int err, i, j, n_sum, multi; + unsigned int pitch; int mix_base = 0, imp_base = 0; if (NULL != apcm->src) { @@ -714,9 +714,9 @@ static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; + struct src *src; struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; - int i = 0, multi = 0; + int i, multi; if (apcm->started) return 0; @@ -776,10 +776,10 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct src_desc desc = {0}; struct amixer_desc mix_dsc = {0}; - struct src *src = NULL; - int err = 0; - int n_amixer = apcm->substream->runtime->channels, i = 0; - unsigned int pitch = 0, rsr = atc->pll_rate; + struct src *src; + int err; + int n_amixer = apcm->substream->runtime->channels, i; + unsigned int pitch, rsr = atc->pll_rate; /* first release old resources */ atc->pcm_release_resources(atc, apcm); @@ -832,15 +832,24 @@ error1: return err; } +static int atc_pll_init(struct ct_atc *atc, int rate) +{ + struct hw *hw = atc->hw; + int err; + err = hw->pll_init(hw, rate); + atc->pll_rate = err ? 0 : rate; + return err; +} + static int spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio); unsigned long flags; unsigned int rate = apcm->substream->runtime->rate; - unsigned int status = 0; - int err = 0; - unsigned char iec958_con_fs = 0; + unsigned int status; + int err; + unsigned char iec958_con_fs; switch (rate) { case 48000: @@ -864,10 +873,8 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) dao->ops->set_spos(dao, status); dao->ops->commit_write(dao); } - if ((rate != atc->pll_rate) && (32000 != rate)) { - err = ((struct hw *)atc->hw)->pll_init(atc->hw, rate); - atc->pll_rate = err ? 0 : rate; - } + if ((rate != atc->pll_rate) && (32000 != rate)) + err = atc_pll_init(atc, rate); spin_unlock_irqrestore(&atc->atc_lock, flags); return err; @@ -876,11 +883,11 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; - struct amixer *amixer = NULL; - struct dao *dao = NULL; - int err = 0; - int i = 0; + struct src *src; + struct amixer *amixer; + struct dao *dao; + int err; + int i; unsigned long flags; if (NULL != apcm->src) @@ -924,7 +931,7 @@ static int atc_select_line_in(struct ct_atc *atc) { struct hw *hw = atc->hw; struct ct_mixer *mixer = atc->mixer; - struct src *src = NULL; + struct src *src; if (hw->is_adc_source_selected(hw, ADC_LINEIN)) return 0; @@ -946,7 +953,7 @@ static int atc_select_mic_in(struct ct_atc *atc) { struct hw *hw = atc->hw; struct ct_mixer *mixer = atc->mixer; - struct src *src = NULL; + struct src *src; if (hw->is_adc_source_selected(hw, ADC_MICIN)) return 0; @@ -1063,8 +1070,8 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) { unsigned long flags; struct dao_desc da_dsc = {0}; - struct dao *dao = NULL; - int err = 0; + struct dao *dao; + int err; struct ct_mixer *mixer = atc->mixer; struct rsc *rscs[2] = {NULL}; unsigned int spos = 0; @@ -1082,11 +1089,8 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) dao->ops->set_left_input(dao, rscs[0]); dao->ops->set_right_input(dao, rscs[1]); /* Restore PLL to atc->rsr if needed. */ - if (atc->pll_rate != atc->rsr) { - err = ((struct hw *)atc->hw)->pll_init(atc->hw, - atc->rsr); - atc->pll_rate = err ? 0 : atc->rsr; - } + if (atc->pll_rate != atc->rsr) + err = atc_pll_init(atc, atc->rsr); } dao->ops->set_spos(dao, spos); dao->ops->commit_write(dao); @@ -1097,15 +1101,15 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) static int ct_atc_destroy(struct ct_atc *atc) { - struct daio_mgr *daio_mgr = NULL; - struct dao *dao = NULL; - struct dai *dai = NULL; - struct daio *daio = NULL; - struct sum_mgr *sum_mgr = NULL; - struct src_mgr *src_mgr = NULL; - struct srcimp_mgr *srcimp_mgr = NULL; - struct srcimp *srcimp = NULL; - struct ct_mixer *mixer = NULL; + struct daio_mgr *daio_mgr; + struct dao *dao; + struct dai *dai; + struct daio *daio; + struct sum_mgr *sum_mgr; + struct src_mgr *src_mgr; + struct srcimp_mgr *srcimp_mgr; + struct srcimp *srcimp; + struct ct_mixer *mixer; int i = 0; if (NULL == atc) @@ -1279,9 +1283,9 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) static int __devinit atc_create_hw_devs(struct ct_atc *atc) { - struct hw *hw = NULL; + struct hw *hw; struct card_conf info = {0}; - int i = 0, err = 0; + int i, err; err = create_hw_obj(atc->pci, &hw); if (err) { @@ -1316,14 +1320,14 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) static int __devinit atc_get_resources(struct ct_atc *atc) { struct daio_desc da_desc = {0}; - struct daio_mgr *daio_mgr = NULL; + struct daio_mgr *daio_mgr; struct src_desc src_dsc = {0}; - struct src_mgr *src_mgr = NULL; + struct src_mgr *src_mgr; struct srcimp_desc srcimp_dsc = {0}; - struct srcimp_mgr *srcimp_mgr = NULL; + struct srcimp_mgr *srcimp_mgr; struct sum_desc sum_dsc = {0}; - struct sum_mgr *sum_mgr = NULL; - int err = 0, i = 0; + struct sum_mgr *sum_mgr; + int err, i; unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); @@ -1428,8 +1432,8 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, struct src **srcs, struct srcimp **srcimps) { struct rsc *rscs[2] = {NULL}; - struct src *src = NULL; - struct srcimp *srcimp = NULL; + struct src *src; + struct srcimp *srcimp; int i = 0; rscs[0] = &dai->daio.rscl; @@ -1464,13 +1468,13 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, static void __devinit atc_connect_resources(struct ct_atc *atc) { - struct dai *dai = NULL; - struct dao *dao = NULL; - struct src *src = NULL; - struct sum *sum = NULL; - struct ct_mixer *mixer = NULL; + struct dai *dai; + struct dao *dao; + struct src *src; + struct sum *sum; + struct ct_mixer *mixer; struct rsc *rscs[2] = {NULL}; - int i = 0, j = 0; + int i, j; mixer = atc->mixer; @@ -1553,11 +1557,11 @@ static struct ct_atc atc_preset __devinitdata = { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, struct ct_atc **ratc) { - struct ct_atc *atc = NULL; + struct ct_atc *atc; static struct snd_device_ops ops = { .dev_free = atc_dev_free, }; - int err = 0; + int err; *ratc = NULL; diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index a2aea399eba..befead4eeaa 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -168,9 +168,9 @@ static int dao_commit_write(struct dao *dao) static int dao_set_left_input(struct dao *dao, struct rsc *input) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); if (NULL == entry) @@ -196,9 +196,9 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) static int dao_set_right_input(struct dao *dao, struct rsc *input) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); if (NULL == entry) @@ -224,9 +224,9 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) static int dao_clear_left_input(struct dao *dao) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; if (NULL == dao->imappers[0]) return 0; @@ -248,9 +248,9 @@ static int dao_clear_left_input(struct dao *dao) static int dao_clear_right_input(struct dao *dao) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; if (NULL == dao->imappers[daio->rscl.msr]) return 0; @@ -299,7 +299,7 @@ static int dai_set_srt_srcr(struct dai *dai, struct rsc *src) static int dai_set_srt_msr(struct dai *dai, unsigned int msr) { - unsigned int rsr = 0; + unsigned int rsr; for (rsr = 0; msr > 1; msr >>= 1) rsr++; @@ -340,8 +340,8 @@ static int daio_rsc_init(struct daio *daio, const struct daio_desc *desc, void *hw) { - int err = 0; - unsigned int idx_l = 0, idx_r = 0; + int err; + unsigned int idx_l, idx_r; switch (((struct hw *)hw)->get_chip_type(hw)) { case ATC20K1: @@ -400,8 +400,8 @@ static int dao_rsc_init(struct dao *dao, struct daio_mgr *mgr) { struct hw *hw = mgr->mgr.hw; - unsigned int conf = 0; - int err = 0; + unsigned int conf; + int err; err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw); if (err) @@ -423,7 +423,7 @@ static int dao_rsc_init(struct dao *dao, daio_device_index(dao->daio.type, hw)); hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); - conf |= (desc->msr & 0x7) | (desc->passthru << 3); + 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, @@ -475,9 +475,9 @@ static int dai_rsc_init(struct dai *dai, const struct daio_desc *desc, struct daio_mgr *mgr) { - int err = 0; + int err; struct hw *hw = mgr->mgr.hw; - unsigned int rsr = 0, msr = 0; + unsigned int rsr, msr; err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw); if (err) @@ -536,7 +536,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, const struct daio_desc *desc, struct daio **rdaio) { - int err = 0; + int err; struct dai *dai = NULL; struct dao *dao = NULL; unsigned long flags; @@ -660,7 +660,7 @@ static int daio_map_op(void *data, struct imapper *entry) static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); if ((0 == entry->addr) && (mgr->init_imap_added)) { @@ -677,7 +677,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr); @@ -701,7 +701,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr) int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) { - int err = 0, i = 0; + int err, i; struct daio_mgr *daio_mgr; struct imapper *entry; diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 53d1acadc0e..5ec6813d391 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -22,7 +22,7 @@ static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) { - enum CHIPTYP type = ATCNONE; + enum CHIPTYP type; switch (hw->pci->device) { case 0x0005: /* 20k1 device */ @@ -41,7 +41,7 @@ static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) { - int err = 0; + int err; switch (pci->device) { case 0x0005: /* 20k1 device */ @@ -65,7 +65,7 @@ int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) int destroy_hw_obj(struct hw *hw) { - int err = 0; + int err; switch (hw->pci->device) { case 0x0005: /* 20k1 device */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index b165466e1a5..38b87b6ee6d 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -369,7 +369,7 @@ static unsigned int src_param_pitch_mixer(unsigned int src_idx) static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) { struct src_rsc_ctrl_blk *ctl = blk; - int i = 0; + int i; if (ctl->dirty.bf.czbfs) { /* Clear Z-Buffer registers */ @@ -468,8 +468,8 @@ static int src_mgr_dsb_src(void *blk, unsigned int idx) static int src_mgr_commit_write(struct hw *hw, void *blk) { struct src_mgr_ctrl_blk *ctl = blk; - int i = 0; - unsigned int ret = 0; + int i; + unsigned int ret; if (ctl->dirty.bf.enbsa) { do { @@ -1108,7 +1108,7 @@ static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) static int daio_mgr_commit_write(struct hw *hw, void *blk) { struct daio_mgr_ctrl_blk *ctl = blk; - int i = 0; + int i; if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) { for (i = 0; i < 4; i++) { @@ -1212,8 +1212,8 @@ struct trn_conf { static int hw_daio_init(struct hw *hw, const struct daio_conf *info) { - u32 i2sorg = 0; - u32 spdorg = 0; + u32 i2sorg; + u32 spdorg; /* Read I2S CTL. Keep original value. */ /*i2sorg = hw_read_20kx(hw, I2SCTL);*/ @@ -1263,8 +1263,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) /* TRANSPORT operations */ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) { - u32 trnctl = 0; - unsigned long ptp_phys_low = 0, ptp_phys_high = 0; + u32 trnctl; + u32 ptp_phys_low, ptp_phys_high; /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { @@ -1316,7 +1316,7 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) static int hw_pll_init(struct hw *hw, unsigned int rsr) { unsigned int pllctl; - int i = 0; + int i; pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731; for (i = 0; i < 3; i++) { @@ -1384,7 +1384,7 @@ static void i2c_lock(struct hw *hw) static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) { - unsigned int ret = 0; + unsigned int ret; do { ret = hw_read_pci(hw, 0xEC); @@ -1397,9 +1397,9 @@ static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) static int hw_reset_dac(struct hw *hw) { - u32 i = 0; - u16 gpioorg = 0; - unsigned int ret = 0; + u32 i; + u16 gpioorg; + unsigned int ret; if (i2c_unlock(hw)) return -1; @@ -1430,10 +1430,10 @@ static int hw_reset_dac(struct hw *hw) static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { - u32 data = 0; - u16 gpioorg = 0; - u16 subsys_id = 0; - unsigned int ret = 0; + u32 data; + u16 gpioorg; + u16 subsys_id; + unsigned int ret; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1494,13 +1494,12 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type) { - u32 data = 0; - return data; + return 0; } static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO); switch (type) { @@ -1521,7 +1520,7 @@ static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO); switch (type) { @@ -1539,7 +1538,7 @@ static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u16 subsys_id = 0; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1559,7 +1558,7 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) static int adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) { - u32 data = 0; + u32 data; /* * check and set the following GPIO bits accordingly @@ -1599,9 +1598,9 @@ adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) static int adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) { - u32 data = 0; - u32 i2c_data = 0; - unsigned int ret = 0; + u32 data; + u32 i2c_data; + unsigned int ret; if (i2c_unlock(hw)) return -1; @@ -1649,9 +1648,9 @@ adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) static int adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) { - u32 data = 0; - u32 i2c_data = 0; - unsigned int ret = 0; + u32 data; + u32 i2c_data; + unsigned int ret; if (i2c_unlock(hw)) return -1; @@ -1693,7 +1692,7 @@ adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u16 subsys_id = 0; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1719,8 +1718,8 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) { u16 gpioorg; u16 input_source; - u32 adcdata = 0; - unsigned int ret = 0; + u32 adcdata; + unsigned int ret; input_source = 0x100; /* default to analog */ switch (input) { @@ -1742,6 +1741,7 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) input_source = 0x0; /* set to Digital */ break; default: + adcdata = 0x0; break; } @@ -1781,8 +1781,8 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err = 0; - u16 subsys_id = 0; + int err; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1797,7 +1797,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) static int hw_have_digit_io_switch(struct hw *hw) { - u16 subsys_id = 0; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* SB073x and Vista compatible cards have no digit IO switch */ @@ -1814,11 +1814,11 @@ static int uaa_to_xfi(struct pci_dev *pci) { unsigned int bar0, bar1, bar2, bar3, bar4, bar5; unsigned int cmd, irq, cl_size, l_timer, pwr; - unsigned int is_uaa = 0; + unsigned int is_uaa; unsigned int data[4] = {0}; unsigned int io_base; void *mem_base; - int i = 0; + int i; const u32 CTLX = CTLBITS('C', 'T', 'L', 'X'); const u32 CTL_ = CTLBITS('C', 'T', 'L', '-'); const u32 CTLF = CTLBITS('C', 'T', 'L', 'F'); @@ -1916,9 +1916,9 @@ static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id) static int hw_card_start(struct hw *hw) { - int err = 0; + int err; struct pci_dev *pci = hw->pci; - u16 subsys_id = 0; + u16 subsys_id; err = pci_enable_device(pci); if (err < 0) @@ -2004,8 +2004,8 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) { int err; unsigned int gctl; - u16 subsys_id = 0; - u32 data = 0; + u16 subsys_id; + u32 data; struct dac_conf dac_info = {0}; struct adc_conf adc_info = {0}; struct daio_conf daio_info = {0}; diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index edbfb482746..7d6dcbaf524 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) static int src_put_rsc_ctrl_blk(void *blk) { - kfree((struct src_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -359,7 +359,7 @@ static unsigned int src_param_pitch_mixer(unsigned int src_idx) static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) { struct src_rsc_ctrl_blk *ctl = blk; - int i = 0; + int i; if (ctl->dirty.bf.czbfs) { /* Clear Z-Buffer registers */ @@ -458,8 +458,8 @@ static int src_mgr_dsb_src(void *blk, unsigned int idx) static int src_mgr_commit_write(struct hw *hw, void *blk) { struct src_mgr_ctrl_blk *ctl = blk; - int i = 0; - unsigned int ret = 0; + int i; + unsigned int ret; if (ctl->dirty.bf.enbsa) { do { @@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) static int src_mgr_put_ctrl_blk(void *blk) { - kfree((struct src_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) static int srcimp_mgr_put_ctrl_blk(void *blk) { - kfree((struct srcimp_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -704,7 +704,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) static int amixer_rsc_put_ctrl_blk(void *blk) { - kfree((struct amixer_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -893,7 +893,7 @@ static int dai_get_ctrl_blk(void **rblk) static int dai_put_ctrl_blk(void *blk) { - kfree((struct dai_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -943,7 +943,7 @@ static int dao_get_ctrl_blk(void **rblk) static int dao_put_ctrl_blk(void *blk) { - kfree((struct dao_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -1051,8 +1051,8 @@ static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) static int daio_mgr_commit_write(struct hw *hw, void *blk) { struct daio_mgr_ctrl_blk *ctl = blk; - unsigned int data = 0; - int i = 0; + unsigned int data; + int i; for (i = 0; i < 8; i++) { if ((ctl->dirty.bf.atxctl & (0x1 << i))) { @@ -1080,7 +1080,7 @@ static int daio_mgr_commit_write(struct hw *hw, void *blk) static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) { struct daio_mgr_ctrl_blk *blk; - int i = 0; + int i; *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); @@ -1099,7 +1099,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) static int daio_mgr_put_ctrl_blk(void *blk) { - kfree((struct daio_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -1125,7 +1125,7 @@ struct trn_conf { static int hw_daio_init(struct hw *hw, const struct daio_conf *info) { - u32 dwData = 0; + u32 dwData; int i; /* Program I2S with proper sample rate and enable the correct I2S @@ -1195,9 +1195,9 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) /* TRANSPORT operations */ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) { - u32 vmctl = 0, data = 0; - unsigned long ptp_phys_low = 0, ptp_phys_high = 0; - int i = 0; + u32 vmctl, data; + u32 ptp_phys_low, ptp_phys_high; + int i; /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { @@ -1433,7 +1433,7 @@ static int I2CLockChip(struct hw *hw) static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) { - int err = 0; + int err; unsigned int RegI2CStatus; unsigned int RegI2CAddress; @@ -1481,7 +1481,7 @@ static int I2CUninit(struct hw *hw) static int I2CWaitDataReady(struct hw *hw) { int i = 0x400000; - unsigned int ret = 0; + unsigned int ret; do { ret = hw_read_20kx(hw, I2C_IF_STATUS); @@ -1541,9 +1541,9 @@ static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { - int err = 0; - u32 dwData = 0; - int i = 0; + int err; + u32 dwData; + int i; struct REGS_CS4382 cs4382_Read = {0}; struct REGS_CS4382 cs4382_Def = { 0x00000001, /* Mode Control 1 */ @@ -1696,7 +1696,7 @@ End: static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO_DATA); switch (type) { @@ -1714,7 +1714,7 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO_DATA); switch (type) { @@ -1747,8 +1747,8 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err = 0; - u32 dwMux = 2, dwData = 0, dwCtl = 0; + int err; + u32 dwMux = 2, dwData, dwCtl; /* Set ADC reset bit as output */ dwData = hw_read_20kx(hw, GPIO_CTRL); diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c index d34eacd902c..0b73368a4df 100644 --- a/sound/pci/ctxfi/ctimap.c +++ b/sound/pci/ctxfi/ctimap.c @@ -99,8 +99,8 @@ int input_mapper_delete(struct list_head *mappers, struct imapper *entry, void free_input_mapper_list(struct list_head *head) { - struct imapper *entry = NULL; - struct list_head *pos = NULL; + struct imapper *entry; + struct list_head *pos; while (!list_empty(head)) { pos = head->next; diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 796156e4bd3..666722d9de4 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -298,7 +298,7 @@ set_switch_state(struct ct_mixer *mixer, * from 2^-6 to (1+1023/1024) */ static unsigned int uint16_to_float14(unsigned int x) { - unsigned int i = 0; + unsigned int i; if (x < 17) return 0; @@ -318,7 +318,7 @@ static unsigned int uint16_to_float14(unsigned int x) static unsigned int float14_to_uint16(unsigned int x) { - unsigned int e = 0; + unsigned int e; if (!x) return x; @@ -491,7 +491,7 @@ static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol, struct ct_atc *atc = snd_kcontrol_chip(kcontrol); struct ct_mixer *mixer = atc->mixer; enum CTALSA_MIXER_CTL type = kcontrol->private_value; - int state = 0; + int state; state = ucontrol->value.integer.value[0]; if (get_switch_state(mixer, type) == state) @@ -574,7 +574,7 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ct_atc *atc = snd_kcontrol_chip(kcontrol); - unsigned int status = 0; + unsigned int status; atc->spdif_out_get_status(atc, &status); ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; @@ -589,8 +589,8 @@ static int ct_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ct_atc *atc = snd_kcontrol_chip(kcontrol); - int change = 1; - unsigned int status = 0, old_status = 0; + int change; + unsigned int status, old_status; status = (ucontrol->value.iec958.status[0] << 0) | (ucontrol->value.iec958.status[1] << 8) | @@ -641,8 +641,8 @@ static struct snd_kcontrol_new iec958_ctl = { static int ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) { - struct snd_kcontrol *kctl = NULL; - int err = 0; + struct snd_kcontrol *kctl; + int err; kctl = snd_ctl_new1(new, mixer->atc); if (NULL == kctl) @@ -669,9 +669,9 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) { - enum CTALSA_MIXER_CTL type = 0; + enum CTALSA_MIXER_CTL type; struct ct_atc *atc = mixer->atc; - int err = 0; + int err; /* Create snd kcontrol instances on demand */ for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) { @@ -733,9 +733,9 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) static void ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) { - struct amixer *amix_d = NULL; - struct sum *sum_c = NULL; - int i = 0; + struct amixer *amix_d; + struct sum *sum_c; + int i; for (i = 0; i < 2; i++) { amix_d = mixer->amixers[type*CHN_NUM+i]; @@ -748,8 +748,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) static void ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) { - struct amixer *amix_d = NULL; - int i = 0; + struct amixer *amix_d; + int i; for (i = 0; i < 2; i++) { amix_d = mixer->amixers[type*CHN_NUM+i]; @@ -760,14 +760,14 @@ ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) static int ct_mixer_get_resources(struct ct_mixer *mixer) { - struct sum_mgr *sum_mgr = NULL; - struct sum *sum = NULL; + struct sum_mgr *sum_mgr; + struct sum *sum; struct sum_desc sum_desc = {0}; - struct amixer_mgr *amixer_mgr = NULL; - struct amixer *amixer = NULL; + struct amixer_mgr *amixer_mgr; + struct amixer *amixer; struct amixer_desc am_desc = {0}; - int err = 0; - int i = 0; + int err; + int i; /* Allocate sum resources for mixer obj */ sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; @@ -822,8 +822,8 @@ error1: static int ct_mixer_get_mem(struct ct_mixer **rmixer) { - struct ct_mixer *mixer = NULL; - int err = 0; + struct ct_mixer *mixer; + int err; *rmixer = NULL; /* Allocate mem for mixer obj */ @@ -855,9 +855,9 @@ error1: static int ct_mixer_topology_build(struct ct_mixer *mixer) { - struct sum *sum = NULL; - struct amixer *amix_d = NULL, *amix_s = NULL; - enum CT_AMIXER_CTL i = 0, j = 0; + struct sum *sum; + struct amixer *amix_d, *amix_s; + enum CT_AMIXER_CTL i, j; /* Build topology from destination to source */ @@ -1044,7 +1044,7 @@ int ct_mixer_destroy(struct ct_mixer *mixer) struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; struct amixer_mgr *amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; - struct amixer *amixer = NULL; + struct amixer *amixer; int i = 0; /* Release amixer resources */ @@ -1071,8 +1071,8 @@ int ct_mixer_destroy(struct ct_mixer *mixer) int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer) { - struct ct_mixer *mixer = NULL; - int err = 0; + struct ct_mixer *mixer; + int err; *rmixer = NULL; @@ -1109,7 +1109,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, enum CTALSADEVS device, const char *device_name) { - int err = 0; + int err; /* Create snd kcontrol instances on demand */ /* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */ diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c index da21a717a07..889c495bb7d 100644 --- a/sound/pci/ctxfi/ctresource.c +++ b/sound/pci/ctxfi/ctresource.c @@ -27,7 +27,7 @@ static int get_resource(u8 *rscs, unsigned int amount, unsigned int multi, unsigned int *ridx) { - int i = 0, j = 0, k = 0, n = 0; + int i, j, k, n; /* Check whether there are sufficient resources to meet request. */ for (i = 0, n = multi; i < amount; i++) { @@ -61,7 +61,7 @@ get_resource(u8 *rscs, unsigned int amount, static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) { - unsigned int i = 0, j = 0, k = 0, n = 0; + unsigned int i, j, k, n; /* Mark the contiguous bits in resource bit-map as used */ for (n = multi, i = idx; n > 0; n--) { @@ -76,7 +76,7 @@ static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx) { - int err = 0; + int err; if (n > mgr->avail) return -ENOENT; diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 77e118c5bc9..e1c145d8b70 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -37,9 +37,9 @@ static int (*src_default_config[3])(struct src *) = { static int src_set_state(struct src *src, unsigned int state) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_state(src->rsc.ctrl_blk, state); return 0; @@ -47,9 +47,9 @@ static int src_set_state(struct src *src, unsigned int state) static int src_set_bm(struct src *src, unsigned int bm) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_bm(src->rsc.ctrl_blk, bm); return 0; @@ -57,9 +57,9 @@ static int src_set_bm(struct src *src, unsigned int bm) static int src_set_sf(struct src *src, unsigned int sf) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_sf(src->rsc.ctrl_blk, sf); return 0; @@ -67,9 +67,9 @@ static int src_set_sf(struct src *src, unsigned int sf) static int src_set_pm(struct src *src, unsigned int pm) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_pm(src->rsc.ctrl_blk, pm); return 0; @@ -77,9 +77,9 @@ static int src_set_pm(struct src *src, unsigned int pm) static int src_set_rom(struct src *src, unsigned int rom) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_rom(src->rsc.ctrl_blk, rom); return 0; @@ -87,9 +87,9 @@ static int src_set_rom(struct src *src, unsigned int rom) static int src_set_vo(struct src *src, unsigned int vo) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_vo(src->rsc.ctrl_blk, vo); return 0; @@ -97,9 +97,9 @@ static int src_set_vo(struct src *src, unsigned int vo) static int src_set_st(struct src *src, unsigned int st) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_st(src->rsc.ctrl_blk, st); return 0; @@ -107,9 +107,9 @@ static int src_set_st(struct src *src, unsigned int st) static int src_set_bp(struct src *src, unsigned int bp) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_bp(src->rsc.ctrl_blk, bp); return 0; @@ -117,9 +117,9 @@ static int src_set_bp(struct src *src, unsigned int bp) static int src_set_cisz(struct src *src, unsigned int cisz) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_cisz(src->rsc.ctrl_blk, cisz); return 0; @@ -127,9 +127,9 @@ static int src_set_cisz(struct src *src, unsigned int cisz) static int src_set_ca(struct src *src, unsigned int ca) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_ca(src->rsc.ctrl_blk, ca); return 0; @@ -137,9 +137,9 @@ static int src_set_ca(struct src *src, unsigned int ca) static int src_set_sa(struct src *src, unsigned int sa) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_sa(src->rsc.ctrl_blk, sa); return 0; @@ -147,9 +147,9 @@ static int src_set_sa(struct src *src, unsigned int sa) static int src_set_la(struct src *src, unsigned int la) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_la(src->rsc.ctrl_blk, la); return 0; @@ -157,9 +157,9 @@ static int src_set_la(struct src *src, unsigned int la) static int src_set_pitch(struct src *src, unsigned int pitch) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_pitch(src->rsc.ctrl_blk, pitch); return 0; @@ -167,9 +167,9 @@ static int src_set_pitch(struct src *src, unsigned int pitch) static int src_set_clear_zbufs(struct src *src) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); return 0; @@ -177,11 +177,11 @@ static int src_set_clear_zbufs(struct src *src) static int src_commit_write(struct src *src) { - struct hw *hw = NULL; - int i = 0; + struct hw *hw; + int i; unsigned int dirty = 0; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; src->rsc.ops->master(&src->rsc); if (src->rsc.msr > 1) { /* Save dirty flags for conjugate resource programming */ @@ -207,9 +207,9 @@ static int src_commit_write(struct src *src) static int src_get_ca(struct src *src) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc), src->rsc.ctrl_blk); } @@ -229,7 +229,7 @@ static struct src *src_next_interleave(struct src *src) static int src_default_config_memrd(struct src *src) { struct hw *hw = src->rsc.hw; - unsigned int rsr = 0, msr = 0; + unsigned int rsr, msr; hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); hw->src_set_bm(src->rsc.ctrl_blk, 1); @@ -297,7 +297,7 @@ static int src_default_config_memwr(struct src *src) static int src_default_config_arcrw(struct src *src) { struct hw *hw = src->rsc.hw; - unsigned int rsr = 0, msr = 0; + unsigned int rsr, msr; unsigned int dirty; hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); @@ -360,8 +360,8 @@ static int src_rsc_init(struct src *src, u32 idx, const struct src_desc *desc, struct src_mgr *mgr) { - int err = 0; - int i = 0, n = 0; + int err; + int i, n; struct src *p; n = (MEMRD == desc->mode) ? desc->multi : 1; @@ -395,7 +395,7 @@ error1: static int src_rsc_uninit(struct src *src, struct src_mgr *mgr) { - int i = 0, n = 0; + int i, n; struct src *p; n = (MEMRD == src->mode) ? src->multi : 1; @@ -416,8 +416,8 @@ 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 = 0; - struct src *src = NULL; + int err; + struct src *src; unsigned long flags; *rsrc = NULL; @@ -489,7 +489,7 @@ static int put_src_rsc(struct src_mgr *mgr, struct src *src) static int src_enable_s(struct src_mgr *mgr, struct src *src) { struct hw *hw = mgr->mgr.hw; - int i = 0; + int i; src->rsc.ops->master(&src->rsc); for (i = 0; i < src->rsc.msr; i++) { @@ -505,7 +505,7 @@ static int src_enable_s(struct src_mgr *mgr, struct src *src) static int src_enable(struct src_mgr *mgr, struct src *src) { struct hw *hw = mgr->mgr.hw; - int i = 0; + int i; src->rsc.ops->master(&src->rsc); for (i = 0; i < src->rsc.msr; i++) { @@ -521,7 +521,7 @@ static int src_enable(struct src_mgr *mgr, struct src *src) static int src_disable(struct src_mgr *mgr, struct src *src) { struct hw *hw = mgr->mgr.hw; - int i = 0; + int i; src->rsc.ops->master(&src->rsc); for (i = 0; i < src->rsc.msr; i++) { @@ -545,7 +545,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr) int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) { - int err = 0, i = 0; + int err, i; struct src_mgr *src_mgr; *rsrc_mgr = NULL; @@ -618,8 +618,8 @@ static struct rsc_ops srcimp_basic_rsc_ops = { static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input) { - struct imapper *entry = NULL; - int i = 0; + struct imapper *entry; + int i; srcimp->rsc.ops->master(&srcimp->rsc); src->rsc.ops->master(&src->rsc); @@ -646,7 +646,7 @@ static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input) static int srcimp_unmap(struct srcimp *srcimp) { - int i = 0; + int i; /* Program master and conjugate resources */ for (i = 0; i < srcimp->rsc.msr; i++) { @@ -669,7 +669,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp, const struct srcimp_desc *desc, struct srcimp_mgr *mgr) { - int err = 0; + int err; err = rsc_init(&srcimp->rsc, srcimp->idx[0], SRCIMP, desc->msr, mgr->mgr.hw); @@ -715,9 +715,9 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr, const struct srcimp_desc *desc, struct srcimp **rsrcimp) { - int err = 0, i = 0; - unsigned int idx = 0; - struct srcimp *srcimp = NULL; + int err, i; + unsigned int idx; + struct srcimp *srcimp; unsigned long flags; *rsrcimp = NULL; @@ -765,7 +765,7 @@ error1: static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp) { unsigned long flags; - int i = 0; + int i; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < srcimp->rsc.msr; i++) @@ -795,7 +795,7 @@ static int srcimp_map_op(void *data, struct imapper *entry) static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); if ((0 == entry->addr) && (mgr->init_imap_added)) { @@ -812,7 +812,7 @@ static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry) static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr); @@ -828,7 +828,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) { - int err = 0; + int err; struct srcimp_mgr *srcimp_mgr; struct imapper *entry; diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index b7f8e58ae07..67665a7e43c 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -31,8 +31,8 @@ static struct ct_vm_block * get_vm_block(struct ct_vm *vm, unsigned int size) { - struct ct_vm_block *block = NULL, *entry = NULL; - struct list_head *pos = NULL; + struct ct_vm_block *block = NULL, *entry; + struct list_head *pos; size = CT_PAGE_ALIGN(size); if (size > vm->size) { @@ -77,8 +77,8 @@ get_vm_block(struct ct_vm *vm, unsigned int size) static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) { - struct ct_vm_block *entry = NULL, *pre_ent = NULL; - struct list_head *pos = NULL, *pre = NULL; + struct ct_vm_block *entry, *pre_ent; + struct list_head *pos, *pre; block->size = CT_PAGE_ALIGN(block->size); @@ -223,8 +223,8 @@ int ct_vm_create(struct ct_vm **rvm) void ct_vm_destroy(struct ct_vm *vm) { int i; - struct list_head *pos = NULL; - struct ct_vm_block *entry = NULL; + struct list_head *pos; + struct ct_vm_block *entry; /* free used and unused list nodes */ while (!list_empty(&vm->used)) { -- cgit v1.2.3 From af8500bbbd18438495d2f91ad07bda49fff3b770 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 15:07:46 +0200 Subject: ALSA: ctxfi - Fix possible buffer pointer overrun Fix possible buffer pointer overruns. Back to zero when it's equal or over the buffer size. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctpcm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index a0bd31c6090..870fa170f04 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -243,6 +243,8 @@ ct_pcm_playback_pointer(struct snd_pcm_substream *substream) /* 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; } @@ -343,6 +345,8 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream) /* 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; } -- cgit v1.2.3 From d362af62ed98f58c64a2b3dd58c79d25ad181b0b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 15:31:22 +0200 Subject: ALSA: ctxfi - Fix / clean up hw20k2 chip code - Clean up Hungarian coding style - Don't use static variables for I2C information; this unables to use multiple instances. Now they are stored in struct hw20k2 fields. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k2.c | 346 +++++++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 169 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 7d6dcbaf524..4493a51c6b0 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -15,8 +15,6 @@ * */ -#include "cthw20k2.h" -#include "ct20k2reg.h" #include #include #include @@ -25,6 +23,8 @@ #include #include #include +#include "cthw20k2.h" +#include "ct20k2reg.h" #if BITS_PER_LONG == 32 #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ @@ -32,6 +32,14 @@ #define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ #endif +struct hw20k2 { + struct hw hw; + /* for i2c */ + unsigned char dev_id; + unsigned char addr_size; + unsigned char data_size; +}; + static u32 hw_read_20kx(struct hw *hw, u32 reg); static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); @@ -1125,7 +1133,7 @@ struct trn_conf { static int hw_daio_init(struct hw *hw, const struct daio_conf *info) { - u32 dwData; + u32 data; int i; /* Program I2S with proper sample rate and enable the correct I2S @@ -1156,12 +1164,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) if (i <= 3) { /* 1st 3 channels are SPDIFs (SB0960) */ if (i == 3) - dwData = 0x1001001; + data = 0x1001001; else - dwData = 0x1000001; + data = 0x1000001; - hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), dwData); - hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), dwData); + hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); + hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); /* Initialize the SPDIF Out Channel status registers. * The value specified here is based on the typical @@ -1179,13 +1187,13 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); } else { /* Next 5 channels are I2S (SB0960) */ - dwData = 0x11; - hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), dwData); + data = 0x11; + hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data); if (2 == info->msr) { /* Four channels per sample period */ - dwData |= 0x1000; + data |= 0x1000; } - hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), dwData); + hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data); } } @@ -1377,34 +1385,32 @@ static int hw_auto_init(struct hw *hw) #define I2C_ADDRESS_PTAD 0x0000FFFF #define I2C_ADDRESS_SLAD 0x007F0000 -struct REGS_CS4382 { - u32 dwModeControl_1; - u32 dwModeControl_2; - u32 dwModeControl_3; +struct regs_cs4382 { + u32 mode_control_1; + u32 mode_control_2; + u32 mode_control_3; - u32 dwFilterControl; - u32 dwInvertControl; + u32 filter_control; + u32 invert_control; - u32 dwMixControl_P1; - u32 dwVolControl_A1; - u32 dwVolControl_B1; + u32 mix_control_P1; + u32 vol_control_A1; + u32 vol_control_B1; - u32 dwMixControl_P2; - u32 dwVolControl_A2; - u32 dwVolControl_B2; + u32 mix_control_P2; + u32 vol_control_A2; + u32 vol_control_B2; - u32 dwMixControl_P3; - u32 dwVolControl_A3; - u32 dwVolControl_B3; + u32 mix_control_P3; + u32 vol_control_A3; + u32 vol_control_B3; - u32 dwMixControl_P4; - u32 dwVolControl_A4; - u32 dwVolControl_B4; + u32 mix_control_P4; + u32 vol_control_A4; + u32 vol_control_B4; }; -static u8 m_bAddressSize, m_bDataSize, m_bDeviceID; - -static int I2CUnlockFullAccess(struct hw *hw) +static int hw20k2_i2c_unlock_full_access(struct hw *hw) { u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] = {0xB3, 0xD4}; @@ -1420,7 +1426,7 @@ static int I2CUnlockFullAccess(struct hw *hw) return -1; } -static int I2CLockChip(struct hw *hw) +static int hw20k2_i2c_lock_chip(struct hw *hw) { /* Write twice */ hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); @@ -1431,54 +1437,55 @@ static int I2CLockChip(struct hw *hw) return -1; } -static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) +static int hw20k2_i2c_init(struct hw *hw, u8 dev_id, u8 addr_size, u8 data_size) { + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; int err; - unsigned int RegI2CStatus; - unsigned int RegI2CAddress; + unsigned int i2c_status; + unsigned int i2c_addr; - err = I2CUnlockFullAccess(hw); + err = hw20k2_i2c_unlock_full_access(hw); if (err < 0) return err; - m_bAddressSize = bAddressSize; - m_bDataSize = bDataSize; - m_bDeviceID = bDeviceID; + hw20k2->addr_size = addr_size; + hw20k2->data_size = data_size; + hw20k2->dev_id = dev_id; - RegI2CAddress = 0; - set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, bDeviceID); + i2c_addr = 0; + set_field(&i2c_addr, I2C_ADDRESS_SLAD, dev_id); - hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr); - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_DCM, 1); /* Direct control mode */ + set_field(&i2c_status, I2C_STATUS_DCM, 1); /* Direct control mode */ - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); return 0; } -static int I2CUninit(struct hw *hw) +static int hw20k2_i2c_uninit(struct hw *hw) { - unsigned int RegI2CStatus; - unsigned int RegI2CAddress; + unsigned int i2c_status; + unsigned int i2c_addr; - RegI2CAddress = 0; - set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ + i2c_addr = 0; + set_field(&i2c_addr, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ - hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr); - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_DCM, 0); /* I2C mode */ + set_field(&i2c_status, I2C_STATUS_DCM, 0); /* I2C mode */ - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); - return I2CLockChip(hw); + return hw20k2_i2c_lock_chip(hw); } -static int I2CWaitDataReady(struct hw *hw) +static int hw20k2_i2c_wait_data_ready(struct hw *hw) { int i = 0x400000; unsigned int ret; @@ -1490,51 +1497,53 @@ static int I2CWaitDataReady(struct hw *hw) return i; } -static int I2CRead(struct hw *hw, u16 wAddress, u32 *pdwData) +static int hw20k2_i2c_read(struct hw *hw, u16 addr, u32 *datap) { - unsigned int RegI2CStatus; + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + unsigned int i2c_status; - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_BC, - (4 == m_bAddressSize) ? 0 : m_bAddressSize); - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); - if (!I2CWaitDataReady(hw)) + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); + set_field(&i2c_status, I2C_STATUS_BC, + (4 == hw20k2->addr_size) ? 0 : hw20k2->addr_size); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); + if (!hw20k2_i2c_wait_data_ready(hw)) return -1; - hw_write_20kx(hw, I2C_IF_WDATA, (u32)wAddress); - if (!I2CWaitDataReady(hw)) + hw_write_20kx(hw, I2C_IF_WDATA, addr); + if (!hw20k2_i2c_wait_data_ready(hw)) return -1; /* Force a read operation */ hw_write_20kx(hw, I2C_IF_RDATA, 0); - if (!I2CWaitDataReady(hw)) + if (!hw20k2_i2c_wait_data_ready(hw)) return -1; - *pdwData = hw_read_20kx(hw, I2C_IF_RDATA); + *datap = hw_read_20kx(hw, I2C_IF_RDATA); return 0; } -static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) +static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) { - unsigned int dwI2CData = (dwData << (m_bAddressSize * 8)) | wAddress; - unsigned int RegI2CStatus; + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + unsigned int i2c_data = (data << (hw20k2->addr_size * 8)) | addr; + unsigned int i2c_status; - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_BC, - (4 == (m_bAddressSize + m_bDataSize)) ? - 0 : (m_bAddressSize + m_bDataSize)); + set_field(&i2c_status, I2C_STATUS_BC, + (4 == (hw20k2->addr_size + hw20k2->data_size)) ? + 0 : (hw20k2->addr_size + hw20k2->data_size)); - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); - I2CWaitDataReady(hw); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); + hw20k2_i2c_wait_data_ready(hw); /* Dummy write to trigger the write oprtation */ hw_write_20kx(hw, I2C_IF_WDATA, 0); - I2CWaitDataReady(hw); + hw20k2_i2c_wait_data_ready(hw); /* This is the real data */ - hw_write_20kx(hw, I2C_IF_WDATA, dwI2CData); - I2CWaitDataReady(hw); + hw_write_20kx(hw, I2C_IF_WDATA, i2c_data); + hw20k2_i2c_wait_data_ready(hw); return 0; } @@ -1542,10 +1551,10 @@ static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { int err; - u32 dwData; + u32 data; int i; - struct REGS_CS4382 cs4382_Read = {0}; - struct REGS_CS4382 cs4382_Def = { + struct regs_cs4382 cs_read = {0}; + struct regs_cs4382 cs_def = { 0x00000001, /* Mode Control 1 */ 0x00000000, /* Mode Control 2 */ 0x00000084, /* Mode Control 3 */ @@ -1566,87 +1575,86 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) }; /* Set DAC reset bit as output */ - dwData = hw_read_20kx(hw, GPIO_CTRL); - dwData |= 0x02; - hw_write_20kx(hw, GPIO_CTRL, dwData); + data = hw_read_20kx(hw, GPIO_CTRL); + data |= 0x02; + hw_write_20kx(hw, GPIO_CTRL, data); - err = I2CInit(hw, 0x18, 1, 1); + err = hw20k2_i2c_init(hw, 0x18, 1, 1); if (err < 0) goto End; for (i = 0; i < 2; i++) { /* Reset DAC twice just in-case the chip * didn't initialized properly */ - dwData = hw_read_20kx(hw, GPIO_DATA); + data = hw_read_20kx(hw, GPIO_DATA); /* GPIO data bit 1 */ - dwData &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, dwData); + data &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(10); - dwData |= 0x2; - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= 0x2; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); /* Reset the 2nd time */ - dwData &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, dwData); + data &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(10); - dwData |= 0x2; - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= 0x2; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); - if (I2CRead(hw, CS4382_MC1, &cs4382_Read.dwModeControl_1)) + if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1)) continue; - if (I2CRead(hw, CS4382_MC2, &cs4382_Read.dwModeControl_2)) + if (hw20k2_i2c_read(hw, CS4382_MC2, &cs_read.mode_control_2)) continue; - if (I2CRead(hw, CS4382_MC3, &cs4382_Read.dwModeControl_3)) + if (hw20k2_i2c_read(hw, CS4382_MC3, &cs_read.mode_control_3)) continue; - if (I2CRead(hw, CS4382_FC, &cs4382_Read.dwFilterControl)) + if (hw20k2_i2c_read(hw, CS4382_FC, &cs_read.filter_control)) continue; - if (I2CRead(hw, CS4382_IC, &cs4382_Read.dwInvertControl)) + if (hw20k2_i2c_read(hw, CS4382_IC, &cs_read.invert_control)) continue; - if (I2CRead(hw, CS4382_XC1, &cs4382_Read.dwMixControl_P1)) + if (hw20k2_i2c_read(hw, CS4382_XC1, &cs_read.mix_control_P1)) continue; - if (I2CRead(hw, CS4382_VCA1, &cs4382_Read.dwVolControl_A1)) + if (hw20k2_i2c_read(hw, CS4382_VCA1, &cs_read.vol_control_A1)) continue; - if (I2CRead(hw, CS4382_VCB1, &cs4382_Read.dwVolControl_B1)) + if (hw20k2_i2c_read(hw, CS4382_VCB1, &cs_read.vol_control_B1)) continue; - if (I2CRead(hw, CS4382_XC2, &cs4382_Read.dwMixControl_P2)) + if (hw20k2_i2c_read(hw, CS4382_XC2, &cs_read.mix_control_P2)) continue; - if (I2CRead(hw, CS4382_VCA2, &cs4382_Read.dwVolControl_A2)) + if (hw20k2_i2c_read(hw, CS4382_VCA2, &cs_read.vol_control_A2)) continue; - if (I2CRead(hw, CS4382_VCB2, &cs4382_Read.dwVolControl_B2)) + if (hw20k2_i2c_read(hw, CS4382_VCB2, &cs_read.vol_control_B2)) continue; - if (I2CRead(hw, CS4382_XC3, &cs4382_Read.dwMixControl_P3)) + if (hw20k2_i2c_read(hw, CS4382_XC3, &cs_read.mix_control_P3)) continue; - if (I2CRead(hw, CS4382_VCA3, &cs4382_Read.dwVolControl_A3)) + if (hw20k2_i2c_read(hw, CS4382_VCA3, &cs_read.vol_control_A3)) continue; - if (I2CRead(hw, CS4382_VCB3, &cs4382_Read.dwVolControl_B3)) + if (hw20k2_i2c_read(hw, CS4382_VCB3, &cs_read.vol_control_B3)) continue; - if (I2CRead(hw, CS4382_XC4, &cs4382_Read.dwMixControl_P4)) + if (hw20k2_i2c_read(hw, CS4382_XC4, &cs_read.mix_control_P4)) continue; - if (I2CRead(hw, CS4382_VCA4, &cs4382_Read.dwVolControl_A4)) + if (hw20k2_i2c_read(hw, CS4382_VCA4, &cs_read.vol_control_A4)) continue; - if (I2CRead(hw, CS4382_VCB4, &cs4382_Read.dwVolControl_B4)) + if (hw20k2_i2c_read(hw, CS4382_VCB4, &cs_read.vol_control_B4)) continue; - if (memcmp(&cs4382_Read, &cs4382_Def, - sizeof(struct REGS_CS4382))) + if (memcmp(&cs_read, &cs_def, sizeof(cs_read))) continue; else break; @@ -1657,29 +1665,29 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) /* Note: Every I2C write must have some delay. * This is not a requirement but the delay works here... */ - I2CWrite(hw, CS4382_MC1, 0x80); - I2CWrite(hw, CS4382_MC2, 0x10); + hw20k2_i2c_write(hw, CS4382_MC1, 0x80); + hw20k2_i2c_write(hw, CS4382_MC2, 0x10); if (1 == info->msr) { - I2CWrite(hw, CS4382_XC1, 0x24); - I2CWrite(hw, CS4382_XC2, 0x24); - I2CWrite(hw, CS4382_XC3, 0x24); - I2CWrite(hw, CS4382_XC4, 0x24); + hw20k2_i2c_write(hw, CS4382_XC1, 0x24); + hw20k2_i2c_write(hw, CS4382_XC2, 0x24); + hw20k2_i2c_write(hw, CS4382_XC3, 0x24); + hw20k2_i2c_write(hw, CS4382_XC4, 0x24); } else if (2 == info->msr) { - I2CWrite(hw, CS4382_XC1, 0x25); - I2CWrite(hw, CS4382_XC2, 0x25); - I2CWrite(hw, CS4382_XC3, 0x25); - I2CWrite(hw, CS4382_XC4, 0x25); + hw20k2_i2c_write(hw, CS4382_XC1, 0x25); + hw20k2_i2c_write(hw, CS4382_XC2, 0x25); + hw20k2_i2c_write(hw, CS4382_XC3, 0x25); + hw20k2_i2c_write(hw, CS4382_XC4, 0x25); } else { - I2CWrite(hw, CS4382_XC1, 0x26); - I2CWrite(hw, CS4382_XC2, 0x26); - I2CWrite(hw, CS4382_XC3, 0x26); - I2CWrite(hw, CS4382_XC4, 0x26); + hw20k2_i2c_write(hw, CS4382_XC1, 0x26); + hw20k2_i2c_write(hw, CS4382_XC2, 0x26); + hw20k2_i2c_write(hw, CS4382_XC3, 0x26); + hw20k2_i2c_write(hw, CS4382_XC4, 0x26); } return 0; End: - I2CUninit(hw); + hw20k2_i2c_uninit(hw); return -1; } @@ -1721,21 +1729,21 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) case ADC_MICIN: data |= (0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), MAKE_WM8775_DATA(0x101)); /* Mic-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ break; case ADC_LINEIN: data &= ~(0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), MAKE_WM8775_DATA(0x102)); /* Line-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ break; default: @@ -1748,34 +1756,34 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { int err; - u32 dwMux = 2, dwData, dwCtl; + u32 mux = 2, data, ctl; /* Set ADC reset bit as output */ - dwData = hw_read_20kx(hw, GPIO_CTRL); - dwData |= (0x1 << 15); - hw_write_20kx(hw, GPIO_CTRL, dwData); + data = hw_read_20kx(hw, GPIO_CTRL); + data |= (0x1 << 15); + hw_write_20kx(hw, GPIO_CTRL, data); /* Initialize I2C */ - err = I2CInit(hw, 0x1A, 1, 1); + err = hw20k2_i2c_init(hw, 0x1A, 1, 1); if (err < 0) { printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n"); goto error; } /* Make ADC in normal operation */ - dwData = hw_read_20kx(hw, GPIO_DATA); - dwData &= ~(0x1 << 15); + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~(0x1 << 15); mdelay(10); - dwData |= (0x1 << 15); - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= (0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); /* Set the master mode (256fs) */ if (1 == info->msr) { - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), MAKE_WM8775_DATA(0x02)); } else if (2 == info->msr) { - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { printk(KERN_ALERT "ctxfi: Invalid master sampling " @@ -1785,35 +1793,35 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) } /* Configure GPIO bit 14 change to line-in/mic-in */ - dwCtl = hw_read_20kx(hw, GPIO_CTRL); - dwCtl |= 0x1<<14; - hw_write_20kx(hw, GPIO_CTRL, dwCtl); + ctl = hw_read_20kx(hw, GPIO_CTRL); + ctl |= 0x1 << 14; + hw_write_20kx(hw, GPIO_CTRL, ctl); /* Check using Mic-in or Line-in */ - dwData = hw_read_20kx(hw, GPIO_DATA); + data = hw_read_20kx(hw, GPIO_DATA); - if (dwMux == 1) { + if (mux == 1) { /* Configures GPIO data to select Mic-in */ - dwData |= 0x1<<14; - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= 0x1 << 14; + hw_write_20kx(hw, GPIO_DATA, data); - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), MAKE_WM8775_DATA(0x101)); /* Mic-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - } else if (dwMux == 2) { + } else if (mux == 2) { /* Configures GPIO data to select Line-in */ - dwData &= ~(0x1<<14); - hw_write_20kx(hw, GPIO_DATA, dwData); + data &= ~(0x1 << 14); + hw_write_20kx(hw, GPIO_DATA, data); /* Setup ADC */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), MAKE_WM8775_DATA(0x102)); /* Line-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ } else { printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); @@ -1824,7 +1832,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) return 0; error: - I2CUninit(hw); + hw20k2_i2c_uninit(hw); return err; } @@ -2106,15 +2114,15 @@ static struct hw ct20k2_preset __devinitdata = { int __devinit create_20k2_hw_obj(struct hw **rhw) { - struct hw *hw; + struct hw20k2 *hw20k2; *rhw = NULL; - hw = kzalloc(sizeof(*hw), GFP_KERNEL); - if (NULL == hw) + hw20k2 = kzalloc(sizeof(*hw20k2), GFP_KERNEL); + if (!hw20k2) return -ENOMEM; - *hw = ct20k2_preset; - *rhw = hw; + hw20k2->hw = ct20k2_preset; + *rhw = &hw20k2->hw; return 0; } -- cgit v1.2.3 From 9470195a9cd13e6d90221b8b5d897e9232da8d28 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 18:10:32 +0200 Subject: ALSA: ctxfi - Clean up probe routines Clean up probe routines and model detection routines so that the driver won't call and check the PCI subsystem id at each time. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 132 +++++++++++++++++++------------------------ sound/pci/ctxfi/ctatc.h | 17 ++---- sound/pci/ctxfi/ctdaio.c | 6 +- sound/pci/ctxfi/cthardware.c | 31 +++------- sound/pci/ctxfi/cthardware.h | 20 ++++++- sound/pci/ctxfi/cthw20k1.c | 88 +++++++++++------------------ sound/pci/ctxfi/xfi.c | 15 +++-- 7 files changed, 135 insertions(+), 174 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 7898a375df0..002a70e0b13 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -39,29 +39,40 @@ | (0x10 << 16) \ | ((IEC958_AES3_CON_FS_48000) << 24)) -static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { - [CTSB0760] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB0760, - .nm_model = "SB076x"}, - [CTHENDRIX] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, - .nm_model = "Hendrix"}, - [CTSB08801] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08801, - .nm_model = "SB0880"}, - [CTSB08802] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08802, - .nm_model = "SB0880"}, - [CTSB08803] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08803, - .nm_model = "SB0880"} +static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTUAA), + SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE, + "Unknown", CT20K1_UNKNOWN), + { } /* terminator */ }; -static struct ct_atc_chip_details atc_chip_details[] = { - {.vendor = PCI_VENDOR_ID_CREATIVE, - .device = PCI_DEVICE_ID_CREATIVE_20K1, - .sub_details = NULL, - .nm_card = "X-Fi 20k1"}, - {.vendor = PCI_VENDOR_ID_CREATIVE, - .device = PCI_DEVICE_ID_CREATIVE_20K2, - .sub_details = atc_sub_details, - .nm_card = "X-Fi 20k2"}, - {} /* terminator */ +static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, + "SB0760", CTSB0760), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, + "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, + "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, + "SB0880", CTSB0880), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTHENDRIX), + { } /* terminator */ +}; + +static const char *ct_subsys_name[NUM_CTCARDS] = { + [CTSB055X] = "SB055x", + [CTSB073X] = "SB073x", + [CTSB0760] = "SB076x", + [CTUAA] = "UAA", + [CT20K1_UNKNOWN] = "Unknown", + [CTHENDRIX] = "Hendrix", + [CTSB0880] = "SB0880", }; static struct { @@ -1208,62 +1219,39 @@ static int atc_dev_free(struct snd_device *dev) static int __devinit atc_identify_card(struct ct_atc *atc) { - u16 subsys; - u8 revision; - struct pci_dev *pci = atc->pci; - const struct ct_atc_chip_details *d; - enum CTCARDS i; - - subsys = pci->subsystem_device; - revision = pci->revision; - atc->chip_details = NULL; - atc->model = NUM_CTCARDS; - for (d = atc_chip_details; d->vendor; d++) { - if (d->vendor != pci->vendor || d->device != pci->device) - continue; + const struct snd_pci_quirk *p; + const struct snd_pci_quirk *list; - if (NULL == d->sub_details) { - atc->chip_details = d; - break; - } - for (i = 0; i < NUM_CTCARDS; i++) { - if ((d->sub_details[i].subsys == subsys) || - (((subsys & 0x6000) == 0x6000) && - ((d->sub_details[i].subsys & 0x6000) == 0x6000))) { - atc->model = i; - break; - } - } - if (i >= NUM_CTCARDS) - continue; - - atc->chip_details = d; + switch (atc->chip_type) { + case ATC20K1: + atc->chip_name = "20K1"; + list = subsys_20k1_list; + break; + case ATC20K2: + atc->chip_name = "20K2"; + list = subsys_20k2_list; break; - /* not take revision into consideration now */ + default: + return -ENOENT; } - if (!d->vendor) + p = snd_pci_quirk_lookup(atc->pci, list); + if (!p) return -ENOENT; - + atc->model = p->value; + atc->model_name = ct_subsys_name[atc->model]; + snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", + atc->chip_name, atc->model_name, + atc->pci->subsystem_vendor, + atc->pci->subsystem_device); return 0; } int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) { enum CTALSADEVS i; - struct hw *hw = atc->hw; int err; - switch (hw->get_chip_type(hw)) { - case ATC20K1: - alsa_dev_funcs[MIXER].public_name = "20K1"; - break; - case ATC20K2: - alsa_dev_funcs[MIXER].public_name = "20K2"; - break; - default: - alsa_dev_funcs[MIXER].public_name = "Unknown"; - break; - } + alsa_dev_funcs[MIXER].public_name = atc->chip_name; for (i = 0; i < NUM_CTALSADEVS; i++) { if (NULL == alsa_dev_funcs[i].create) @@ -1287,7 +1275,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) struct card_conf info = {0}; int i, err; - err = create_hw_obj(atc->pci, &hw); + err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw); if (err) { printk(KERN_ERR "Failed to create hw obj!!!\n"); return err; @@ -1328,7 +1316,6 @@ static int __devinit atc_get_resources(struct ct_atc *atc) struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; int err, i; - unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); if (NULL == atc->daios) @@ -1359,13 +1346,10 @@ static int __devinit atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - subsys_id = atc->pci->subsystem_device; - if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + if (atc->model == CTSB073X) da_desc.type = SPDIFI1; - } else { + else da_desc.type = SPDIFIO; - } err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { @@ -1555,7 +1539,8 @@ static struct ct_atc atc_preset __devinitdata = { */ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, - unsigned int rsr, unsigned int msr, struct ct_atc **ratc) + unsigned int rsr, unsigned int msr, + int chip_type, struct ct_atc **ratc) { struct ct_atc *atc; static struct snd_device_ops ops = { @@ -1576,6 +1561,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, atc->pci = pci; atc->rsr = rsr; atc->msr = msr; + atc->chip_type = chip_type; spin_lock_init(&atc->atc_lock); diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 04459aa0d4d..a03347232e8 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -37,15 +37,6 @@ enum CTALSADEVS { /* Types of alsa devices */ NUM_CTALSADEVS /* This should always be the last */ }; -enum CTCARDS { - CTSB0760, - CTHENDRIX, - CTSB08801, - CTSB08802, - CTSB08803, - NUM_CTCARDS /* This should always be the last */ -}; - struct ct_atc_chip_sub_details { u16 subsys; const char *nm_model; @@ -89,8 +80,10 @@ struct ct_atc { unsigned int msr; /* master sample rate in rsr */ unsigned int pll_rate; /* current rate of Phase Lock Loop */ - const struct ct_atc_chip_details *chip_details; - enum CTCARDS model; + 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); @@ -147,7 +140,7 @@ struct ct_atc { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, - unsigned int rsr, unsigned int msr, + unsigned int rsr, unsigned int msr, int chip_type, struct ct_atc **ratc); int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index befead4eeaa..082e35c08c0 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -116,7 +116,7 @@ static struct rsc_ops daio_in_rsc_ops_20k2 = { static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) { - switch (hw->get_chip_type(hw)) { + switch (hw->chip_type) { case ATC20K1: switch (type) { case SPDIFOO: return 0; @@ -343,7 +343,7 @@ static int daio_rsc_init(struct daio *daio, int err; unsigned int idx_l, idx_r; - switch (((struct hw *)hw)->get_chip_type(hw)) { + switch (((struct hw *)hw)->chip_type) { case ATC20K1: idx_l = idx_20k1[desc->type].left; idx_r = idx_20k1[desc->type].right; @@ -367,7 +367,7 @@ static int daio_rsc_init(struct daio *daio, if (desc->type <= DAIO_OUT_MAX) { daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; } else { - switch (((struct hw *)hw)->get_chip_type(hw)) { + switch (((struct hw *)hw)->chip_type) { case ATC20K1: daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1; break; diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 5ec6813d391..8e64f4862e8 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -20,34 +20,16 @@ #include "cthw20k2.h" #include -static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) -{ - enum CHIPTYP type; - - switch (hw->pci->device) { - case 0x0005: /* 20k1 device */ - type = ATC20K1; - break; - case 0x000B: /* 20k2 device */ - type = ATC20K2; - break; - default: - type = ATCNONE; - break; - } - - return type; -} - -int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) +int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, + enum CTCARDS model, struct hw **rhw) { int err; - switch (pci->device) { - case 0x0005: /* 20k1 device */ + switch (chip_type) { + case ATC20K1: err = create_20k1_hw_obj(rhw); break; - case 0x000B: /* 20k2 device */ + case ATC20K2: err = create_20k2_hw_obj(rhw); break; default: @@ -58,7 +40,8 @@ int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) return err; (*rhw)->pci = pci; - (*rhw)->get_chip_type = get_chip_type; + (*rhw)->chip_type = chip_type; + (*rhw)->model = model; return 0; } diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 8f11644ddc9..4a8e04f090a 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -27,6 +27,19 @@ enum CHIPTYP { 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, @@ -48,7 +61,6 @@ 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); - enum CHIPTYP (*get_chip_type)(struct hw *hw); 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); @@ -156,9 +168,13 @@ struct hw { 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, struct hw **rhw); +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); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 38b87b6ee6d..5d58650beb7 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1432,11 +1432,9 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { u32 data; u16 gpioorg; - u16 subsys_id; unsigned int ret; - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + if (hw->model == CTSB055X) { /* SB055x, unmute outputs */ gpioorg = (u16)hw_read_20kx(hw, GPIO); gpioorg &= 0xffbf; /* set GPIO6 to low */ @@ -1538,19 +1536,14 @@ static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ + switch (hw->model) { + case CTSB055X: return is_adc_input_selected_SB055x(hw, type); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + case CTSB073X: return is_adc_input_selected_hendrix(hw, type); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ + case CTHENDRIX: return is_adc_input_selected_hendrix(hw, type); - } else { + default: return is_adc_input_selected_SBx(hw, type); } } @@ -1692,20 +1685,17 @@ adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ - return adc_input_select_SB055x(hw, type, (ADC_MICIN == type)); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ - return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ - return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); - } else { - return adc_input_select_SBx(hw, type, (ADC_MICIN == type)); + int state = type == ADC_MICIN; + + switch (hw->model) { + case CTSB055X: + return adc_input_select_SB055x(hw, type, state); + case CTSB073X: + return adc_input_select_hendrix(hw, type, state); + case CTHENDRIX: + return adc_input_select_hendrix(hw, type, state); + default: + return adc_input_select_SBx(hw, type, state); } } @@ -1781,28 +1771,16 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err; - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* Sb055x card */ - err = adc_init_SB055x(hw, info->input, info->mic20db); - } else { - err = adc_init_SBx(hw, info->input, info->mic20db); - } - - return err; + if (hw->model == CTSB055X) + return adc_init_SB055x(hw, info->input, info->mic20db); + else + return adc_init_SBx(hw, info->input, info->mic20db); } static int hw_have_digit_io_switch(struct hw *hw) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* SB073x and Vista compatible cards have no digit IO switch */ - return !((subsys_id == 0x0029) || (subsys_id == 0x0031) - || ((subsys_id & 0xf000) == 0x6000)); + return !(hw->model == CTSB073X || hw->model == CTHENDRIX); } #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -1918,7 +1896,6 @@ static int hw_card_start(struct hw *hw) { int err; struct pci_dev *pci = hw->pci; - u16 subsys_id; err = pci_enable_device(pci); if (err < 0) @@ -1939,8 +1916,7 @@ static int hw_card_start(struct hw *hw) goto error1; /* Switch to X-Fi mode from UAA mode if neeeded */ - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) { + if (hw->model == CTHENDRIX) { err = uaa_to_xfi(pci); if (err) goto error2; @@ -2004,7 +1980,6 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) { int err; unsigned int gctl; - u16 subsys_id; u32 data; struct dac_conf dac_info = {0}; struct adc_conf adc_info = {0}; @@ -2044,19 +2019,20 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) hw_write_20kx(hw, SRCIP, 0); mdelay(30); - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* Detect the card ID and configure GPIO accordingly. */ - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ + switch (hw->model) { + case CTSB055X: hw_write_20kx(hw, GPIOCTL, 0x13fe); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + break; + case CTSB073X: hw_write_20kx(hw, GPIOCTL, 0x00e6); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ + break; + case CTHENDRIX: /* Vista compatible cards */ hw_write_20kx(hw, GPIOCTL, 0x00c2); - } else { + break; + default: hw_write_20kx(hw, GPIOCTL, 0x01e6); + break; } trn_info.vm_pgt_phys = info->vm_pgt_phys; diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 279dac6c34d..2d3dd89af15 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -15,6 +15,7 @@ #include #include #include "ctatc.h" +#include "cthardware.h" MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); @@ -41,8 +42,12 @@ 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) }, - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2) }, + { 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); @@ -79,7 +84,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) "1 and 2, Value 2 is assumed.\n"); multiple = 2; } - err = ct_atc_create(card, pci, reference_rate, multiple, &atc); + err = ct_atc_create(card, pci, reference_rate, multiple, + pci_id->driver_data, &atc); if (err < 0) goto error; @@ -92,7 +98,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) strcpy(card->driver, "SB-XFi"); strcpy(card->shortname, "Creative X-Fi"); - strcpy(card->longname, "Creative ALSA Driver 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) -- cgit v1.2.3 From 09521d2e3edd0bf02b66e5b8c13f1559f2d6958a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 18:29:38 +0200 Subject: ALSA: ctxfi - Fix wrong model id for UAA CTUAA should be checked instead of CTHENDRIX. The latter is for 20k2 chip. Also, fixed the detection of UAA/HENDRIX models by fixing the mask bits. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 9 +++++---- sound/pci/ctxfi/cthw20k1.c | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 002a70e0b13..4e25b24848b 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -44,8 +44,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, - PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTUAA), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, 0x6000, + "UAA", CTUAA), SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE, "Unknown", CT20K1_UNKNOWN), { } /* terminator */ @@ -60,8 +60,9 @@ static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { "SB0880", CTSB0880), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, "SB0880", CTSB0880), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, - PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTHENDRIX), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX", + CTHENDRIX), { } /* terminator */ }; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 5d58650beb7..cb69d9ddfbe 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1541,7 +1541,7 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) return is_adc_input_selected_SB055x(hw, type); case CTSB073X: return is_adc_input_selected_hendrix(hw, type); - case CTHENDRIX: + case CTUAA: return is_adc_input_selected_hendrix(hw, type); default: return is_adc_input_selected_SBx(hw, type); @@ -1692,7 +1692,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) return adc_input_select_SB055x(hw, type, state); case CTSB073X: return adc_input_select_hendrix(hw, type, state); - case CTHENDRIX: + case CTUAA: return adc_input_select_hendrix(hw, type, state); default: return adc_input_select_SBx(hw, type, state); @@ -1780,7 +1780,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) static int hw_have_digit_io_switch(struct hw *hw) { /* SB073x and Vista compatible cards have no digit IO switch */ - return !(hw->model == CTSB073X || hw->model == CTHENDRIX); + return !(hw->model == CTSB073X || hw->model == CTUAA); } #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -1916,7 +1916,7 @@ static int hw_card_start(struct hw *hw) goto error1; /* Switch to X-Fi mode from UAA mode if neeeded */ - if (hw->model == CTHENDRIX) { + if (hw->model == CTUAA) { err = uaa_to_xfi(pci); if (err) goto error2; @@ -2027,7 +2027,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) case CTSB073X: hw_write_20kx(hw, GPIOCTL, 0x00e6); break; - case CTHENDRIX: /* Vista compatible cards */ + case CTUAA: hw_write_20kx(hw, GPIOCTL, 0x00c2); break; default: -- cgit v1.2.3 From 04145f2bfbc3ac4dbb71fa085b82380444d50b4f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 21:31:50 +0200 Subject: ALSA: ctxfi - Add use_system_timer module option Added use_system_timer module option to force to use the system timer instead of emu20k1 timer irq for debugging. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cttimer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index ec869a4fe2b..779c6c3591a 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -8,12 +8,17 @@ #include #include +#include #include #include #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 *); @@ -390,8 +395,6 @@ void ct_timer_instance_free(struct ct_timer_instance *ti) * timer manager */ -#define USE_SYSTEM_TIMER 0 - static void ct_timer_interrupt(void *data, unsigned int status) { struct ct_timer *timer = data; @@ -415,7 +418,7 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) INIT_LIST_HEAD(&atimer->running_head); atimer->atc = atc; hw = atc->hw; - if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { + 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; -- cgit v1.2.3 From c399f3be82e1249221a7f410855ab1aa747fa82b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2009 08:16:20 +0200 Subject: ALSA: ctxfi - Add missing start check in atc_pcm_playback_start() Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 4e25b24848b..799eb98e779 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -392,6 +392,10 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) unsigned int max_cisz; struct src *src = apcm->src; + if (apcm->started) + return 0; + apcm->started = 1; + max_cisz = src->multi * src->rsc.msr; max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8); -- cgit v1.2.3 From 5242bc7613311aa1a3d5ed41e9cf81015b65563f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2009 08:17:14 +0200 Subject: ALSA: ctxfi - Check the presence of SRC instance in PCM pointer callbacks The SRC instances may not exist when PCM pointer callback is called at the state before initialization is finished. Add the NULL check just to be sure. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 799eb98e779..e54006ef83c 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -445,6 +445,8 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) u32 size, max_cisz; int position; + if (!src) + return 0; position = src->ops->get_ca(src); size = apcm->vm_block->size; @@ -782,6 +784,8 @@ atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct src *src = apcm->src; + if (!src) + return 0; return src->ops->get_ca(src) - apcm->vm_block->addr; } -- cgit v1.2.3 From a5990dc5b96f537618b0f057c8723a6a0b0cdc74 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2009 08:19:02 +0200 Subject: ALSA: ctxfi - Clear PCM resources at hw_params and hw_free Currently the PCM resources are allocated only once and ever in prepare callback, assuming that the PCM parameters are never changed. But it's not true. This patch adds the call of atc->pcm_release_resources() at hw_params and hw_free callbacks to assure that the PCM setup is done correctly for each h/w parameter changes. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 4 ++-- sound/pci/ctxfi/ctpcm.c | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index e54006ef83c..80fb2baed7a 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -523,7 +523,7 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct src_node_conf_t src_node_conf[2] = {{0} }; /* first release old resources */ - atc->pcm_release_resources(atc, apcm); + atc_pcm_release_resources(atc, apcm); /* The numbers of converting SRCs and SRCIMPs should be determined * by pitch value. */ @@ -802,7 +802,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, unsigned int pitch, rsr = atc->pll_rate; /* first release old resources */ - atc->pcm_release_resources(atc, apcm); + atc_pcm_release_resources(atc, apcm); /* Get SRC resource */ desc.multi = apcm->substream->runtime->channels; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 870fa170f04..9e5c0c4da72 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -176,12 +176,26 @@ static int ct_pcm_playback_close(struct snd_pcm_substream *substream) static int ct_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - return snd_pcm_lib_malloc_pages(substream, + 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); } -- cgit v1.2.3