Commit 8cc72361 authored by Wai Yew CHAY's avatar Wai Yew CHAY Committed by Takashi Iwai

ALSA: SB X-Fi driver merge

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: default avatarWai Yew CHAY <wychay@ctl.creative.com>
Singed-off-by: default avatarRyan RICHARDS <ryan_richards@creativelabs.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 091bf762
......@@ -460,6 +460,25 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-ctxfi
----------------
Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
* Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
* Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
* Creative Sound Blaster X-Fi Titanium Professional Audio
* Creative Sound Blaster X-Fi Titanium
* Creative Sound Blaster X-Fi Elite Pro
* Creative Sound Blaster X-Fi Platinum
* Creative Sound Blaster X-Fi Fatal1ty
* Creative Sound Blaster X-Fi XtremeGamer
* Creative Sound Blaster X-Fi XtremeMusic
reference_rate - reference sample rate, 44100 or 48000 (default)
multiple - multiple to ref. sample rate, 1 or 2 (default)
This module supports multiple cards.
Module snd-darla20
------------------
......
......@@ -275,6 +275,16 @@ config SND_CS5535AUDIO
To compile this driver as a module, choose M here: the module
will be called snd-cs5535audio.
config SND_CTXFI
tristate "Creative Sound Blaster X-Fi"
select SND_PCM
help
If you want to use soundcards based on Creative Sound Blastr X-Fi
boards with 20k1 or 20k2 chips, say Y here.
To compile this driver as a module, choose M here: the module
will be called snd-ctxfi.
config SND_DARLA20
tristate "(Echoaudio) Darla20"
select FW_LOADER
......
......@@ -59,6 +59,7 @@ obj-$(CONFIG_SND) += \
ali5451/ \
au88x0/ \
aw2/ \
ctxfi/ \
ca0106/ \
cs46xx/ \
cs5535audio/ \
......
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
/**
* 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 */
/**
* 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
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctamixer.c
*
* @Brief
* This file contains the implementation of the Audio Mixer
* resource management object.
*
* @Author Liu Chun
* @Date May 21 2008
*
*/
#include "ctamixer.h"
#include "cthardware.h"
#include <linux/slab.h>
#define AMIXER_RESOURCE_NUM 256
#define SUM_RESOURCE_NUM 256
#define AMIXER_Y_IMMEDIATE 1
#define BLANK_SLOT 4094
static int amixer_master(struct rsc *rsc)
{
rsc->conj = 0;
return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
}
static int amixer_next_conj(struct rsc *rsc)
{
rsc->conj++;
return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
}
static int amixer_index(const struct rsc *rsc)
{
return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
}
static int amixer_output_slot(const struct rsc *rsc)
{
return (amixer_index(rsc) << 4) + 0x4;
}
static struct rsc_ops amixer_basic_rsc_ops = {
.master = amixer_master,
.next_conj = amixer_next_conj,
.index = amixer_index,
.output_slot = amixer_output_slot,
};
static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
{
struct hw *hw = 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;
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctamixer.h
*
* @Brief
* This file contains the definition of the Audio Mixer
* resource management object.
*
* @Author Liu Chun
* @Date May 21 2008
*
*/
#ifndef CTAMIXER_H
#define CTAMIXER_H
#include "ctresource.h"
#include <linux/spinlock.h>
/* Define the descriptor of a summation node resource */
struct sum {
struct rsc rsc; /* Basic resource info */
unsigned char idx[8];
};
/* Define sum resource request description info */
struct sum_desc {
unsigned int msr;
};
struct sum_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
spinlock_t mgr_lock;
/* request one sum resource */
int (*get_sum)(struct sum_mgr *mgr,
const struct sum_desc *desc, struct sum **rsum);
/* return one sum resource */
int (*put_sum)(struct sum_mgr *mgr, struct sum *sum);
};
/* Constructor and destructor of daio resource manager */
int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
int sum_mgr_destroy(struct sum_mgr *sum_mgr);
/* Define the descriptor of a amixer resource */
struct amixer_rsc_ops;
struct amixer {
struct rsc rsc; /* Basic resource info */
unsigned char idx[8];
struct rsc *input; /* pointer to a resource acting as source */
struct sum *sum; /* Put amixer output to this summation node */
struct amixer_rsc_ops *ops; /* AMixer specific operations */
};
struct amixer_rsc_ops {
int (*set_input)(struct amixer *amixer, struct rsc *rsc);
int (*set_scale)(struct amixer *amixer, unsigned int scale);
int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv);
int (*set_sum)(struct amixer *amixer, struct sum *sum);
int (*commit_write)(struct amixer *amixer);
/* Only for interleaved recording */
int (*commit_raw_write)(struct amixer *amixer);
int (*setup)(struct amixer *amixer, struct rsc *input,
unsigned int scale, struct sum *sum);
int (*get_scale)(struct amixer *amixer);
};
/* Define amixer resource request description info */
struct amixer_desc {
unsigned int msr;
};
struct amixer_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
spinlock_t mgr_lock;
/* request one amixer resource */
int (*get_amixer)(struct amixer_mgr *mgr,
const struct amixer_desc *desc,
struct amixer **ramixer);
/* return one amixer resource */
int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer);
};
/* Constructor and destructor of amixer resource manager */
int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
#endif /* CTAMIXER_H */
/**
* 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 <linux/delay.h>
#include <sound/pcm.h>
#include <sound/control.h>
#include <sound/asoundef.h>
#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;
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctatc.h
*
* @Brief
* This file contains the definition of the device resource management object.
*
* @Author Liu Chun
* @Date Mar 28 2008
*
*/
#ifndef CTATC_H
#define CTATC_H
#include <linux/types.h>
#include <linux/spinlock_types.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include <sound/core.h>
#include "ctvmem.h"
#include "ctresource.h"
enum CTALSADEVS { /* Types of alsa devices */
FRONT,
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 */
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctdaio.c
*
* @Brief
* This file contains the implementation of Digital Audio Input Output
* resource management object.
*
* @Author Liu Chun
* @Date May 23 2008
*
*/
#include "ctdaio.h"
#include "cthardware.h"
#include "ctimap.h"
#include <linux/slab.h>
#include <linux/kernel.h>
#define DAIO_RESOURCE_NUM NUM_DAIOTYP
#define DAIO_OUT_MAX SPDIFOO
union daio_usage {
struct {
unsigned short lineo1:1;
unsigned short lineo2:1;
unsigned short lineo3:1;
unsigned short lineo4:1;
unsigned short spdifoo:1;
unsigned short lineim:1;
unsigned short spdifio:1;
unsigned short spdifi1:1;
} bf;
unsigned short data;
};
struct daio_rsc_idx {
unsigned short left;
unsigned short right;
};
struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x00, .right = 0x01},
[LINEO2] = {.left = 0x18, .right = 0x19},
[LINEO3] = {.left = 0x08, .right = 0x09},
[LINEO4] = {.left = 0x10, .right = 0x11},
[LINEIM] = {.left = 0x1b5, .right = 0x1bd},
[SPDIFOO] = {.left = 0x20, .right = 0x21},
[SPDIFIO] = {.left = 0x15, .right = 0x1d},
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
};
struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x40, .right = 0x41},
[LINEO2] = {.left = 0x70, .right = 0x71},
[LINEO3] = {.left = 0x50, .right = 0x51},
[LINEO4] = {.left = 0x60, .right = 0x61},
[LINEIM] = {.left = 0x45, .right = 0xc5},
[SPDIFOO] = {.left = 0x00, .right = 0x01},
[SPDIFIO] = {.left = 0x05, .right = 0x85},
};
static int daio_master(struct rsc *rsc)
{
/* Actually, this is not the resource index of DAIO.
* For DAO, it is the input mapper index. And, for DAI,
* it is the output time-slot index. */
return rsc->conj = rsc->idx;
}
static int daio_index(const struct rsc *rsc)
{
return rsc->conj;
}
static int daio_out_next_conj(struct rsc *rsc)
{
return rsc->conj += 2;
}
static int daio_in_next_conj_20k1(struct rsc *rsc)
{
return rsc->conj += 0x200;
}
static int daio_in_next_conj_20k2(struct rsc *rsc)
{
return rsc->conj += 0x100;
}
static struct rsc_ops daio_out_rsc_ops = {
.master = daio_master,
.next_conj = daio_out_next_conj,
.index = daio_index,
.output_slot = NULL,
};
static struct rsc_ops daio_in_rsc_ops_20k1 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k1,
.index = NULL,
.output_slot = daio_index,
};
static struct rsc_ops daio_in_rsc_ops_20k2 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k2,
.index = NULL,
.output_slot = daio_index,
};
static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
{
switch (hw->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;
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctdaio.h
*
* @Brief
* This file contains the definition of Digital Audio Input Output
* resource management object.
*
* @Author Liu Chun
* @Date May 23 2008
*
*/
#ifndef CTDAIO_H
#define CTDAIO_H
#include "ctresource.h"
#include "ctimap.h"
#include <linux/spinlock.h>
#include <linux/list.h>
/* Define the descriptor of a daio resource */
enum DAIOTYP {
LINEO1,
LINEO2,
LINEO3,
LINEO4,
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
LINEIM,
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP
};
struct dao_rsc_ops;
struct dai_rsc_ops;
struct daio_mgr;
struct daio {
struct rsc rscl; /* Basic resource info for left TX/RX */
struct rsc rscr; /* Basic resource info for right TX/RX */
enum DAIOTYP type;
};
struct dao {
struct daio daio;
struct dao_rsc_ops *ops; /* DAO specific operations */
struct imapper **imappers;
struct daio_mgr *mgr;
void *hw;
void *ctrl_blk;
};
struct dai {
struct daio daio;
struct dai_rsc_ops *ops; /* DAI specific operations */
void *hw;
void *ctrl_blk;
};
struct dao_desc {
unsigned int msr:4;
unsigned int passthru:1;
};
struct dao_rsc_ops {
int (*set_spos)(struct dao *dao, unsigned int spos);
int (*commit_write)(struct dao *dao);
int (*get_spos)(struct dao *dao, unsigned int *spos);
int (*reinit)(struct dao *dao, const struct dao_desc *desc);
int (*set_left_input)(struct dao *dao, struct rsc *input);
int (*set_right_input)(struct dao *dao, struct rsc *input);
int (*clear_left_input)(struct dao *dao);
int (*clear_right_input)(struct dao *dao);
};
struct dai_rsc_ops {
int (*set_srt_srcl)(struct dai *dai, struct rsc *src);
int (*set_srt_srcr)(struct dai *dai, struct rsc *src);
int (*set_srt_msr)(struct dai *dai, unsigned int msr);
int (*set_enb_src)(struct dai *dai, unsigned int enb);
int (*set_enb_srt)(struct dai *dai, unsigned int enb);
int (*commit_write)(struct dai *dai);
};
/* Define daio resource request description info */
struct daio_desc {
unsigned int type:4;
unsigned int msr:4;
unsigned int passthru:1;
};
struct daio_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
spinlock_t mgr_lock;
spinlock_t imap_lock;
struct list_head imappers;
struct imapper *init_imap;
unsigned int init_imap_added;
/* request one daio resource */
int (*get_daio)(struct daio_mgr *mgr,
const struct daio_desc *desc, struct daio **rdaio);
/* return one daio resource */
int (*put_daio)(struct daio_mgr *mgr, struct daio *daio);
int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio);
int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio);
int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry);
int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry);
int (*commit_write)(struct daio_mgr *mgr);
};
/* Constructor and destructor of daio resource manager */
int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
int daio_mgr_destroy(struct daio_mgr *daio_mgr);
#endif /* CTDAIO_H */
/**
* 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 */
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File cthardware.c
*
* @Brief
* This file contains the implementation of hardware access methord.
*
* @Author Liu Chun
* @Date Jun 26 2008
*
*/
#include "cthardware.h"
#include "cthw20k1.h"
#include "cthw20k2.h"
#include <linux/bug.h>
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);
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File cthardware.h
*
* @Brief
* This file contains the definition of hardware access methord.
*
* @Author Liu Chun
* @Date May 13 2008
*
*/
#ifndef CTHARDWARE_H
#define CTHARDWARE_H
#include <linux/types.h>
#include <linux/pci.h>
enum CHIPTYP {
ATC20K1,
ATC20K2,
ATCNONE
};
/* 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 */
/**
* 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 <linux/types.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#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;
}
/**
* 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 */
/**
* 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 <linux/types.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#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;
}
/**
* 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 */
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctimap.c
*
* @Brief
* This file contains the implementation of generic input mapper operations
* for input mapper management.
*
* @Author Liu Chun
* @Date May 23 2008
*
*/
#include "ctimap.h"
#include <linux/slab.h>
int input_mapper_add(struct list_head *mappers, struct imapper *entry,
int (*map_op)(void *, struct imapper *), void *data)
{
struct list_head *pos, *pre, *head;
struct imapper *pre_ent, *pos_ent;
head = mappers;
if (list_empty(head)) {
entry->next = entry->addr;
map_op(data, entry);
list_add(&entry->list, head);
return 0;
}
list_for_each(pos, head) {
pos_ent = list_entry(pos, struct imapper, list);
if (pos_ent->slot > entry->slot) {
/* found a position in list */
break;
}
}
if (pos != head) {
pre = pos->prev;
if (pre == head)
pre = head->prev;
__list_add(&entry->list, pos->prev, pos);
} else {
pre = head->prev;
pos = head->next;
list_add_tail(&entry->list, head);
}
pre_ent = list_entry(pre, struct imapper, list);
pos_ent = list_entry(pos, struct imapper, list);
entry->next = pos_ent->addr;
map_op(data, entry);
pre_ent->next = entry->addr;
map_op(data, pre_ent);
return 0;
}
int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
int (*map_op)(void *, struct imapper *), void *data)
{
struct list_head *next, *pre, *head;
struct imapper *pre_ent, *next_ent;
head = mappers;
if (list_empty(head))
return 0;
pre = (entry->list.prev == head) ? head->prev : entry->list.prev;
next = (entry->list.next == head) ? head->next : entry->list.next;
if (pre == &entry->list) {
/* entry is the only one node in mappers list */
entry->next = entry->addr = entry->user = entry->slot = 0;
map_op(data, entry);
list_del(&entry->list);
return 0;
}
pre_ent = list_entry(pre, struct imapper, list);
next_ent = list_entry(next, struct imapper, list);
pre_ent->next = next_ent->addr;
map_op(data, pre_ent);
list_del(&entry->list);
return 0;
}
void free_input_mapper_list(struct list_head *head)
{
struct imapper *entry = 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);
}
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctimap.h
*
* @Brief
* This file contains the definition of generic input mapper operations
* for input mapper management.
*
* @Author Liu Chun
* @Date May 23 2008
*
*/
#ifndef CTIMAP_H
#define CTIMAP_H
#include <linux/list.h>
struct imapper {
unsigned short slot; /* the id of the slot containing input data */
unsigned short user; /* the id of the user resource consuming data */
unsigned short addr; /* the input mapper ram id */
unsigned short next; /* the next input mapper ram id */
struct list_head list;
};
int input_mapper_add(struct list_head *mappers, struct imapper *entry,
int (*map_op)(void *, struct imapper *), void *data);
int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
int (*map_op)(void *, struct imapper *), void *data);
void free_input_mapper_list(struct list_head *mappers);
#endif /* CTIMAP_H */
/**
* 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 <sound/core.h>
#include <sound/control.h>
#include <sound/asoundef.h>
#include <sound/pcm.h>
#include <linux/slab.h>
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;
}
/**
* 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 */
/**
* 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 <sound/pcm.h>
/* Hardware descriptions for playback */
static struct snd_pcm_hardware ct_pcm_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_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;
}
/**
* 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 */
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctresource.c
*
* @Brief
* This file contains the implementation of some generic helper functions.
*
* @Author Liu Chun
* @Date May 15 2008
*
*/
#include "ctresource.h"
#include "cthardware.h"
#include <linux/err.h>
#include <linux/slab.h>
#define AUDIO_SLOT_BLOCK_NUM 256
/* Resource allocation based on bit-map management mechanism */
static int
get_resource(u8 *rscs, unsigned int amount,
unsigned int multi, unsigned int *ridx)
{
int i = 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;
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctresource.h
*
* @Brief
* This file contains the definition of generic hardware resources for
* resource management.
*
* @Author Liu Chun
* @Date May 13 2008
*
*/
#ifndef CTRESOURCE_H
#define CTRESOURCE_H
#include <linux/types.h>
enum RSCTYP {
SRC,
SRCIMP,
AMIXER,
SUM,
DAIO,
NUM_RSCTYP /* This must be the last one and less than 16 */
};
struct rsc_ops;
struct rsc {
u32 idx:12; /* The index of a resource */
u32 type:4; /* The type (RSCTYP) of a resource */
u32 conj:12; /* Current conjugate index */
u32 msr:4; /* The Master Sample Rate a resource working on */
void *ctrl_blk; /* Chip specific control info block for a resource */
void *hw; /* Chip specific object for hardware access means */
struct rsc_ops *ops; /* Generic resource operations */
};
struct rsc_ops {
int (*master)(struct rsc *rsc); /* Move to master resource */
int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
int (*index)(const struct rsc *rsc); /* Return the index of resource */
/* Return the output slot number */
int (*output_slot)(const struct rsc *rsc);
};
int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
int rsc_uninit(struct rsc *rsc);
struct rsc_mgr {
enum RSCTYP type; /* The type (RSCTYP) of resource to manage */
unsigned int amount; /* The total amount of a kind of resource */
unsigned int avail; /* The amount of currently available resources */
unsigned char *rscs; /* The bit-map for resource allocation */
void *ctrl_blk; /* Chip specific control info block */
void *hw; /* Chip specific object for hardware access */
};
/* Resource management is based on bit-map mechanism */
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
unsigned int amount, void *hw);
int rsc_mgr_uninit(struct rsc_mgr *mgr);
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
#endif /* CTRESOURCE_H */
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctsrc.c
*
* @Brief
* This file contains the implementation of the Sample Rate Convertor
* resource management object.
*
* @Author Liu Chun
* @Date May 13 2008
*
*/
#include "ctsrc.h"
#include "cthardware.h"
#include <linux/slab.h>
#define SRC_RESOURCE_NUM 64
#define SRCIMP_RESOURCE_NUM 256
static unsigned int conj_mask;
static int src_default_config_memrd(struct src *src);
static int src_default_config_memwr(struct src *src);
static int src_default_config_arcrw(struct src *src);
static int (*src_default_config[3])(struct src *) = {
[MEMRD] = src_default_config_memrd,
[MEMWR] = src_default_config_memwr,
[ARCRW] = src_default_config_arcrw
};
static int src_set_state(struct src *src, unsigned int state)
{
struct hw *hw = 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;
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctsrc.h
*
* @Brief
* This file contains the definition of the Sample Rate Convertor
* resource management object.
*
* @Author Liu Chun
* @Date May 13 2008
*
*/
#ifndef CTSRC_H
#define CTSRC_H
#include "ctresource.h"
#include "ctimap.h"
#include <linux/spinlock.h>
#include <linux/list.h>
#define SRC_STATE_OFF 0x0
#define SRC_STATE_INIT 0x4
#define SRC_STATE_RUN 0x5
#define SRC_SF_U8 0x0
#define SRC_SF_S16 0x1
#define SRC_SF_S24 0x2
#define SRC_SF_S32 0x3
#define SRC_SF_F32 0x4
/* Define the descriptor of a src resource */
enum SRCMODE {
MEMRD, /* Read data from host memory */
MEMWR, /* Write data to host memory */
ARCRW, /* Read from and write to audio ring channel */
NUM_SRCMODES
};
struct src_rsc_ops;
struct src {
struct rsc rsc; /* Basic resource info */
struct src *intlv; /* Pointer to next interleaved SRC in a series */
struct src_rsc_ops *ops; /* SRC specific operations */
/* Number of contiguous srcs for interleaved usage */
unsigned char multi;
unsigned char mode; /* Working mode of this SRC resource */
};
struct src_rsc_ops {
int (*set_state)(struct src *src, unsigned int state);
int (*set_bm)(struct src *src, unsigned int bm);
int (*set_sf)(struct src *src, unsigned int sf);
int (*set_pm)(struct src *src, unsigned int pm);
int (*set_rom)(struct src *src, unsigned int rom);
int (*set_vo)(struct src *src, unsigned int vo);
int (*set_st)(struct src *src, unsigned int st);
int (*set_bp)(struct src *src, unsigned int bp);
int (*set_cisz)(struct src *src, unsigned int cisz);
int (*set_ca)(struct src *src, unsigned int ca);
int (*set_sa)(struct src *src, unsigned int sa);
int (*set_la)(struct src *src, unsigned int la);
int (*set_pitch)(struct src *src, unsigned int pitch);
int (*set_clr_zbufs)(struct src *src);
int (*commit_write)(struct src *src);
int (*get_ca)(struct src *src);
int (*init)(struct src *src);
struct src* (*next_interleave)(struct src *src);
};
/* Define src resource request description info */
struct src_desc {
/* Number of contiguous master srcs for interleaved usage */
unsigned char multi;
unsigned char msr;
unsigned char mode; /* Working mode of the requested srcs */
};
/* Define src manager object */
struct src_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
spinlock_t mgr_lock;
/* request src resource */
int (*get_src)(struct src_mgr *mgr,
const struct src_desc *desc, struct src **rsrc);
/* return src resource */
int (*put_src)(struct src_mgr *mgr, struct src *src);
int (*src_enable_s)(struct src_mgr *mgr, struct src *src);
int (*src_enable)(struct src_mgr *mgr, struct src *src);
int (*src_disable)(struct src_mgr *mgr, struct src *src);
int (*commit_write)(struct src_mgr *mgr);
};
/* Define the descriptor of a SRC Input Mapper resource */
struct srcimp_mgr;
struct srcimp_rsc_ops;
struct srcimp {
struct rsc rsc;
unsigned char idx[8];
struct imapper *imappers;
unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
struct srcimp_mgr *mgr;
struct srcimp_rsc_ops *ops;
};
struct srcimp_rsc_ops {
int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input);
int (*unmap)(struct srcimp *srcimp);
};
/* Define SRCIMP resource request description info */
struct srcimp_desc {
unsigned int msr;
};
struct srcimp_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
spinlock_t mgr_lock;
spinlock_t imap_lock;
struct list_head imappers;
struct imapper *init_imap;
unsigned int init_imap_added;
/* request srcimp resource */
int (*get_srcimp)(struct srcimp_mgr *mgr,
const struct srcimp_desc *desc,
struct srcimp **rsrcimp);
/* return srcimp resource */
int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp);
int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry);
int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry);
};
/* Constructor and destructor of SRC resource manager */
int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
int src_mgr_destroy(struct src_mgr *src_mgr);
/* Constructor and destructor of SRCIMP resource manager */
int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
#endif /* CTSRC_H */
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctvmem.c
*
* @Brief
* This file contains the implementation of virtual memory management object
* for card device.
*
* @Author Liu Chun
* @Date Apr 1 2008
*/
#include "ctvmem.h"
#include <linux/slab.h>
#include <linux/mm.h>
#include <asm/page.h> /* for PAGE_SIZE macro definition */
#include <linux/io.h>
#include <asm/pgtable.h>
#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);
}
/**
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*
* @File ctvmem.h
*
* @Brief
* This file contains the definition of virtual memory management object
* for card device.
*
* @Author Liu Chun
* @Date Mar 28 2008
*/
#ifndef CTVMEM_H
#define CTVMEM_H
#define CT_PTP_NUM 1 /* num of device page table pages */
#include <linux/spinlock.h>
#include <linux/list.h>
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 */
/*
* xfi linux driver.
*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
* This source file is released under GPL v2 license (no other versions).
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
#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)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment