diff options
Diffstat (limited to 'sound')
31 files changed, 12368 insertions, 0 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93422e3a3f0c..3a7640feaf92 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig | |||
@@ -275,6 +275,16 @@ config SND_CS5535AUDIO | |||
275 | To compile this driver as a module, choose M here: the module | 275 | To compile this driver as a module, choose M here: the module |
276 | will be called snd-cs5535audio. | 276 | will be called snd-cs5535audio. |
277 | 277 | ||
278 | config SND_CTXFI | ||
279 | tristate "Creative Sound Blaster X-Fi" | ||
280 | select SND_PCM | ||
281 | help | ||
282 | If you want to use soundcards based on Creative Sound Blastr X-Fi | ||
283 | boards with 20k1 or 20k2 chips, say Y here. | ||
284 | |||
285 | To compile this driver as a module, choose M here: the module | ||
286 | will be called snd-ctxfi. | ||
287 | |||
278 | config SND_DARLA20 | 288 | config SND_DARLA20 |
279 | tristate "(Echoaudio) Darla20" | 289 | tristate "(Echoaudio) Darla20" |
280 | select FW_LOADER | 290 | select FW_LOADER |
diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 65b25d221cd2..6a1281ec01e3 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile | |||
@@ -59,6 +59,7 @@ obj-$(CONFIG_SND) += \ | |||
59 | ali5451/ \ | 59 | ali5451/ \ |
60 | au88x0/ \ | 60 | au88x0/ \ |
61 | aw2/ \ | 61 | aw2/ \ |
62 | ctxfi/ \ | ||
62 | ca0106/ \ | 63 | ca0106/ \ |
63 | cs46xx/ \ | 64 | cs46xx/ \ |
64 | cs5535audio/ \ | 65 | cs5535audio/ \ |
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile new file mode 100644 index 000000000000..29043237f9f8 --- /dev/null +++ b/sound/pci/ctxfi/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ | ||
2 | ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \ | ||
3 | cthw20k2.o cthw20k1.o | ||
4 | |||
5 | obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o | ||
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h new file mode 100644 index 000000000000..c62e6775dab3 --- /dev/null +++ b/sound/pci/ctxfi/ct20k1reg.h | |||
@@ -0,0 +1,634 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | */ | ||
8 | |||
9 | #ifndef CT20K1REG_H | ||
10 | #define CT20k1REG_H | ||
11 | |||
12 | /* 20k1 registers */ | ||
13 | #define DSPXRAM_START 0x000000 | ||
14 | #define DSPXRAM_END 0x013FFC | ||
15 | #define DSPAXRAM_START 0x020000 | ||
16 | #define DSPAXRAM_END 0x023FFC | ||
17 | #define DSPYRAM_START 0x040000 | ||
18 | #define DSPYRAM_END 0x04FFFC | ||
19 | #define DSPAYRAM_START 0x020000 | ||
20 | #define DSPAYRAM_END 0x063FFC | ||
21 | #define DSPMICRO_START 0x080000 | ||
22 | #define DSPMICRO_END 0x0B3FFC | ||
23 | #define DSP0IO_START 0x100000 | ||
24 | #define DSP0IO_END 0x101FFC | ||
25 | #define AUDIORINGIPDSP0_START 0x100000 | ||
26 | #define AUDIORINGIPDSP0_END 0x1003FC | ||
27 | #define AUDIORINGOPDSP0_START 0x100400 | ||
28 | #define AUDIORINGOPDSP0_END 0x1007FC | ||
29 | #define AUDPARARINGIODSP0_START 0x100800 | ||
30 | #define AUDPARARINGIODSP0_END 0x100BFC | ||
31 | #define DSP0LOCALHWREG_START 0x100C00 | ||
32 | #define DSP0LOCALHWREG_END 0x100C3C | ||
33 | #define DSP0XYRAMAGINDEX_START 0x100C40 | ||
34 | #define DSP0XYRAMAGINDEX_END 0x100C5C | ||
35 | #define DSP0XYRAMAGMDFR_START 0x100C60 | ||
36 | #define DSP0XYRAMAGMDFR_END 0x100C7C | ||
37 | #define DSP0INTCONTLVEC_START 0x100C80 | ||
38 | #define DSP0INTCONTLVEC_END 0x100CD8 | ||
39 | #define INTCONTLGLOBALREG_START 0x100D1C | ||
40 | #define INTCONTLGLOBALREG_END 0x100D3C | ||
41 | #define HOSTINTFPORTADDRCONTDSP0 0x100D40 | ||
42 | #define HOSTINTFPORTDATADSP0 0x100D44 | ||
43 | #define TIME0PERENBDSP0 0x100D60 | ||
44 | #define TIME0COUNTERDSP0 0x100D64 | ||
45 | #define TIME1PERENBDSP0 0x100D68 | ||
46 | #define TIME1COUNTERDSP0 0x100D6C | ||
47 | #define TIME2PERENBDSP0 0x100D70 | ||
48 | #define TIME2COUNTERDSP0 0x100D74 | ||
49 | #define TIME3PERENBDSP0 0x100D78 | ||
50 | #define TIME3COUNTERDSP0 0x100D7C | ||
51 | #define XRAMINDOPERREFNOUP_STARTDSP0 0x100D80 | ||
52 | #define XRAMINDOPERREFNOUP_ENDDSP0 0x100D9C | ||
53 | #define XRAMINDOPERREFUP_STARTDSP0 0x100DA0 | ||
54 | #define XRAMINDOPERREFUP_ENDDSP0 0x100DBC | ||
55 | #define YRAMINDOPERREFNOUP_STARTDSP0 0x100DC0 | ||
56 | #define YRAMINDOPERREFNOUP_ENDDSP0 0x100DDC | ||
57 | #define YRAMINDOPERREFUP_STARTDSP0 0x100DE0 | ||
58 | #define YRAMINDOPERREFUP_ENDDSP0 0x100DFC | ||
59 | #define DSP0CONDCODE 0x100E00 | ||
60 | #define DSP0STACKFLAG 0x100E04 | ||
61 | #define DSP0PROGCOUNTSTACKPTREG 0x100E08 | ||
62 | #define DSP0PROGCOUNTSTACKDATAREG 0x100E0C | ||
63 | #define DSP0CURLOOPADDRREG 0x100E10 | ||
64 | #define DSP0CURLOOPCOUNT 0x100E14 | ||
65 | #define DSP0TOPLOOPCOUNTSTACK 0x100E18 | ||
66 | #define DSP0TOPLOOPADDRSTACK 0x100E1C | ||
67 | #define DSP0LOOPSTACKPTR 0x100E20 | ||
68 | #define DSP0STASSTACKDATAREG 0x100E24 | ||
69 | #define DSP0STASSTACKPTR 0x100E28 | ||
70 | #define DSP0PROGCOUNT 0x100E2C | ||
71 | #define GLOBDSPDEBGREG 0x100E30 | ||
72 | #define GLOBDSPBREPTRREG 0x100E30 | ||
73 | #define DSP0XYRAMBASE_START 0x100EA0 | ||
74 | #define DSP0XYRAMBASE_END 0x100EBC | ||
75 | #define DSP0XYRAMLENG_START 0x100EC0 | ||
76 | #define DSP0XYRAMLENG_END 0x100EDC | ||
77 | #define SEMAPHOREREGDSP0 0x100EE0 | ||
78 | #define DSP0INTCONTMASKREG 0x100EE4 | ||
79 | #define DSP0INTCONTPENDREG 0x100EE8 | ||
80 | #define DSP0INTCONTSERVINT 0x100EEC | ||
81 | #define DSPINTCONTEXTINTMODREG 0x100EEC | ||
82 | #define GPIODSP0 0x100EFC | ||
83 | #define DMADSPBASEADDRREG_STARTDSP0 0x100F00 | ||
84 | #define DMADSPBASEADDRREG_ENDDSP0 0x100F1C | ||
85 | #define DMAHOSTBASEADDRREG_STARTDSP0 0x100F20 | ||
86 | #define DMAHOSTBASEADDRREG_ENDDSP0 0x100F3C | ||
87 | #define DMADSPCURADDRREG_STARTDSP0 0x100F40 | ||
88 | #define DMADSPCURADDRREG_ENDDSP0 0x100F5C | ||
89 | #define DMAHOSTCURADDRREG_STARTDSP0 0x100F60 | ||
90 | #define DMAHOSTCURADDRREG_ENDDSP0 0x100F7C | ||
91 | #define DMATANXCOUNTREG_STARTDSP0 0x100F80 | ||
92 | #define DMATANXCOUNTREG_ENDDSP0 0x100F9C | ||
93 | #define DMATIMEBUGREG_STARTDSP0 0x100FA0 | ||
94 | #define DMATIMEBUGREG_ENDDSP0 0x100FAC | ||
95 | #define DMACNTLMODFREG_STARTDSP0 0x100FA0 | ||
96 | #define DMACNTLMODFREG_ENDDSP0 0x100FAC | ||
97 | |||
98 | #define DMAGLOBSTATSREGDSP0 0x100FEC | ||
99 | #define DSP0XGPRAM_START 0x101000 | ||
100 | #define DSP0XGPRAM_END 0x1017FC | ||
101 | #define DSP0YGPRAM_START 0x101800 | ||
102 | #define DSP0YGPRAM_END 0x101FFC | ||
103 | |||
104 | |||
105 | |||
106 | |||
107 | #define AUDIORINGIPDSP1_START 0x102000 | ||
108 | #define AUDIORINGIPDSP1_END 0x1023FC | ||
109 | #define AUDIORINGOPDSP1_START 0x102400 | ||
110 | #define AUDIORINGOPDSP1_END 0x1027FC | ||
111 | #define AUDPARARINGIODSP1_START 0x102800 | ||
112 | #define AUDPARARINGIODSP1_END 0x102BFC | ||
113 | #define DSP1LOCALHWREG_START 0x102C00 | ||
114 | #define DSP1LOCALHWREG_END 0x102C3C | ||
115 | #define DSP1XYRAMAGINDEX_START 0x102C40 | ||
116 | #define DSP1XYRAMAGINDEX_END 0x102C5C | ||
117 | #define DSP1XYRAMAGMDFR_START 0x102C60 | ||
118 | #define DSP1XYRAMAGMDFR_END 0x102C7C | ||
119 | #define DSP1INTCONTLVEC_START 0x102C80 | ||
120 | #define DSP1INTCONTLVEC_END 0x102CD8 | ||
121 | #define HOSTINTFPORTADDRCONTDSP1 0x102D40 | ||
122 | #define HOSTINTFPORTDATADSP1 0x102D44 | ||
123 | #define TIME0PERENBDSP1 0x102D60 | ||
124 | #define TIME0COUNTERDSP1 0x102D64 | ||
125 | #define TIME1PERENBDSP1 0x102D68 | ||
126 | #define TIME1COUNTERDSP1 0x102D6C | ||
127 | #define TIME2PERENBDSP1 0x102D70 | ||
128 | #define TIME2COUNTERDSP1 0x102D74 | ||
129 | #define TIME3PERENBDSP1 0x102D78 | ||
130 | #define TIME3COUNTERDSP1 0x102D7C | ||
131 | #define XRAMINDOPERREFNOUP_STARTDSP1 0x102D80 | ||
132 | #define XRAMINDOPERREFNOUP_ENDDSP1 0x102D9C | ||
133 | #define XRAMINDOPERREFUP_STARTDSP1 0x102DA0 | ||
134 | #define XRAMINDOPERREFUP_ENDDSP1 0x102DBC | ||
135 | #define YRAMINDOPERREFNOUP_STARTDSP1 0x102DC0 | ||
136 | #define YRAMINDOPERREFNOUP_ENDDSP1 0x102DDC | ||
137 | #define YRAMINDOPERREFUP_STARTDSP1 0x102DE0 | ||
138 | #define YRAMINDOPERREFUP_ENDDSP1 0x102DFC | ||
139 | |||
140 | #define DSP1CONDCODE 0x102E00 | ||
141 | #define DSP1STACKFLAG 0x102E04 | ||
142 | #define DSP1PROGCOUNTSTACKPTREG 0x102E08 | ||
143 | #define DSP1PROGCOUNTSTACKDATAREG 0x102E0C | ||
144 | #define DSP1CURLOOPADDRREG 0x102E10 | ||
145 | #define DSP1CURLOOPCOUNT 0x102E14 | ||
146 | #define DSP1TOPLOOPCOUNTSTACK 0x102E18 | ||
147 | #define DSP1TOPLOOPADDRSTACK 0x102E1C | ||
148 | #define DSP1LOOPSTACKPTR 0x102E20 | ||
149 | #define DSP1STASSTACKDATAREG 0x102E24 | ||
150 | #define DSP1STASSTACKPTR 0x102E28 | ||
151 | #define DSP1PROGCOUNT 0x102E2C | ||
152 | #define DSP1XYRAMBASE_START 0x102EA0 | ||
153 | #define DSP1XYRAMBASE_END 0x102EBC | ||
154 | #define DSP1XYRAMLENG_START 0x102EC0 | ||
155 | #define DSP1XYRAMLENG_END 0x102EDC | ||
156 | #define SEMAPHOREREGDSP1 0x102EE0 | ||
157 | #define DSP1INTCONTMASKREG 0x102EE4 | ||
158 | #define DSP1INTCONTPENDREG 0x102EE8 | ||
159 | #define DSP1INTCONTSERVINT 0x102EEC | ||
160 | #define GPIODSP1 0x102EFC | ||
161 | #define DMADSPBASEADDRREG_STARTDSP1 0x102F00 | ||
162 | #define DMADSPBASEADDRREG_ENDDSP1 0x102F1C | ||
163 | #define DMAHOSTBASEADDRREG_STARTDSP1 0x102F20 | ||
164 | #define DMAHOSTBASEADDRREG_ENDDSP1 0x102F3C | ||
165 | #define DMADSPCURADDRREG_STARTDSP1 0x102F40 | ||
166 | #define DMADSPCURADDRREG_ENDDSP1 0x102F5C | ||
167 | #define DMAHOSTCURADDRREG_STARTDSP1 0x102F60 | ||
168 | #define DMAHOSTCURADDRREG_ENDDSP1 0x102F7C | ||
169 | #define DMATANXCOUNTREG_STARTDSP1 0x102F80 | ||
170 | #define DMATANXCOUNTREG_ENDDSP1 0x102F9C | ||
171 | #define DMATIMEBUGREG_STARTDSP1 0x102FA0 | ||
172 | #define DMATIMEBUGREG_ENDDSP1 0x102FAC | ||
173 | #define DMACNTLMODFREG_STARTDSP1 0x102FA0 | ||
174 | #define DMACNTLMODFREG_ENDDSP1 0x102FAC | ||
175 | |||
176 | #define DMAGLOBSTATSREGDSP1 0x102FEC | ||
177 | #define DSP1XGPRAM_START 0x103000 | ||
178 | #define DSP1XGPRAM_END 0x1033FC | ||
179 | #define DSP1YGPRAM_START 0x103400 | ||
180 | #define DSP1YGPRAM_END 0x1037FC | ||
181 | |||
182 | |||
183 | |||
184 | #define AUDIORINGIPDSP2_START 0x104000 | ||
185 | #define AUDIORINGIPDSP2_END 0x1043FC | ||
186 | #define AUDIORINGOPDSP2_START 0x104400 | ||
187 | #define AUDIORINGOPDSP2_END 0x1047FC | ||
188 | #define AUDPARARINGIODSP2_START 0x104800 | ||
189 | #define AUDPARARINGIODSP2_END 0x104BFC | ||
190 | #define DSP2LOCALHWREG_START 0x104C00 | ||
191 | #define DSP2LOCALHWREG_END 0x104C3C | ||
192 | #define DSP2XYRAMAGINDEX_START 0x104C40 | ||
193 | #define DSP2XYRAMAGINDEX_END 0x104C5C | ||
194 | #define DSP2XYRAMAGMDFR_START 0x104C60 | ||
195 | #define DSP2XYRAMAGMDFR_END 0x104C7C | ||
196 | #define DSP2INTCONTLVEC_START 0x104C80 | ||
197 | #define DSP2INTCONTLVEC_END 0x104CD8 | ||
198 | #define HOSTINTFPORTADDRCONTDSP2 0x104D40 | ||
199 | #define HOSTINTFPORTDATADSP2 0x104D44 | ||
200 | #define TIME0PERENBDSP2 0x104D60 | ||
201 | #define TIME0COUNTERDSP2 0x104D64 | ||
202 | #define TIME1PERENBDSP2 0x104D68 | ||
203 | #define TIME1COUNTERDSP2 0x104D6C | ||
204 | #define TIME2PERENBDSP2 0x104D70 | ||
205 | #define TIME2COUNTERDSP2 0x104D74 | ||
206 | #define TIME3PERENBDSP2 0x104D78 | ||
207 | #define TIME3COUNTERDSP2 0x104D7C | ||
208 | #define XRAMINDOPERREFNOUP_STARTDSP2 0x104D80 | ||
209 | #define XRAMINDOPERREFNOUP_ENDDSP2 0x104D9C | ||
210 | #define XRAMINDOPERREFUP_STARTDSP2 0x104DA0 | ||
211 | #define XRAMINDOPERREFUP_ENDDSP2 0x104DBC | ||
212 | #define YRAMINDOPERREFNOUP_STARTDSP2 0x104DC0 | ||
213 | #define YRAMINDOPERREFNOUP_ENDDSP2 0x104DDC | ||
214 | #define YRAMINDOPERREFUP_STARTDSP2 0x104DE0 | ||
215 | #define YRAMINDOPERREFUP_ENDDSP2 0x104DFC | ||
216 | #define DSP2CONDCODE 0x104E00 | ||
217 | #define DSP2STACKFLAG 0x104E04 | ||
218 | #define DSP2PROGCOUNTSTACKPTREG 0x104E08 | ||
219 | #define DSP2PROGCOUNTSTACKDATAREG 0x104E0C | ||
220 | #define DSP2CURLOOPADDRREG 0x104E10 | ||
221 | #define DSP2CURLOOPCOUNT 0x104E14 | ||
222 | #define DSP2TOPLOOPCOUNTSTACK 0x104E18 | ||
223 | #define DSP2TOPLOOPADDRSTACK 0x104E1C | ||
224 | #define DSP2LOOPSTACKPTR 0x104E20 | ||
225 | #define DSP2STASSTACKDATAREG 0x104E24 | ||
226 | #define DSP2STASSTACKPTR 0x104E28 | ||
227 | #define DSP2PROGCOUNT 0x104E2C | ||
228 | #define DSP2XYRAMBASE_START 0x104EA0 | ||
229 | #define DSP2XYRAMBASE_END 0x104EBC | ||
230 | #define DSP2XYRAMLENG_START 0x104EC0 | ||
231 | #define DSP2XYRAMLENG_END 0x104EDC | ||
232 | #define SEMAPHOREREGDSP2 0x104EE0 | ||
233 | #define DSP2INTCONTMASKREG 0x104EE4 | ||
234 | #define DSP2INTCONTPENDREG 0x104EE8 | ||
235 | #define DSP2INTCONTSERVINT 0x104EEC | ||
236 | #define GPIODSP2 0x104EFC | ||
237 | #define DMADSPBASEADDRREG_STARTDSP2 0x104F00 | ||
238 | #define DMADSPBASEADDRREG_ENDDSP2 0x104F1C | ||
239 | #define DMAHOSTBASEADDRREG_STARTDSP2 0x104F20 | ||
240 | #define DMAHOSTBASEADDRREG_ENDDSP2 0x104F3C | ||
241 | #define DMADSPCURADDRREG_STARTDSP2 0x104F40 | ||
242 | #define DMADSPCURADDRREG_ENDDSP2 0x104F5C | ||
243 | #define DMAHOSTCURADDRREG_STARTDSP2 0x104F60 | ||
244 | #define DMAHOSTCURADDRREG_ENDDSP2 0x104F7C | ||
245 | #define DMATANXCOUNTREG_STARTDSP2 0x104F80 | ||
246 | #define DMATANXCOUNTREG_ENDDSP2 0x104F9C | ||
247 | #define DMATIMEBUGREG_STARTDSP2 0x104FA0 | ||
248 | #define DMATIMEBUGREG_ENDDSP2 0x104FAC | ||
249 | #define DMACNTLMODFREG_STARTDSP2 0x104FA0 | ||
250 | #define DMACNTLMODFREG_ENDDSP2 0x104FAC | ||
251 | |||
252 | #define DMAGLOBSTATSREGDSP2 0x104FEC | ||
253 | #define DSP2XGPRAM_START 0x105000 | ||
254 | #define DSP2XGPRAM_END 0x1051FC | ||
255 | #define DSP2YGPRAM_START 0x105800 | ||
256 | #define DSP2YGPRAM_END 0x1059FC | ||
257 | |||
258 | |||
259 | |||
260 | #define AUDIORINGIPDSP3_START 0x106000 | ||
261 | #define AUDIORINGIPDSP3_END 0x1063FC | ||
262 | #define AUDIORINGOPDSP3_START 0x106400 | ||
263 | #define AUDIORINGOPDSP3_END 0x1067FC | ||
264 | #define AUDPARARINGIODSP3_START 0x106800 | ||
265 | #define AUDPARARINGIODSP3_END 0x106BFC | ||
266 | #define DSP3LOCALHWREG_START 0x106C00 | ||
267 | #define DSP3LOCALHWREG_END 0x106C3C | ||
268 | #define DSP3XYRAMAGINDEX_START 0x106C40 | ||
269 | #define DSP3XYRAMAGINDEX_END 0x106C5C | ||
270 | #define DSP3XYRAMAGMDFR_START 0x106C60 | ||
271 | #define DSP3XYRAMAGMDFR_END 0x106C7C | ||
272 | #define DSP3INTCONTLVEC_START 0x106C80 | ||
273 | #define DSP3INTCONTLVEC_END 0x106CD8 | ||
274 | #define HOSTINTFPORTADDRCONTDSP3 0x106D40 | ||
275 | #define HOSTINTFPORTDATADSP3 0x106D44 | ||
276 | #define TIME0PERENBDSP3 0x106D60 | ||
277 | #define TIME0COUNTERDSP3 0x106D64 | ||
278 | #define TIME1PERENBDSP3 0x106D68 | ||
279 | #define TIME1COUNTERDSP3 0x106D6C | ||
280 | #define TIME2PERENBDSP3 0x106D70 | ||
281 | #define TIME2COUNTERDSP3 0x106D74 | ||
282 | #define TIME3PERENBDSP3 0x106D78 | ||
283 | #define TIME3COUNTERDSP3 0x106D7C | ||
284 | #define XRAMINDOPERREFNOUP_STARTDSP3 0x106D80 | ||
285 | #define XRAMINDOPERREFNOUP_ENDDSP3 0x106D9C | ||
286 | #define XRAMINDOPERREFUP_STARTDSP3 0x106DA0 | ||
287 | #define XRAMINDOPERREFUP_ENDDSP3 0x106DBC | ||
288 | #define YRAMINDOPERREFNOUP_STARTDSP3 0x106DC0 | ||
289 | #define YRAMINDOPERREFNOUP_ENDDSP3 0x106DDC | ||
290 | #define YRAMINDOPERREFUP_STARTDSP3 0x106DE0 | ||
291 | #define YRAMINDOPERREFUP_ENDDSP3 0x100DFC | ||
292 | |||
293 | #define DSP3CONDCODE 0x106E00 | ||
294 | #define DSP3STACKFLAG 0x106E04 | ||
295 | #define DSP3PROGCOUNTSTACKPTREG 0x106E08 | ||
296 | #define DSP3PROGCOUNTSTACKDATAREG 0x106E0C | ||
297 | #define DSP3CURLOOPADDRREG 0x106E10 | ||
298 | #define DSP3CURLOOPCOUNT 0x106E14 | ||
299 | #define DSP3TOPLOOPCOUNTSTACK 0x106E18 | ||
300 | #define DSP3TOPLOOPADDRSTACK 0x106E1C | ||
301 | #define DSP3LOOPSTACKPTR 0x106E20 | ||
302 | #define DSP3STASSTACKDATAREG 0x106E24 | ||
303 | #define DSP3STASSTACKPTR 0x106E28 | ||
304 | #define DSP3PROGCOUNT 0x106E2C | ||
305 | #define DSP3XYRAMBASE_START 0x106EA0 | ||
306 | #define DSP3XYRAMBASE_END 0x106EBC | ||
307 | #define DSP3XYRAMLENG_START 0x106EC0 | ||
308 | #define DSP3XYRAMLENG_END 0x106EDC | ||
309 | #define SEMAPHOREREGDSP3 0x106EE0 | ||
310 | #define DSP3INTCONTMASKREG 0x106EE4 | ||
311 | #define DSP3INTCONTPENDREG 0x106EE8 | ||
312 | #define DSP3INTCONTSERVINT 0x106EEC | ||
313 | #define GPIODSP3 0x106EFC | ||
314 | #define DMADSPBASEADDRREG_STARTDSP3 0x106F00 | ||
315 | #define DMADSPBASEADDRREG_ENDDSP3 0x106F1C | ||
316 | #define DMAHOSTBASEADDRREG_STARTDSP3 0x106F20 | ||
317 | #define DMAHOSTBASEADDRREG_ENDDSP3 0x106F3C | ||
318 | #define DMADSPCURADDRREG_STARTDSP3 0x106F40 | ||
319 | #define DMADSPCURADDRREG_ENDDSP3 0x106F5C | ||
320 | #define DMAHOSTCURADDRREG_STARTDSP3 0x106F60 | ||
321 | #define DMAHOSTCURADDRREG_ENDDSP3 0x106F7C | ||
322 | #define DMATANXCOUNTREG_STARTDSP3 0x106F80 | ||
323 | #define DMATANXCOUNTREG_ENDDSP3 0x106F9C | ||
324 | #define DMATIMEBUGREG_STARTDSP3 0x106FA0 | ||
325 | #define DMATIMEBUGREG_ENDDSP3 0x106FAC | ||
326 | #define DMACNTLMODFREG_STARTDSP3 0x106FA0 | ||
327 | #define DMACNTLMODFREG_ENDDSP3 0x106FAC | ||
328 | |||
329 | #define DMAGLOBSTATSREGDSP3 0x106FEC | ||
330 | #define DSP3XGPRAM_START 0x107000 | ||
331 | #define DSP3XGPRAM_END 0x1071FC | ||
332 | #define DSP3YGPRAM_START 0x107800 | ||
333 | #define DSP3YGPRAM_END 0x1079FC | ||
334 | |||
335 | /* end of DSP reg definitions */ | ||
336 | |||
337 | #define DSPAIMAP_START 0x108000 | ||
338 | #define DSPAIMAP_END 0x1083FC | ||
339 | #define DSPPIMAP_START 0x108400 | ||
340 | #define DSPPIMAP_END 0x1087FC | ||
341 | #define DSPPOMAP_START 0x108800 | ||
342 | #define DSPPOMAP_END 0x108BFC | ||
343 | #define DSPPOCTL 0x108C00 | ||
344 | #define TKCTL_START 0x110000 | ||
345 | #define TKCTL_END 0x110FFC | ||
346 | #define TKCC_START 0x111000 | ||
347 | #define TKCC_END 0x111FFC | ||
348 | #define TKIMAP_START 0x112000 | ||
349 | #define TKIMAP_END 0x112FFC | ||
350 | #define TKDCTR16 0x113000 | ||
351 | #define TKPB16 0x113004 | ||
352 | #define TKBS16 0x113008 | ||
353 | #define TKDCTR32 0x11300C | ||
354 | #define TKPB32 0x113010 | ||
355 | #define TKBS32 0x113014 | ||
356 | #define ICDCTR16 0x113018 | ||
357 | #define ITBS16 0x11301C | ||
358 | #define ICDCTR32 0x113020 | ||
359 | #define ITBS32 0x113024 | ||
360 | #define ITSTART 0x113028 | ||
361 | #define TKSQ 0x11302C | ||
362 | |||
363 | #define TKSCCTL_START 0x114000 | ||
364 | #define TKSCCTL_END 0x11403C | ||
365 | #define TKSCADR_START 0x114100 | ||
366 | #define TKSCADR_END 0x11413C | ||
367 | #define TKSCDATAX_START 0x114800 | ||
368 | #define TKSCDATAX_END 0x1149FC | ||
369 | #define TKPCDATAX_START 0x120000 | ||
370 | #define TKPCDATAX_END 0x12FFFC | ||
371 | |||
372 | #define MALSA 0x130000 | ||
373 | #define MAPPHA 0x130004 | ||
374 | #define MAPPLA 0x130008 | ||
375 | #define MALSB 0x130010 | ||
376 | #define MAPPHB 0x130014 | ||
377 | #define MAPPLB 0x130018 | ||
378 | |||
379 | #define TANSPORTMAPABREGS_START 0x130020 | ||
380 | #define TANSPORTMAPABREGS_END 0x13A2FC | ||
381 | |||
382 | #define PTPAHX 0x13B000 | ||
383 | #define PTPALX 0x13B004 | ||
384 | |||
385 | #define TANSPPAGETABLEPHYADDR015_START 0x13B008 | ||
386 | #define TANSPPAGETABLEPHYADDR015_END 0x13B07C | ||
387 | #define TRNQADRX_START 0x13B100 | ||
388 | #define TRNQADRX_END 0x13B13C | ||
389 | #define TRNQTIMX_START 0x13B200 | ||
390 | #define TRNQTIMX_END 0x13B23C | ||
391 | #define TRNQAPARMX_START 0x13B300 | ||
392 | #define TRNQAPARMX_END 0x13B33C | ||
393 | |||
394 | #define TRNQCNT 0x13B400 | ||
395 | #define TRNCTL 0x13B404 | ||
396 | #define TRNIS 0x13B408 | ||
397 | #define TRNCURTS 0x13B40C | ||
398 | |||
399 | #define AMOP_START 0x140000 | ||
400 | #define AMOPLO 0x140000 | ||
401 | #define AMOPHI 0x140004 | ||
402 | #define AMOP_END 0x147FFC | ||
403 | #define PMOP_START 0x148000 | ||
404 | #define PMOPLO 0x148000 | ||
405 | #define PMOPHI 0x148004 | ||
406 | #define PMOP_END 0x14FFFC | ||
407 | #define PCURR_START 0x150000 | ||
408 | #define PCURR_END 0x153FFC | ||
409 | #define PTRAG_START 0x154000 | ||
410 | #define PTRAG_END 0x157FFC | ||
411 | #define PSR_START 0x158000 | ||
412 | #define PSR_END 0x15BFFC | ||
413 | |||
414 | #define PFSTAT4SEG_START 0x160000 | ||
415 | #define PFSTAT4SEG_END 0x160BFC | ||
416 | #define PFSTAT2SEG_START 0x160C00 | ||
417 | #define PFSTAT2SEG_END 0x1617FC | ||
418 | #define PFTARG4SEG_START 0x164000 | ||
419 | #define PFTARG4SEG_END 0x164BFC | ||
420 | #define PFTARG2SEG_START 0x164C00 | ||
421 | #define PFTARG2SEG_END 0x1657FC | ||
422 | #define PFSR4SEG_START 0x168000 | ||
423 | #define PFSR4SEG_END 0x168BFC | ||
424 | #define PFSR2SEG_START 0x168C00 | ||
425 | #define PFSR2SEG_END 0x1697FC | ||
426 | #define PCURRMS4SEG_START 0x16C000 | ||
427 | #define PCURRMS4SEG_END 0x16CCFC | ||
428 | #define PCURRMS2SEG_START 0x16CC00 | ||
429 | #define PCURRMS2SEG_END 0x16D7FC | ||
430 | #define PTARGMS4SEG_START 0x170000 | ||
431 | #define PTARGMS4SEG_END 0x172FFC | ||
432 | #define PTARGMS2SEG_START 0x173000 | ||
433 | #define PTARGMS2SEG_END 0x1747FC | ||
434 | #define PSRMS4SEG_START 0x170000 | ||
435 | #define PSRMS4SEG_END 0x172FFC | ||
436 | #define PSRMS2SEG_START 0x173000 | ||
437 | #define PSRMS2SEG_END 0x1747FC | ||
438 | |||
439 | #define PRING_LO_START 0x190000 | ||
440 | #define PRING_LO_END 0x193FFC | ||
441 | #define PRING_HI_START 0x194000 | ||
442 | #define PRING_HI_END 0x197FFC | ||
443 | #define PRING_LO_HI_START 0x198000 | ||
444 | #define PRING_LO_HI 0x198000 | ||
445 | #define PRING_LO_HI_END 0x19BFFC | ||
446 | |||
447 | #define PINTFIFO 0x1A0000 | ||
448 | #define SRCCTL 0x1B0000 | ||
449 | #define SRCCCR 0x1B0004 | ||
450 | #define SRCIMAP 0x1B0008 | ||
451 | #define SRCODDC 0x1B000C | ||
452 | #define SRCCA 0x1B0010 | ||
453 | #define SRCCF 0x1B0014 | ||
454 | #define SRCSA 0x1B0018 | ||
455 | #define SRCLA 0x1B001C | ||
456 | #define SRCCTLSWR 0x1B0020 | ||
457 | |||
458 | /* SRC HERE */ | ||
459 | #define SRCALBA 0x1B002C | ||
460 | #define SRCMCTL 0x1B012C | ||
461 | #define SRCCERR 0x1B022C | ||
462 | #define SRCITB 0x1B032C | ||
463 | #define SRCIPM 0x1B082C | ||
464 | #define SRCIP 0x1B102C | ||
465 | #define SRCENBSTAT 0x1B202C | ||
466 | #define SRCENBLO 0x1B212C | ||
467 | #define SRCENBHI 0x1B222C | ||
468 | #define SRCENBS 0x1B232C | ||
469 | #define SRCENB 0x1B282C | ||
470 | #define SRCENB07 0x1B282C | ||
471 | #define SRCENBS07 0x1B302C | ||
472 | |||
473 | #define SRCDN0Z 0x1B0030 | ||
474 | #define SRCDN0Z0 0x1B0030 | ||
475 | #define SRCDN0Z1 0x1B0034 | ||
476 | #define SRCDN0Z2 0x1B0038 | ||
477 | #define SRCDN0Z3 0x1B003C | ||
478 | #define SRCDN1Z 0x1B0040 | ||
479 | #define SRCDN1Z0 0x1B0040 | ||
480 | #define SRCDN1Z1 0x1B0044 | ||
481 | #define SRCDN1Z2 0x1B0048 | ||
482 | #define SRCDN1Z3 0x1B004C | ||
483 | #define SRCDN1Z4 0x1B0050 | ||
484 | #define SRCDN1Z5 0x1B0054 | ||
485 | #define SRCDN1Z6 0x1B0058 | ||
486 | #define SRCDN1Z7 0x1B005C | ||
487 | #define SRCUPZ 0x1B0060 | ||
488 | #define SRCUPZ0 0x1B0060 | ||
489 | #define SRCUPZ1 0x1B0064 | ||
490 | #define SRCUPZ2 0x1B0068 | ||
491 | #define SRCUPZ3 0x1B006C | ||
492 | #define SRCUPZ4 0x1B0070 | ||
493 | #define SRCUPZ5 0x1B0074 | ||
494 | #define SRCUPZ6 0x1B0078 | ||
495 | #define SRCUPZ7 0x1B007C | ||
496 | #define SRCCD0 0x1B0080 | ||
497 | #define SRCCD1 0x1B0084 | ||
498 | #define SRCCD2 0x1B0088 | ||
499 | #define SRCCD3 0x1B008C | ||
500 | #define SRCCD4 0x1B0090 | ||
501 | #define SRCCD5 0x1B0094 | ||
502 | #define SRCCD6 0x1B0098 | ||
503 | #define SRCCD7 0x1B009C | ||
504 | #define SRCCD8 0x1B00A0 | ||
505 | #define SRCCD9 0x1B00A4 | ||
506 | #define SRCCDA 0x1B00A8 | ||
507 | #define SRCCDB 0x1B00AC | ||
508 | #define SRCCDC 0x1B00B0 | ||
509 | #define SRCCDD 0x1B00B4 | ||
510 | #define SRCCDE 0x1B00B8 | ||
511 | #define SRCCDF 0x1B00BC | ||
512 | #define SRCCD10 0x1B00C0 | ||
513 | #define SRCCD11 0x1B00C4 | ||
514 | #define SRCCD12 0x1B00C8 | ||
515 | #define SRCCD13 0x1B00CC | ||
516 | #define SRCCD14 0x1B00D0 | ||
517 | #define SRCCD15 0x1B00D4 | ||
518 | #define SRCCD16 0x1B00D8 | ||
519 | #define SRCCD17 0x1B00DC | ||
520 | #define SRCCD18 0x1B00E0 | ||
521 | #define SRCCD19 0x1B00E4 | ||
522 | #define SRCCD1A 0x1B00E8 | ||
523 | #define SRCCD1B 0x1B00EC | ||
524 | #define SRCCD1C 0x1B00F0 | ||
525 | #define SRCCD1D 0x1B00F4 | ||
526 | #define SRCCD1E 0x1B00F8 | ||
527 | #define SRCCD1F 0x1B00FC | ||
528 | |||
529 | #define SRCCONTRBLOCK_START 0x1B0100 | ||
530 | #define SRCCONTRBLOCK_END 0x1BFFFC | ||
531 | #define FILTOP_START 0x1C0000 | ||
532 | #define FILTOP_END 0x1C05FC | ||
533 | #define FILTIMAP_START 0x1C0800 | ||
534 | #define FILTIMAP_END 0x1C0DFC | ||
535 | #define FILTZ1_START 0x1C1000 | ||
536 | #define FILTZ1_END 0x1C15FC | ||
537 | #define FILTZ2_START 0x1C1800 | ||
538 | #define FILTZ2_END 0x1C1DFC | ||
539 | #define DAOIMAP_START 0x1C5000 | ||
540 | #define DAOIMAP 0x1C5000 | ||
541 | #define DAOIMAP_END 0x1C5124 | ||
542 | |||
543 | #define AC97D 0x1C5400 | ||
544 | #define AC97A 0x1C5404 | ||
545 | #define AC97CTL 0x1C5408 | ||
546 | #define I2SCTL 0x1C5420 | ||
547 | |||
548 | #define SPOS 0x1C5440 | ||
549 | #define SPOSA 0x1C5440 | ||
550 | #define SPOSB 0x1C5444 | ||
551 | #define SPOSC 0x1C5448 | ||
552 | #define SPOSD 0x1C544C | ||
553 | |||
554 | #define SPISA 0x1C5450 | ||
555 | #define SPISB 0x1C5454 | ||
556 | #define SPISC 0x1C5458 | ||
557 | #define SPISD 0x1C545C | ||
558 | |||
559 | #define SPFSCTL 0x1C5460 | ||
560 | |||
561 | #define SPFS0 0x1C5468 | ||
562 | #define SPFS1 0x1C546C | ||
563 | #define SPFS2 0x1C5470 | ||
564 | #define SPFS3 0x1C5474 | ||
565 | #define SPFS4 0x1C5478 | ||
566 | #define SPFS5 0x1C547C | ||
567 | |||
568 | #define SPOCTL 0x1C5480 | ||
569 | #define SPICTL 0x1C5484 | ||
570 | #define SPISTS 0x1C5488 | ||
571 | #define SPINTP 0x1C548C | ||
572 | #define SPINTE 0x1C5490 | ||
573 | #define SPUTCTLAB 0x1C5494 | ||
574 | #define SPUTCTLCD 0x1C5498 | ||
575 | |||
576 | #define SRTSPA 0x1C54C0 | ||
577 | #define SRTSPB 0x1C54C4 | ||
578 | #define SRTSPC 0x1C54C8 | ||
579 | #define SRTSPD 0x1C54CC | ||
580 | |||
581 | #define SRTSCTL 0x1C54D0 | ||
582 | #define SRTSCTLA 0x1C54D0 | ||
583 | #define SRTSCTLB 0x1C54D4 | ||
584 | #define SRTSCTLC 0x1C54D8 | ||
585 | #define SRTSCTLD 0x1C54DC | ||
586 | |||
587 | #define SRTI2S 0x1C54E0 | ||
588 | #define SRTICTL 0x1C54F0 | ||
589 | |||
590 | #define WC 0x1C6000 | ||
591 | #define TIMR 0x1C6004 | ||
592 | |||
593 | #define GIP 0x1C6010 | ||
594 | #define GIE 0x1C6014 | ||
595 | #define DIE 0x1C6018 | ||
596 | #define DIC 0x1C601C | ||
597 | #define GPIO 0x1C6020 | ||
598 | #define GPIOCTL 0x1C6024 | ||
599 | #define GPIP 0x1C6028 | ||
600 | #define GPIE 0x1C602C | ||
601 | #define DSPINT0 0x1C6030 | ||
602 | #define DSPEIOC 0x1C6034 | ||
603 | #define MUADAT 0x1C6040 | ||
604 | #define MUACMD 0x1C6044 | ||
605 | #define MUASTAT 0x1C6044 | ||
606 | #define MUBDAT 0x1C6048 | ||
607 | #define MUBCMD 0x1C604C | ||
608 | #define MUBSTAT 0x1C604C | ||
609 | #define UARTCMA 0x1C6050 | ||
610 | #define UARTCMB 0x1C6054 | ||
611 | #define UARTIP 0x1C6058 | ||
612 | #define UARTIE 0x1C605C | ||
613 | #define PLLCTL 0x1C6060 | ||
614 | #define PLLDCD 0x1C6064 | ||
615 | #define GCTL 0x1C6070 | ||
616 | #define ID0 0x1C6080 | ||
617 | #define ID1 0x1C6084 | ||
618 | #define ID2 0x1C6088 | ||
619 | #define ID3 0x1C608C | ||
620 | #define SDRCTL 0x1C7000 | ||
621 | |||
622 | |||
623 | #define I2SA_L 0x0L | ||
624 | #define I2SA_R 0x1L | ||
625 | #define I2SB_L 0x8L | ||
626 | #define I2SB_R 0x9L | ||
627 | #define I2SC_L 0x10L | ||
628 | #define I2SC_R 0x11L | ||
629 | #define I2SD_L 0x18L | ||
630 | #define I2SD_R 0x19L | ||
631 | |||
632 | #endif /* CT20K1REG_H */ | ||
633 | |||
634 | |||
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h new file mode 100644 index 000000000000..2d07986f57cc --- /dev/null +++ b/sound/pci/ctxfi/ct20k2reg.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | */ | ||
8 | |||
9 | #ifndef _20K2REGISTERS_H_ | ||
10 | #define _20K2REGISTERS_H_ | ||
11 | |||
12 | |||
13 | /* Timer Registers */ | ||
14 | #define TIMER_TIMR 0x1B7004 | ||
15 | #define INTERRUPT_GIP 0x1B7010 | ||
16 | #define INTERRUPT_GIE 0x1B7014 | ||
17 | |||
18 | /* I2C Registers */ | ||
19 | #define I2C_IF_ADDRESS 0x1B9000 | ||
20 | #define I2C_IF_WDATA 0x1B9004 | ||
21 | #define I2C_IF_RDATA 0x1B9008 | ||
22 | #define I2C_IF_STATUS 0x1B900C | ||
23 | #define I2C_IF_WLOCK 0x1B9010 | ||
24 | |||
25 | /* Global Control Registers */ | ||
26 | #define GLOBAL_CNTL_GCTL 0x1B7090 | ||
27 | |||
28 | /* PLL Registers */ | ||
29 | #define PLL_CTL 0x1B7080 | ||
30 | #define PLL_STAT 0x1B7084 | ||
31 | #define PLL_ENB 0x1B7088 | ||
32 | |||
33 | /* SRC Registers */ | ||
34 | #define SRC_CTL 0x1A0000 /* 0x1A0000 + (256 * Chn) */ | ||
35 | #define SRC_CCR 0x1A0004 /* 0x1A0004 + (256 * Chn) */ | ||
36 | #define SRC_IMAP 0x1A0008 /* 0x1A0008 + (256 * Chn) */ | ||
37 | #define SRC_CA 0x1A0010 /* 0x1A0010 + (256 * Chn) */ | ||
38 | #define SRC_CF 0x1A0014 /* 0x1A0014 + (256 * Chn) */ | ||
39 | #define SRC_SA 0x1A0018 /* 0x1A0018 + (256 * Chn) */ | ||
40 | #define SRC_LA 0x1A001C /* 0x1A001C + (256 * Chn) */ | ||
41 | #define SRC_CTLSWR 0x1A0020 /* 0x1A0020 + (256 * Chn) */ | ||
42 | #define SRC_CD 0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */ | ||
43 | #define SRC_MCTL 0x1A012C | ||
44 | #define SRC_IP 0x1A102C /* 0x1A102C + (256 * Regn) */ | ||
45 | #define SRC_ENB 0x1A282C /* 0x1A282C + (256 * Regn) */ | ||
46 | #define SRC_ENBSTAT 0x1A202C | ||
47 | #define SRC_ENBSA 0x1A232C | ||
48 | #define SRC_DN0Z 0x1A0030 | ||
49 | #define SRC_DN1Z 0x1A0040 | ||
50 | #define SRC_UPZ 0x1A0060 | ||
51 | |||
52 | /* GPIO Registers */ | ||
53 | #define GPIO_DATA 0x1B7020 | ||
54 | #define GPIO_CTRL 0x1B7024 | ||
55 | |||
56 | /* Virtual memory registers */ | ||
57 | #define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */ | ||
58 | #define VMEM_PTPAH 0x1C6304 /* 0x1C6304 + (16 * Chn) */ | ||
59 | #define VMEM_CTL 0x1C7000 | ||
60 | |||
61 | /* Transport Registers */ | ||
62 | #define TRANSPORT_ENB 0x1B6000 | ||
63 | #define TRANSPORT_CTL 0x1B6004 | ||
64 | #define TRANSPORT_INT 0x1B6008 | ||
65 | |||
66 | /* Audio IO */ | ||
67 | #define AUDIO_IO_AIM 0x1B5000 /* 0x1B5000 + (0x04 * Chn) */ | ||
68 | #define AUDIO_IO_TX_CTL 0x1B5400 /* 0x1B5400 + (0x40 * Chn) */ | ||
69 | #define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */ | ||
70 | #define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */ | ||
71 | #define AUDIO_IO_RX_CTL 0x1B5410 /* 0x1B5410 + (0x40 * Chn) */ | ||
72 | #define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */ | ||
73 | #define AUDIO_IO_MCLK 0x1B5600 | ||
74 | #define AUDIO_IO_TX_BLRCLK 0x1B5604 | ||
75 | #define AUDIO_IO_RX_BLRCLK 0x1B5608 | ||
76 | |||
77 | /* Mixer */ | ||
78 | #define MIXER_AMOPLO 0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */ | ||
79 | #define MIXER_AMOPHI 0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */ | ||
80 | #define MIXER_PRING_LO_HI 0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */ | ||
81 | #define MIXER_PMOPLO 0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */ | ||
82 | #define MIXER_PMOPHI 0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */ | ||
83 | #define MIXER_AR_ENABLE 0x19000C | ||
84 | |||
85 | #endif | ||
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c new file mode 100644 index 000000000000..119791aa70eb --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.c | |||
@@ -0,0 +1,488 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctamixer.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of the Audio Mixer | ||
12 | * resource management object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 21 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include "ctamixer.h" | ||
20 | #include "cthardware.h" | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #define AMIXER_RESOURCE_NUM 256 | ||
24 | #define SUM_RESOURCE_NUM 256 | ||
25 | |||
26 | #define AMIXER_Y_IMMEDIATE 1 | ||
27 | |||
28 | #define BLANK_SLOT 4094 | ||
29 | |||
30 | static int amixer_master(struct rsc *rsc) | ||
31 | { | ||
32 | rsc->conj = 0; | ||
33 | return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0]; | ||
34 | } | ||
35 | |||
36 | static int amixer_next_conj(struct rsc *rsc) | ||
37 | { | ||
38 | rsc->conj++; | ||
39 | return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; | ||
40 | } | ||
41 | |||
42 | static int amixer_index(const struct rsc *rsc) | ||
43 | { | ||
44 | return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; | ||
45 | } | ||
46 | |||
47 | static int amixer_output_slot(const struct rsc *rsc) | ||
48 | { | ||
49 | return (amixer_index(rsc) << 4) + 0x4; | ||
50 | } | ||
51 | |||
52 | static struct rsc_ops amixer_basic_rsc_ops = { | ||
53 | .master = amixer_master, | ||
54 | .next_conj = amixer_next_conj, | ||
55 | .index = amixer_index, | ||
56 | .output_slot = amixer_output_slot, | ||
57 | }; | ||
58 | |||
59 | static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) | ||
60 | { | ||
61 | struct hw *hw = NULL; | ||
62 | |||
63 | hw = (struct hw *)amixer->rsc.hw; | ||
64 | hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); | ||
65 | amixer->input = rsc; | ||
66 | if (NULL == rsc) | ||
67 | hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT); | ||
68 | else | ||
69 | hw->amixer_set_x(amixer->rsc.ctrl_blk, | ||
70 | rsc->ops->output_slot(rsc)); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | /* y is a 14-bit immediate constant */ | ||
76 | static int amixer_set_y(struct amixer *amixer, unsigned int y) | ||
77 | { | ||
78 | struct hw *hw = NULL; | ||
79 | |||
80 | hw = (struct hw *)amixer->rsc.hw; | ||
81 | hw->amixer_set_y(amixer->rsc.ctrl_blk, y); | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) | ||
87 | { | ||
88 | struct hw *hw = NULL; | ||
89 | |||
90 | hw = (struct hw *)amixer->rsc.hw; | ||
91 | hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int amixer_set_sum(struct amixer *amixer, struct sum *sum) | ||
97 | { | ||
98 | struct hw *hw = NULL; | ||
99 | |||
100 | hw = (struct hw *)amixer->rsc.hw; | ||
101 | amixer->sum = sum; | ||
102 | if (NULL == sum) { | ||
103 | hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); | ||
104 | } else { | ||
105 | hw->amixer_set_se(amixer->rsc.ctrl_blk, 1); | ||
106 | hw->amixer_set_sadr(amixer->rsc.ctrl_blk, | ||
107 | sum->rsc.ops->index(&sum->rsc)); | ||
108 | } | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int amixer_commit_write(struct amixer *amixer) | ||
114 | { | ||
115 | struct hw *hw = NULL; | ||
116 | unsigned int index = 0; | ||
117 | int i = 0; | ||
118 | struct rsc *input = NULL; | ||
119 | struct sum *sum = NULL; | ||
120 | |||
121 | hw = (struct hw *)amixer->rsc.hw; | ||
122 | input = amixer->input; | ||
123 | sum = amixer->sum; | ||
124 | |||
125 | /* Program master and conjugate resources */ | ||
126 | amixer->rsc.ops->master(&amixer->rsc); | ||
127 | if (NULL != input) | ||
128 | input->ops->master(input); | ||
129 | |||
130 | if (NULL != sum) | ||
131 | sum->rsc.ops->master(&sum->rsc); | ||
132 | |||
133 | for (i = 0; i < amixer->rsc.msr; i++) { | ||
134 | hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk); | ||
135 | if (NULL != input) { | ||
136 | hw->amixer_set_x(amixer->rsc.ctrl_blk, | ||
137 | input->ops->output_slot(input)); | ||
138 | input->ops->next_conj(input); | ||
139 | } | ||
140 | if (NULL != sum) { | ||
141 | hw->amixer_set_sadr(amixer->rsc.ctrl_blk, | ||
142 | sum->rsc.ops->index(&sum->rsc)); | ||
143 | sum->rsc.ops->next_conj(&sum->rsc); | ||
144 | } | ||
145 | index = amixer->rsc.ops->output_slot(&amixer->rsc); | ||
146 | hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); | ||
147 | amixer->rsc.ops->next_conj(&amixer->rsc); | ||
148 | } | ||
149 | amixer->rsc.ops->master(&amixer->rsc); | ||
150 | if (NULL != input) | ||
151 | input->ops->master(input); | ||
152 | |||
153 | if (NULL != sum) | ||
154 | sum->rsc.ops->master(&sum->rsc); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int amixer_commit_raw_write(struct amixer *amixer) | ||
160 | { | ||
161 | struct hw *hw = NULL; | ||
162 | unsigned int index = 0; | ||
163 | |||
164 | hw = (struct hw *)amixer->rsc.hw; | ||
165 | index = amixer->rsc.ops->output_slot(&amixer->rsc); | ||
166 | hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int amixer_get_y(struct amixer *amixer) | ||
172 | { | ||
173 | struct hw *hw = NULL; | ||
174 | |||
175 | hw = (struct hw *)amixer->rsc.hw; | ||
176 | return hw->amixer_get_y(amixer->rsc.ctrl_blk); | ||
177 | } | ||
178 | |||
179 | static int amixer_setup(struct amixer *amixer, struct rsc *input, | ||
180 | unsigned int scale, struct sum *sum) | ||
181 | { | ||
182 | amixer_set_input(amixer, input); | ||
183 | amixer_set_y(amixer, scale); | ||
184 | amixer_set_sum(amixer, sum); | ||
185 | amixer_commit_write(amixer); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static struct amixer_rsc_ops amixer_ops = { | ||
190 | .set_input = amixer_set_input, | ||
191 | .set_invalid_squash = amixer_set_invalid_squash, | ||
192 | .set_scale = amixer_set_y, | ||
193 | .set_sum = amixer_set_sum, | ||
194 | .commit_write = amixer_commit_write, | ||
195 | .commit_raw_write = amixer_commit_raw_write, | ||
196 | .setup = amixer_setup, | ||
197 | .get_scale = amixer_get_y, | ||
198 | }; | ||
199 | |||
200 | static int amixer_rsc_init(struct amixer *amixer, | ||
201 | const struct amixer_desc *desc, | ||
202 | struct amixer_mgr *mgr) | ||
203 | { | ||
204 | int err = 0; | ||
205 | |||
206 | err = rsc_init(&amixer->rsc, amixer->idx[0], | ||
207 | AMIXER, desc->msr, mgr->mgr.hw); | ||
208 | if (err) | ||
209 | return err; | ||
210 | |||
211 | /* Set amixer specific operations */ | ||
212 | amixer->rsc.ops = &amixer_basic_rsc_ops; | ||
213 | amixer->ops = &amixer_ops; | ||
214 | amixer->input = NULL; | ||
215 | amixer->sum = NULL; | ||
216 | |||
217 | amixer_setup(amixer, NULL, 0, NULL); | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static int amixer_rsc_uninit(struct amixer *amixer) | ||
223 | { | ||
224 | amixer_setup(amixer, NULL, 0, NULL); | ||
225 | rsc_uninit(&amixer->rsc); | ||
226 | amixer->ops = NULL; | ||
227 | amixer->input = NULL; | ||
228 | amixer->sum = NULL; | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int get_amixer_rsc(struct amixer_mgr *mgr, | ||
233 | const struct amixer_desc *desc, | ||
234 | struct amixer **ramixer) | ||
235 | { | ||
236 | int err = 0, i = 0; | ||
237 | unsigned int idx = 0; | ||
238 | struct amixer *amixer = NULL; | ||
239 | unsigned long flags; | ||
240 | |||
241 | *ramixer = NULL; | ||
242 | |||
243 | /* Allocate mem for amixer resource */ | ||
244 | amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); | ||
245 | if (NULL == amixer) { | ||
246 | err = -ENOMEM; | ||
247 | return err; | ||
248 | } | ||
249 | |||
250 | /* Check whether there are sufficient | ||
251 | * amixer resources to meet request. */ | ||
252 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
253 | for (i = 0; i < desc->msr; i++) { | ||
254 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | ||
255 | if (err) | ||
256 | break; | ||
257 | |||
258 | amixer->idx[i] = idx; | ||
259 | } | ||
260 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
261 | if (err) { | ||
262 | printk(KERN_ERR "Can't meet AMIXER resource request!\n"); | ||
263 | goto error; | ||
264 | } | ||
265 | |||
266 | err = amixer_rsc_init(amixer, desc, mgr); | ||
267 | if (err) | ||
268 | goto error; | ||
269 | |||
270 | *ramixer = amixer; | ||
271 | |||
272 | return 0; | ||
273 | |||
274 | error: | ||
275 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
276 | for (i--; i >= 0; i--) | ||
277 | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | ||
278 | |||
279 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
280 | kfree(amixer); | ||
281 | return err; | ||
282 | } | ||
283 | |||
284 | static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) | ||
285 | { | ||
286 | unsigned long flags; | ||
287 | int i = 0; | ||
288 | |||
289 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
290 | for (i = 0; i < amixer->rsc.msr; i++) | ||
291 | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | ||
292 | |||
293 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
294 | amixer_rsc_uninit(amixer); | ||
295 | kfree(amixer); | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) | ||
301 | { | ||
302 | int err = 0; | ||
303 | struct amixer_mgr *amixer_mgr; | ||
304 | |||
305 | *ramixer_mgr = NULL; | ||
306 | amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); | ||
307 | if (NULL == amixer_mgr) | ||
308 | return -ENOMEM; | ||
309 | |||
310 | err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); | ||
311 | if (err) | ||
312 | goto error; | ||
313 | |||
314 | spin_lock_init(&amixer_mgr->mgr_lock); | ||
315 | |||
316 | amixer_mgr->get_amixer = get_amixer_rsc; | ||
317 | amixer_mgr->put_amixer = put_amixer_rsc; | ||
318 | |||
319 | *ramixer_mgr = amixer_mgr; | ||
320 | |||
321 | return 0; | ||
322 | |||
323 | error: | ||
324 | kfree(amixer_mgr); | ||
325 | return err; | ||
326 | } | ||
327 | |||
328 | int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) | ||
329 | { | ||
330 | rsc_mgr_uninit(&amixer_mgr->mgr); | ||
331 | kfree(amixer_mgr); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /* SUM resource management */ | ||
336 | |||
337 | static int sum_master(struct rsc *rsc) | ||
338 | { | ||
339 | rsc->conj = 0; | ||
340 | return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0]; | ||
341 | } | ||
342 | |||
343 | static int sum_next_conj(struct rsc *rsc) | ||
344 | { | ||
345 | rsc->conj++; | ||
346 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | ||
347 | } | ||
348 | |||
349 | static int sum_index(const struct rsc *rsc) | ||
350 | { | ||
351 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | ||
352 | } | ||
353 | |||
354 | static int sum_output_slot(const struct rsc *rsc) | ||
355 | { | ||
356 | return (sum_index(rsc) << 4) + 0xc; | ||
357 | } | ||
358 | |||
359 | static struct rsc_ops sum_basic_rsc_ops = { | ||
360 | .master = sum_master, | ||
361 | .next_conj = sum_next_conj, | ||
362 | .index = sum_index, | ||
363 | .output_slot = sum_output_slot, | ||
364 | }; | ||
365 | |||
366 | static int sum_rsc_init(struct sum *sum, | ||
367 | const struct sum_desc *desc, | ||
368 | struct sum_mgr *mgr) | ||
369 | { | ||
370 | int err = 0; | ||
371 | |||
372 | err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); | ||
373 | if (err) | ||
374 | return err; | ||
375 | |||
376 | sum->rsc.ops = &sum_basic_rsc_ops; | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int sum_rsc_uninit(struct sum *sum) | ||
382 | { | ||
383 | rsc_uninit(&sum->rsc); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static int get_sum_rsc(struct sum_mgr *mgr, | ||
388 | const struct sum_desc *desc, | ||
389 | struct sum **rsum) | ||
390 | { | ||
391 | int err = 0, i = 0; | ||
392 | unsigned int idx = 0; | ||
393 | struct sum *sum = NULL; | ||
394 | unsigned long flags; | ||
395 | |||
396 | *rsum = NULL; | ||
397 | |||
398 | /* Allocate mem for sum resource */ | ||
399 | sum = kzalloc(sizeof(*sum), GFP_KERNEL); | ||
400 | if (NULL == sum) { | ||
401 | err = -ENOMEM; | ||
402 | return err; | ||
403 | } | ||
404 | |||
405 | /* Check whether there are sufficient sum resources to meet request. */ | ||
406 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
407 | for (i = 0; i < desc->msr; i++) { | ||
408 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | ||
409 | if (err) | ||
410 | break; | ||
411 | |||
412 | sum->idx[i] = idx; | ||
413 | } | ||
414 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
415 | if (err) { | ||
416 | printk(KERN_ERR "Can't meet SUM resource request!\n"); | ||
417 | goto error; | ||
418 | } | ||
419 | |||
420 | err = sum_rsc_init(sum, desc, mgr); | ||
421 | if (err) | ||
422 | goto error; | ||
423 | |||
424 | *rsum = sum; | ||
425 | |||
426 | return 0; | ||
427 | |||
428 | error: | ||
429 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
430 | for (i--; i >= 0; i--) | ||
431 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | ||
432 | |||
433 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
434 | kfree(sum); | ||
435 | return err; | ||
436 | } | ||
437 | |||
438 | static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) | ||
439 | { | ||
440 | unsigned long flags; | ||
441 | int i = 0; | ||
442 | |||
443 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
444 | for (i = 0; i < sum->rsc.msr; i++) | ||
445 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | ||
446 | |||
447 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
448 | sum_rsc_uninit(sum); | ||
449 | kfree(sum); | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) | ||
455 | { | ||
456 | int err = 0; | ||
457 | struct sum_mgr *sum_mgr; | ||
458 | |||
459 | *rsum_mgr = NULL; | ||
460 | sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); | ||
461 | if (NULL == sum_mgr) | ||
462 | return -ENOMEM; | ||
463 | |||
464 | err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); | ||
465 | if (err) | ||
466 | goto error; | ||
467 | |||
468 | spin_lock_init(&sum_mgr->mgr_lock); | ||
469 | |||
470 | sum_mgr->get_sum = get_sum_rsc; | ||
471 | sum_mgr->put_sum = put_sum_rsc; | ||
472 | |||
473 | *rsum_mgr = sum_mgr; | ||
474 | |||
475 | return 0; | ||
476 | |||
477 | error: | ||
478 | kfree(sum_mgr); | ||
479 | return err; | ||
480 | } | ||
481 | |||
482 | int sum_mgr_destroy(struct sum_mgr *sum_mgr) | ||
483 | { | ||
484 | rsc_mgr_uninit(&sum_mgr->mgr); | ||
485 | kfree(sum_mgr); | ||
486 | return 0; | ||
487 | } | ||
488 | |||
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h new file mode 100644 index 000000000000..cc49e5ab4750 --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.h | |||
@@ -0,0 +1,96 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctamixer.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the Audio Mixer | ||
12 | * resource management object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 21 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef CTAMIXER_H | ||
20 | #define CTAMIXER_H | ||
21 | |||
22 | #include "ctresource.h" | ||
23 | #include <linux/spinlock.h> | ||
24 | |||
25 | /* Define the descriptor of a summation node resource */ | ||
26 | struct sum { | ||
27 | struct rsc rsc; /* Basic resource info */ | ||
28 | unsigned char idx[8]; | ||
29 | }; | ||
30 | |||
31 | /* Define sum resource request description info */ | ||
32 | struct sum_desc { | ||
33 | unsigned int msr; | ||
34 | }; | ||
35 | |||
36 | struct sum_mgr { | ||
37 | struct rsc_mgr mgr; /* Basic resource manager info */ | ||
38 | spinlock_t mgr_lock; | ||
39 | |||
40 | /* request one sum resource */ | ||
41 | int (*get_sum)(struct sum_mgr *mgr, | ||
42 | const struct sum_desc *desc, struct sum **rsum); | ||
43 | /* return one sum resource */ | ||
44 | int (*put_sum)(struct sum_mgr *mgr, struct sum *sum); | ||
45 | }; | ||
46 | |||
47 | /* Constructor and destructor of daio resource manager */ | ||
48 | int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr); | ||
49 | int sum_mgr_destroy(struct sum_mgr *sum_mgr); | ||
50 | |||
51 | /* Define the descriptor of a amixer resource */ | ||
52 | struct amixer_rsc_ops; | ||
53 | |||
54 | struct amixer { | ||
55 | struct rsc rsc; /* Basic resource info */ | ||
56 | unsigned char idx[8]; | ||
57 | struct rsc *input; /* pointer to a resource acting as source */ | ||
58 | struct sum *sum; /* Put amixer output to this summation node */ | ||
59 | struct amixer_rsc_ops *ops; /* AMixer specific operations */ | ||
60 | }; | ||
61 | |||
62 | struct amixer_rsc_ops { | ||
63 | int (*set_input)(struct amixer *amixer, struct rsc *rsc); | ||
64 | int (*set_scale)(struct amixer *amixer, unsigned int scale); | ||
65 | int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv); | ||
66 | int (*set_sum)(struct amixer *amixer, struct sum *sum); | ||
67 | int (*commit_write)(struct amixer *amixer); | ||
68 | /* Only for interleaved recording */ | ||
69 | int (*commit_raw_write)(struct amixer *amixer); | ||
70 | int (*setup)(struct amixer *amixer, struct rsc *input, | ||
71 | unsigned int scale, struct sum *sum); | ||
72 | int (*get_scale)(struct amixer *amixer); | ||
73 | }; | ||
74 | |||
75 | /* Define amixer resource request description info */ | ||
76 | struct amixer_desc { | ||
77 | unsigned int msr; | ||
78 | }; | ||
79 | |||
80 | struct amixer_mgr { | ||
81 | struct rsc_mgr mgr; /* Basic resource manager info */ | ||
82 | spinlock_t mgr_lock; | ||
83 | |||
84 | /* request one amixer resource */ | ||
85 | int (*get_amixer)(struct amixer_mgr *mgr, | ||
86 | const struct amixer_desc *desc, | ||
87 | struct amixer **ramixer); | ||
88 | /* return one amixer resource */ | ||
89 | int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer); | ||
90 | }; | ||
91 | |||
92 | /* Constructor and destructor of amixer resource manager */ | ||
93 | int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr); | ||
94 | int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr); | ||
95 | |||
96 | #endif /* CTAMIXER_H */ | ||
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c new file mode 100644 index 000000000000..5f35374863fb --- /dev/null +++ b/sound/pci/ctxfi/ctatc.c | |||
@@ -0,0 +1,1605 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctatc.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of the device resource management | ||
12 | * object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date Mar 28 2008 | ||
16 | */ | ||
17 | |||
18 | #include "ctatc.h" | ||
19 | #include "ctpcm.h" | ||
20 | #include "ctmixer.h" | ||
21 | #include "ctdrv.h" | ||
22 | #include "cthardware.h" | ||
23 | #include "ctsrc.h" | ||
24 | #include "ctamixer.h" | ||
25 | #include "ctdaio.h" | ||
26 | #include <linux/delay.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/control.h> | ||
29 | #include <sound/asoundef.h> | ||
30 | |||
31 | #define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ | ||
32 | #define DAIONUM 7 | ||
33 | #define MAX_MULTI_CHN 8 | ||
34 | |||
35 | #define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \ | ||
36 | | IEC958_AES0_CON_NOT_COPYRIGHT) \ | ||
37 | | ((IEC958_AES1_CON_MIXER \ | ||
38 | | IEC958_AES1_CON_ORIGINAL) << 8) \ | ||
39 | | (0x10 << 16) \ | ||
40 | | ((IEC958_AES3_CON_FS_48000) << 24)) | ||
41 | |||
42 | static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { | ||
43 | [CTSB0760] = {.subsys = PCI_SUBSYS_CREATIVE_SB0760, | ||
44 | .nm_model = "SB076x"}, | ||
45 | [CTHENDRIX] = {.subsys = PCI_SUBSYS_CREATIVE_HENDRIX, | ||
46 | .nm_model = "Hendrix"}, | ||
47 | [CTSB08801] = {.subsys = PCI_SUBSYS_CREATIVE_SB08801, | ||
48 | .nm_model = "SB0880"}, | ||
49 | [CTSB08802] = {.subsys = PCI_SUBSYS_CREATIVE_SB08802, | ||
50 | .nm_model = "SB0880"}, | ||
51 | [CTSB08803] = {.subsys = PCI_SUBSYS_CREATIVE_SB08803, | ||
52 | .nm_model = "SB0880"} | ||
53 | }; | ||
54 | |||
55 | static struct ct_atc_chip_details atc_chip_details[] = { | ||
56 | {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K1, | ||
57 | .sub_details = NULL, | ||
58 | .nm_card = "X-Fi 20k1"}, | ||
59 | {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K2, | ||
60 | .sub_details = atc_sub_details, | ||
61 | .nm_card = "X-Fi 20k2"}, | ||
62 | {} /* terminator */ | ||
63 | }; | ||
64 | |||
65 | static struct { | ||
66 | int (*create)(struct ct_atc *atc, | ||
67 | enum CTALSADEVS device, const char *device_name); | ||
68 | int (*destroy)(void *alsa_dev); | ||
69 | const char *public_name; | ||
70 | } alsa_dev_funcs[NUM_CTALSADEVS] = { | ||
71 | [FRONT] = { .create = ct_alsa_pcm_create, | ||
72 | .destroy = NULL, | ||
73 | .public_name = "Front/WaveIn"}, | ||
74 | [REAR] = { .create = ct_alsa_pcm_create, | ||
75 | .destroy = NULL, | ||
76 | .public_name = "Rear"}, | ||
77 | [CLFE] = { .create = ct_alsa_pcm_create, | ||
78 | .destroy = NULL, | ||
79 | .public_name = "Center/LFE"}, | ||
80 | [SURROUND] = { .create = ct_alsa_pcm_create, | ||
81 | .destroy = NULL, | ||
82 | .public_name = "Surround"}, | ||
83 | [IEC958] = { .create = ct_alsa_pcm_create, | ||
84 | .destroy = NULL, | ||
85 | .public_name = "IEC958 Non-audio"}, | ||
86 | |||
87 | [MIXER] = { .create = ct_alsa_mix_create, | ||
88 | .destroy = NULL, | ||
89 | .public_name = "Mixer"} | ||
90 | }; | ||
91 | |||
92 | typedef int (*create_t)(void *, void **); | ||
93 | typedef int (*destroy_t)(void *); | ||
94 | |||
95 | static struct { | ||
96 | int (*create)(void *hw, void **rmgr); | ||
97 | int (*destroy)(void *mgr); | ||
98 | } rsc_mgr_funcs[NUM_RSCTYP] = { | ||
99 | [SRC] = { .create = (create_t)src_mgr_create, | ||
100 | .destroy = (destroy_t)src_mgr_destroy }, | ||
101 | [SRCIMP] = { .create = (create_t)srcimp_mgr_create, | ||
102 | .destroy = (destroy_t)srcimp_mgr_destroy }, | ||
103 | [AMIXER] = { .create = (create_t)amixer_mgr_create, | ||
104 | .destroy = (destroy_t)amixer_mgr_destroy }, | ||
105 | [SUM] = { .create = (create_t)sum_mgr_create, | ||
106 | .destroy = (destroy_t)sum_mgr_destroy }, | ||
107 | [DAIO] = { .create = (create_t)daio_mgr_create, | ||
108 | .destroy = (destroy_t)daio_mgr_destroy } | ||
109 | }; | ||
110 | |||
111 | static int | ||
112 | atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
113 | |||
114 | /* * | ||
115 | * Only mono and interleaved modes are supported now. | ||
116 | * Always allocates a contiguous channel block. | ||
117 | * */ | ||
118 | |||
119 | static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
120 | { | ||
121 | unsigned long flags; | ||
122 | struct snd_pcm_runtime *runtime; | ||
123 | struct ct_vm *vm; | ||
124 | |||
125 | if (NULL == apcm->substream) | ||
126 | return 0; | ||
127 | |||
128 | runtime = apcm->substream->runtime; | ||
129 | vm = atc->vm; | ||
130 | |||
131 | spin_lock_irqsave(&atc->vm_lock, flags); | ||
132 | apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); | ||
133 | spin_unlock_irqrestore(&atc->vm_lock, flags); | ||
134 | |||
135 | if (NULL == apcm->vm_block) | ||
136 | return -ENOENT; | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
142 | { | ||
143 | unsigned long flags; | ||
144 | struct ct_vm *vm; | ||
145 | |||
146 | if (NULL == apcm->vm_block) | ||
147 | return; | ||
148 | |||
149 | vm = atc->vm; | ||
150 | |||
151 | spin_lock_irqsave(&atc->vm_lock, flags); | ||
152 | vm->unmap(vm, apcm->vm_block); | ||
153 | spin_unlock_irqrestore(&atc->vm_lock, flags); | ||
154 | |||
155 | apcm->vm_block = NULL; | ||
156 | } | ||
157 | |||
158 | static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) | ||
159 | { | ||
160 | struct ct_vm *vm; | ||
161 | void *kvirt_addr; | ||
162 | unsigned long phys_addr; | ||
163 | unsigned long flags; | ||
164 | |||
165 | spin_lock_irqsave(&atc->vm_lock, flags); | ||
166 | vm = atc->vm; | ||
167 | kvirt_addr = vm->get_ptp_virt(vm, index); | ||
168 | if (kvirt_addr == NULL) | ||
169 | phys_addr = (~0UL); | ||
170 | else | ||
171 | phys_addr = virt_to_phys(kvirt_addr); | ||
172 | |||
173 | spin_unlock_irqrestore(&atc->vm_lock, flags); | ||
174 | |||
175 | return phys_addr; | ||
176 | } | ||
177 | |||
178 | static unsigned int convert_format(snd_pcm_format_t snd_format) | ||
179 | { | ||
180 | switch (snd_format) { | ||
181 | case SNDRV_PCM_FORMAT_U8: | ||
182 | case SNDRV_PCM_FORMAT_S8: | ||
183 | return SRC_SF_U8; | ||
184 | case SNDRV_PCM_FORMAT_S16_LE: | ||
185 | case SNDRV_PCM_FORMAT_U16_LE: | ||
186 | return SRC_SF_S16; | ||
187 | case SNDRV_PCM_FORMAT_S24_3LE: | ||
188 | return SRC_SF_S24; | ||
189 | case SNDRV_PCM_FORMAT_S24_LE: | ||
190 | case SNDRV_PCM_FORMAT_S32_LE: | ||
191 | return SRC_SF_S32; | ||
192 | default: | ||
193 | printk(KERN_ERR "not recognized snd format is %d \n", | ||
194 | snd_format); | ||
195 | return SRC_SF_S16; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static unsigned int | ||
200 | atc_get_pitch(unsigned int input_rate, unsigned int output_rate) | ||
201 | { | ||
202 | unsigned int pitch = 0; | ||
203 | int b = 0; | ||
204 | |||
205 | /* get pitch and convert to fixed-point 8.24 format. */ | ||
206 | pitch = (input_rate / output_rate) << 24; | ||
207 | input_rate %= output_rate; | ||
208 | input_rate /= 100; | ||
209 | output_rate /= 100; | ||
210 | for (b = 31; ((b >= 0) && !(input_rate >> b)); ) | ||
211 | b--; | ||
212 | |||
213 | if (b >= 0) { | ||
214 | input_rate <<= (31 - b); | ||
215 | input_rate /= output_rate; | ||
216 | b = 24 - (31 - b); | ||
217 | if (b >= 0) | ||
218 | input_rate <<= b; | ||
219 | else | ||
220 | input_rate >>= -b; | ||
221 | |||
222 | pitch |= input_rate; | ||
223 | } | ||
224 | |||
225 | return pitch; | ||
226 | } | ||
227 | |||
228 | static int select_rom(unsigned int pitch) | ||
229 | { | ||
230 | if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) { | ||
231 | /* 0.26 <= pitch <= 1.72 */ | ||
232 | return 1; | ||
233 | } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) { | ||
234 | /* pitch == 1.8375 */ | ||
235 | return 2; | ||
236 | } else if (0x02000000 == pitch) { | ||
237 | /* pitch == 2 */ | ||
238 | return 3; | ||
239 | } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) { | ||
240 | /* 0 <= pitch <= 8 */ | ||
241 | return 0; | ||
242 | } else { | ||
243 | return -ENOENT; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
248 | { | ||
249 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
250 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
251 | struct src_desc desc = {0}; | ||
252 | struct amixer_desc mix_dsc = {0}; | ||
253 | struct src *src = NULL; | ||
254 | struct amixer *amixer = NULL; | ||
255 | int err = 0; | ||
256 | int n_amixer = apcm->substream->runtime->channels, i = 0; | ||
257 | int device = apcm->substream->pcm->device; | ||
258 | unsigned int pitch = 0; | ||
259 | unsigned long flags; | ||
260 | |||
261 | if (NULL != apcm->src) { | ||
262 | /* Prepared pcm playback */ | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* Get SRC resource */ | ||
267 | desc.multi = apcm->substream->runtime->channels; | ||
268 | desc.msr = atc->msr; | ||
269 | desc.mode = MEMRD; | ||
270 | err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); | ||
271 | if (err) | ||
272 | goto error1; | ||
273 | |||
274 | pitch = atc_get_pitch(apcm->substream->runtime->rate, | ||
275 | (atc->rsr * atc->msr)); | ||
276 | src = apcm->src; | ||
277 | src->ops->set_pitch(src, pitch); | ||
278 | src->ops->set_rom(src, select_rom(pitch)); | ||
279 | src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); | ||
280 | src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); | ||
281 | |||
282 | /* Get AMIXER resource */ | ||
283 | n_amixer = (n_amixer < 2) ? 2 : n_amixer; | ||
284 | apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||
285 | if (NULL == apcm->amixers) { | ||
286 | err = -ENOMEM; | ||
287 | goto error1; | ||
288 | } | ||
289 | mix_dsc.msr = atc->msr; | ||
290 | for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { | ||
291 | err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, | ||
292 | (struct amixer **)&apcm->amixers[i]); | ||
293 | if (err) | ||
294 | goto error1; | ||
295 | |||
296 | apcm->n_amixer++; | ||
297 | } | ||
298 | |||
299 | /* Set up device virtual mem map */ | ||
300 | err = ct_map_audio_buffer(atc, apcm); | ||
301 | if (err < 0) | ||
302 | goto error1; | ||
303 | |||
304 | /* Connect resources */ | ||
305 | src = apcm->src; | ||
306 | for (i = 0; i < n_amixer; i++) { | ||
307 | amixer = apcm->amixers[i]; | ||
308 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
309 | amixer->ops->setup(amixer, &src->rsc, | ||
310 | INIT_VOL, atc->pcm[i+device*2]); | ||
311 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
312 | src = src->ops->next_interleave(src); | ||
313 | if (NULL == src) | ||
314 | src = apcm->src; | ||
315 | } | ||
316 | |||
317 | return 0; | ||
318 | |||
319 | error1: | ||
320 | atc_pcm_release_resources(atc, apcm); | ||
321 | return err; | ||
322 | } | ||
323 | |||
324 | static int | ||
325 | atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
326 | { | ||
327 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
328 | struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
329 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
330 | struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; | ||
331 | struct srcimp *srcimp = NULL; | ||
332 | int i = 0; | ||
333 | |||
334 | if (NULL != apcm->srcimps) { | ||
335 | for (i = 0; i < apcm->n_srcimp; i++) { | ||
336 | srcimp = apcm->srcimps[i]; | ||
337 | srcimp->ops->unmap(srcimp); | ||
338 | srcimp_mgr->put_srcimp(srcimp_mgr, srcimp); | ||
339 | apcm->srcimps[i] = NULL; | ||
340 | } | ||
341 | kfree(apcm->srcimps); | ||
342 | apcm->srcimps = NULL; | ||
343 | } | ||
344 | |||
345 | if (NULL != apcm->srccs) { | ||
346 | for (i = 0; i < apcm->n_srcc; i++) { | ||
347 | src_mgr->put_src(src_mgr, apcm->srccs[i]); | ||
348 | apcm->srccs[i] = NULL; | ||
349 | } | ||
350 | kfree(apcm->srccs); | ||
351 | apcm->srccs = NULL; | ||
352 | } | ||
353 | |||
354 | if (NULL != apcm->amixers) { | ||
355 | for (i = 0; i < apcm->n_amixer; i++) { | ||
356 | amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]); | ||
357 | apcm->amixers[i] = NULL; | ||
358 | } | ||
359 | kfree(apcm->amixers); | ||
360 | apcm->amixers = NULL; | ||
361 | } | ||
362 | |||
363 | if (NULL != apcm->mono) { | ||
364 | sum_mgr->put_sum(sum_mgr, apcm->mono); | ||
365 | apcm->mono = NULL; | ||
366 | } | ||
367 | |||
368 | if (NULL != apcm->src) { | ||
369 | src_mgr->put_src(src_mgr, apcm->src); | ||
370 | apcm->src = NULL; | ||
371 | } | ||
372 | |||
373 | if (NULL != apcm->vm_block) { | ||
374 | /* Undo device virtual mem map */ | ||
375 | ct_unmap_audio_buffer(atc, apcm); | ||
376 | apcm->vm_block = NULL; | ||
377 | } | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
383 | { | ||
384 | unsigned int max_cisz = 0; | ||
385 | struct src *src = apcm->src; | ||
386 | |||
387 | max_cisz = src->multi * src->rsc.msr; | ||
388 | max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8); | ||
389 | |||
390 | src->ops->set_sa(src, apcm->vm_block->addr); | ||
391 | src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); | ||
392 | src->ops->set_ca(src, apcm->vm_block->addr + max_cisz); | ||
393 | src->ops->set_cisz(src, max_cisz); | ||
394 | |||
395 | src->ops->set_bm(src, 1); | ||
396 | src->ops->set_state(src, SRC_STATE_INIT); | ||
397 | src->ops->commit_write(src); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
403 | { | ||
404 | struct src *src = NULL; | ||
405 | int i = 0; | ||
406 | |||
407 | src = apcm->src; | ||
408 | src->ops->set_bm(src, 0); | ||
409 | src->ops->set_state(src, SRC_STATE_OFF); | ||
410 | src->ops->commit_write(src); | ||
411 | |||
412 | if (NULL != apcm->srccs) { | ||
413 | for (i = 0; i < apcm->n_srcc; i++) { | ||
414 | src = apcm->srccs[i]; | ||
415 | src->ops->set_bm(src, 0); | ||
416 | src->ops->set_state(src, SRC_STATE_OFF); | ||
417 | src->ops->commit_write(src); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | apcm->started = 0; | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int | ||
427 | atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
428 | { | ||
429 | struct src *src = apcm->src; | ||
430 | u32 size = 0, max_cisz = 0; | ||
431 | int position = 0; | ||
432 | |||
433 | position = src->ops->get_ca(src); | ||
434 | |||
435 | size = apcm->vm_block->size; | ||
436 | max_cisz = src->multi * src->rsc.msr; | ||
437 | max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8); | ||
438 | |||
439 | return (position + size - max_cisz - apcm->vm_block->addr) % size; | ||
440 | } | ||
441 | |||
442 | struct src_node_conf_t { | ||
443 | unsigned int pitch; | ||
444 | unsigned int msr:8; | ||
445 | unsigned int mix_msr:8; | ||
446 | unsigned int imp_msr:8; | ||
447 | unsigned int vo:1; | ||
448 | }; | ||
449 | |||
450 | static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, | ||
451 | struct src_node_conf_t *conf, int *n_srcc) | ||
452 | { | ||
453 | unsigned int pitch = 0; | ||
454 | |||
455 | /* get pitch and convert to fixed-point 8.24 format. */ | ||
456 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
457 | apcm->substream->runtime->rate); | ||
458 | *n_srcc = 0; | ||
459 | |||
460 | if (1 == atc->msr) { | ||
461 | *n_srcc = apcm->substream->runtime->channels; | ||
462 | conf[0].pitch = pitch; | ||
463 | conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; | ||
464 | conf[0].vo = 1; | ||
465 | } else if (2 == atc->msr) { | ||
466 | if (0x8000000 < pitch) { | ||
467 | /* Need two-stage SRCs, SRCIMPs and | ||
468 | * AMIXERs for converting format */ | ||
469 | conf[0].pitch = (atc->msr << 24); | ||
470 | conf[0].msr = conf[0].mix_msr = 1; | ||
471 | conf[0].imp_msr = atc->msr; | ||
472 | conf[0].vo = 0; | ||
473 | conf[1].pitch = atc_get_pitch(atc->rsr, | ||
474 | apcm->substream->runtime->rate); | ||
475 | conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1; | ||
476 | conf[1].vo = 1; | ||
477 | *n_srcc = apcm->substream->runtime->channels * 2; | ||
478 | } else if (0x1000000 < pitch) { | ||
479 | /* Need one-stage SRCs, SRCIMPs and | ||
480 | * AMIXERs for converting format */ | ||
481 | conf[0].pitch = pitch; | ||
482 | conf[0].msr = conf[0].mix_msr | ||
483 | = conf[0].imp_msr = atc->msr; | ||
484 | conf[0].vo = 1; | ||
485 | *n_srcc = apcm->substream->runtime->channels; | ||
486 | } | ||
487 | } | ||
488 | } | ||
489 | |||
490 | static int | ||
491 | atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
492 | { | ||
493 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
494 | struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
495 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
496 | struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; | ||
497 | struct src_desc src_dsc = {0}; | ||
498 | struct src *src = NULL; | ||
499 | struct srcimp_desc srcimp_dsc = {0}; | ||
500 | struct srcimp *srcimp = NULL; | ||
501 | struct amixer_desc mix_dsc = {0}; | ||
502 | struct sum_desc sum_dsc = {0}; | ||
503 | unsigned int pitch = 0; | ||
504 | int multi = 0, err = 0, i = 0; | ||
505 | int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; | ||
506 | struct src_node_conf_t src_node_conf[2] = {{0} }; | ||
507 | |||
508 | /* The numbers of converting SRCs and SRCIMPs should be determined | ||
509 | * by pitch value. */ | ||
510 | |||
511 | multi = apcm->substream->runtime->channels; | ||
512 | |||
513 | /* get pitch and convert to fixed-point 8.24 format. */ | ||
514 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
515 | apcm->substream->runtime->rate); | ||
516 | |||
517 | setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc); | ||
518 | n_sum = (1 == multi) ? 1 : 0; | ||
519 | n_amixer += n_sum * 2 + n_srcc; | ||
520 | n_srcimp += n_srcc; | ||
521 | if ((multi > 1) && (0x8000000 >= pitch)) { | ||
522 | /* Need extra AMIXERs and SRCIMPs for special treatment | ||
523 | * of interleaved recording of conjugate channels */ | ||
524 | n_amixer += multi * atc->msr; | ||
525 | n_srcimp += multi * atc->msr; | ||
526 | } else { | ||
527 | n_srcimp += multi; | ||
528 | } | ||
529 | |||
530 | if (n_srcc) { | ||
531 | apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); | ||
532 | if (NULL == apcm->srccs) | ||
533 | return -ENOMEM; | ||
534 | } | ||
535 | if (n_amixer) { | ||
536 | apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||
537 | if (NULL == apcm->amixers) { | ||
538 | err = -ENOMEM; | ||
539 | goto error1; | ||
540 | } | ||
541 | } | ||
542 | apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); | ||
543 | if (NULL == apcm->srcimps) { | ||
544 | err = -ENOMEM; | ||
545 | goto error1; | ||
546 | } | ||
547 | |||
548 | /* Allocate SRCs for sample rate conversion if needed */ | ||
549 | src_dsc.multi = 1; | ||
550 | src_dsc.mode = ARCRW; | ||
551 | for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) { | ||
552 | src_dsc.msr = src_node_conf[i/multi].msr; | ||
553 | err = src_mgr->get_src(src_mgr, &src_dsc, | ||
554 | (struct src **)&apcm->srccs[i]); | ||
555 | if (err) | ||
556 | goto error1; | ||
557 | |||
558 | src = apcm->srccs[i]; | ||
559 | pitch = src_node_conf[i/multi].pitch; | ||
560 | src->ops->set_pitch(src, pitch); | ||
561 | src->ops->set_rom(src, select_rom(pitch)); | ||
562 | src->ops->set_vo(src, src_node_conf[i/multi].vo); | ||
563 | |||
564 | apcm->n_srcc++; | ||
565 | } | ||
566 | |||
567 | /* Allocate AMIXERs for routing SRCs of conversion if needed */ | ||
568 | for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { | ||
569 | if (i < (n_sum*2)) | ||
570 | mix_dsc.msr = atc->msr; | ||
571 | else if (i < (n_sum*2+n_srcc)) | ||
572 | mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr; | ||
573 | else | ||
574 | mix_dsc.msr = 1; | ||
575 | |||
576 | err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, | ||
577 | (struct amixer **)&apcm->amixers[i]); | ||
578 | if (err) | ||
579 | goto error1; | ||
580 | |||
581 | apcm->n_amixer++; | ||
582 | } | ||
583 | |||
584 | /* Allocate a SUM resource to mix all input channels together */ | ||
585 | sum_dsc.msr = atc->msr; | ||
586 | err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono); | ||
587 | if (err) | ||
588 | goto error1; | ||
589 | |||
590 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
591 | apcm->substream->runtime->rate); | ||
592 | /* Allocate SRCIMP resources */ | ||
593 | for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) { | ||
594 | if (i < (n_srcc)) | ||
595 | srcimp_dsc.msr = src_node_conf[i/multi].imp_msr; | ||
596 | else if (1 == multi) | ||
597 | srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1; | ||
598 | else | ||
599 | srcimp_dsc.msr = 1; | ||
600 | |||
601 | err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp); | ||
602 | if (err) | ||
603 | goto error1; | ||
604 | |||
605 | apcm->srcimps[i] = srcimp; | ||
606 | apcm->n_srcimp++; | ||
607 | } | ||
608 | |||
609 | /* Allocate a SRC for writing data to host memory */ | ||
610 | src_dsc.multi = apcm->substream->runtime->channels; | ||
611 | src_dsc.msr = 1; | ||
612 | src_dsc.mode = MEMWR; | ||
613 | err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src); | ||
614 | if (err) | ||
615 | goto error1; | ||
616 | |||
617 | src = apcm->src; | ||
618 | src->ops->set_pitch(src, pitch); | ||
619 | |||
620 | /* Set up device virtual mem map */ | ||
621 | err = ct_map_audio_buffer(atc, apcm); | ||
622 | if (err < 0) | ||
623 | goto error1; | ||
624 | |||
625 | return 0; | ||
626 | |||
627 | error1: | ||
628 | atc_pcm_release_resources(atc, apcm); | ||
629 | return err; | ||
630 | } | ||
631 | |||
632 | static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
633 | { | ||
634 | struct src *src = NULL; | ||
635 | struct amixer *amixer = NULL; | ||
636 | struct srcimp *srcimp = NULL; | ||
637 | struct ct_mixer *mixer = atc->mixer; | ||
638 | struct sum *mono = NULL; | ||
639 | struct rsc *out_ports[8] = {NULL}; | ||
640 | int err = 0, i = 0, j = 0, n_sum = 0, multi = 0; | ||
641 | unsigned int pitch = 0; | ||
642 | int mix_base = 0, imp_base = 0; | ||
643 | |||
644 | if (NULL != apcm->src) { | ||
645 | /* Prepared pcm capture */ | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | /* Get needed resources. */ | ||
650 | err = atc_pcm_capture_get_resources(atc, apcm); | ||
651 | if (err) | ||
652 | return err; | ||
653 | |||
654 | /* Connect resources */ | ||
655 | mixer->get_output_ports(mixer, MIX_PCMO_FRONT, | ||
656 | &out_ports[0], &out_ports[1]); | ||
657 | |||
658 | multi = apcm->substream->runtime->channels; | ||
659 | if (1 == multi) { | ||
660 | mono = apcm->mono; | ||
661 | for (i = 0; i < 2; i++) { | ||
662 | amixer = apcm->amixers[i]; | ||
663 | amixer->ops->setup(amixer, out_ports[i], | ||
664 | MONO_SUM_SCALE, mono); | ||
665 | } | ||
666 | out_ports[0] = &mono->rsc; | ||
667 | n_sum = 1; | ||
668 | mix_base = n_sum * 2; | ||
669 | } | ||
670 | |||
671 | for (i = 0; i < apcm->n_srcc; i++) { | ||
672 | src = apcm->srccs[i]; | ||
673 | srcimp = apcm->srcimps[imp_base+i]; | ||
674 | amixer = apcm->amixers[mix_base+i]; | ||
675 | srcimp->ops->map(srcimp, src, out_ports[i%multi]); | ||
676 | amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); | ||
677 | out_ports[i%multi] = &amixer->rsc; | ||
678 | } | ||
679 | |||
680 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
681 | apcm->substream->runtime->rate); | ||
682 | |||
683 | if ((multi > 1) && (pitch <= 0x8000000)) { | ||
684 | /* Special connection for interleaved | ||
685 | * recording with conjugate channels */ | ||
686 | for (i = 0; i < multi; i++) { | ||
687 | out_ports[i]->ops->master(out_ports[i]); | ||
688 | for (j = 0; j < atc->msr; j++) { | ||
689 | amixer = apcm->amixers[apcm->n_srcc+j*multi+i]; | ||
690 | amixer->ops->set_input(amixer, out_ports[i]); | ||
691 | amixer->ops->set_scale(amixer, INIT_VOL); | ||
692 | amixer->ops->set_sum(amixer, NULL); | ||
693 | amixer->ops->commit_raw_write(amixer); | ||
694 | out_ports[i]->ops->next_conj(out_ports[i]); | ||
695 | |||
696 | srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i]; | ||
697 | srcimp->ops->map(srcimp, apcm->src, | ||
698 | &amixer->rsc); | ||
699 | } | ||
700 | } | ||
701 | } else { | ||
702 | for (i = 0; i < multi; i++) { | ||
703 | srcimp = apcm->srcimps[apcm->n_srcc+i]; | ||
704 | srcimp->ops->map(srcimp, apcm->src, out_ports[i]); | ||
705 | } | ||
706 | } | ||
707 | |||
708 | return 0; | ||
709 | } | ||
710 | |||
711 | static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
712 | { | ||
713 | struct src *src = NULL; | ||
714 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
715 | int i = 0, multi = 0; | ||
716 | |||
717 | if (apcm->started) | ||
718 | return 0; | ||
719 | |||
720 | apcm->started = 1; | ||
721 | multi = apcm->substream->runtime->channels; | ||
722 | /* Set up converting SRCs */ | ||
723 | for (i = 0; i < apcm->n_srcc; i++) { | ||
724 | src = apcm->srccs[i]; | ||
725 | src->ops->set_pm(src, ((i%multi) != (multi-1))); | ||
726 | src_mgr->src_disable(src_mgr, src); | ||
727 | } | ||
728 | |||
729 | /* Set up recording SRC */ | ||
730 | src = apcm->src; | ||
731 | src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); | ||
732 | src->ops->set_sa(src, apcm->vm_block->addr); | ||
733 | src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); | ||
734 | src->ops->set_ca(src, apcm->vm_block->addr); | ||
735 | src_mgr->src_disable(src_mgr, src); | ||
736 | |||
737 | /* Disable relevant SRCs firstly */ | ||
738 | src_mgr->commit_write(src_mgr); | ||
739 | |||
740 | /* Enable SRCs respectively */ | ||
741 | for (i = 0; i < apcm->n_srcc; i++) { | ||
742 | src = apcm->srccs[i]; | ||
743 | src->ops->set_state(src, SRC_STATE_RUN); | ||
744 | src->ops->commit_write(src); | ||
745 | src_mgr->src_enable_s(src_mgr, src); | ||
746 | } | ||
747 | src = apcm->src; | ||
748 | src->ops->set_bm(src, 1); | ||
749 | src->ops->set_state(src, SRC_STATE_RUN); | ||
750 | src->ops->commit_write(src); | ||
751 | src_mgr->src_enable_s(src_mgr, src); | ||
752 | |||
753 | /* Enable relevant SRCs synchronously */ | ||
754 | src_mgr->commit_write(src_mgr); | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int | ||
760 | atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
761 | { | ||
762 | struct src *src = apcm->src; | ||
763 | |||
764 | return src->ops->get_ca(src) - apcm->vm_block->addr; | ||
765 | } | ||
766 | |||
767 | static int spdif_passthru_playback_get_resources(struct ct_atc *atc, | ||
768 | struct ct_atc_pcm *apcm) | ||
769 | { | ||
770 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
771 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
772 | struct src_desc desc = {0}; | ||
773 | struct amixer_desc mix_dsc = {0}; | ||
774 | struct src *src = NULL; | ||
775 | int err = 0; | ||
776 | int n_amixer = apcm->substream->runtime->channels, i = 0; | ||
777 | unsigned int pitch = 0, rsr = atc->pll_rate; | ||
778 | |||
779 | /* Get SRC resource */ | ||
780 | desc.multi = apcm->substream->runtime->channels; | ||
781 | desc.msr = 1; | ||
782 | while (apcm->substream->runtime->rate > (rsr * desc.msr)) | ||
783 | desc.msr <<= 1; | ||
784 | |||
785 | desc.mode = MEMRD; | ||
786 | err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); | ||
787 | if (err) | ||
788 | goto error1; | ||
789 | |||
790 | pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr)); | ||
791 | src = apcm->src; | ||
792 | src->ops->set_pitch(src, pitch); | ||
793 | src->ops->set_rom(src, select_rom(pitch)); | ||
794 | src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); | ||
795 | src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); | ||
796 | src->ops->set_bp(src, 1); | ||
797 | |||
798 | /* Get AMIXER resource */ | ||
799 | n_amixer = (n_amixer < 2) ? 2 : n_amixer; | ||
800 | apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||
801 | if (NULL == apcm->amixers) { | ||
802 | err = -ENOMEM; | ||
803 | goto error1; | ||
804 | } | ||
805 | mix_dsc.msr = desc.msr; | ||
806 | for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { | ||
807 | err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, | ||
808 | (struct amixer **)&apcm->amixers[i]); | ||
809 | if (err) | ||
810 | goto error1; | ||
811 | |||
812 | apcm->n_amixer++; | ||
813 | } | ||
814 | |||
815 | /* Set up device virtual mem map */ | ||
816 | err = ct_map_audio_buffer(atc, apcm); | ||
817 | if (err < 0) | ||
818 | goto error1; | ||
819 | |||
820 | return 0; | ||
821 | |||
822 | error1: | ||
823 | atc_pcm_release_resources(atc, apcm); | ||
824 | return err; | ||
825 | } | ||
826 | |||
827 | static int | ||
828 | spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
829 | { | ||
830 | struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio); | ||
831 | unsigned long flags; | ||
832 | unsigned int rate = apcm->substream->runtime->rate; | ||
833 | unsigned int status = 0; | ||
834 | int err = 0; | ||
835 | unsigned char iec958_con_fs = 0; | ||
836 | |||
837 | switch (rate) { | ||
838 | case 48000: | ||
839 | iec958_con_fs = IEC958_AES3_CON_FS_48000; | ||
840 | break; | ||
841 | case 44100: | ||
842 | iec958_con_fs = IEC958_AES3_CON_FS_44100; | ||
843 | break; | ||
844 | case 32000: | ||
845 | iec958_con_fs = IEC958_AES3_CON_FS_32000; | ||
846 | break; | ||
847 | default: | ||
848 | return -ENOENT; | ||
849 | } | ||
850 | |||
851 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
852 | dao->ops->get_spos(dao, &status); | ||
853 | if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { | ||
854 | status &= ((~IEC958_AES3_CON_FS) << 24); | ||
855 | status |= (iec958_con_fs << 24); | ||
856 | dao->ops->set_spos(dao, status); | ||
857 | dao->ops->commit_write(dao); | ||
858 | } | ||
859 | if ((rate != atc->pll_rate) && (32000 != rate)) { | ||
860 | err = ((struct hw *)atc->hw)->pll_init(atc->hw, rate); | ||
861 | atc->pll_rate = err ? 0 : rate; | ||
862 | } | ||
863 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
864 | |||
865 | return err; | ||
866 | } | ||
867 | |||
868 | static int | ||
869 | spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
870 | { | ||
871 | struct src *src = NULL; | ||
872 | struct amixer *amixer = NULL; | ||
873 | struct dao *dao = NULL; | ||
874 | int err = 0; | ||
875 | int i = 0; | ||
876 | unsigned long flags; | ||
877 | |||
878 | if (NULL != apcm->src) | ||
879 | return 0; | ||
880 | |||
881 | /* Configure SPDIFOO and PLL to passthrough mode; | ||
882 | * determine pll_rate. */ | ||
883 | err = spdif_passthru_playback_setup(atc, apcm); | ||
884 | if (err) | ||
885 | return err; | ||
886 | |||
887 | /* Get needed resources. */ | ||
888 | err = spdif_passthru_playback_get_resources(atc, apcm); | ||
889 | if (err) | ||
890 | return err; | ||
891 | |||
892 | /* Connect resources */ | ||
893 | src = apcm->src; | ||
894 | for (i = 0; i < apcm->n_amixer; i++) { | ||
895 | amixer = apcm->amixers[i]; | ||
896 | amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); | ||
897 | src = src->ops->next_interleave(src); | ||
898 | if (NULL == src) | ||
899 | src = apcm->src; | ||
900 | } | ||
901 | /* Connect to SPDIFOO */ | ||
902 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
903 | dao = container_of(atc->daios[SPDIFOO], struct dao, daio); | ||
904 | amixer = apcm->amixers[0]; | ||
905 | dao->ops->set_left_input(dao, &amixer->rsc); | ||
906 | amixer = apcm->amixers[1]; | ||
907 | dao->ops->set_right_input(dao, &amixer->rsc); | ||
908 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
909 | |||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | static int atc_select_line_in(struct ct_atc *atc) | ||
914 | { | ||
915 | struct hw *hw = atc->hw; | ||
916 | struct ct_mixer *mixer = atc->mixer; | ||
917 | struct src *src = NULL; | ||
918 | |||
919 | if (hw->is_adc_source_selected(hw, ADC_LINEIN)) | ||
920 | return 0; | ||
921 | |||
922 | mixer->set_input_left(mixer, MIX_MIC_IN, NULL); | ||
923 | mixer->set_input_right(mixer, MIX_MIC_IN, NULL); | ||
924 | |||
925 | hw->select_adc_source(hw, ADC_LINEIN); | ||
926 | |||
927 | src = atc->srcs[2]; | ||
928 | mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); | ||
929 | src = atc->srcs[3]; | ||
930 | mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); | ||
931 | |||
932 | return 0; | ||
933 | } | ||
934 | |||
935 | static int atc_select_mic_in(struct ct_atc *atc) | ||
936 | { | ||
937 | struct hw *hw = atc->hw; | ||
938 | struct ct_mixer *mixer = atc->mixer; | ||
939 | struct src *src = NULL; | ||
940 | |||
941 | if (hw->is_adc_source_selected(hw, ADC_MICIN)) | ||
942 | return 0; | ||
943 | |||
944 | mixer->set_input_left(mixer, MIX_LINE_IN, NULL); | ||
945 | mixer->set_input_right(mixer, MIX_LINE_IN, NULL); | ||
946 | |||
947 | hw->select_adc_source(hw, ADC_MICIN); | ||
948 | |||
949 | src = atc->srcs[2]; | ||
950 | mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc); | ||
951 | src = atc->srcs[3]; | ||
952 | mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | static int atc_have_digit_io_switch(struct ct_atc *atc) | ||
958 | { | ||
959 | struct hw *hw = atc->hw; | ||
960 | |||
961 | return hw->have_digit_io_switch(hw); | ||
962 | } | ||
963 | |||
964 | static int atc_select_digit_io(struct ct_atc *atc) | ||
965 | { | ||
966 | struct hw *hw = atc->hw; | ||
967 | |||
968 | if (hw->is_adc_source_selected(hw, ADC_NONE)) | ||
969 | return 0; | ||
970 | |||
971 | hw->select_adc_source(hw, ADC_NONE); | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type) | ||
977 | { | ||
978 | struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO]; | ||
979 | |||
980 | if (state) | ||
981 | daio_mgr->daio_enable(daio_mgr, atc->daios[type]); | ||
982 | else | ||
983 | daio_mgr->daio_disable(daio_mgr, atc->daios[type]); | ||
984 | |||
985 | daio_mgr->commit_write(daio_mgr); | ||
986 | |||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | static int | ||
991 | atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type) | ||
992 | { | ||
993 | struct dao *dao = container_of(atc->daios[type], struct dao, daio); | ||
994 | return dao->ops->get_spos(dao, status); | ||
995 | } | ||
996 | |||
997 | static int | ||
998 | atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type) | ||
999 | { | ||
1000 | struct dao *dao = container_of(atc->daios[type], struct dao, daio); | ||
1001 | |||
1002 | dao->ops->set_spos(dao, status); | ||
1003 | dao->ops->commit_write(dao); | ||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state) | ||
1008 | { | ||
1009 | return atc_daio_unmute(atc, state, LINEO1); | ||
1010 | } | ||
1011 | |||
1012 | static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state) | ||
1013 | { | ||
1014 | return atc_daio_unmute(atc, state, LINEO4); | ||
1015 | } | ||
1016 | |||
1017 | static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state) | ||
1018 | { | ||
1019 | return atc_daio_unmute(atc, state, LINEO3); | ||
1020 | } | ||
1021 | |||
1022 | static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state) | ||
1023 | { | ||
1024 | return atc_daio_unmute(atc, state, LINEO2); | ||
1025 | } | ||
1026 | |||
1027 | static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) | ||
1028 | { | ||
1029 | return atc_daio_unmute(atc, state, LINEIM); | ||
1030 | } | ||
1031 | |||
1032 | static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) | ||
1033 | { | ||
1034 | return atc_daio_unmute(atc, state, SPDIFOO); | ||
1035 | } | ||
1036 | |||
1037 | static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state) | ||
1038 | { | ||
1039 | return atc_daio_unmute(atc, state, SPDIFIO); | ||
1040 | } | ||
1041 | |||
1042 | static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status) | ||
1043 | { | ||
1044 | return atc_dao_get_status(atc, status, SPDIFOO); | ||
1045 | } | ||
1046 | |||
1047 | static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status) | ||
1048 | { | ||
1049 | return atc_dao_set_status(atc, status, SPDIFOO); | ||
1050 | } | ||
1051 | |||
1052 | static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) | ||
1053 | { | ||
1054 | unsigned long flags; | ||
1055 | struct dao_desc da_dsc = {0}; | ||
1056 | struct dao *dao = NULL; | ||
1057 | int err = 0; | ||
1058 | struct ct_mixer *mixer = atc->mixer; | ||
1059 | struct rsc *rscs[2] = {NULL}; | ||
1060 | unsigned int spos = 0; | ||
1061 | |||
1062 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
1063 | dao = container_of(atc->daios[SPDIFOO], struct dao, daio); | ||
1064 | da_dsc.msr = state ? 1 : atc->msr; | ||
1065 | da_dsc.passthru = state ? 1 : 0; | ||
1066 | err = dao->ops->reinit(dao, &da_dsc); | ||
1067 | if (state) { | ||
1068 | spos = IEC958_DEFAULT_CON; | ||
1069 | } else { | ||
1070 | mixer->get_output_ports(mixer, MIX_SPDIF_OUT, | ||
1071 | &rscs[0], &rscs[1]); | ||
1072 | dao->ops->set_left_input(dao, rscs[0]); | ||
1073 | dao->ops->set_right_input(dao, rscs[1]); | ||
1074 | /* Restore PLL to atc->rsr if needed. */ | ||
1075 | if (atc->pll_rate != atc->rsr) { | ||
1076 | err = ((struct hw *)atc->hw)->pll_init(atc->hw, | ||
1077 | atc->rsr); | ||
1078 | atc->pll_rate = err ? 0 : atc->rsr; | ||
1079 | } | ||
1080 | } | ||
1081 | dao->ops->set_spos(dao, spos); | ||
1082 | dao->ops->commit_write(dao); | ||
1083 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
1084 | |||
1085 | return err; | ||
1086 | } | ||
1087 | |||
1088 | static int ct_atc_destroy(struct ct_atc *atc) | ||
1089 | { | ||
1090 | struct daio_mgr *daio_mgr = NULL; | ||
1091 | struct dao *dao = NULL; | ||
1092 | struct dai *dai = NULL; | ||
1093 | struct daio *daio = NULL; | ||
1094 | struct sum_mgr *sum_mgr = NULL; | ||
1095 | struct src_mgr *src_mgr = NULL; | ||
1096 | struct srcimp_mgr *srcimp_mgr = NULL; | ||
1097 | struct srcimp *srcimp = NULL; | ||
1098 | struct ct_mixer *mixer = NULL; | ||
1099 | int i = 0; | ||
1100 | |||
1101 | if (NULL == atc) | ||
1102 | return 0; | ||
1103 | |||
1104 | /* Stop hardware and disable all interrupts */ | ||
1105 | if (NULL != atc->hw) | ||
1106 | ((struct hw *)atc->hw)->card_stop(atc->hw); | ||
1107 | |||
1108 | /* Destroy internal mixer objects */ | ||
1109 | if (NULL != atc->mixer) { | ||
1110 | mixer = atc->mixer; | ||
1111 | mixer->set_input_left(mixer, MIX_LINE_IN, NULL); | ||
1112 | mixer->set_input_right(mixer, MIX_LINE_IN, NULL); | ||
1113 | mixer->set_input_left(mixer, MIX_MIC_IN, NULL); | ||
1114 | mixer->set_input_right(mixer, MIX_MIC_IN, NULL); | ||
1115 | mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL); | ||
1116 | mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL); | ||
1117 | ct_mixer_destroy(atc->mixer); | ||
1118 | } | ||
1119 | |||
1120 | if (NULL != atc->daios) { | ||
1121 | daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; | ||
1122 | for (i = 0; i < atc->n_daio; i++) { | ||
1123 | daio = atc->daios[i]; | ||
1124 | if (daio->type < LINEIM) { | ||
1125 | dao = container_of(daio, struct dao, daio); | ||
1126 | dao->ops->clear_left_input(dao); | ||
1127 | dao->ops->clear_right_input(dao); | ||
1128 | } else { | ||
1129 | dai = container_of(daio, struct dai, daio); | ||
1130 | /* some thing to do for dai ... */ | ||
1131 | } | ||
1132 | daio_mgr->put_daio(daio_mgr, daio); | ||
1133 | } | ||
1134 | kfree(atc->daios); | ||
1135 | } | ||
1136 | |||
1137 | if (NULL != atc->pcm) { | ||
1138 | sum_mgr = atc->rsc_mgrs[SUM]; | ||
1139 | for (i = 0; i < atc->n_pcm; i++) | ||
1140 | sum_mgr->put_sum(sum_mgr, atc->pcm[i]); | ||
1141 | |||
1142 | kfree(atc->pcm); | ||
1143 | } | ||
1144 | |||
1145 | if (NULL != atc->srcs) { | ||
1146 | src_mgr = atc->rsc_mgrs[SRC]; | ||
1147 | for (i = 0; i < atc->n_src; i++) | ||
1148 | src_mgr->put_src(src_mgr, atc->srcs[i]); | ||
1149 | |||
1150 | kfree(atc->srcs); | ||
1151 | } | ||
1152 | |||
1153 | if (NULL != atc->srcimps) { | ||
1154 | srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
1155 | for (i = 0; i < atc->n_srcimp; i++) { | ||
1156 | srcimp = atc->srcimps[i]; | ||
1157 | srcimp->ops->unmap(srcimp); | ||
1158 | srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]); | ||
1159 | } | ||
1160 | kfree(atc->srcimps); | ||
1161 | } | ||
1162 | |||
1163 | for (i = 0; i < NUM_RSCTYP; i++) { | ||
1164 | if ((NULL != rsc_mgr_funcs[i].destroy) && | ||
1165 | (NULL != atc->rsc_mgrs[i])) | ||
1166 | rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]); | ||
1167 | |||
1168 | } | ||
1169 | |||
1170 | if (NULL != atc->hw) | ||
1171 | destroy_hw_obj((struct hw *)atc->hw); | ||
1172 | |||
1173 | /* Destroy device virtual memory manager object */ | ||
1174 | if (NULL != atc->vm) { | ||
1175 | ct_vm_destroy(atc->vm); | ||
1176 | atc->vm = NULL; | ||
1177 | } | ||
1178 | |||
1179 | kfree(atc); | ||
1180 | |||
1181 | return 0; | ||
1182 | } | ||
1183 | |||
1184 | static int atc_dev_free(struct snd_device *dev) | ||
1185 | { | ||
1186 | struct ct_atc *atc = dev->device_data; | ||
1187 | return ct_atc_destroy(atc); | ||
1188 | } | ||
1189 | |||
1190 | static int atc_identify_card(struct ct_atc *atc) | ||
1191 | { | ||
1192 | u16 subsys = 0; | ||
1193 | u8 revision = 0; | ||
1194 | struct pci_dev *pci = atc->pci; | ||
1195 | const struct ct_atc_chip_details *d; | ||
1196 | enum CTCARDS i; | ||
1197 | |||
1198 | pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys); | ||
1199 | pci_read_config_byte(pci, PCI_REVISION_ID, &revision); | ||
1200 | atc->chip_details = NULL; | ||
1201 | atc->model = NUM_CTCARDS; | ||
1202 | for (d = atc_chip_details; d->vendor; d++) { | ||
1203 | if (d->vendor != pci->vendor || d->device != pci->device) | ||
1204 | continue; | ||
1205 | |||
1206 | if (NULL == d->sub_details) { | ||
1207 | atc->chip_details = d; | ||
1208 | break; | ||
1209 | } | ||
1210 | for (i = 0; i < NUM_CTCARDS; i++) { | ||
1211 | if ((d->sub_details[i].subsys == subsys) || | ||
1212 | (((subsys & 0x6000) == 0x6000) && | ||
1213 | ((d->sub_details[i].subsys & 0x6000) == 0x6000))) { | ||
1214 | atc->model = i; | ||
1215 | break; | ||
1216 | } | ||
1217 | } | ||
1218 | if (i >= NUM_CTCARDS) | ||
1219 | continue; | ||
1220 | |||
1221 | atc->chip_details = d; | ||
1222 | break; | ||
1223 | /* not take revision into consideration now */ | ||
1224 | } | ||
1225 | if (!d->vendor) | ||
1226 | return -ENOENT; | ||
1227 | |||
1228 | return 0; | ||
1229 | } | ||
1230 | |||
1231 | static int ct_create_alsa_devs(struct ct_atc *atc) | ||
1232 | { | ||
1233 | enum CTALSADEVS i; | ||
1234 | struct hw *hw = atc->hw; | ||
1235 | int err; | ||
1236 | |||
1237 | switch (hw->get_chip_type(hw)) { | ||
1238 | case ATC20K1: | ||
1239 | alsa_dev_funcs[MIXER].public_name = "20K1"; | ||
1240 | break; | ||
1241 | case ATC20K2: | ||
1242 | alsa_dev_funcs[MIXER].public_name = "20K2"; | ||
1243 | break; | ||
1244 | default: | ||
1245 | alsa_dev_funcs[MIXER].public_name = "Unknown"; | ||
1246 | break; | ||
1247 | } | ||
1248 | |||
1249 | for (i = 0; i < NUM_CTALSADEVS; i++) { | ||
1250 | if (NULL == alsa_dev_funcs[i].create) | ||
1251 | continue; | ||
1252 | |||
1253 | err = alsa_dev_funcs[i].create(atc, i, | ||
1254 | alsa_dev_funcs[i].public_name); | ||
1255 | if (err) { | ||
1256 | printk(KERN_ERR "Creating alsa device %d failed!\n", i); | ||
1257 | return err; | ||
1258 | } | ||
1259 | } | ||
1260 | |||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1264 | static int atc_create_hw_devs(struct ct_atc *atc) | ||
1265 | { | ||
1266 | struct hw *hw = NULL; | ||
1267 | struct card_conf info = {0}; | ||
1268 | int i = 0, err = 0; | ||
1269 | |||
1270 | err = create_hw_obj(atc->pci, &hw); | ||
1271 | if (err) { | ||
1272 | printk(KERN_ERR "Failed to create hw obj!!!\n"); | ||
1273 | return err; | ||
1274 | } | ||
1275 | atc->hw = hw; | ||
1276 | |||
1277 | /* Initialize card hardware. */ | ||
1278 | info.rsr = atc->rsr; | ||
1279 | info.msr = atc->msr; | ||
1280 | info.vm_pgt_phys = atc_get_ptp_phys(atc, 0); | ||
1281 | err = hw->card_init(hw, &info); | ||
1282 | if (err < 0) | ||
1283 | return err; | ||
1284 | |||
1285 | for (i = 0; i < NUM_RSCTYP; i++) { | ||
1286 | if (NULL == rsc_mgr_funcs[i].create) | ||
1287 | continue; | ||
1288 | |||
1289 | err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); | ||
1290 | if (err) { | ||
1291 | printk(KERN_ERR "Failed to create rsc_mgr %d!!!\n", i); | ||
1292 | return err; | ||
1293 | } | ||
1294 | } | ||
1295 | |||
1296 | return 0; | ||
1297 | } | ||
1298 | |||
1299 | static int atc_get_resources(struct ct_atc *atc) | ||
1300 | { | ||
1301 | struct daio_desc da_desc = {0}; | ||
1302 | struct daio_mgr *daio_mgr = NULL; | ||
1303 | struct src_desc src_dsc = {0}; | ||
1304 | struct src_mgr *src_mgr = NULL; | ||
1305 | struct srcimp_desc srcimp_dsc = {0}; | ||
1306 | struct srcimp_mgr *srcimp_mgr = NULL; | ||
1307 | struct sum_desc sum_dsc = {0}; | ||
1308 | struct sum_mgr *sum_mgr = NULL; | ||
1309 | int err = 0, i = 0; | ||
1310 | unsigned short subsys_id = 0; | ||
1311 | |||
1312 | atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); | ||
1313 | if (NULL == atc->daios) | ||
1314 | return -ENOMEM; | ||
1315 | |||
1316 | atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); | ||
1317 | if (NULL == atc->srcs) | ||
1318 | return -ENOMEM; | ||
1319 | |||
1320 | atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); | ||
1321 | if (NULL == atc->srcimps) | ||
1322 | return -ENOMEM; | ||
1323 | |||
1324 | atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); | ||
1325 | if (NULL == atc->pcm) | ||
1326 | return -ENOMEM; | ||
1327 | |||
1328 | daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; | ||
1329 | da_desc.msr = atc->msr; | ||
1330 | for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { | ||
1331 | da_desc.type = i; | ||
1332 | err = daio_mgr->get_daio(daio_mgr, &da_desc, | ||
1333 | (struct daio **)&atc->daios[i]); | ||
1334 | if (err) { | ||
1335 | printk(KERN_ERR "Failed to get DAIO " | ||
1336 | "resource %d!!!\n", i); | ||
1337 | return err; | ||
1338 | } | ||
1339 | atc->n_daio++; | ||
1340 | } | ||
1341 | pci_read_config_word(atc->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1342 | if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { | ||
1343 | /* SB073x cards */ | ||
1344 | da_desc.type = SPDIFI1; | ||
1345 | } else { | ||
1346 | da_desc.type = SPDIFIO; | ||
1347 | } | ||
1348 | err = daio_mgr->get_daio(daio_mgr, &da_desc, | ||
1349 | (struct daio **)&atc->daios[i]); | ||
1350 | if (err) { | ||
1351 | printk(KERN_ERR "Failed to get S/PDIF-in resource!!!\n"); | ||
1352 | return err; | ||
1353 | } | ||
1354 | atc->n_daio++; | ||
1355 | |||
1356 | src_mgr = atc->rsc_mgrs[SRC]; | ||
1357 | src_dsc.multi = 1; | ||
1358 | src_dsc.msr = atc->msr; | ||
1359 | src_dsc.mode = ARCRW; | ||
1360 | for (i = 0, atc->n_src = 0; i < (2*2); i++) { | ||
1361 | err = src_mgr->get_src(src_mgr, &src_dsc, | ||
1362 | (struct src **)&atc->srcs[i]); | ||
1363 | if (err) | ||
1364 | return err; | ||
1365 | |||
1366 | atc->n_src++; | ||
1367 | } | ||
1368 | |||
1369 | srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
1370 | srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ | ||
1371 | for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { | ||
1372 | err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, | ||
1373 | (struct srcimp **)&atc->srcimps[i]); | ||
1374 | if (err) | ||
1375 | return err; | ||
1376 | |||
1377 | atc->n_srcimp++; | ||
1378 | } | ||
1379 | srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */ | ||
1380 | for (i = 0; i < (2*1); i++) { | ||
1381 | err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, | ||
1382 | (struct srcimp **)&atc->srcimps[2*1+i]); | ||
1383 | if (err) | ||
1384 | return err; | ||
1385 | |||
1386 | atc->n_srcimp++; | ||
1387 | } | ||
1388 | |||
1389 | sum_mgr = atc->rsc_mgrs[SUM]; | ||
1390 | sum_dsc.msr = atc->msr; | ||
1391 | for (i = 0, atc->n_pcm = 0; i < (2*4); i++) { | ||
1392 | err = sum_mgr->get_sum(sum_mgr, &sum_dsc, | ||
1393 | (struct sum **)&atc->pcm[i]); | ||
1394 | if (err) | ||
1395 | return err; | ||
1396 | |||
1397 | atc->n_pcm++; | ||
1398 | } | ||
1399 | |||
1400 | err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); | ||
1401 | if (err) { | ||
1402 | printk(KERN_ERR "Failed to create mixer obj!!!\n"); | ||
1403 | return err; | ||
1404 | } | ||
1405 | |||
1406 | return 0; | ||
1407 | } | ||
1408 | |||
1409 | static void | ||
1410 | atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, | ||
1411 | struct src **srcs, struct srcimp **srcimps) | ||
1412 | { | ||
1413 | struct rsc *rscs[2] = {NULL}; | ||
1414 | struct src *src = NULL; | ||
1415 | struct srcimp *srcimp = NULL; | ||
1416 | int i = 0; | ||
1417 | |||
1418 | rscs[0] = &dai->daio.rscl; | ||
1419 | rscs[1] = &dai->daio.rscr; | ||
1420 | for (i = 0; i < 2; i++) { | ||
1421 | src = srcs[i]; | ||
1422 | srcimp = srcimps[i]; | ||
1423 | srcimp->ops->map(srcimp, src, rscs[i]); | ||
1424 | src_mgr->src_disable(src_mgr, src); | ||
1425 | } | ||
1426 | |||
1427 | src_mgr->commit_write(src_mgr); /* Actually disable SRCs */ | ||
1428 | |||
1429 | src = srcs[0]; | ||
1430 | src->ops->set_pm(src, 1); | ||
1431 | for (i = 0; i < 2; i++) { | ||
1432 | src = srcs[i]; | ||
1433 | src->ops->set_state(src, SRC_STATE_RUN); | ||
1434 | src->ops->commit_write(src); | ||
1435 | src_mgr->src_enable_s(src_mgr, src); | ||
1436 | } | ||
1437 | |||
1438 | dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc)); | ||
1439 | dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc)); | ||
1440 | |||
1441 | dai->ops->set_enb_src(dai, 1); | ||
1442 | dai->ops->set_enb_srt(dai, 1); | ||
1443 | dai->ops->commit_write(dai); | ||
1444 | |||
1445 | src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */ | ||
1446 | } | ||
1447 | |||
1448 | static void atc_connect_resources(struct ct_atc *atc) | ||
1449 | { | ||
1450 | struct dai *dai = NULL; | ||
1451 | struct dao *dao = NULL; | ||
1452 | struct src *src = NULL; | ||
1453 | struct sum *sum = NULL; | ||
1454 | struct ct_mixer *mixer = NULL; | ||
1455 | struct rsc *rscs[2] = {NULL}; | ||
1456 | int i = 0, j = 0; | ||
1457 | |||
1458 | mixer = atc->mixer; | ||
1459 | |||
1460 | for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) { | ||
1461 | mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]); | ||
1462 | dao = container_of(atc->daios[j], struct dao, daio); | ||
1463 | dao->ops->set_left_input(dao, rscs[0]); | ||
1464 | dao->ops->set_right_input(dao, rscs[1]); | ||
1465 | } | ||
1466 | |||
1467 | dai = container_of(atc->daios[LINEIM], struct dai, daio); | ||
1468 | atc_connect_dai(atc->rsc_mgrs[SRC], dai, | ||
1469 | (struct src **)&atc->srcs[2], | ||
1470 | (struct srcimp **)&atc->srcimps[2]); | ||
1471 | src = atc->srcs[2]; | ||
1472 | mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); | ||
1473 | src = atc->srcs[3]; | ||
1474 | mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); | ||
1475 | |||
1476 | dai = container_of(atc->daios[SPDIFIO], struct dai, daio); | ||
1477 | atc_connect_dai(atc->rsc_mgrs[SRC], dai, | ||
1478 | (struct src **)&atc->srcs[0], | ||
1479 | (struct srcimp **)&atc->srcimps[0]); | ||
1480 | |||
1481 | src = atc->srcs[0]; | ||
1482 | mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc); | ||
1483 | src = atc->srcs[1]; | ||
1484 | mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc); | ||
1485 | |||
1486 | for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) { | ||
1487 | sum = atc->pcm[j]; | ||
1488 | mixer->set_input_left(mixer, i, &sum->rsc); | ||
1489 | sum = atc->pcm[j+1]; | ||
1490 | mixer->set_input_right(mixer, i, &sum->rsc); | ||
1491 | } | ||
1492 | } | ||
1493 | |||
1494 | static void atc_set_ops(struct ct_atc *atc) | ||
1495 | { | ||
1496 | /* Set operations */ | ||
1497 | atc->map_audio_buffer = ct_map_audio_buffer; | ||
1498 | atc->unmap_audio_buffer = ct_unmap_audio_buffer; | ||
1499 | atc->pcm_playback_prepare = atc_pcm_playback_prepare; | ||
1500 | atc->pcm_release_resources = atc_pcm_release_resources; | ||
1501 | atc->pcm_playback_start = atc_pcm_playback_start; | ||
1502 | atc->pcm_playback_stop = atc_pcm_stop; | ||
1503 | atc->pcm_playback_position = atc_pcm_playback_position; | ||
1504 | atc->pcm_capture_prepare = atc_pcm_capture_prepare; | ||
1505 | atc->pcm_capture_start = atc_pcm_capture_start; | ||
1506 | atc->pcm_capture_stop = atc_pcm_stop; | ||
1507 | atc->pcm_capture_position = atc_pcm_capture_position; | ||
1508 | atc->spdif_passthru_playback_prepare = spdif_passthru_playback_prepare; | ||
1509 | atc->get_ptp_phys = atc_get_ptp_phys; | ||
1510 | atc->select_line_in = atc_select_line_in; | ||
1511 | atc->select_mic_in = atc_select_mic_in; | ||
1512 | atc->select_digit_io = atc_select_digit_io; | ||
1513 | atc->line_front_unmute = atc_line_front_unmute; | ||
1514 | atc->line_surround_unmute = atc_line_surround_unmute; | ||
1515 | atc->line_clfe_unmute = atc_line_clfe_unmute; | ||
1516 | atc->line_rear_unmute = atc_line_rear_unmute; | ||
1517 | atc->line_in_unmute = atc_line_in_unmute; | ||
1518 | atc->spdif_out_unmute = atc_spdif_out_unmute; | ||
1519 | atc->spdif_in_unmute = atc_spdif_in_unmute; | ||
1520 | atc->spdif_out_get_status = atc_spdif_out_get_status; | ||
1521 | atc->spdif_out_set_status = atc_spdif_out_set_status; | ||
1522 | atc->spdif_out_passthru = atc_spdif_out_passthru; | ||
1523 | atc->have_digit_io_switch = atc_have_digit_io_switch; | ||
1524 | } | ||
1525 | |||
1526 | /** | ||
1527 | * ct_atc_create - create and initialize a hardware manager | ||
1528 | * @card: corresponding alsa card object | ||
1529 | * @pci: corresponding kernel pci device object | ||
1530 | * @ratc: return created object address in it | ||
1531 | * | ||
1532 | * Creates and initializes a hardware manager. | ||
1533 | * | ||
1534 | * Creates kmallocated ct_atc structure. Initializes hardware. | ||
1535 | * Returns 0 if suceeds, or negative error code if fails. | ||
1536 | */ | ||
1537 | |||
1538 | int ct_atc_create(struct snd_card *card, struct pci_dev *pci, | ||
1539 | unsigned int rsr, unsigned int msr, struct ct_atc **ratc) | ||
1540 | { | ||
1541 | struct ct_atc *atc = NULL; | ||
1542 | static struct snd_device_ops ops = { | ||
1543 | .dev_free = atc_dev_free, | ||
1544 | }; | ||
1545 | int err = 0; | ||
1546 | |||
1547 | *ratc = NULL; | ||
1548 | |||
1549 | atc = kzalloc(sizeof(*atc), GFP_KERNEL); | ||
1550 | if (NULL == atc) | ||
1551 | return -ENOMEM; | ||
1552 | |||
1553 | atc->card = card; | ||
1554 | atc->pci = pci; | ||
1555 | atc->rsr = rsr; | ||
1556 | atc->msr = msr; | ||
1557 | |||
1558 | /* Set operations */ | ||
1559 | atc_set_ops(atc); | ||
1560 | |||
1561 | spin_lock_init(&atc->atc_lock); | ||
1562 | spin_lock_init(&atc->vm_lock); | ||
1563 | |||
1564 | /* Find card model */ | ||
1565 | err = atc_identify_card(atc); | ||
1566 | if (err < 0) { | ||
1567 | printk(KERN_ERR "ctatc: Card not recognised\n"); | ||
1568 | goto error1; | ||
1569 | } | ||
1570 | |||
1571 | /* Set up device virtual memory management object */ | ||
1572 | err = ct_vm_create(&atc->vm); | ||
1573 | if (err < 0) | ||
1574 | goto error1; | ||
1575 | |||
1576 | /* Create all atc hw devices */ | ||
1577 | err = atc_create_hw_devs(atc); | ||
1578 | if (err < 0) | ||
1579 | goto error1; | ||
1580 | |||
1581 | /* Get resources */ | ||
1582 | err = atc_get_resources(atc); | ||
1583 | if (err < 0) | ||
1584 | goto error1; | ||
1585 | |||
1586 | /* Build topology */ | ||
1587 | atc_connect_resources(atc); | ||
1588 | |||
1589 | atc->create_alsa_devs = ct_create_alsa_devs; | ||
1590 | |||
1591 | err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); | ||
1592 | if (err < 0) | ||
1593 | goto error1; | ||
1594 | |||
1595 | snd_card_set_dev(card, &pci->dev); | ||
1596 | |||
1597 | *ratc = atc; | ||
1598 | return 0; | ||
1599 | |||
1600 | error1: | ||
1601 | ct_atc_destroy(atc); | ||
1602 | printk(KERN_ERR "Something wrong!!!\n"); | ||
1603 | return err; | ||
1604 | } | ||
1605 | |||
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h new file mode 100644 index 000000000000..286c993d461a --- /dev/null +++ b/sound/pci/ctxfi/ctatc.h | |||
@@ -0,0 +1,155 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctatc.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the device resource management object. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Mar 28 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef CTATC_H | ||
19 | #define CTATC_H | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/spinlock_types.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/timer.h> | ||
25 | #include <sound/core.h> | ||
26 | |||
27 | #include "ctvmem.h" | ||
28 | #include "ctresource.h" | ||
29 | |||
30 | enum CTALSADEVS { /* Types of alsa devices */ | ||
31 | FRONT, | ||
32 | REAR, | ||
33 | CLFE, | ||
34 | SURROUND, | ||
35 | IEC958, | ||
36 | MIXER, | ||
37 | NUM_CTALSADEVS /* This should always be the last */ | ||
38 | }; | ||
39 | |||
40 | enum CTCARDS { | ||
41 | CTSB0760, | ||
42 | CTHENDRIX, | ||
43 | CTSB08801, | ||
44 | CTSB08802, | ||
45 | CTSB08803, | ||
46 | NUM_CTCARDS /* This should always be the last */ | ||
47 | }; | ||
48 | |||
49 | struct ct_atc_chip_sub_details { | ||
50 | u16 subsys; | ||
51 | const char *nm_model; | ||
52 | }; | ||
53 | |||
54 | struct ct_atc_chip_details { | ||
55 | u16 vendor; | ||
56 | u16 device; | ||
57 | const struct ct_atc_chip_sub_details *sub_details; | ||
58 | const char *nm_card; | ||
59 | }; | ||
60 | |||
61 | struct ct_atc; | ||
62 | |||
63 | /* alsa pcm stream descriptor */ | ||
64 | struct ct_atc_pcm { | ||
65 | struct snd_pcm_substream *substream; | ||
66 | void (*interrupt)(struct ct_atc_pcm *apcm); | ||
67 | unsigned int started:1; | ||
68 | unsigned int stop_timer:1; | ||
69 | struct timer_list timer; | ||
70 | spinlock_t timer_lock; | ||
71 | unsigned int position; | ||
72 | |||
73 | /* Only mono and interleaved modes are supported now. */ | ||
74 | struct ct_vm_block *vm_block; | ||
75 | void *src; /* SRC for interacting with host memory */ | ||
76 | void **srccs; /* SRCs for sample rate conversion */ | ||
77 | void **srcimps; /* SRC Input Mappers */ | ||
78 | void **amixers; /* AMIXERs for routing converted data */ | ||
79 | void *mono; /* A SUM resource for mixing chs to one */ | ||
80 | unsigned char n_srcc; /* Number of converting SRCs */ | ||
81 | unsigned char n_srcimp; /* Number of SRC Input Mappers */ | ||
82 | unsigned char n_amixer; /* Number of AMIXERs */ | ||
83 | }; | ||
84 | |||
85 | /* Chip resource management object */ | ||
86 | struct ct_atc { | ||
87 | struct pci_dev *pci; | ||
88 | struct snd_card *card; | ||
89 | unsigned int rsr; /* reference sample rate in Hz */ | ||
90 | unsigned int msr; /* master sample rate in rsr */ | ||
91 | unsigned int pll_rate; /* current rate of Phase Lock Loop */ | ||
92 | |||
93 | const struct ct_atc_chip_details *chip_details; | ||
94 | enum CTCARDS model; | ||
95 | /* Create all alsa devices */ | ||
96 | int (*create_alsa_devs)(struct ct_atc *atc); | ||
97 | |||
98 | struct ct_vm *vm; /* device virtual memory manager for this card */ | ||
99 | int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
100 | void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
101 | unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index); | ||
102 | |||
103 | spinlock_t atc_lock; | ||
104 | spinlock_t vm_lock; | ||
105 | |||
106 | int (*pcm_playback_prepare)(struct ct_atc *atc, | ||
107 | struct ct_atc_pcm *apcm); | ||
108 | int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
109 | int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
110 | int (*pcm_playback_position)(struct ct_atc *atc, | ||
111 | struct ct_atc_pcm *apcm); | ||
112 | int (*spdif_passthru_playback_prepare)(struct ct_atc *atc, | ||
113 | struct ct_atc_pcm *apcm); | ||
114 | int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
115 | int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
116 | int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
117 | int (*pcm_capture_position)(struct ct_atc *atc, | ||
118 | struct ct_atc_pcm *apcm); | ||
119 | int (*pcm_release_resources)(struct ct_atc *atc, | ||
120 | struct ct_atc_pcm *apcm); | ||
121 | int (*select_line_in)(struct ct_atc *atc); | ||
122 | int (*select_mic_in)(struct ct_atc *atc); | ||
123 | int (*select_digit_io)(struct ct_atc *atc); | ||
124 | int (*line_front_unmute)(struct ct_atc *atc, unsigned char state); | ||
125 | int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state); | ||
126 | int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state); | ||
127 | int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state); | ||
128 | int (*line_in_unmute)(struct ct_atc *atc, unsigned char state); | ||
129 | int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state); | ||
130 | int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state); | ||
131 | int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status); | ||
132 | int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status); | ||
133 | int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state); | ||
134 | int (*have_digit_io_switch)(struct ct_atc *atc); | ||
135 | |||
136 | /* Don't touch! Used for internal object. */ | ||
137 | void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ | ||
138 | void *mixer; /* internal mixer object */ | ||
139 | void *hw; /* chip specific hardware access object */ | ||
140 | void **daios; /* digital audio io resources */ | ||
141 | void **pcm; /* SUMs for collecting all pcm stream */ | ||
142 | void **srcs; /* Sample Rate Converters for input signal */ | ||
143 | void **srcimps; /* input mappers for SRCs */ | ||
144 | unsigned char n_daio; | ||
145 | unsigned char n_src; | ||
146 | unsigned char n_srcimp; | ||
147 | unsigned char n_pcm; | ||
148 | }; | ||
149 | |||
150 | |||
151 | int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, | ||
152 | unsigned int rsr, unsigned int msr, | ||
153 | struct ct_atc **ratc); | ||
154 | |||
155 | #endif /* CTATC_H */ | ||
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c new file mode 100644 index 000000000000..a2aea399eba1 --- /dev/null +++ b/sound/pci/ctxfi/ctdaio.c | |||
@@ -0,0 +1,769 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctdaio.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of Digital Audio Input Output | ||
12 | * resource management object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 23 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include "ctdaio.h" | ||
20 | #include "cthardware.h" | ||
21 | #include "ctimap.h" | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/kernel.h> | ||
24 | |||
25 | #define DAIO_RESOURCE_NUM NUM_DAIOTYP | ||
26 | #define DAIO_OUT_MAX SPDIFOO | ||
27 | |||
28 | union daio_usage { | ||
29 | struct { | ||
30 | unsigned short lineo1:1; | ||
31 | unsigned short lineo2:1; | ||
32 | unsigned short lineo3:1; | ||
33 | unsigned short lineo4:1; | ||
34 | unsigned short spdifoo:1; | ||
35 | unsigned short lineim:1; | ||
36 | unsigned short spdifio:1; | ||
37 | unsigned short spdifi1:1; | ||
38 | } bf; | ||
39 | unsigned short data; | ||
40 | }; | ||
41 | |||
42 | struct daio_rsc_idx { | ||
43 | unsigned short left; | ||
44 | unsigned short right; | ||
45 | }; | ||
46 | |||
47 | struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { | ||
48 | [LINEO1] = {.left = 0x00, .right = 0x01}, | ||
49 | [LINEO2] = {.left = 0x18, .right = 0x19}, | ||
50 | [LINEO3] = {.left = 0x08, .right = 0x09}, | ||
51 | [LINEO4] = {.left = 0x10, .right = 0x11}, | ||
52 | [LINEIM] = {.left = 0x1b5, .right = 0x1bd}, | ||
53 | [SPDIFOO] = {.left = 0x20, .right = 0x21}, | ||
54 | [SPDIFIO] = {.left = 0x15, .right = 0x1d}, | ||
55 | [SPDIFI1] = {.left = 0x95, .right = 0x9d}, | ||
56 | }; | ||
57 | |||
58 | struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { | ||
59 | [LINEO1] = {.left = 0x40, .right = 0x41}, | ||
60 | [LINEO2] = {.left = 0x70, .right = 0x71}, | ||
61 | [LINEO3] = {.left = 0x50, .right = 0x51}, | ||
62 | [LINEO4] = {.left = 0x60, .right = 0x61}, | ||
63 | [LINEIM] = {.left = 0x45, .right = 0xc5}, | ||
64 | [SPDIFOO] = {.left = 0x00, .right = 0x01}, | ||
65 | [SPDIFIO] = {.left = 0x05, .right = 0x85}, | ||
66 | }; | ||
67 | |||
68 | static int daio_master(struct rsc *rsc) | ||
69 | { | ||
70 | /* Actually, this is not the resource index of DAIO. | ||
71 | * For DAO, it is the input mapper index. And, for DAI, | ||
72 | * it is the output time-slot index. */ | ||
73 | return rsc->conj = rsc->idx; | ||
74 | } | ||
75 | |||
76 | static int daio_index(const struct rsc *rsc) | ||
77 | { | ||
78 | return rsc->conj; | ||
79 | } | ||
80 | |||
81 | static int daio_out_next_conj(struct rsc *rsc) | ||
82 | { | ||
83 | return rsc->conj += 2; | ||
84 | } | ||
85 | |||
86 | static int daio_in_next_conj_20k1(struct rsc *rsc) | ||
87 | { | ||
88 | return rsc->conj += 0x200; | ||
89 | } | ||
90 | |||
91 | static int daio_in_next_conj_20k2(struct rsc *rsc) | ||
92 | { | ||
93 | return rsc->conj += 0x100; | ||
94 | } | ||
95 | |||
96 | static struct rsc_ops daio_out_rsc_ops = { | ||
97 | .master = daio_master, | ||
98 | .next_conj = daio_out_next_conj, | ||
99 | .index = daio_index, | ||
100 | .output_slot = NULL, | ||
101 | }; | ||
102 | |||
103 | static struct rsc_ops daio_in_rsc_ops_20k1 = { | ||
104 | .master = daio_master, | ||
105 | .next_conj = daio_in_next_conj_20k1, | ||
106 | .index = NULL, | ||
107 | .output_slot = daio_index, | ||
108 | }; | ||
109 | |||
110 | static struct rsc_ops daio_in_rsc_ops_20k2 = { | ||
111 | .master = daio_master, | ||
112 | .next_conj = daio_in_next_conj_20k2, | ||
113 | .index = NULL, | ||
114 | .output_slot = daio_index, | ||
115 | }; | ||
116 | |||
117 | static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) | ||
118 | { | ||
119 | switch (hw->get_chip_type(hw)) { | ||
120 | case ATC20K1: | ||
121 | switch (type) { | ||
122 | case SPDIFOO: return 0; | ||
123 | case SPDIFIO: return 0; | ||
124 | case SPDIFI1: return 1; | ||
125 | case LINEO1: return 4; | ||
126 | case LINEO2: return 7; | ||
127 | case LINEO3: return 5; | ||
128 | case LINEO4: return 6; | ||
129 | case LINEIM: return 7; | ||
130 | default: return -EINVAL; | ||
131 | } | ||
132 | case ATC20K2: | ||
133 | switch (type) { | ||
134 | case SPDIFOO: return 0; | ||
135 | case SPDIFIO: return 0; | ||
136 | case LINEO1: return 4; | ||
137 | case LINEO2: return 7; | ||
138 | case LINEO3: return 5; | ||
139 | case LINEO4: return 6; | ||
140 | case LINEIM: return 4; | ||
141 | default: return -EINVAL; | ||
142 | } | ||
143 | default: | ||
144 | return -EINVAL; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc); | ||
149 | |||
150 | static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos) | ||
151 | { | ||
152 | ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static int dao_spdif_set_spos(struct dao *dao, unsigned int spos) | ||
157 | { | ||
158 | ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos); | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int dao_commit_write(struct dao *dao) | ||
163 | { | ||
164 | ((struct hw *)dao->hw)->dao_commit_write(dao->hw, | ||
165 | daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int dao_set_left_input(struct dao *dao, struct rsc *input) | ||
170 | { | ||
171 | struct imapper *entry = NULL; | ||
172 | struct daio *daio = &dao->daio; | ||
173 | int i = 0; | ||
174 | |||
175 | entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); | ||
176 | if (NULL == entry) | ||
177 | return -ENOMEM; | ||
178 | |||
179 | /* Program master and conjugate resources */ | ||
180 | input->ops->master(input); | ||
181 | daio->rscl.ops->master(&daio->rscl); | ||
182 | for (i = 0; i < daio->rscl.msr; i++, entry++) { | ||
183 | entry->slot = input->ops->output_slot(input); | ||
184 | entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl); | ||
185 | dao->mgr->imap_add(dao->mgr, entry); | ||
186 | dao->imappers[i] = entry; | ||
187 | |||
188 | input->ops->next_conj(input); | ||
189 | daio->rscl.ops->next_conj(&daio->rscl); | ||
190 | } | ||
191 | input->ops->master(input); | ||
192 | daio->rscl.ops->master(&daio->rscl); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int dao_set_right_input(struct dao *dao, struct rsc *input) | ||
198 | { | ||
199 | struct imapper *entry = NULL; | ||
200 | struct daio *daio = &dao->daio; | ||
201 | int i = 0; | ||
202 | |||
203 | entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); | ||
204 | if (NULL == entry) | ||
205 | return -ENOMEM; | ||
206 | |||
207 | /* Program master and conjugate resources */ | ||
208 | input->ops->master(input); | ||
209 | daio->rscr.ops->master(&daio->rscr); | ||
210 | for (i = 0; i < daio->rscr.msr; i++, entry++) { | ||
211 | entry->slot = input->ops->output_slot(input); | ||
212 | entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr); | ||
213 | dao->mgr->imap_add(dao->mgr, entry); | ||
214 | dao->imappers[daio->rscl.msr + i] = entry; | ||
215 | |||
216 | input->ops->next_conj(input); | ||
217 | daio->rscr.ops->next_conj(&daio->rscr); | ||
218 | } | ||
219 | input->ops->master(input); | ||
220 | daio->rscr.ops->master(&daio->rscr); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int dao_clear_left_input(struct dao *dao) | ||
226 | { | ||
227 | struct imapper *entry = NULL; | ||
228 | struct daio *daio = &dao->daio; | ||
229 | int i = 0; | ||
230 | |||
231 | if (NULL == dao->imappers[0]) | ||
232 | return 0; | ||
233 | |||
234 | entry = dao->imappers[0]; | ||
235 | dao->mgr->imap_delete(dao->mgr, entry); | ||
236 | /* Program conjugate resources */ | ||
237 | for (i = 1; i < daio->rscl.msr; i++) { | ||
238 | entry = dao->imappers[i]; | ||
239 | dao->mgr->imap_delete(dao->mgr, entry); | ||
240 | dao->imappers[i] = NULL; | ||
241 | } | ||
242 | |||
243 | kfree(dao->imappers[0]); | ||
244 | dao->imappers[0] = NULL; | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int dao_clear_right_input(struct dao *dao) | ||
250 | { | ||
251 | struct imapper *entry = NULL; | ||
252 | struct daio *daio = &dao->daio; | ||
253 | int i = 0; | ||
254 | |||
255 | if (NULL == dao->imappers[daio->rscl.msr]) | ||
256 | return 0; | ||
257 | |||
258 | entry = dao->imappers[daio->rscl.msr]; | ||
259 | dao->mgr->imap_delete(dao->mgr, entry); | ||
260 | /* Program conjugate resources */ | ||
261 | for (i = 1; i < daio->rscr.msr; i++) { | ||
262 | entry = dao->imappers[daio->rscl.msr + i]; | ||
263 | dao->mgr->imap_delete(dao->mgr, entry); | ||
264 | dao->imappers[daio->rscl.msr + i] = NULL; | ||
265 | } | ||
266 | |||
267 | kfree(dao->imappers[daio->rscl.msr]); | ||
268 | dao->imappers[daio->rscl.msr] = NULL; | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static struct dao_rsc_ops dao_ops = { | ||
274 | .set_spos = dao_spdif_set_spos, | ||
275 | .commit_write = dao_commit_write, | ||
276 | .get_spos = dao_spdif_get_spos, | ||
277 | .reinit = dao_rsc_reinit, | ||
278 | .set_left_input = dao_set_left_input, | ||
279 | .set_right_input = dao_set_right_input, | ||
280 | .clear_left_input = dao_clear_left_input, | ||
281 | .clear_right_input = dao_clear_right_input, | ||
282 | }; | ||
283 | |||
284 | static int dai_set_srt_srcl(struct dai *dai, struct rsc *src) | ||
285 | { | ||
286 | src->ops->master(src); | ||
287 | ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk, | ||
288 | src->ops->index(src)); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int dai_set_srt_srcr(struct dai *dai, struct rsc *src) | ||
293 | { | ||
294 | src->ops->master(src); | ||
295 | ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk, | ||
296 | src->ops->index(src)); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int dai_set_srt_msr(struct dai *dai, unsigned int msr) | ||
301 | { | ||
302 | unsigned int rsr = 0; | ||
303 | |||
304 | for (rsr = 0; msr > 1; msr >>= 1) | ||
305 | rsr++; | ||
306 | |||
307 | ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr); | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int dai_set_enb_src(struct dai *dai, unsigned int enb) | ||
312 | { | ||
313 | ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static int dai_set_enb_srt(struct dai *dai, unsigned int enb) | ||
318 | { | ||
319 | ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb); | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int dai_commit_write(struct dai *dai) | ||
324 | { | ||
325 | ((struct hw *)dai->hw)->dai_commit_write(dai->hw, | ||
326 | daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static struct dai_rsc_ops dai_ops = { | ||
331 | .set_srt_srcl = dai_set_srt_srcl, | ||
332 | .set_srt_srcr = dai_set_srt_srcr, | ||
333 | .set_srt_msr = dai_set_srt_msr, | ||
334 | .set_enb_src = dai_set_enb_src, | ||
335 | .set_enb_srt = dai_set_enb_srt, | ||
336 | .commit_write = dai_commit_write, | ||
337 | }; | ||
338 | |||
339 | static int daio_rsc_init(struct daio *daio, | ||
340 | const struct daio_desc *desc, | ||
341 | void *hw) | ||
342 | { | ||
343 | int err = 0; | ||
344 | unsigned int idx_l = 0, idx_r = 0; | ||
345 | |||
346 | switch (((struct hw *)hw)->get_chip_type(hw)) { | ||
347 | case ATC20K1: | ||
348 | idx_l = idx_20k1[desc->type].left; | ||
349 | idx_r = idx_20k1[desc->type].right; | ||
350 | break; | ||
351 | case ATC20K2: | ||
352 | idx_l = idx_20k2[desc->type].left; | ||
353 | idx_r = idx_20k2[desc->type].right; | ||
354 | break; | ||
355 | default: | ||
356 | return -EINVAL; | ||
357 | } | ||
358 | err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw); | ||
359 | if (err) | ||
360 | return err; | ||
361 | |||
362 | err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw); | ||
363 | if (err) | ||
364 | goto error1; | ||
365 | |||
366 | /* Set daio->rscl/r->ops to daio specific ones */ | ||
367 | if (desc->type <= DAIO_OUT_MAX) { | ||
368 | daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; | ||
369 | } else { | ||
370 | switch (((struct hw *)hw)->get_chip_type(hw)) { | ||
371 | case ATC20K1: | ||
372 | daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1; | ||
373 | break; | ||
374 | case ATC20K2: | ||
375 | daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2; | ||
376 | break; | ||
377 | default: | ||
378 | break; | ||
379 | } | ||
380 | } | ||
381 | daio->type = desc->type; | ||
382 | |||
383 | return 0; | ||
384 | |||
385 | error1: | ||
386 | rsc_uninit(&daio->rscl); | ||
387 | return err; | ||
388 | } | ||
389 | |||
390 | static int daio_rsc_uninit(struct daio *daio) | ||
391 | { | ||
392 | rsc_uninit(&daio->rscl); | ||
393 | rsc_uninit(&daio->rscr); | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | static int dao_rsc_init(struct dao *dao, | ||
399 | const struct daio_desc *desc, | ||
400 | struct daio_mgr *mgr) | ||
401 | { | ||
402 | struct hw *hw = mgr->mgr.hw; | ||
403 | unsigned int conf = 0; | ||
404 | int err = 0; | ||
405 | |||
406 | err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw); | ||
407 | if (err) | ||
408 | return err; | ||
409 | |||
410 | dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL); | ||
411 | if (NULL == dao->imappers) { | ||
412 | err = -ENOMEM; | ||
413 | goto error1; | ||
414 | } | ||
415 | dao->ops = &dao_ops; | ||
416 | dao->mgr = mgr; | ||
417 | dao->hw = hw; | ||
418 | err = hw->dao_get_ctrl_blk(&dao->ctrl_blk); | ||
419 | if (err) | ||
420 | goto error2; | ||
421 | |||
422 | hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, | ||
423 | daio_device_index(dao->daio.type, hw)); | ||
424 | hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); | ||
425 | |||
426 | conf |= (desc->msr & 0x7) | (desc->passthru << 3); | ||
427 | hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk, | ||
428 | daio_device_index(dao->daio.type, hw), conf); | ||
429 | hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, | ||
430 | daio_device_index(dao->daio.type, hw)); | ||
431 | hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); | ||
432 | |||
433 | return 0; | ||
434 | |||
435 | error2: | ||
436 | kfree(dao->imappers); | ||
437 | dao->imappers = NULL; | ||
438 | error1: | ||
439 | daio_rsc_uninit(&dao->daio); | ||
440 | return err; | ||
441 | } | ||
442 | |||
443 | static int dao_rsc_uninit(struct dao *dao) | ||
444 | { | ||
445 | if (NULL != dao->imappers) { | ||
446 | if (NULL != dao->imappers[0]) | ||
447 | dao_clear_left_input(dao); | ||
448 | |||
449 | if (NULL != dao->imappers[dao->daio.rscl.msr]) | ||
450 | dao_clear_right_input(dao); | ||
451 | |||
452 | kfree(dao->imappers); | ||
453 | dao->imappers = NULL; | ||
454 | } | ||
455 | ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk); | ||
456 | dao->hw = dao->ctrl_blk = NULL; | ||
457 | daio_rsc_uninit(&dao->daio); | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc) | ||
463 | { | ||
464 | struct daio_mgr *mgr = dao->mgr; | ||
465 | struct daio_desc dsc = {0}; | ||
466 | |||
467 | dsc.type = dao->daio.type; | ||
468 | dsc.msr = desc->msr; | ||
469 | dsc.passthru = desc->passthru; | ||
470 | dao_rsc_uninit(dao); | ||
471 | return dao_rsc_init(dao, &dsc, mgr); | ||
472 | } | ||
473 | |||
474 | static int dai_rsc_init(struct dai *dai, | ||
475 | const struct daio_desc *desc, | ||
476 | struct daio_mgr *mgr) | ||
477 | { | ||
478 | int err = 0; | ||
479 | struct hw *hw = mgr->mgr.hw; | ||
480 | unsigned int rsr = 0, msr = 0; | ||
481 | |||
482 | err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw); | ||
483 | if (err) | ||
484 | return err; | ||
485 | |||
486 | dai->ops = &dai_ops; | ||
487 | dai->hw = mgr->mgr.hw; | ||
488 | err = hw->dai_get_ctrl_blk(&dai->ctrl_blk); | ||
489 | if (err) | ||
490 | goto error1; | ||
491 | |||
492 | for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1) | ||
493 | rsr++; | ||
494 | |||
495 | hw->dai_srt_set_rsr(dai->ctrl_blk, rsr); | ||
496 | hw->dai_srt_set_drat(dai->ctrl_blk, 0); | ||
497 | /* default to disabling control of a SRC */ | ||
498 | hw->dai_srt_set_ec(dai->ctrl_blk, 0); | ||
499 | hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */ | ||
500 | hw->dai_commit_write(hw, | ||
501 | daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); | ||
502 | |||
503 | return 0; | ||
504 | |||
505 | error1: | ||
506 | daio_rsc_uninit(&dai->daio); | ||
507 | return err; | ||
508 | } | ||
509 | |||
510 | static int dai_rsc_uninit(struct dai *dai) | ||
511 | { | ||
512 | ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk); | ||
513 | dai->hw = dai->ctrl_blk = NULL; | ||
514 | daio_rsc_uninit(&dai->daio); | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) | ||
519 | { | ||
520 | if (((union daio_usage *)mgr->rscs)->data & (0x1 << type)) | ||
521 | return -ENOENT; | ||
522 | |||
523 | ((union daio_usage *)mgr->rscs)->data |= (0x1 << type); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) | ||
529 | { | ||
530 | ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type); | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static int get_daio_rsc(struct daio_mgr *mgr, | ||
536 | const struct daio_desc *desc, | ||
537 | struct daio **rdaio) | ||
538 | { | ||
539 | int err = 0; | ||
540 | struct dai *dai = NULL; | ||
541 | struct dao *dao = NULL; | ||
542 | unsigned long flags; | ||
543 | |||
544 | *rdaio = NULL; | ||
545 | |||
546 | /* Check whether there are sufficient daio resources to meet request. */ | ||
547 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
548 | err = daio_mgr_get_rsc(&mgr->mgr, desc->type); | ||
549 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
550 | if (err) { | ||
551 | printk(KERN_ERR "Can't meet DAIO resource request!\n"); | ||
552 | return err; | ||
553 | } | ||
554 | |||
555 | /* Allocate mem for daio resource */ | ||
556 | if (desc->type <= DAIO_OUT_MAX) { | ||
557 | dao = kzalloc(sizeof(*dao), GFP_KERNEL); | ||
558 | if (NULL == dao) { | ||
559 | err = -ENOMEM; | ||
560 | goto error; | ||
561 | } | ||
562 | err = dao_rsc_init(dao, desc, mgr); | ||
563 | if (err) | ||
564 | goto error; | ||
565 | |||
566 | *rdaio = &dao->daio; | ||
567 | } else { | ||
568 | dai = kzalloc(sizeof(*dai), GFP_KERNEL); | ||
569 | if (NULL == dai) { | ||
570 | err = -ENOMEM; | ||
571 | goto error; | ||
572 | } | ||
573 | err = dai_rsc_init(dai, desc, mgr); | ||
574 | if (err) | ||
575 | goto error; | ||
576 | |||
577 | *rdaio = &dai->daio; | ||
578 | } | ||
579 | |||
580 | mgr->daio_enable(mgr, *rdaio); | ||
581 | mgr->commit_write(mgr); | ||
582 | |||
583 | return 0; | ||
584 | |||
585 | error: | ||
586 | if (NULL != dao) | ||
587 | kfree(dao); | ||
588 | else if (NULL != dai) | ||
589 | kfree(dai); | ||
590 | |||
591 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
592 | daio_mgr_put_rsc(&mgr->mgr, desc->type); | ||
593 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
594 | return err; | ||
595 | } | ||
596 | |||
597 | static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio) | ||
598 | { | ||
599 | unsigned long flags; | ||
600 | |||
601 | mgr->daio_disable(mgr, daio); | ||
602 | mgr->commit_write(mgr); | ||
603 | |||
604 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
605 | daio_mgr_put_rsc(&mgr->mgr, daio->type); | ||
606 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
607 | |||
608 | if (daio->type <= DAIO_OUT_MAX) { | ||
609 | dao_rsc_uninit(container_of(daio, struct dao, daio)); | ||
610 | kfree(container_of(daio, struct dao, daio)); | ||
611 | } else { | ||
612 | dai_rsc_uninit(container_of(daio, struct dai, daio)); | ||
613 | kfree(container_of(daio, struct dai, daio)); | ||
614 | } | ||
615 | |||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio) | ||
620 | { | ||
621 | struct hw *hw = mgr->mgr.hw; | ||
622 | |||
623 | if (DAIO_OUT_MAX >= daio->type) { | ||
624 | hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, | ||
625 | daio_device_index(daio->type, hw)); | ||
626 | } else { | ||
627 | hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk, | ||
628 | daio_device_index(daio->type, hw)); | ||
629 | } | ||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio) | ||
634 | { | ||
635 | struct hw *hw = mgr->mgr.hw; | ||
636 | |||
637 | if (DAIO_OUT_MAX >= daio->type) { | ||
638 | hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, | ||
639 | daio_device_index(daio->type, hw)); | ||
640 | } else { | ||
641 | hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk, | ||
642 | daio_device_index(daio->type, hw)); | ||
643 | } | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static int daio_map_op(void *data, struct imapper *entry) | ||
648 | { | ||
649 | struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr; | ||
650 | struct hw *hw = mgr->hw; | ||
651 | |||
652 | hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot); | ||
653 | hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next); | ||
654 | hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr); | ||
655 | hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk); | ||
656 | |||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) | ||
661 | { | ||
662 | unsigned long flags; | ||
663 | int err = 0; | ||
664 | |||
665 | spin_lock_irqsave(&mgr->imap_lock, flags); | ||
666 | if ((0 == entry->addr) && (mgr->init_imap_added)) { | ||
667 | input_mapper_delete(&mgr->imappers, mgr->init_imap, | ||
668 | daio_map_op, mgr); | ||
669 | mgr->init_imap_added = 0; | ||
670 | } | ||
671 | err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr); | ||
672 | spin_unlock_irqrestore(&mgr->imap_lock, flags); | ||
673 | |||
674 | return err; | ||
675 | } | ||
676 | |||
677 | static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry) | ||
678 | { | ||
679 | unsigned long flags; | ||
680 | int err = 0; | ||
681 | |||
682 | spin_lock_irqsave(&mgr->imap_lock, flags); | ||
683 | err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr); | ||
684 | if (list_empty(&mgr->imappers)) { | ||
685 | input_mapper_add(&mgr->imappers, mgr->init_imap, | ||
686 | daio_map_op, mgr); | ||
687 | mgr->init_imap_added = 1; | ||
688 | } | ||
689 | spin_unlock_irqrestore(&mgr->imap_lock, flags); | ||
690 | |||
691 | return err; | ||
692 | } | ||
693 | |||
694 | static int daio_mgr_commit_write(struct daio_mgr *mgr) | ||
695 | { | ||
696 | struct hw *hw = mgr->mgr.hw; | ||
697 | |||
698 | hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); | ||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) | ||
703 | { | ||
704 | int err = 0, i = 0; | ||
705 | struct daio_mgr *daio_mgr; | ||
706 | struct imapper *entry; | ||
707 | |||
708 | *rdaio_mgr = NULL; | ||
709 | daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL); | ||
710 | if (NULL == daio_mgr) | ||
711 | return -ENOMEM; | ||
712 | |||
713 | err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw); | ||
714 | if (err) | ||
715 | goto error1; | ||
716 | |||
717 | spin_lock_init(&daio_mgr->mgr_lock); | ||
718 | spin_lock_init(&daio_mgr->imap_lock); | ||
719 | INIT_LIST_HEAD(&daio_mgr->imappers); | ||
720 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
721 | if (NULL == entry) { | ||
722 | err = -ENOMEM; | ||
723 | goto error2; | ||
724 | } | ||
725 | entry->slot = entry->addr = entry->next = entry->user = 0; | ||
726 | list_add(&entry->list, &daio_mgr->imappers); | ||
727 | daio_mgr->init_imap = entry; | ||
728 | daio_mgr->init_imap_added = 1; | ||
729 | |||
730 | daio_mgr->get_daio = get_daio_rsc; | ||
731 | daio_mgr->put_daio = put_daio_rsc; | ||
732 | daio_mgr->daio_enable = daio_mgr_enb_daio; | ||
733 | daio_mgr->daio_disable = daio_mgr_dsb_daio; | ||
734 | daio_mgr->imap_add = daio_imap_add; | ||
735 | daio_mgr->imap_delete = daio_imap_delete; | ||
736 | daio_mgr->commit_write = daio_mgr_commit_write; | ||
737 | |||
738 | for (i = 0; i < 8; i++) { | ||
739 | ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i); | ||
740 | ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i); | ||
741 | } | ||
742 | ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk); | ||
743 | |||
744 | *rdaio_mgr = daio_mgr; | ||
745 | |||
746 | return 0; | ||
747 | |||
748 | error2: | ||
749 | rsc_mgr_uninit(&daio_mgr->mgr); | ||
750 | error1: | ||
751 | kfree(daio_mgr); | ||
752 | return err; | ||
753 | } | ||
754 | |||
755 | int daio_mgr_destroy(struct daio_mgr *daio_mgr) | ||
756 | { | ||
757 | unsigned long flags; | ||
758 | |||
759 | /* free daio input mapper list */ | ||
760 | spin_lock_irqsave(&daio_mgr->imap_lock, flags); | ||
761 | free_input_mapper_list(&daio_mgr->imappers); | ||
762 | spin_unlock_irqrestore(&daio_mgr->imap_lock, flags); | ||
763 | |||
764 | rsc_mgr_uninit(&daio_mgr->mgr); | ||
765 | kfree(daio_mgr); | ||
766 | |||
767 | return 0; | ||
768 | } | ||
769 | |||
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h new file mode 100644 index 000000000000..0f52ce571ee8 --- /dev/null +++ b/sound/pci/ctxfi/ctdaio.h | |||
@@ -0,0 +1,122 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctdaio.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of Digital Audio Input Output | ||
12 | * resource management object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 23 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef CTDAIO_H | ||
20 | #define CTDAIO_H | ||
21 | |||
22 | #include "ctresource.h" | ||
23 | #include "ctimap.h" | ||
24 | #include <linux/spinlock.h> | ||
25 | #include <linux/list.h> | ||
26 | |||
27 | /* Define the descriptor of a daio resource */ | ||
28 | enum DAIOTYP { | ||
29 | LINEO1, | ||
30 | LINEO2, | ||
31 | LINEO3, | ||
32 | LINEO4, | ||
33 | SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */ | ||
34 | LINEIM, | ||
35 | SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ | ||
36 | SPDIFI1, /* S/PDIF In on internal Drive Bay */ | ||
37 | NUM_DAIOTYP | ||
38 | }; | ||
39 | |||
40 | struct dao_rsc_ops; | ||
41 | struct dai_rsc_ops; | ||
42 | struct daio_mgr; | ||
43 | |||
44 | struct daio { | ||
45 | struct rsc rscl; /* Basic resource info for left TX/RX */ | ||
46 | struct rsc rscr; /* Basic resource info for right TX/RX */ | ||
47 | enum DAIOTYP type; | ||
48 | }; | ||
49 | |||
50 | struct dao { | ||
51 | struct daio daio; | ||
52 | struct dao_rsc_ops *ops; /* DAO specific operations */ | ||
53 | struct imapper **imappers; | ||
54 | struct daio_mgr *mgr; | ||
55 | void *hw; | ||
56 | void *ctrl_blk; | ||
57 | }; | ||
58 | |||
59 | struct dai { | ||
60 | struct daio daio; | ||
61 | struct dai_rsc_ops *ops; /* DAI specific operations */ | ||
62 | void *hw; | ||
63 | void *ctrl_blk; | ||
64 | }; | ||
65 | |||
66 | struct dao_desc { | ||
67 | unsigned int msr:4; | ||
68 | unsigned int passthru:1; | ||
69 | }; | ||
70 | |||
71 | struct dao_rsc_ops { | ||
72 | int (*set_spos)(struct dao *dao, unsigned int spos); | ||
73 | int (*commit_write)(struct dao *dao); | ||
74 | int (*get_spos)(struct dao *dao, unsigned int *spos); | ||
75 | int (*reinit)(struct dao *dao, const struct dao_desc *desc); | ||
76 | int (*set_left_input)(struct dao *dao, struct rsc *input); | ||
77 | int (*set_right_input)(struct dao *dao, struct rsc *input); | ||
78 | int (*clear_left_input)(struct dao *dao); | ||
79 | int (*clear_right_input)(struct dao *dao); | ||
80 | }; | ||
81 | |||
82 | struct dai_rsc_ops { | ||
83 | int (*set_srt_srcl)(struct dai *dai, struct rsc *src); | ||
84 | int (*set_srt_srcr)(struct dai *dai, struct rsc *src); | ||
85 | int (*set_srt_msr)(struct dai *dai, unsigned int msr); | ||
86 | int (*set_enb_src)(struct dai *dai, unsigned int enb); | ||
87 | int (*set_enb_srt)(struct dai *dai, unsigned int enb); | ||
88 | int (*commit_write)(struct dai *dai); | ||
89 | }; | ||
90 | |||
91 | /* Define daio resource request description info */ | ||
92 | struct daio_desc { | ||
93 | unsigned int type:4; | ||
94 | unsigned int msr:4; | ||
95 | unsigned int passthru:1; | ||
96 | }; | ||
97 | |||
98 | struct daio_mgr { | ||
99 | struct rsc_mgr mgr; /* Basic resource manager info */ | ||
100 | spinlock_t mgr_lock; | ||
101 | spinlock_t imap_lock; | ||
102 | struct list_head imappers; | ||
103 | struct imapper *init_imap; | ||
104 | unsigned int init_imap_added; | ||
105 | |||
106 | /* request one daio resource */ | ||
107 | int (*get_daio)(struct daio_mgr *mgr, | ||
108 | const struct daio_desc *desc, struct daio **rdaio); | ||
109 | /* return one daio resource */ | ||
110 | int (*put_daio)(struct daio_mgr *mgr, struct daio *daio); | ||
111 | int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio); | ||
112 | int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio); | ||
113 | int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry); | ||
114 | int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry); | ||
115 | int (*commit_write)(struct daio_mgr *mgr); | ||
116 | }; | ||
117 | |||
118 | /* Constructor and destructor of daio resource manager */ | ||
119 | int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr); | ||
120 | int daio_mgr_destroy(struct daio_mgr *daio_mgr); | ||
121 | |||
122 | #endif /* CTDAIO_H */ | ||
diff --git a/sound/pci/ctxfi/ctdrv.h b/sound/pci/ctxfi/ctdrv.h new file mode 100644 index 000000000000..f776a44f52cf --- /dev/null +++ b/sound/pci/ctxfi/ctdrv.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @file ctdrv.h | ||
9 | * | ||
10 | * @breaf | ||
11 | * This file contains the definition of card IDs supported by this driver. | ||
12 | * | ||
13 | * @author Liu Chun | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef CTDRV_H | ||
18 | #define CTDRV_H | ||
19 | |||
20 | #define PCI_VENDOR_CREATIVE 0x1102 | ||
21 | #define PCI_DEVICE_CREATIVE_20K1 0x0005 | ||
22 | #define PCI_DEVICE_CREATIVE_20K2 0x000B | ||
23 | #define PCI_SUBVENDOR_CREATIVE 0x1102 | ||
24 | #define PCI_SUBSYS_CREATIVE_SB0760 0x0024 | ||
25 | #define PCI_SUBSYS_CREATIVE_SB08801 0x0041 | ||
26 | #define PCI_SUBSYS_CREATIVE_SB08802 0x0042 | ||
27 | #define PCI_SUBSYS_CREATIVE_SB08803 0x0043 | ||
28 | #define PCI_SUBSYS_CREATIVE_HENDRIX 0x6000 | ||
29 | |||
30 | #endif /* CTDRV_H */ | ||
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c new file mode 100644 index 000000000000..8e58860f641c --- /dev/null +++ b/sound/pci/ctxfi/cthardware.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File cthardware.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of hardware access methord. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Jun 26 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include "cthardware.h" | ||
19 | #include "cthw20k1.h" | ||
20 | #include "cthw20k2.h" | ||
21 | #include <linux/bug.h> | ||
22 | |||
23 | static enum CHIPTYP get_chip_type(struct hw *hw) | ||
24 | { | ||
25 | enum CHIPTYP type = ATCNONE; | ||
26 | |||
27 | switch (hw->pci->device) { | ||
28 | case 0x0005: /* 20k1 device */ | ||
29 | type = ATC20K1; | ||
30 | break; | ||
31 | case 0x000B: /* 20k2 device */ | ||
32 | type = ATC20K2; | ||
33 | break; | ||
34 | default: | ||
35 | type = ATCNONE; | ||
36 | break; | ||
37 | } | ||
38 | |||
39 | return type; | ||
40 | } | ||
41 | |||
42 | int create_hw_obj(struct pci_dev *pci, struct hw **rhw) | ||
43 | { | ||
44 | int err = 0; | ||
45 | |||
46 | switch (pci->device) { | ||
47 | case 0x0005: /* 20k1 device */ | ||
48 | err = create_20k1_hw_obj(rhw); | ||
49 | break; | ||
50 | case 0x000B: /* 20k2 device */ | ||
51 | err = create_20k2_hw_obj(rhw); | ||
52 | break; | ||
53 | default: | ||
54 | err = -ENODEV; | ||
55 | break; | ||
56 | } | ||
57 | if (err) | ||
58 | return err; | ||
59 | |||
60 | (*rhw)->pci = pci; | ||
61 | (*rhw)->get_chip_type = get_chip_type; | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | int destroy_hw_obj(struct hw *hw) | ||
67 | { | ||
68 | int err = 0; | ||
69 | |||
70 | switch (hw->pci->device) { | ||
71 | case 0x0005: /* 20k1 device */ | ||
72 | err = destroy_20k1_hw_obj(hw); | ||
73 | break; | ||
74 | case 0x000B: /* 20k2 device */ | ||
75 | err = destroy_20k2_hw_obj(hw); | ||
76 | break; | ||
77 | default: | ||
78 | err = -ENODEV; | ||
79 | break; | ||
80 | } | ||
81 | |||
82 | return err; | ||
83 | } | ||
84 | |||
85 | unsigned int get_field(unsigned int data, unsigned int field) | ||
86 | { | ||
87 | int i; | ||
88 | |||
89 | BUG_ON(!field); | ||
90 | /* @field should always be greater than 0 */ | ||
91 | for (i = 0; !(field & (1 << i)); ) | ||
92 | i++; | ||
93 | |||
94 | return (data & field) >> i; | ||
95 | } | ||
96 | |||
97 | void set_field(unsigned int *data, unsigned int field, unsigned int value) | ||
98 | { | ||
99 | int i; | ||
100 | |||
101 | BUG_ON(!field); | ||
102 | /* @field should always be greater than 0 */ | ||
103 | for (i = 0; !(field & (1 << i)); ) | ||
104 | i++; | ||
105 | |||
106 | *data = (*data & (~field)) | ((value << i) & field); | ||
107 | } | ||
108 | |||
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h new file mode 100644 index 000000000000..b0512df8b334 --- /dev/null +++ b/sound/pci/ctxfi/cthardware.h | |||
@@ -0,0 +1,160 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File cthardware.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of hardware access methord. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date May 13 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef CTHARDWARE_H | ||
19 | #define CTHARDWARE_H | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/pci.h> | ||
23 | |||
24 | enum CHIPTYP { | ||
25 | ATC20K1, | ||
26 | ATC20K2, | ||
27 | ATCNONE | ||
28 | }; | ||
29 | |||
30 | /* Type of input source for ADC */ | ||
31 | enum ADCSRC{ | ||
32 | ADC_MICIN, | ||
33 | ADC_LINEIN, | ||
34 | ADC_VIDEO, | ||
35 | ADC_AUX, | ||
36 | ADC_NONE /* Switch to digital input */ | ||
37 | }; | ||
38 | |||
39 | struct card_conf { | ||
40 | /* device virtual mem page table page physical addr | ||
41 | * (supporting one page table page now) */ | ||
42 | unsigned long vm_pgt_phys; | ||
43 | unsigned int rsr; /* reference sample rate in Hzs*/ | ||
44 | unsigned int msr; /* master sample rate in rsrs */ | ||
45 | }; | ||
46 | |||
47 | struct hw { | ||
48 | int (*card_init)(struct hw *hw, struct card_conf *info); | ||
49 | int (*card_stop)(struct hw *hw); | ||
50 | int (*pll_init)(struct hw *hw, unsigned int rsr); | ||
51 | enum CHIPTYP (*get_chip_type)(struct hw *hw); | ||
52 | int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); | ||
53 | int (*select_adc_source)(struct hw *hw, enum ADCSRC source); | ||
54 | int (*have_digit_io_switch)(struct hw *hw); | ||
55 | |||
56 | /* SRC operations */ | ||
57 | int (*src_rsc_get_ctrl_blk)(void **rblk); | ||
58 | int (*src_rsc_put_ctrl_blk)(void *blk); | ||
59 | int (*src_set_state)(void *blk, unsigned int state); | ||
60 | int (*src_set_bm)(void *blk, unsigned int bm); | ||
61 | int (*src_set_rsr)(void *blk, unsigned int rsr); | ||
62 | int (*src_set_sf)(void *blk, unsigned int sf); | ||
63 | int (*src_set_wr)(void *blk, unsigned int wr); | ||
64 | int (*src_set_pm)(void *blk, unsigned int pm); | ||
65 | int (*src_set_rom)(void *blk, unsigned int rom); | ||
66 | int (*src_set_vo)(void *blk, unsigned int vo); | ||
67 | int (*src_set_st)(void *blk, unsigned int st); | ||
68 | int (*src_set_ie)(void *blk, unsigned int ie); | ||
69 | int (*src_set_ilsz)(void *blk, unsigned int ilsz); | ||
70 | int (*src_set_bp)(void *blk, unsigned int bp); | ||
71 | int (*src_set_cisz)(void *blk, unsigned int cisz); | ||
72 | int (*src_set_ca)(void *blk, unsigned int ca); | ||
73 | int (*src_set_sa)(void *blk, unsigned int sa); | ||
74 | int (*src_set_la)(void *blk, unsigned int la); | ||
75 | int (*src_set_pitch)(void *blk, unsigned int pitch); | ||
76 | int (*src_set_clear_zbufs)(void *blk, unsigned int clear); | ||
77 | int (*src_set_dirty)(void *blk, unsigned int flags); | ||
78 | int (*src_set_dirty_all)(void *blk); | ||
79 | int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
80 | int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk); | ||
81 | unsigned int (*src_get_dirty)(void *blk); | ||
82 | unsigned int (*src_dirty_conj_mask)(void); | ||
83 | int (*src_mgr_get_ctrl_blk)(void **rblk); | ||
84 | int (*src_mgr_put_ctrl_blk)(void *blk); | ||
85 | /* syncly enable src @idx */ | ||
86 | int (*src_mgr_enbs_src)(void *blk, unsigned int idx); | ||
87 | /* enable src @idx */ | ||
88 | int (*src_mgr_enb_src)(void *blk, unsigned int idx); | ||
89 | /* disable src @idx */ | ||
90 | int (*src_mgr_dsb_src)(void *blk, unsigned int idx); | ||
91 | int (*src_mgr_commit_write)(struct hw *hw, void *blk); | ||
92 | |||
93 | /* SRC Input Mapper operations */ | ||
94 | int (*srcimp_mgr_get_ctrl_blk)(void **rblk); | ||
95 | int (*srcimp_mgr_put_ctrl_blk)(void *blk); | ||
96 | int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot); | ||
97 | int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user); | ||
98 | int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next); | ||
99 | int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr); | ||
100 | int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk); | ||
101 | |||
102 | /* AMIXER operations */ | ||
103 | int (*amixer_rsc_get_ctrl_blk)(void **rblk); | ||
104 | int (*amixer_rsc_put_ctrl_blk)(void *blk); | ||
105 | int (*amixer_mgr_get_ctrl_blk)(void **rblk); | ||
106 | int (*amixer_mgr_put_ctrl_blk)(void *blk); | ||
107 | int (*amixer_set_mode)(void *blk, unsigned int mode); | ||
108 | int (*amixer_set_iv)(void *blk, unsigned int iv); | ||
109 | int (*amixer_set_x)(void *blk, unsigned int x); | ||
110 | int (*amixer_set_y)(void *blk, unsigned int y); | ||
111 | int (*amixer_set_sadr)(void *blk, unsigned int sadr); | ||
112 | int (*amixer_set_se)(void *blk, unsigned int se); | ||
113 | int (*amixer_set_dirty)(void *blk, unsigned int flags); | ||
114 | int (*amixer_set_dirty_all)(void *blk); | ||
115 | int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
116 | int (*amixer_get_y)(void *blk); | ||
117 | unsigned int (*amixer_get_dirty)(void *blk); | ||
118 | |||
119 | /* DAIO operations */ | ||
120 | int (*dai_get_ctrl_blk)(void **rblk); | ||
121 | int (*dai_put_ctrl_blk)(void *blk); | ||
122 | int (*dai_srt_set_srco)(void *blk, unsigned int src); | ||
123 | int (*dai_srt_set_srcm)(void *blk, unsigned int src); | ||
124 | int (*dai_srt_set_rsr)(void *blk, unsigned int rsr); | ||
125 | int (*dai_srt_set_drat)(void *blk, unsigned int drat); | ||
126 | int (*dai_srt_set_ec)(void *blk, unsigned int ec); | ||
127 | int (*dai_srt_set_et)(void *blk, unsigned int et); | ||
128 | int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
129 | int (*dao_get_ctrl_blk)(void **rblk); | ||
130 | int (*dao_put_ctrl_blk)(void *blk); | ||
131 | int (*dao_set_spos)(void *blk, unsigned int spos); | ||
132 | int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
133 | int (*dao_get_spos)(void *blk, unsigned int *spos); | ||
134 | |||
135 | int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk); | ||
136 | int (*daio_mgr_put_ctrl_blk)(void *blk); | ||
137 | int (*daio_mgr_enb_dai)(void *blk, unsigned int idx); | ||
138 | int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx); | ||
139 | int (*daio_mgr_enb_dao)(void *blk, unsigned int idx); | ||
140 | int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx); | ||
141 | int (*daio_mgr_dao_init)(void *blk, unsigned int idx, | ||
142 | unsigned int conf); | ||
143 | int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot); | ||
144 | int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next); | ||
145 | int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); | ||
146 | int (*daio_mgr_commit_write)(struct hw *hw, void *blk); | ||
147 | |||
148 | struct pci_dev *pci; /* the pci kernel structure of this card */ | ||
149 | int irq; | ||
150 | unsigned long io_base; | ||
151 | unsigned long mem_base; | ||
152 | }; | ||
153 | |||
154 | int create_hw_obj(struct pci_dev *pci, struct hw **rhw); | ||
155 | int destroy_hw_obj(struct hw *hw); | ||
156 | |||
157 | unsigned int get_field(unsigned int data, unsigned int field); | ||
158 | void set_field(unsigned int *data, unsigned int field, unsigned int value); | ||
159 | |||
160 | #endif /* CTHARDWARE_H */ | ||
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c new file mode 100644 index 000000000000..53572d92ef5d --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.c | |||
@@ -0,0 +1,2230 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File cthw20k1.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of hardware access methord for 20k1. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Jun 24 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include "cthw20k1.h" | ||
19 | #include "ct20k1reg.h" | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | |||
29 | #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ | ||
30 | |||
31 | struct hw20k1 { | ||
32 | struct hw hw; | ||
33 | spinlock_t reg_20k1_lock; | ||
34 | spinlock_t reg_pci_lock; | ||
35 | }; | ||
36 | |||
37 | static u32 hw_read_20kx(struct hw *hw, u32 reg); | ||
38 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); | ||
39 | static u32 hw_read_pci(struct hw *hw, u32 reg); | ||
40 | static void hw_write_pci(struct hw *hw, u32 reg, u32 data); | ||
41 | |||
42 | /* | ||
43 | * Type definition block. | ||
44 | * The layout of control structures can be directly applied on 20k2 chip. | ||
45 | */ | ||
46 | |||
47 | /* | ||
48 | * SRC control block definitions. | ||
49 | */ | ||
50 | |||
51 | /* SRC resource control block */ | ||
52 | #define SRCCTL_STATE 0x00000007 | ||
53 | #define SRCCTL_BM 0x00000008 | ||
54 | #define SRCCTL_RSR 0x00000030 | ||
55 | #define SRCCTL_SF 0x000001C0 | ||
56 | #define SRCCTL_WR 0x00000200 | ||
57 | #define SRCCTL_PM 0x00000400 | ||
58 | #define SRCCTL_ROM 0x00001800 | ||
59 | #define SRCCTL_VO 0x00002000 | ||
60 | #define SRCCTL_ST 0x00004000 | ||
61 | #define SRCCTL_IE 0x00008000 | ||
62 | #define SRCCTL_ILSZ 0x000F0000 | ||
63 | #define SRCCTL_BP 0x00100000 | ||
64 | |||
65 | #define SRCCCR_CISZ 0x000007FF | ||
66 | #define SRCCCR_CWA 0x001FF800 | ||
67 | #define SRCCCR_D 0x00200000 | ||
68 | #define SRCCCR_RS 0x01C00000 | ||
69 | #define SRCCCR_NAL 0x3E000000 | ||
70 | #define SRCCCR_RA 0xC0000000 | ||
71 | |||
72 | #define SRCCA_CA 0x03FFFFFF | ||
73 | #define SRCCA_RS 0x1C000000 | ||
74 | #define SRCCA_NAL 0xE0000000 | ||
75 | |||
76 | #define SRCSA_SA 0x03FFFFFF | ||
77 | |||
78 | #define SRCLA_LA 0x03FFFFFF | ||
79 | |||
80 | /* Mixer Parameter Ring ram Low and Hight register. | ||
81 | * Fixed-point value in 8.24 format for parameter channel */ | ||
82 | #define MPRLH_PITCH 0xFFFFFFFF | ||
83 | |||
84 | /* SRC resource register dirty flags */ | ||
85 | union src_dirty { | ||
86 | struct { | ||
87 | u16 ctl:1; | ||
88 | u16 ccr:1; | ||
89 | u16 sa:1; | ||
90 | u16 la:1; | ||
91 | u16 ca:1; | ||
92 | u16 mpr:1; | ||
93 | u16 czbfs:1; /* Clear Z-Buffers */ | ||
94 | u16 rsv:9; | ||
95 | } bf; | ||
96 | u16 data; | ||
97 | }; | ||
98 | |||
99 | struct src_rsc_ctrl_blk { | ||
100 | unsigned int ctl; | ||
101 | unsigned int ccr; | ||
102 | unsigned int ca; | ||
103 | unsigned int sa; | ||
104 | unsigned int la; | ||
105 | unsigned int mpr; | ||
106 | union src_dirty dirty; | ||
107 | }; | ||
108 | |||
109 | /* SRC manager control block */ | ||
110 | union src_mgr_dirty { | ||
111 | struct { | ||
112 | u16 enb0:1; | ||
113 | u16 enb1:1; | ||
114 | u16 enb2:1; | ||
115 | u16 enb3:1; | ||
116 | u16 enb4:1; | ||
117 | u16 enb5:1; | ||
118 | u16 enb6:1; | ||
119 | u16 enb7:1; | ||
120 | u16 enbsa:1; | ||
121 | u16 rsv:7; | ||
122 | } bf; | ||
123 | u16 data; | ||
124 | }; | ||
125 | |||
126 | struct src_mgr_ctrl_blk { | ||
127 | unsigned int enbsa; | ||
128 | unsigned int enb[8]; | ||
129 | union src_mgr_dirty dirty; | ||
130 | }; | ||
131 | |||
132 | /* SRCIMP manager control block */ | ||
133 | #define SRCAIM_ARC 0x00000FFF | ||
134 | #define SRCAIM_NXT 0x00FF0000 | ||
135 | #define SRCAIM_SRC 0xFF000000 | ||
136 | |||
137 | struct srcimap { | ||
138 | unsigned int srcaim; | ||
139 | unsigned int idx; | ||
140 | }; | ||
141 | |||
142 | /* SRCIMP manager register dirty flags */ | ||
143 | union srcimp_mgr_dirty { | ||
144 | struct { | ||
145 | u16 srcimap:1; | ||
146 | u16 rsv:15; | ||
147 | } bf; | ||
148 | u16 data; | ||
149 | }; | ||
150 | |||
151 | struct srcimp_mgr_ctrl_blk { | ||
152 | struct srcimap srcimap; | ||
153 | union srcimp_mgr_dirty dirty; | ||
154 | }; | ||
155 | |||
156 | /* | ||
157 | * Function implementation block. | ||
158 | */ | ||
159 | |||
160 | static int src_get_rsc_ctrl_blk(void **rblk) | ||
161 | { | ||
162 | struct src_rsc_ctrl_blk *blk; | ||
163 | |||
164 | *rblk = NULL; | ||
165 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
166 | if (NULL == blk) | ||
167 | return -ENOMEM; | ||
168 | |||
169 | *rblk = blk; | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int src_put_rsc_ctrl_blk(void *blk) | ||
175 | { | ||
176 | kfree((struct src_rsc_ctrl_blk *)blk); | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int src_set_state(void *blk, unsigned int state) | ||
182 | { | ||
183 | struct src_rsc_ctrl_blk *ctl = blk; | ||
184 | |||
185 | set_field(&ctl->ctl, SRCCTL_STATE, state); | ||
186 | ctl->dirty.bf.ctl = 1; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int src_set_bm(void *blk, unsigned int bm) | ||
191 | { | ||
192 | struct src_rsc_ctrl_blk *ctl = blk; | ||
193 | |||
194 | set_field(&ctl->ctl, SRCCTL_BM, bm); | ||
195 | ctl->dirty.bf.ctl = 1; | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static int src_set_rsr(void *blk, unsigned int rsr) | ||
200 | { | ||
201 | struct src_rsc_ctrl_blk *ctl = blk; | ||
202 | |||
203 | set_field(&ctl->ctl, SRCCTL_RSR, rsr); | ||
204 | ctl->dirty.bf.ctl = 1; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int src_set_sf(void *blk, unsigned int sf) | ||
209 | { | ||
210 | struct src_rsc_ctrl_blk *ctl = blk; | ||
211 | |||
212 | set_field(&ctl->ctl, SRCCTL_SF, sf); | ||
213 | ctl->dirty.bf.ctl = 1; | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int src_set_wr(void *blk, unsigned int wr) | ||
218 | { | ||
219 | struct src_rsc_ctrl_blk *ctl = blk; | ||
220 | |||
221 | set_field(&ctl->ctl, SRCCTL_WR, wr); | ||
222 | ctl->dirty.bf.ctl = 1; | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int src_set_pm(void *blk, unsigned int pm) | ||
227 | { | ||
228 | struct src_rsc_ctrl_blk *ctl = blk; | ||
229 | |||
230 | set_field(&ctl->ctl, SRCCTL_PM, pm); | ||
231 | ctl->dirty.bf.ctl = 1; | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int src_set_rom(void *blk, unsigned int rom) | ||
236 | { | ||
237 | struct src_rsc_ctrl_blk *ctl = blk; | ||
238 | |||
239 | set_field(&ctl->ctl, SRCCTL_ROM, rom); | ||
240 | ctl->dirty.bf.ctl = 1; | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int src_set_vo(void *blk, unsigned int vo) | ||
245 | { | ||
246 | struct src_rsc_ctrl_blk *ctl = blk; | ||
247 | |||
248 | set_field(&ctl->ctl, SRCCTL_VO, vo); | ||
249 | ctl->dirty.bf.ctl = 1; | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int src_set_st(void *blk, unsigned int st) | ||
254 | { | ||
255 | struct src_rsc_ctrl_blk *ctl = blk; | ||
256 | |||
257 | set_field(&ctl->ctl, SRCCTL_ST, st); | ||
258 | ctl->dirty.bf.ctl = 1; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int src_set_ie(void *blk, unsigned int ie) | ||
263 | { | ||
264 | struct src_rsc_ctrl_blk *ctl = blk; | ||
265 | |||
266 | set_field(&ctl->ctl, SRCCTL_IE, ie); | ||
267 | ctl->dirty.bf.ctl = 1; | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int src_set_ilsz(void *blk, unsigned int ilsz) | ||
272 | { | ||
273 | struct src_rsc_ctrl_blk *ctl = blk; | ||
274 | |||
275 | set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); | ||
276 | ctl->dirty.bf.ctl = 1; | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int src_set_bp(void *blk, unsigned int bp) | ||
281 | { | ||
282 | struct src_rsc_ctrl_blk *ctl = blk; | ||
283 | |||
284 | set_field(&ctl->ctl, SRCCTL_BP, bp); | ||
285 | ctl->dirty.bf.ctl = 1; | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int src_set_cisz(void *blk, unsigned int cisz) | ||
290 | { | ||
291 | struct src_rsc_ctrl_blk *ctl = blk; | ||
292 | |||
293 | set_field(&ctl->ccr, SRCCCR_CISZ, cisz); | ||
294 | ctl->dirty.bf.ccr = 1; | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int src_set_ca(void *blk, unsigned int ca) | ||
299 | { | ||
300 | struct src_rsc_ctrl_blk *ctl = blk; | ||
301 | |||
302 | set_field(&ctl->ca, SRCCA_CA, ca); | ||
303 | ctl->dirty.bf.ca = 1; | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static int src_set_sa(void *blk, unsigned int sa) | ||
308 | { | ||
309 | struct src_rsc_ctrl_blk *ctl = blk; | ||
310 | |||
311 | set_field(&ctl->sa, SRCSA_SA, sa); | ||
312 | ctl->dirty.bf.sa = 1; | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int src_set_la(void *blk, unsigned int la) | ||
317 | { | ||
318 | struct src_rsc_ctrl_blk *ctl = blk; | ||
319 | |||
320 | set_field(&ctl->la, SRCLA_LA, la); | ||
321 | ctl->dirty.bf.la = 1; | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int src_set_pitch(void *blk, unsigned int pitch) | ||
326 | { | ||
327 | struct src_rsc_ctrl_blk *ctl = blk; | ||
328 | |||
329 | set_field(&ctl->mpr, MPRLH_PITCH, pitch); | ||
330 | ctl->dirty.bf.mpr = 1; | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static int src_set_clear_zbufs(void *blk, unsigned int clear) | ||
335 | { | ||
336 | ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static int src_set_dirty(void *blk, unsigned int flags) | ||
341 | { | ||
342 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int src_set_dirty_all(void *blk) | ||
347 | { | ||
348 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | #define AR_SLOT_SIZE 4096 | ||
353 | #define AR_SLOT_BLOCK_SIZE 16 | ||
354 | #define AR_PTS_PITCH 6 | ||
355 | #define AR_PARAM_SRC_OFFSET 0x60 | ||
356 | |||
357 | static unsigned int src_param_pitch_mixer(unsigned int src_idx) | ||
358 | { | ||
359 | return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE | ||
360 | - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; | ||
361 | |||
362 | } | ||
363 | |||
364 | static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
365 | { | ||
366 | struct src_rsc_ctrl_blk *ctl = blk; | ||
367 | int i = 0; | ||
368 | |||
369 | if (ctl->dirty.bf.czbfs) { | ||
370 | /* Clear Z-Buffer registers */ | ||
371 | for (i = 0; i < 8; i++) | ||
372 | hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0); | ||
373 | |||
374 | for (i = 0; i < 4; i++) | ||
375 | hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0); | ||
376 | |||
377 | for (i = 0; i < 8; i++) | ||
378 | hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0); | ||
379 | |||
380 | ctl->dirty.bf.czbfs = 0; | ||
381 | } | ||
382 | if (ctl->dirty.bf.mpr) { | ||
383 | /* Take the parameter mixer resource in the same group as that | ||
384 | * the idx src is in for simplicity. Unlike src, all conjugate | ||
385 | * parameter mixer resources must be programmed for | ||
386 | * corresponding conjugate src resources. */ | ||
387 | unsigned int pm_idx = src_param_pitch_mixer(idx); | ||
388 | hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr); | ||
389 | hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3); | ||
390 | hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0); | ||
391 | ctl->dirty.bf.mpr = 0; | ||
392 | } | ||
393 | if (ctl->dirty.bf.sa) { | ||
394 | hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa); | ||
395 | ctl->dirty.bf.sa = 0; | ||
396 | } | ||
397 | if (ctl->dirty.bf.la) { | ||
398 | hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la); | ||
399 | ctl->dirty.bf.la = 0; | ||
400 | } | ||
401 | if (ctl->dirty.bf.ca) { | ||
402 | hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca); | ||
403 | ctl->dirty.bf.ca = 0; | ||
404 | } | ||
405 | |||
406 | /* Write srccf register */ | ||
407 | hw_write_20kx(hw, SRCCF+idx*0x100, 0x0); | ||
408 | |||
409 | if (ctl->dirty.bf.ccr) { | ||
410 | hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr); | ||
411 | ctl->dirty.bf.ccr = 0; | ||
412 | } | ||
413 | if (ctl->dirty.bf.ctl) { | ||
414 | hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl); | ||
415 | ctl->dirty.bf.ctl = 0; | ||
416 | } | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) | ||
422 | { | ||
423 | struct src_rsc_ctrl_blk *ctl = blk; | ||
424 | |||
425 | ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100); | ||
426 | ctl->dirty.bf.ca = 0; | ||
427 | |||
428 | return get_field(ctl->ca, SRCCA_CA); | ||
429 | } | ||
430 | |||
431 | static unsigned int src_get_dirty(void *blk) | ||
432 | { | ||
433 | return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; | ||
434 | } | ||
435 | |||
436 | static unsigned int src_dirty_conj_mask(void) | ||
437 | { | ||
438 | return 0x20; | ||
439 | } | ||
440 | |||
441 | static int src_mgr_enbs_src(void *blk, unsigned int idx) | ||
442 | { | ||
443 | ((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0); | ||
444 | ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; | ||
445 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int src_mgr_enb_src(void *blk, unsigned int idx) | ||
450 | { | ||
451 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
452 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static int src_mgr_dsb_src(void *blk, unsigned int idx) | ||
457 | { | ||
458 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); | ||
459 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | static int src_mgr_commit_write(struct hw *hw, void *blk) | ||
464 | { | ||
465 | struct src_mgr_ctrl_blk *ctl = blk; | ||
466 | int i = 0; | ||
467 | unsigned int ret = 0; | ||
468 | |||
469 | if (ctl->dirty.bf.enbsa) { | ||
470 | do { | ||
471 | ret = hw_read_20kx(hw, SRCENBSTAT); | ||
472 | } while (ret & 0x1); | ||
473 | hw_write_20kx(hw, SRCENBS, ctl->enbsa); | ||
474 | ctl->dirty.bf.enbsa = 0; | ||
475 | } | ||
476 | for (i = 0; i < 8; i++) { | ||
477 | if ((ctl->dirty.data & (0x1 << i))) { | ||
478 | hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]); | ||
479 | ctl->dirty.data &= ~(0x1 << i); | ||
480 | } | ||
481 | } | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static int src_mgr_get_ctrl_blk(void **rblk) | ||
487 | { | ||
488 | struct src_mgr_ctrl_blk *blk; | ||
489 | |||
490 | *rblk = NULL; | ||
491 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
492 | if (NULL == blk) | ||
493 | return -ENOMEM; | ||
494 | |||
495 | *rblk = blk; | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static int src_mgr_put_ctrl_blk(void *blk) | ||
501 | { | ||
502 | kfree((struct src_mgr_ctrl_blk *)blk); | ||
503 | |||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | static int srcimp_mgr_get_ctrl_blk(void **rblk) | ||
508 | { | ||
509 | struct srcimp_mgr_ctrl_blk *blk; | ||
510 | |||
511 | *rblk = NULL; | ||
512 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
513 | if (NULL == blk) | ||
514 | return -ENOMEM; | ||
515 | |||
516 | *rblk = blk; | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int srcimp_mgr_put_ctrl_blk(void *blk) | ||
522 | { | ||
523 | kfree((struct srcimp_mgr_ctrl_blk *)blk); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) | ||
529 | { | ||
530 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
531 | |||
532 | set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); | ||
533 | ctl->dirty.bf.srcimap = 1; | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) | ||
538 | { | ||
539 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
540 | |||
541 | set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); | ||
542 | ctl->dirty.bf.srcimap = 1; | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) | ||
547 | { | ||
548 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
549 | |||
550 | set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); | ||
551 | ctl->dirty.bf.srcimap = 1; | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
556 | { | ||
557 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
558 | |||
559 | ctl->srcimap.idx = addr; | ||
560 | ctl->dirty.bf.srcimap = 1; | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static int srcimp_mgr_commit_write(struct hw *hw, void *blk) | ||
565 | { | ||
566 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
567 | |||
568 | if (ctl->dirty.bf.srcimap) { | ||
569 | hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100, | ||
570 | ctl->srcimap.srcaim); | ||
571 | ctl->dirty.bf.srcimap = 0; | ||
572 | } | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | /* | ||
578 | * AMIXER control block definitions. | ||
579 | */ | ||
580 | |||
581 | #define AMOPLO_M 0x00000003 | ||
582 | #define AMOPLO_X 0x0003FFF0 | ||
583 | #define AMOPLO_Y 0xFFFC0000 | ||
584 | |||
585 | #define AMOPHI_SADR 0x000000FF | ||
586 | #define AMOPHI_SE 0x80000000 | ||
587 | |||
588 | /* AMIXER resource register dirty flags */ | ||
589 | union amixer_dirty { | ||
590 | struct { | ||
591 | u16 amoplo:1; | ||
592 | u16 amophi:1; | ||
593 | u16 rsv:14; | ||
594 | } bf; | ||
595 | u16 data; | ||
596 | }; | ||
597 | |||
598 | /* AMIXER resource control block */ | ||
599 | struct amixer_rsc_ctrl_blk { | ||
600 | unsigned int amoplo; | ||
601 | unsigned int amophi; | ||
602 | union amixer_dirty dirty; | ||
603 | }; | ||
604 | |||
605 | static int amixer_set_mode(void *blk, unsigned int mode) | ||
606 | { | ||
607 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
608 | |||
609 | set_field(&ctl->amoplo, AMOPLO_M, mode); | ||
610 | ctl->dirty.bf.amoplo = 1; | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static int amixer_set_iv(void *blk, unsigned int iv) | ||
615 | { | ||
616 | /* 20k1 amixer does not have this field */ | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static int amixer_set_x(void *blk, unsigned int x) | ||
621 | { | ||
622 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
623 | |||
624 | set_field(&ctl->amoplo, AMOPLO_X, x); | ||
625 | ctl->dirty.bf.amoplo = 1; | ||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | static int amixer_set_y(void *blk, unsigned int y) | ||
630 | { | ||
631 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
632 | |||
633 | set_field(&ctl->amoplo, AMOPLO_Y, y); | ||
634 | ctl->dirty.bf.amoplo = 1; | ||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | static int amixer_set_sadr(void *blk, unsigned int sadr) | ||
639 | { | ||
640 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
641 | |||
642 | set_field(&ctl->amophi, AMOPHI_SADR, sadr); | ||
643 | ctl->dirty.bf.amophi = 1; | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static int amixer_set_se(void *blk, unsigned int se) | ||
648 | { | ||
649 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
650 | |||
651 | set_field(&ctl->amophi, AMOPHI_SE, se); | ||
652 | ctl->dirty.bf.amophi = 1; | ||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | static int amixer_set_dirty(void *blk, unsigned int flags) | ||
657 | { | ||
658 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static int amixer_set_dirty_all(void *blk) | ||
663 | { | ||
664 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
669 | { | ||
670 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
671 | |||
672 | if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { | ||
673 | hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo); | ||
674 | ctl->dirty.bf.amoplo = 0; | ||
675 | hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi); | ||
676 | ctl->dirty.bf.amophi = 0; | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | static int amixer_get_y(void *blk) | ||
683 | { | ||
684 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
685 | |||
686 | return get_field(ctl->amoplo, AMOPLO_Y); | ||
687 | } | ||
688 | |||
689 | static unsigned int amixer_get_dirty(void *blk) | ||
690 | { | ||
691 | return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; | ||
692 | } | ||
693 | |||
694 | static int amixer_rsc_get_ctrl_blk(void **rblk) | ||
695 | { | ||
696 | struct amixer_rsc_ctrl_blk *blk; | ||
697 | |||
698 | *rblk = NULL; | ||
699 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
700 | if (NULL == blk) | ||
701 | return -ENOMEM; | ||
702 | |||
703 | *rblk = blk; | ||
704 | |||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int amixer_rsc_put_ctrl_blk(void *blk) | ||
709 | { | ||
710 | kfree((struct amixer_rsc_ctrl_blk *)blk); | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static int amixer_mgr_get_ctrl_blk(void **rblk) | ||
716 | { | ||
717 | /*amixer_mgr_ctrl_blk_t *blk;*/ | ||
718 | |||
719 | *rblk = NULL; | ||
720 | /*blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
721 | if (NULL == blk) | ||
722 | return -ENOMEM; | ||
723 | |||
724 | *rblk = blk;*/ | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static int amixer_mgr_put_ctrl_blk(void *blk) | ||
730 | { | ||
731 | /*kfree((amixer_mgr_ctrl_blk_t *)blk);*/ | ||
732 | |||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | /* | ||
737 | * DAIO control block definitions. | ||
738 | */ | ||
739 | |||
740 | /* Receiver Sample Rate Tracker Control register */ | ||
741 | #define SRTCTL_SRCR 0x000000FF | ||
742 | #define SRTCTL_SRCL 0x0000FF00 | ||
743 | #define SRTCTL_RSR 0x00030000 | ||
744 | #define SRTCTL_DRAT 0x000C0000 | ||
745 | #define SRTCTL_RLE 0x10000000 | ||
746 | #define SRTCTL_RLP 0x20000000 | ||
747 | #define SRTCTL_EC 0x40000000 | ||
748 | #define SRTCTL_ET 0x80000000 | ||
749 | |||
750 | /* DAIO Receiver register dirty flags */ | ||
751 | union dai_dirty { | ||
752 | struct { | ||
753 | u16 srtctl:1; | ||
754 | u16 rsv:15; | ||
755 | } bf; | ||
756 | u16 data; | ||
757 | }; | ||
758 | |||
759 | /* DAIO Receiver control block */ | ||
760 | struct dai_ctrl_blk { | ||
761 | unsigned int srtctl; | ||
762 | union dai_dirty dirty; | ||
763 | }; | ||
764 | |||
765 | /* S/PDIF Transmitter register dirty flags */ | ||
766 | union dao_dirty { | ||
767 | struct { | ||
768 | u16 spos:1; | ||
769 | u16 rsv:15; | ||
770 | } bf; | ||
771 | u16 data; | ||
772 | }; | ||
773 | |||
774 | /* S/PDIF Transmitter control block */ | ||
775 | struct dao_ctrl_blk { | ||
776 | unsigned int spos; /* S/PDIF Output Channel Status Register */ | ||
777 | union dao_dirty dirty; | ||
778 | }; | ||
779 | |||
780 | /* Audio Input Mapper RAM */ | ||
781 | #define AIM_ARC 0x00000FFF | ||
782 | #define AIM_NXT 0x007F0000 | ||
783 | |||
784 | struct daoimap { | ||
785 | unsigned int aim; | ||
786 | unsigned int idx; | ||
787 | }; | ||
788 | |||
789 | /* I2S Transmitter/Receiver Control register */ | ||
790 | #define I2SCTL_EA 0x00000004 | ||
791 | #define I2SCTL_EI 0x00000010 | ||
792 | |||
793 | /* S/PDIF Transmitter Control register */ | ||
794 | #define SPOCTL_OE 0x00000001 | ||
795 | #define SPOCTL_OS 0x0000000E | ||
796 | #define SPOCTL_RIV 0x00000010 | ||
797 | #define SPOCTL_LIV 0x00000020 | ||
798 | #define SPOCTL_SR 0x000000C0 | ||
799 | |||
800 | /* S/PDIF Receiver Control register */ | ||
801 | #define SPICTL_EN 0x00000001 | ||
802 | #define SPICTL_I24 0x00000002 | ||
803 | #define SPICTL_IB 0x00000004 | ||
804 | #define SPICTL_SM 0x00000008 | ||
805 | #define SPICTL_VM 0x00000010 | ||
806 | |||
807 | /* DAIO manager register dirty flags */ | ||
808 | union daio_mgr_dirty { | ||
809 | struct { | ||
810 | u32 i2soctl:4; | ||
811 | u32 i2sictl:4; | ||
812 | u32 spoctl:4; | ||
813 | u32 spictl:4; | ||
814 | u32 daoimap:1; | ||
815 | u32 rsv:15; | ||
816 | } bf; | ||
817 | u32 data; | ||
818 | }; | ||
819 | |||
820 | /* DAIO manager control block */ | ||
821 | struct daio_mgr_ctrl_blk { | ||
822 | unsigned int i2sctl; | ||
823 | unsigned int spoctl; | ||
824 | unsigned int spictl; | ||
825 | struct daoimap daoimap; | ||
826 | union daio_mgr_dirty dirty; | ||
827 | }; | ||
828 | |||
829 | static int dai_srt_set_srcr(void *blk, unsigned int src) | ||
830 | { | ||
831 | struct dai_ctrl_blk *ctl = blk; | ||
832 | |||
833 | set_field(&ctl->srtctl, SRTCTL_SRCR, src); | ||
834 | ctl->dirty.bf.srtctl = 1; | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static int dai_srt_set_srcl(void *blk, unsigned int src) | ||
839 | { | ||
840 | struct dai_ctrl_blk *ctl = blk; | ||
841 | |||
842 | set_field(&ctl->srtctl, SRTCTL_SRCL, src); | ||
843 | ctl->dirty.bf.srtctl = 1; | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | static int dai_srt_set_rsr(void *blk, unsigned int rsr) | ||
848 | { | ||
849 | struct dai_ctrl_blk *ctl = blk; | ||
850 | |||
851 | set_field(&ctl->srtctl, SRTCTL_RSR, rsr); | ||
852 | ctl->dirty.bf.srtctl = 1; | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | static int dai_srt_set_drat(void *blk, unsigned int drat) | ||
857 | { | ||
858 | struct dai_ctrl_blk *ctl = blk; | ||
859 | |||
860 | set_field(&ctl->srtctl, SRTCTL_DRAT, drat); | ||
861 | ctl->dirty.bf.srtctl = 1; | ||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static int dai_srt_set_ec(void *blk, unsigned int ec) | ||
866 | { | ||
867 | struct dai_ctrl_blk *ctl = blk; | ||
868 | |||
869 | set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0); | ||
870 | ctl->dirty.bf.srtctl = 1; | ||
871 | return 0; | ||
872 | } | ||
873 | |||
874 | static int dai_srt_set_et(void *blk, unsigned int et) | ||
875 | { | ||
876 | struct dai_ctrl_blk *ctl = blk; | ||
877 | |||
878 | set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0); | ||
879 | ctl->dirty.bf.srtctl = 1; | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
884 | { | ||
885 | struct dai_ctrl_blk *ctl = blk; | ||
886 | |||
887 | if (ctl->dirty.bf.srtctl) { | ||
888 | if (idx < 4) { | ||
889 | /* S/PDIF SRTs */ | ||
890 | hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl); | ||
891 | } else { | ||
892 | /* I2S SRT */ | ||
893 | hw_write_20kx(hw, SRTICTL, ctl->srtctl); | ||
894 | } | ||
895 | ctl->dirty.bf.srtctl = 0; | ||
896 | } | ||
897 | |||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | static int dai_get_ctrl_blk(void **rblk) | ||
902 | { | ||
903 | struct dai_ctrl_blk *blk; | ||
904 | |||
905 | *rblk = NULL; | ||
906 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
907 | if (NULL == blk) | ||
908 | return -ENOMEM; | ||
909 | |||
910 | *rblk = blk; | ||
911 | |||
912 | return 0; | ||
913 | } | ||
914 | |||
915 | static int dai_put_ctrl_blk(void *blk) | ||
916 | { | ||
917 | kfree((struct dai_ctrl_blk *)blk); | ||
918 | |||
919 | return 0; | ||
920 | } | ||
921 | |||
922 | static int dao_set_spos(void *blk, unsigned int spos) | ||
923 | { | ||
924 | ((struct dao_ctrl_blk *)blk)->spos = spos; | ||
925 | ((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1; | ||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
930 | { | ||
931 | struct dao_ctrl_blk *ctl = blk; | ||
932 | |||
933 | if (ctl->dirty.bf.spos) { | ||
934 | if (idx < 4) { | ||
935 | /* S/PDIF SPOSx */ | ||
936 | hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos); | ||
937 | } | ||
938 | ctl->dirty.bf.spos = 0; | ||
939 | } | ||
940 | |||
941 | return 0; | ||
942 | } | ||
943 | |||
944 | static int dao_get_spos(void *blk, unsigned int *spos) | ||
945 | { | ||
946 | *spos = ((struct dao_ctrl_blk *)blk)->spos; | ||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | static int dao_get_ctrl_blk(void **rblk) | ||
951 | { | ||
952 | struct dao_ctrl_blk *blk; | ||
953 | |||
954 | *rblk = NULL; | ||
955 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
956 | if (NULL == blk) | ||
957 | return -ENOMEM; | ||
958 | |||
959 | *rblk = blk; | ||
960 | |||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static int dao_put_ctrl_blk(void *blk) | ||
965 | { | ||
966 | kfree((struct dao_ctrl_blk *)blk); | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | static int daio_mgr_enb_dai(void *blk, unsigned int idx) | ||
972 | { | ||
973 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
974 | |||
975 | if (idx < 4) { | ||
976 | /* S/PDIF input */ | ||
977 | set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1); | ||
978 | ctl->dirty.bf.spictl |= (0x1 << idx); | ||
979 | } else { | ||
980 | /* I2S input */ | ||
981 | idx %= 4; | ||
982 | set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1); | ||
983 | ctl->dirty.bf.i2sictl |= (0x1 << idx); | ||
984 | } | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | static int daio_mgr_dsb_dai(void *blk, unsigned int idx) | ||
989 | { | ||
990 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
991 | |||
992 | if (idx < 4) { | ||
993 | /* S/PDIF input */ | ||
994 | set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0); | ||
995 | ctl->dirty.bf.spictl |= (0x1 << idx); | ||
996 | } else { | ||
997 | /* I2S input */ | ||
998 | idx %= 4; | ||
999 | set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0); | ||
1000 | ctl->dirty.bf.i2sictl |= (0x1 << idx); | ||
1001 | } | ||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | static int daio_mgr_enb_dao(void *blk, unsigned int idx) | ||
1006 | { | ||
1007 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1008 | |||
1009 | if (idx < 4) { | ||
1010 | /* S/PDIF output */ | ||
1011 | set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1); | ||
1012 | ctl->dirty.bf.spoctl |= (0x1 << idx); | ||
1013 | } else { | ||
1014 | /* I2S output */ | ||
1015 | idx %= 4; | ||
1016 | set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1); | ||
1017 | ctl->dirty.bf.i2soctl |= (0x1 << idx); | ||
1018 | } | ||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | static int daio_mgr_dsb_dao(void *blk, unsigned int idx) | ||
1023 | { | ||
1024 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1025 | |||
1026 | if (idx < 4) { | ||
1027 | /* S/PDIF output */ | ||
1028 | set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0); | ||
1029 | ctl->dirty.bf.spoctl |= (0x1 << idx); | ||
1030 | } else { | ||
1031 | /* I2S output */ | ||
1032 | idx %= 4; | ||
1033 | set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0); | ||
1034 | ctl->dirty.bf.i2soctl |= (0x1 << idx); | ||
1035 | } | ||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
1039 | static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) | ||
1040 | { | ||
1041 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1042 | |||
1043 | if (idx < 4) { | ||
1044 | /* S/PDIF output */ | ||
1045 | switch ((conf & 0x7)) { | ||
1046 | case 0: | ||
1047 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3); | ||
1048 | break; /* CDIF */ | ||
1049 | case 1: | ||
1050 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0); | ||
1051 | break; | ||
1052 | case 2: | ||
1053 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1); | ||
1054 | break; | ||
1055 | case 4: | ||
1056 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2); | ||
1057 | break; | ||
1058 | default: | ||
1059 | break; | ||
1060 | } | ||
1061 | set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8), | ||
1062 | (conf >> 4) & 0x1); /* Non-audio */ | ||
1063 | set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8), | ||
1064 | (conf >> 4) & 0x1); /* Non-audio */ | ||
1065 | set_field(&ctl->spoctl, SPOCTL_OS << (idx*8), | ||
1066 | ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */ | ||
1067 | |||
1068 | ctl->dirty.bf.spoctl |= (0x1 << idx); | ||
1069 | } else { | ||
1070 | /* I2S output */ | ||
1071 | /*idx %= 4; */ | ||
1072 | } | ||
1073 | return 0; | ||
1074 | } | ||
1075 | |||
1076 | static int daio_mgr_set_imaparc(void *blk, unsigned int slot) | ||
1077 | { | ||
1078 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1079 | |||
1080 | set_field(&ctl->daoimap.aim, AIM_ARC, slot); | ||
1081 | ctl->dirty.bf.daoimap = 1; | ||
1082 | return 0; | ||
1083 | } | ||
1084 | |||
1085 | static int daio_mgr_set_imapnxt(void *blk, unsigned int next) | ||
1086 | { | ||
1087 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1088 | |||
1089 | set_field(&ctl->daoimap.aim, AIM_NXT, next); | ||
1090 | ctl->dirty.bf.daoimap = 1; | ||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
1095 | { | ||
1096 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1097 | |||
1098 | ctl->daoimap.idx = addr; | ||
1099 | ctl->dirty.bf.daoimap = 1; | ||
1100 | return 0; | ||
1101 | } | ||
1102 | |||
1103 | static int daio_mgr_commit_write(struct hw *hw, void *blk) | ||
1104 | { | ||
1105 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1106 | int i = 0; | ||
1107 | |||
1108 | if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) { | ||
1109 | for (i = 0; i < 4; i++) { | ||
1110 | if ((ctl->dirty.bf.i2sictl & (0x1 << i))) | ||
1111 | ctl->dirty.bf.i2sictl &= ~(0x1 << i); | ||
1112 | |||
1113 | if ((ctl->dirty.bf.i2soctl & (0x1 << i))) | ||
1114 | ctl->dirty.bf.i2soctl &= ~(0x1 << i); | ||
1115 | } | ||
1116 | hw_write_20kx(hw, I2SCTL, ctl->i2sctl); | ||
1117 | mdelay(1); | ||
1118 | } | ||
1119 | if (ctl->dirty.bf.spoctl) { | ||
1120 | for (i = 0; i < 4; i++) { | ||
1121 | if ((ctl->dirty.bf.spoctl & (0x1 << i))) | ||
1122 | ctl->dirty.bf.spoctl &= ~(0x1 << i); | ||
1123 | } | ||
1124 | hw_write_20kx(hw, SPOCTL, ctl->spoctl); | ||
1125 | mdelay(1); | ||
1126 | } | ||
1127 | if (ctl->dirty.bf.spictl) { | ||
1128 | for (i = 0; i < 4; i++) { | ||
1129 | if ((ctl->dirty.bf.spictl & (0x1 << i))) | ||
1130 | ctl->dirty.bf.spictl &= ~(0x1 << i); | ||
1131 | } | ||
1132 | hw_write_20kx(hw, SPICTL, ctl->spictl); | ||
1133 | mdelay(1); | ||
1134 | } | ||
1135 | if (ctl->dirty.bf.daoimap) { | ||
1136 | hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4, | ||
1137 | ctl->daoimap.aim); | ||
1138 | ctl->dirty.bf.daoimap = 0; | ||
1139 | } | ||
1140 | |||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) | ||
1145 | { | ||
1146 | struct daio_mgr_ctrl_blk *blk; | ||
1147 | |||
1148 | *rblk = NULL; | ||
1149 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
1150 | if (NULL == blk) | ||
1151 | return -ENOMEM; | ||
1152 | |||
1153 | blk->i2sctl = hw_read_20kx(hw, I2SCTL); | ||
1154 | blk->spoctl = hw_read_20kx(hw, SPOCTL); | ||
1155 | blk->spictl = hw_read_20kx(hw, SPICTL); | ||
1156 | |||
1157 | *rblk = blk; | ||
1158 | |||
1159 | return 0; | ||
1160 | } | ||
1161 | |||
1162 | static int daio_mgr_put_ctrl_blk(void *blk) | ||
1163 | { | ||
1164 | kfree((struct daio_mgr_ctrl_blk *)blk); | ||
1165 | |||
1166 | return 0; | ||
1167 | } | ||
1168 | |||
1169 | /* Card hardware initialization block */ | ||
1170 | struct dac_conf { | ||
1171 | unsigned int msr; /* master sample rate in rsrs */ | ||
1172 | }; | ||
1173 | |||
1174 | struct adc_conf { | ||
1175 | unsigned int msr; /* master sample rate in rsrs */ | ||
1176 | unsigned char input; /* the input source of ADC */ | ||
1177 | unsigned char mic20db; /* boost mic by 20db if input is microphone */ | ||
1178 | }; | ||
1179 | |||
1180 | struct daio_conf { | ||
1181 | unsigned int msr; /* master sample rate in rsrs */ | ||
1182 | }; | ||
1183 | |||
1184 | struct trn_conf { | ||
1185 | unsigned long vm_pgt_phys; | ||
1186 | }; | ||
1187 | |||
1188 | static int hw_daio_init(struct hw *hw, const struct daio_conf *info) | ||
1189 | { | ||
1190 | u32 i2sorg = 0; | ||
1191 | u32 spdorg = 0; | ||
1192 | |||
1193 | /* Read I2S CTL. Keep original value. */ | ||
1194 | /*i2sorg = hw_read_20kx(hw, I2SCTL);*/ | ||
1195 | i2sorg = 0x94040404; /* enable all audio out and I2S-D input */ | ||
1196 | /* Program I2S with proper master sample rate and enable | ||
1197 | * the correct I2S channel. */ | ||
1198 | i2sorg &= 0xfffffffc; | ||
1199 | |||
1200 | /* Enable S/PDIF-out-A in fixed 24-bit data | ||
1201 | * format and default to 48kHz. */ | ||
1202 | /* Disable all before doing any changes. */ | ||
1203 | hw_write_20kx(hw, SPOCTL, 0x0); | ||
1204 | spdorg = 0x05; | ||
1205 | |||
1206 | switch (info->msr) { | ||
1207 | case 1: | ||
1208 | i2sorg |= 1; | ||
1209 | spdorg |= (0x0 << 6); | ||
1210 | break; | ||
1211 | case 2: | ||
1212 | i2sorg |= 2; | ||
1213 | spdorg |= (0x1 << 6); | ||
1214 | break; | ||
1215 | case 4: | ||
1216 | i2sorg |= 3; | ||
1217 | spdorg |= (0x2 << 6); | ||
1218 | break; | ||
1219 | default: | ||
1220 | i2sorg |= 1; | ||
1221 | break; | ||
1222 | } | ||
1223 | |||
1224 | hw_write_20kx(hw, I2SCTL, i2sorg); | ||
1225 | hw_write_20kx(hw, SPOCTL, spdorg); | ||
1226 | |||
1227 | /* Enable S/PDIF-in-A in fixed 24-bit data format. */ | ||
1228 | /* Disable all before doing any changes. */ | ||
1229 | hw_write_20kx(hw, SPICTL, 0x0); | ||
1230 | mdelay(1); | ||
1231 | spdorg = 0x0a0a0a0a; | ||
1232 | hw_write_20kx(hw, SPICTL, spdorg); | ||
1233 | mdelay(1); | ||
1234 | |||
1235 | return 0; | ||
1236 | } | ||
1237 | |||
1238 | /* TRANSPORT operations */ | ||
1239 | static int hw_trn_init(struct hw *hw, const struct trn_conf *info) | ||
1240 | { | ||
1241 | u32 trnctl = 0; | ||
1242 | unsigned long ptp_phys_low = 0, ptp_phys_high = 0; | ||
1243 | |||
1244 | /* Set up device page table */ | ||
1245 | if ((~0UL) == info->vm_pgt_phys) { | ||
1246 | printk(KERN_ERR "Wrong device page table page address!\n"); | ||
1247 | return -1; | ||
1248 | } | ||
1249 | |||
1250 | trnctl = 0x13; /* 32-bit, 4k-size page */ | ||
1251 | #if BITS_PER_LONG == 64 | ||
1252 | ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); | ||
1253 | ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); | ||
1254 | trnctl |= (1<<2); | ||
1255 | #elif BITS_PER_LONG == 32 | ||
1256 | ptp_phys_low = info->vm_pgt_phys & (~0UL); | ||
1257 | ptp_phys_high = 0; | ||
1258 | #else | ||
1259 | # error "Unknown BITS_PER_LONG!" | ||
1260 | #endif | ||
1261 | #if PAGE_SIZE == 8192 | ||
1262 | trnctl |= (1<<5); | ||
1263 | #endif | ||
1264 | hw_write_20kx(hw, PTPALX, ptp_phys_low); | ||
1265 | hw_write_20kx(hw, PTPAHX, ptp_phys_high); | ||
1266 | hw_write_20kx(hw, TRNCTL, trnctl); | ||
1267 | hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */ | ||
1268 | |||
1269 | return 0; | ||
1270 | } | ||
1271 | |||
1272 | /* Card initialization */ | ||
1273 | #define GCTL_EAC 0x00000001 | ||
1274 | #define GCTL_EAI 0x00000002 | ||
1275 | #define GCTL_BEP 0x00000004 | ||
1276 | #define GCTL_BES 0x00000008 | ||
1277 | #define GCTL_DSP 0x00000010 | ||
1278 | #define GCTL_DBP 0x00000020 | ||
1279 | #define GCTL_ABP 0x00000040 | ||
1280 | #define GCTL_TBP 0x00000080 | ||
1281 | #define GCTL_SBP 0x00000100 | ||
1282 | #define GCTL_FBP 0x00000200 | ||
1283 | #define GCTL_XA 0x00000400 | ||
1284 | #define GCTL_ET 0x00000800 | ||
1285 | #define GCTL_PR 0x00001000 | ||
1286 | #define GCTL_MRL 0x00002000 | ||
1287 | #define GCTL_SDE 0x00004000 | ||
1288 | #define GCTL_SDI 0x00008000 | ||
1289 | #define GCTL_SM 0x00010000 | ||
1290 | #define GCTL_SR 0x00020000 | ||
1291 | #define GCTL_SD 0x00040000 | ||
1292 | #define GCTL_SE 0x00080000 | ||
1293 | #define GCTL_AID 0x00100000 | ||
1294 | |||
1295 | static int hw_pll_init(struct hw *hw, unsigned int rsr) | ||
1296 | { | ||
1297 | unsigned int pllctl; | ||
1298 | int i = 0; | ||
1299 | |||
1300 | pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731; | ||
1301 | for (i = 0; i < 3; i++) { | ||
1302 | if (hw_read_20kx(hw, PLLCTL) == pllctl) | ||
1303 | break; | ||
1304 | |||
1305 | hw_write_20kx(hw, PLLCTL, pllctl); | ||
1306 | mdelay(40); | ||
1307 | } | ||
1308 | if (i >= 3) { | ||
1309 | printk(KERN_ALERT "PLL initialization failed!!!\n"); | ||
1310 | return -EBUSY; | ||
1311 | } | ||
1312 | |||
1313 | return 0; | ||
1314 | } | ||
1315 | |||
1316 | static int hw_auto_init(struct hw *hw) | ||
1317 | { | ||
1318 | unsigned int gctl; | ||
1319 | int i; | ||
1320 | |||
1321 | gctl = hw_read_20kx(hw, GCTL); | ||
1322 | set_field(&gctl, GCTL_EAI, 0); | ||
1323 | hw_write_20kx(hw, GCTL, gctl); | ||
1324 | set_field(&gctl, GCTL_EAI, 1); | ||
1325 | hw_write_20kx(hw, GCTL, gctl); | ||
1326 | mdelay(10); | ||
1327 | for (i = 0; i < 400000; i++) { | ||
1328 | gctl = hw_read_20kx(hw, GCTL); | ||
1329 | if (get_field(gctl, GCTL_AID)) | ||
1330 | break; | ||
1331 | } | ||
1332 | if (!get_field(gctl, GCTL_AID)) { | ||
1333 | printk(KERN_ALERT "Card Auto-init failed!!!\n"); | ||
1334 | return -EBUSY; | ||
1335 | } | ||
1336 | |||
1337 | return 0; | ||
1338 | } | ||
1339 | |||
1340 | static int i2c_unlock(struct hw *hw) | ||
1341 | { | ||
1342 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1343 | return 0; | ||
1344 | |||
1345 | hw_write_pci(hw, 0xcc, 0x8c); | ||
1346 | hw_write_pci(hw, 0xcc, 0x0e); | ||
1347 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1348 | return 0; | ||
1349 | |||
1350 | hw_write_pci(hw, 0xcc, 0xee); | ||
1351 | hw_write_pci(hw, 0xcc, 0xaa); | ||
1352 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1353 | return 0; | ||
1354 | |||
1355 | return -1; | ||
1356 | } | ||
1357 | |||
1358 | static void i2c_lock(struct hw *hw) | ||
1359 | { | ||
1360 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1361 | hw_write_pci(hw, 0xcc, 0x00); | ||
1362 | } | ||
1363 | |||
1364 | static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) | ||
1365 | { | ||
1366 | unsigned int ret = 0; | ||
1367 | |||
1368 | do { | ||
1369 | ret = hw_read_pci(hw, 0xEC); | ||
1370 | } while (!(ret & 0x800000)); | ||
1371 | hw_write_pci(hw, 0xE0, device); | ||
1372 | hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff)); | ||
1373 | } | ||
1374 | |||
1375 | /* DAC operations */ | ||
1376 | |||
1377 | static int hw_reset_dac(struct hw *hw) | ||
1378 | { | ||
1379 | u32 i = 0; | ||
1380 | u16 gpioorg = 0; | ||
1381 | unsigned int ret = 0; | ||
1382 | |||
1383 | if (i2c_unlock(hw)) | ||
1384 | return -1; | ||
1385 | |||
1386 | do { | ||
1387 | ret = hw_read_pci(hw, 0xEC); | ||
1388 | } while (!(ret & 0x800000)); | ||
1389 | hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ | ||
1390 | |||
1391 | /* To be effective, need to reset the DAC twice. */ | ||
1392 | for (i = 0; i < 2; i++) { | ||
1393 | /* set gpio */ | ||
1394 | mdelay(100); | ||
1395 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1396 | gpioorg &= 0xfffd; | ||
1397 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1398 | mdelay(1); | ||
1399 | hw_write_20kx(hw, GPIO, gpioorg | 0x2); | ||
1400 | } | ||
1401 | |||
1402 | i2c_write(hw, 0x00180080, 0x01, 0x80); | ||
1403 | i2c_write(hw, 0x00180080, 0x02, 0x10); | ||
1404 | |||
1405 | i2c_lock(hw); | ||
1406 | |||
1407 | return 0; | ||
1408 | } | ||
1409 | |||
1410 | static int hw_dac_init(struct hw *hw, const struct dac_conf *info) | ||
1411 | { | ||
1412 | u32 data = 0; | ||
1413 | u16 gpioorg = 0; | ||
1414 | u16 subsys_id = 0; | ||
1415 | unsigned int ret = 0; | ||
1416 | |||
1417 | pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1418 | if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { | ||
1419 | /* SB055x, unmute outputs */ | ||
1420 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1421 | gpioorg &= 0xffbf; /* set GPIO6 to low */ | ||
1422 | gpioorg |= 2; /* set GPIO1 to high */ | ||
1423 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1424 | return 0; | ||
1425 | } | ||
1426 | |||
1427 | /* mute outputs */ | ||
1428 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1429 | gpioorg &= 0xffbf; | ||
1430 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1431 | |||
1432 | hw_reset_dac(hw); | ||
1433 | |||
1434 | if (i2c_unlock(hw)) | ||
1435 | return -1; | ||
1436 | |||
1437 | hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ | ||
1438 | do { | ||
1439 | ret = hw_read_pci(hw, 0xEC); | ||
1440 | } while (!(ret & 0x800000)); | ||
1441 | |||
1442 | switch (info->msr) { | ||
1443 | case 1: | ||
1444 | data = 0x24; | ||
1445 | break; | ||
1446 | case 2: | ||
1447 | data = 0x25; | ||
1448 | break; | ||
1449 | case 4: | ||
1450 | data = 0x26; | ||
1451 | break; | ||
1452 | default: | ||
1453 | data = 0x24; | ||
1454 | break; | ||
1455 | } | ||
1456 | |||
1457 | i2c_write(hw, 0x00180080, 0x06, data); | ||
1458 | i2c_write(hw, 0x00180080, 0x09, data); | ||
1459 | i2c_write(hw, 0x00180080, 0x0c, data); | ||
1460 | i2c_write(hw, 0x00180080, 0x0f, data); | ||
1461 | |||
1462 | i2c_lock(hw); | ||
1463 | |||
1464 | /* unmute outputs */ | ||
1465 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1466 | gpioorg = gpioorg | 0x40; | ||
1467 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1468 | |||
1469 | return 0; | ||
1470 | } | ||
1471 | |||
1472 | /* ADC operations */ | ||
1473 | |||
1474 | static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type) | ||
1475 | { | ||
1476 | u32 data = 0; | ||
1477 | return data; | ||
1478 | } | ||
1479 | |||
1480 | static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) | ||
1481 | { | ||
1482 | u32 data = 0; | ||
1483 | |||
1484 | data = hw_read_20kx(hw, GPIO); | ||
1485 | switch (type) { | ||
1486 | case ADC_MICIN: | ||
1487 | data = ((data & (0x1<<7)) && (data & (0x1<<8))); | ||
1488 | break; | ||
1489 | case ADC_LINEIN: | ||
1490 | data = (!(data & (0x1<<7)) && (data & (0x1<<8))); | ||
1491 | break; | ||
1492 | case ADC_NONE: /* Digital I/O */ | ||
1493 | data = (!(data & (0x1<<8))); | ||
1494 | break; | ||
1495 | default: | ||
1496 | data = 0; | ||
1497 | } | ||
1498 | return data; | ||
1499 | } | ||
1500 | |||
1501 | static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) | ||
1502 | { | ||
1503 | u32 data = 0; | ||
1504 | |||
1505 | data = hw_read_20kx(hw, GPIO); | ||
1506 | switch (type) { | ||
1507 | case ADC_MICIN: | ||
1508 | data = (data & (0x1 << 7)) ? 1 : 0; | ||
1509 | break; | ||
1510 | case ADC_LINEIN: | ||
1511 | data = (data & (0x1 << 7)) ? 0 : 1; | ||
1512 | break; | ||
1513 | default: | ||
1514 | data = 0; | ||
1515 | } | ||
1516 | return data; | ||
1517 | } | ||
1518 | |||
1519 | static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) | ||
1520 | { | ||
1521 | u16 subsys_id = 0; | ||
1522 | |||
1523 | pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1524 | if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { | ||
1525 | /* SB055x cards */ | ||
1526 | return is_adc_input_selected_SB055x(hw, type); | ||
1527 | } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { | ||
1528 | /* SB073x cards */ | ||
1529 | return is_adc_input_selected_hendrix(hw, type); | ||
1530 | } else if ((subsys_id & 0xf000) == 0x6000) { | ||
1531 | /* Vista compatible cards */ | ||
1532 | return is_adc_input_selected_hendrix(hw, type); | ||
1533 | } else { | ||
1534 | return is_adc_input_selected_SBx(hw, type); | ||
1535 | } | ||
1536 | } | ||
1537 | |||
1538 | static int | ||
1539 | adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) | ||
1540 | { | ||
1541 | u32 data = 0; | ||
1542 | |||
1543 | /* | ||
1544 | * check and set the following GPIO bits accordingly | ||
1545 | * ADC_Gain = GPIO2 | ||
1546 | * DRM_off = GPIO3 | ||
1547 | * Mic_Pwr_on = GPIO7 | ||
1548 | * Digital_IO_Sel = GPIO8 | ||
1549 | * Mic_Sw = GPIO9 | ||
1550 | * Aux/MicLine_Sw = GPIO12 | ||
1551 | */ | ||
1552 | data = hw_read_20kx(hw, GPIO); | ||
1553 | data &= 0xec73; | ||
1554 | switch (type) { | ||
1555 | case ADC_MICIN: | ||
1556 | data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ; | ||
1557 | data |= boost ? (0x1<<2) : 0; | ||
1558 | break; | ||
1559 | case ADC_LINEIN: | ||
1560 | data |= (0x1<<8); | ||
1561 | break; | ||
1562 | case ADC_AUX: | ||
1563 | data |= (0x1<<8) | (0x1<<12); | ||
1564 | break; | ||
1565 | case ADC_NONE: | ||
1566 | data |= (0x1<<12); /* set to digital */ | ||
1567 | break; | ||
1568 | default: | ||
1569 | return -1; | ||
1570 | } | ||
1571 | |||
1572 | hw_write_20kx(hw, GPIO, data); | ||
1573 | |||
1574 | return 0; | ||
1575 | } | ||
1576 | |||
1577 | |||
1578 | static int | ||
1579 | adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) | ||
1580 | { | ||
1581 | u32 data = 0; | ||
1582 | u32 i2c_data = 0; | ||
1583 | unsigned int ret = 0; | ||
1584 | |||
1585 | if (i2c_unlock(hw)) | ||
1586 | return -1; | ||
1587 | |||
1588 | do { | ||
1589 | ret = hw_read_pci(hw, 0xEC); | ||
1590 | } while (!(ret & 0x800000)); /* i2c ready poll */ | ||
1591 | /* set i2c access mode as Direct Control */ | ||
1592 | hw_write_pci(hw, 0xEC, 0x05); | ||
1593 | |||
1594 | data = hw_read_20kx(hw, GPIO); | ||
1595 | switch (type) { | ||
1596 | case ADC_MICIN: | ||
1597 | data |= ((0x1 << 7) | (0x1 << 8)); | ||
1598 | i2c_data = 0x1; /* Mic-in */ | ||
1599 | break; | ||
1600 | case ADC_LINEIN: | ||
1601 | data &= ~(0x1 << 7); | ||
1602 | data |= (0x1 << 8); | ||
1603 | i2c_data = 0x2; /* Line-in */ | ||
1604 | break; | ||
1605 | case ADC_NONE: | ||
1606 | data &= ~(0x1 << 8); | ||
1607 | i2c_data = 0x0; /* set to Digital */ | ||
1608 | break; | ||
1609 | default: | ||
1610 | i2c_lock(hw); | ||
1611 | return -1; | ||
1612 | } | ||
1613 | hw_write_20kx(hw, GPIO, data); | ||
1614 | i2c_write(hw, 0x001a0080, 0x2a, i2c_data); | ||
1615 | if (boost) { | ||
1616 | i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ | ||
1617 | i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ | ||
1618 | } else { | ||
1619 | i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ | ||
1620 | i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ | ||
1621 | } | ||
1622 | |||
1623 | i2c_lock(hw); | ||
1624 | |||
1625 | return 0; | ||
1626 | } | ||
1627 | |||
1628 | static int | ||
1629 | adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) | ||
1630 | { | ||
1631 | u32 data = 0; | ||
1632 | u32 i2c_data = 0; | ||
1633 | unsigned int ret = 0; | ||
1634 | |||
1635 | if (i2c_unlock(hw)) | ||
1636 | return -1; | ||
1637 | |||
1638 | do { | ||
1639 | ret = hw_read_pci(hw, 0xEC); | ||
1640 | } while (!(ret & 0x800000)); /* i2c ready poll */ | ||
1641 | /* set i2c access mode as Direct Control */ | ||
1642 | hw_write_pci(hw, 0xEC, 0x05); | ||
1643 | |||
1644 | data = hw_read_20kx(hw, GPIO); | ||
1645 | switch (type) { | ||
1646 | case ADC_MICIN: | ||
1647 | data |= (0x1 << 7); | ||
1648 | i2c_data = 0x1; /* Mic-in */ | ||
1649 | break; | ||
1650 | case ADC_LINEIN: | ||
1651 | data &= ~(0x1 << 7); | ||
1652 | i2c_data = 0x2; /* Line-in */ | ||
1653 | break; | ||
1654 | default: | ||
1655 | i2c_lock(hw); | ||
1656 | return -1; | ||
1657 | } | ||
1658 | hw_write_20kx(hw, GPIO, data); | ||
1659 | i2c_write(hw, 0x001a0080, 0x2a, i2c_data); | ||
1660 | if (boost) { | ||
1661 | i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ | ||
1662 | i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ | ||
1663 | } else { | ||
1664 | i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ | ||
1665 | i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ | ||
1666 | } | ||
1667 | |||
1668 | i2c_lock(hw); | ||
1669 | |||
1670 | return 0; | ||
1671 | } | ||
1672 | |||
1673 | static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) | ||
1674 | { | ||
1675 | u16 subsys_id = 0; | ||
1676 | |||
1677 | pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1678 | if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { | ||
1679 | /* SB055x cards */ | ||
1680 | return adc_input_select_SB055x(hw, type, (ADC_MICIN == type)); | ||
1681 | } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { | ||
1682 | /* SB073x cards */ | ||
1683 | return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); | ||
1684 | } else if ((subsys_id & 0xf000) == 0x6000) { | ||
1685 | /* Vista compatible cards */ | ||
1686 | return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); | ||
1687 | } else { | ||
1688 | return adc_input_select_SBx(hw, type, (ADC_MICIN == type)); | ||
1689 | } | ||
1690 | } | ||
1691 | |||
1692 | static int adc_init_SB055x(struct hw *hw, int input, int mic20db) | ||
1693 | { | ||
1694 | return adc_input_select_SB055x(hw, input, mic20db); | ||
1695 | } | ||
1696 | |||
1697 | static int adc_init_SBx(struct hw *hw, int input, int mic20db) | ||
1698 | { | ||
1699 | u16 gpioorg; | ||
1700 | u16 input_source; | ||
1701 | u32 adcdata = 0; | ||
1702 | unsigned int ret = 0; | ||
1703 | |||
1704 | input_source = 0x100; /* default to analog */ | ||
1705 | switch (input) { | ||
1706 | case ADC_MICIN: | ||
1707 | adcdata = 0x1; | ||
1708 | input_source = 0x180; /* set GPIO7 to select Mic */ | ||
1709 | break; | ||
1710 | case ADC_LINEIN: | ||
1711 | adcdata = 0x2; | ||
1712 | break; | ||
1713 | case ADC_VIDEO: | ||
1714 | adcdata = 0x4; | ||
1715 | break; | ||
1716 | case ADC_AUX: | ||
1717 | adcdata = 0x8; | ||
1718 | break; | ||
1719 | case ADC_NONE: | ||
1720 | adcdata = 0x0; | ||
1721 | input_source = 0x0; /* set to Digital */ | ||
1722 | break; | ||
1723 | default: | ||
1724 | break; | ||
1725 | } | ||
1726 | |||
1727 | if (i2c_unlock(hw)) | ||
1728 | return -1; | ||
1729 | |||
1730 | do { | ||
1731 | ret = hw_read_pci(hw, 0xEC); | ||
1732 | } while (!(ret & 0x800000)); /* i2c ready poll */ | ||
1733 | hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ | ||
1734 | |||
1735 | i2c_write(hw, 0x001a0080, 0x0e, 0x08); | ||
1736 | i2c_write(hw, 0x001a0080, 0x18, 0x0a); | ||
1737 | i2c_write(hw, 0x001a0080, 0x28, 0x86); | ||
1738 | i2c_write(hw, 0x001a0080, 0x2a, adcdata); | ||
1739 | |||
1740 | if (mic20db) { | ||
1741 | i2c_write(hw, 0x001a0080, 0x1c, 0xf7); | ||
1742 | i2c_write(hw, 0x001a0080, 0x1e, 0xf7); | ||
1743 | } else { | ||
1744 | i2c_write(hw, 0x001a0080, 0x1c, 0xcf); | ||
1745 | i2c_write(hw, 0x001a0080, 0x1e, 0xcf); | ||
1746 | } | ||
1747 | |||
1748 | if (!(hw_read_20kx(hw, ID0) & 0x100)) | ||
1749 | i2c_write(hw, 0x001a0080, 0x16, 0x26); | ||
1750 | |||
1751 | i2c_lock(hw); | ||
1752 | |||
1753 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1754 | gpioorg &= 0xfe7f; | ||
1755 | gpioorg |= input_source; | ||
1756 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1757 | |||
1758 | return 0; | ||
1759 | } | ||
1760 | |||
1761 | static int hw_adc_init(struct hw *hw, const struct adc_conf *info) | ||
1762 | { | ||
1763 | int err = 0; | ||
1764 | u16 subsys_id = 0; | ||
1765 | |||
1766 | pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1767 | if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { | ||
1768 | /* Sb055x card */ | ||
1769 | err = adc_init_SB055x(hw, info->input, info->mic20db); | ||
1770 | } else { | ||
1771 | err = adc_init_SBx(hw, info->input, info->mic20db); | ||
1772 | } | ||
1773 | |||
1774 | return err; | ||
1775 | } | ||
1776 | |||
1777 | static int hw_have_digit_io_switch(struct hw *hw) | ||
1778 | { | ||
1779 | u16 subsys_id = 0; | ||
1780 | |||
1781 | pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1782 | /* SB073x and Vista compatible cards have no digit IO switch */ | ||
1783 | return !((subsys_id == 0x0029) || (subsys_id == 0x0031) | ||
1784 | || ((subsys_id & 0xf000) == 0x6000)); | ||
1785 | } | ||
1786 | |||
1787 | #define UAA_CFG_PWRSTATUS 0x44 | ||
1788 | #define UAA_CFG_SPACE_FLAG 0xA0 | ||
1789 | #define UAA_CORE_CHANGE 0x3FFC | ||
1790 | static int uaa_to_xfi(struct pci_dev *pci) | ||
1791 | { | ||
1792 | unsigned int bar0, bar1, bar2, bar3, bar4, bar5; | ||
1793 | unsigned int cmd, irq, cl_size, l_timer, pwr; | ||
1794 | unsigned int CTLA, CTLZ, CTLL, CTLX, CTL_, CTLF, CTLi; | ||
1795 | unsigned int is_uaa = 0; | ||
1796 | unsigned int data[4] = {0}; | ||
1797 | unsigned int io_base; | ||
1798 | void *mem_base; | ||
1799 | int i = 0; | ||
1800 | |||
1801 | /* By default, Hendrix card UAA Bar0 should be using memory... */ | ||
1802 | io_base = pci_resource_start(pci, 0); | ||
1803 | mem_base = ioremap(io_base, pci_resource_len(pci, 0)); | ||
1804 | if (NULL == mem_base) | ||
1805 | return -ENOENT; | ||
1806 | |||
1807 | CTLX = ___constant_swab32(*((unsigned int *)"CTLX")); | ||
1808 | CTL_ = ___constant_swab32(*((unsigned int *)"CTL-")); | ||
1809 | CTLF = ___constant_swab32(*((unsigned int *)"CTLF")); | ||
1810 | CTLi = ___constant_swab32(*((unsigned int *)"CTLi")); | ||
1811 | CTLA = ___constant_swab32(*((unsigned int *)"CTLA")); | ||
1812 | CTLZ = ___constant_swab32(*((unsigned int *)"CTLZ")); | ||
1813 | CTLL = ___constant_swab32(*((unsigned int *)"CTLL")); | ||
1814 | |||
1815 | /* Read current mode from Mode Change Register */ | ||
1816 | for (i = 0; i < 4; i++) | ||
1817 | data[i] = readl(mem_base + UAA_CORE_CHANGE); | ||
1818 | |||
1819 | /* Determine current mode... */ | ||
1820 | if (data[0] == CTLA) { | ||
1821 | is_uaa = ((data[1] == CTLZ && data[2] == CTLL | ||
1822 | && data[3] == CTLA) || (data[1] == CTLA | ||
1823 | && data[2] == CTLZ && data[3] == CTLL)); | ||
1824 | } else if (data[0] == CTLZ) { | ||
1825 | is_uaa = (data[1] == CTLL | ||
1826 | && data[2] == CTLA && data[3] == CTLA); | ||
1827 | } else if (data[0] == CTLL) { | ||
1828 | is_uaa = (data[1] == CTLA | ||
1829 | && data[2] == CTLA && data[3] == CTLZ); | ||
1830 | } else { | ||
1831 | is_uaa = 0; | ||
1832 | } | ||
1833 | |||
1834 | if (!is_uaa) { | ||
1835 | /* Not in UAA mode currently. Return directly. */ | ||
1836 | iounmap(mem_base); | ||
1837 | return 0; | ||
1838 | } | ||
1839 | |||
1840 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0); | ||
1841 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1); | ||
1842 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2); | ||
1843 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3); | ||
1844 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4); | ||
1845 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5); | ||
1846 | pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq); | ||
1847 | pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size); | ||
1848 | pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer); | ||
1849 | pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr); | ||
1850 | pci_read_config_dword(pci, PCI_COMMAND, &cmd); | ||
1851 | |||
1852 | /* Set up X-Fi core PCI configuration space. */ | ||
1853 | /* Switch to X-Fi config space with BAR0 exposed. */ | ||
1854 | pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321); | ||
1855 | /* Copy UAA's BAR5 into X-Fi BAR0 */ | ||
1856 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5); | ||
1857 | /* Switch to X-Fi config space without BAR0 exposed. */ | ||
1858 | pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678); | ||
1859 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1); | ||
1860 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2); | ||
1861 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3); | ||
1862 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4); | ||
1863 | pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq); | ||
1864 | pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size); | ||
1865 | pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer); | ||
1866 | pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr); | ||
1867 | pci_write_config_dword(pci, PCI_COMMAND, cmd); | ||
1868 | |||
1869 | /* Switch to X-Fi mode */ | ||
1870 | writel(CTLX, (mem_base + UAA_CORE_CHANGE)); | ||
1871 | writel(CTL_, (mem_base + UAA_CORE_CHANGE)); | ||
1872 | writel(CTLF, (mem_base + UAA_CORE_CHANGE)); | ||
1873 | writel(CTLi, (mem_base + UAA_CORE_CHANGE)); | ||
1874 | |||
1875 | iounmap(mem_base); | ||
1876 | |||
1877 | return 0; | ||
1878 | } | ||
1879 | |||
1880 | static int hw_card_start(struct hw *hw) | ||
1881 | { | ||
1882 | int err = 0; | ||
1883 | struct pci_dev *pci = hw->pci; | ||
1884 | u16 subsys_id = 0; | ||
1885 | unsigned int dma_mask = 0; | ||
1886 | |||
1887 | err = pci_enable_device(pci); | ||
1888 | if (err < 0) | ||
1889 | return err; | ||
1890 | |||
1891 | /* Set DMA transfer mask */ | ||
1892 | dma_mask = CT_XFI_DMA_MASK; | ||
1893 | if (pci_set_dma_mask(pci, dma_mask) < 0 || | ||
1894 | pci_set_consistent_dma_mask(pci, dma_mask) < 0) { | ||
1895 | printk(KERN_ERR "architecture does not support PCI " | ||
1896 | "busmaster DMA with mask 0x%x\n", dma_mask); | ||
1897 | err = -ENXIO; | ||
1898 | goto error1; | ||
1899 | } | ||
1900 | |||
1901 | err = pci_request_regions(pci, "XFi"); | ||
1902 | if (err < 0) | ||
1903 | goto error1; | ||
1904 | |||
1905 | /* Switch to X-Fi mode from UAA mode if neeeded */ | ||
1906 | pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
1907 | if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) { | ||
1908 | err = uaa_to_xfi(pci); | ||
1909 | if (err) | ||
1910 | goto error2; | ||
1911 | |||
1912 | hw->io_base = pci_resource_start(pci, 5); | ||
1913 | } else { | ||
1914 | hw->io_base = pci_resource_start(pci, 0); | ||
1915 | } | ||
1916 | |||
1917 | /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, | ||
1918 | atc->chip_details->nm_card, hw))) { | ||
1919 | goto error2; | ||
1920 | } | ||
1921 | hw->irq = pci->irq; | ||
1922 | */ | ||
1923 | |||
1924 | pci_set_master(pci); | ||
1925 | |||
1926 | return 0; | ||
1927 | |||
1928 | error2: | ||
1929 | pci_release_regions(pci); | ||
1930 | hw->io_base = 0; | ||
1931 | error1: | ||
1932 | pci_disable_device(pci); | ||
1933 | return err; | ||
1934 | } | ||
1935 | |||
1936 | static int hw_card_stop(struct hw *hw) | ||
1937 | { | ||
1938 | /* TODO: Disable interrupt and so on... */ | ||
1939 | return 0; | ||
1940 | } | ||
1941 | |||
1942 | static int hw_card_shutdown(struct hw *hw) | ||
1943 | { | ||
1944 | if (hw->irq >= 0) | ||
1945 | free_irq(hw->irq, hw); | ||
1946 | |||
1947 | hw->irq = -1; | ||
1948 | |||
1949 | if (NULL != ((void *)hw->mem_base)) | ||
1950 | iounmap((void *)hw->mem_base); | ||
1951 | |||
1952 | hw->mem_base = (unsigned long)NULL; | ||
1953 | |||
1954 | if (hw->io_base) | ||
1955 | pci_release_regions(hw->pci); | ||
1956 | |||
1957 | hw->io_base = 0; | ||
1958 | |||
1959 | pci_disable_device(hw->pci); | ||
1960 | |||
1961 | return 0; | ||
1962 | } | ||
1963 | |||
1964 | static int hw_card_init(struct hw *hw, struct card_conf *info) | ||
1965 | { | ||
1966 | int err; | ||
1967 | unsigned int gctl; | ||
1968 | u16 subsys_id = 0; | ||
1969 | u32 data = 0; | ||
1970 | struct dac_conf dac_info = {0}; | ||
1971 | struct adc_conf adc_info = {0}; | ||
1972 | struct daio_conf daio_info = {0}; | ||
1973 | struct trn_conf trn_info = {0}; | ||
1974 | |||
1975 | /* Get PCI io port base address and do Hendrix switch if needed. */ | ||
1976 | if (!hw->io_base) { | ||
1977 | err = hw_card_start(hw); | ||
1978 | if (err) | ||
1979 | return err; | ||
1980 | } | ||
1981 | |||
1982 | /* PLL init */ | ||
1983 | err = hw_pll_init(hw, info->rsr); | ||
1984 | if (err < 0) | ||
1985 | return err; | ||
1986 | |||
1987 | /* kick off auto-init */ | ||
1988 | err = hw_auto_init(hw); | ||
1989 | if (err < 0) | ||
1990 | return err; | ||
1991 | |||
1992 | /* Enable audio ring */ | ||
1993 | gctl = hw_read_20kx(hw, GCTL); | ||
1994 | set_field(&gctl, GCTL_EAC, 1); | ||
1995 | set_field(&gctl, GCTL_DBP, 1); | ||
1996 | set_field(&gctl, GCTL_TBP, 1); | ||
1997 | set_field(&gctl, GCTL_FBP, 1); | ||
1998 | set_field(&gctl, GCTL_ET, 1); | ||
1999 | hw_write_20kx(hw, GCTL, gctl); | ||
2000 | mdelay(10); | ||
2001 | |||
2002 | /* Reset all global pending interrupts */ | ||
2003 | hw_write_20kx(hw, GIE, 0); | ||
2004 | /* Reset all SRC pending interrupts */ | ||
2005 | hw_write_20kx(hw, SRCIP, 0); | ||
2006 | mdelay(30); | ||
2007 | |||
2008 | pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); | ||
2009 | /* Detect the card ID and configure GPIO accordingly. */ | ||
2010 | if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { | ||
2011 | /* SB055x cards */ | ||
2012 | hw_write_20kx(hw, GPIOCTL, 0x13fe); | ||
2013 | } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { | ||
2014 | /* SB073x cards */ | ||
2015 | hw_write_20kx(hw, GPIOCTL, 0x00e6); | ||
2016 | } else if ((subsys_id & 0xf000) == 0x6000) { | ||
2017 | /* Vista compatible cards */ | ||
2018 | hw_write_20kx(hw, GPIOCTL, 0x00c2); | ||
2019 | } else { | ||
2020 | hw_write_20kx(hw, GPIOCTL, 0x01e6); | ||
2021 | } | ||
2022 | |||
2023 | trn_info.vm_pgt_phys = info->vm_pgt_phys; | ||
2024 | err = hw_trn_init(hw, &trn_info); | ||
2025 | if (err < 0) | ||
2026 | return err; | ||
2027 | |||
2028 | daio_info.msr = info->msr; | ||
2029 | err = hw_daio_init(hw, &daio_info); | ||
2030 | if (err < 0) | ||
2031 | return err; | ||
2032 | |||
2033 | dac_info.msr = info->msr; | ||
2034 | err = hw_dac_init(hw, &dac_info); | ||
2035 | if (err < 0) | ||
2036 | return err; | ||
2037 | |||
2038 | adc_info.msr = info->msr; | ||
2039 | adc_info.input = ADC_LINEIN; | ||
2040 | adc_info.mic20db = 0; | ||
2041 | err = hw_adc_init(hw, &adc_info); | ||
2042 | if (err < 0) | ||
2043 | return err; | ||
2044 | |||
2045 | data = hw_read_20kx(hw, SRCMCTL); | ||
2046 | data |= 0x1; /* Enables input from the audio ring */ | ||
2047 | hw_write_20kx(hw, SRCMCTL, data); | ||
2048 | |||
2049 | return 0; | ||
2050 | } | ||
2051 | |||
2052 | static u32 hw_read_20kx(struct hw *hw, u32 reg) | ||
2053 | { | ||
2054 | u32 value; | ||
2055 | unsigned long flags; | ||
2056 | |||
2057 | spin_lock_irqsave( | ||
2058 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2059 | outl(reg, hw->io_base + 0x0); | ||
2060 | value = inl(hw->io_base + 0x4); | ||
2061 | spin_unlock_irqrestore( | ||
2062 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2063 | |||
2064 | return value; | ||
2065 | } | ||
2066 | |||
2067 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) | ||
2068 | { | ||
2069 | unsigned long flags; | ||
2070 | |||
2071 | spin_lock_irqsave( | ||
2072 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2073 | outl(reg, hw->io_base + 0x0); | ||
2074 | outl(data, hw->io_base + 0x4); | ||
2075 | spin_unlock_irqrestore( | ||
2076 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2077 | |||
2078 | } | ||
2079 | |||
2080 | static u32 hw_read_pci(struct hw *hw, u32 reg) | ||
2081 | { | ||
2082 | u32 value; | ||
2083 | unsigned long flags; | ||
2084 | |||
2085 | spin_lock_irqsave( | ||
2086 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2087 | outl(reg, hw->io_base + 0x10); | ||
2088 | value = inl(hw->io_base + 0x14); | ||
2089 | spin_unlock_irqrestore( | ||
2090 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2091 | |||
2092 | return value; | ||
2093 | } | ||
2094 | |||
2095 | static void hw_write_pci(struct hw *hw, u32 reg, u32 data) | ||
2096 | { | ||
2097 | unsigned long flags; | ||
2098 | |||
2099 | spin_lock_irqsave( | ||
2100 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2101 | outl(reg, hw->io_base + 0x10); | ||
2102 | outl(data, hw->io_base + 0x14); | ||
2103 | spin_unlock_irqrestore( | ||
2104 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2105 | } | ||
2106 | |||
2107 | int create_20k1_hw_obj(struct hw **rhw) | ||
2108 | { | ||
2109 | struct hw *hw; | ||
2110 | struct hw20k1 *hw20k1; | ||
2111 | |||
2112 | *rhw = NULL; | ||
2113 | hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL); | ||
2114 | if (NULL == hw20k1) | ||
2115 | return -ENOMEM; | ||
2116 | |||
2117 | spin_lock_init(&hw20k1->reg_20k1_lock); | ||
2118 | spin_lock_init(&hw20k1->reg_pci_lock); | ||
2119 | |||
2120 | hw = &hw20k1->hw; | ||
2121 | |||
2122 | hw->io_base = 0; | ||
2123 | hw->mem_base = (unsigned long)NULL; | ||
2124 | hw->irq = -1; | ||
2125 | |||
2126 | hw->card_init = hw_card_init; | ||
2127 | hw->card_stop = hw_card_stop; | ||
2128 | hw->pll_init = hw_pll_init; | ||
2129 | hw->is_adc_source_selected = hw_is_adc_input_selected; | ||
2130 | hw->select_adc_source = hw_adc_input_select; | ||
2131 | hw->have_digit_io_switch = hw_have_digit_io_switch; | ||
2132 | |||
2133 | hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; | ||
2134 | hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; | ||
2135 | hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; | ||
2136 | hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; | ||
2137 | hw->src_set_state = src_set_state; | ||
2138 | hw->src_set_bm = src_set_bm; | ||
2139 | hw->src_set_rsr = src_set_rsr; | ||
2140 | hw->src_set_sf = src_set_sf; | ||
2141 | hw->src_set_wr = src_set_wr; | ||
2142 | hw->src_set_pm = src_set_pm; | ||
2143 | hw->src_set_rom = src_set_rom; | ||
2144 | hw->src_set_vo = src_set_vo; | ||
2145 | hw->src_set_st = src_set_st; | ||
2146 | hw->src_set_ie = src_set_ie; | ||
2147 | hw->src_set_ilsz = src_set_ilsz; | ||
2148 | hw->src_set_bp = src_set_bp; | ||
2149 | hw->src_set_cisz = src_set_cisz; | ||
2150 | hw->src_set_ca = src_set_ca; | ||
2151 | hw->src_set_sa = src_set_sa; | ||
2152 | hw->src_set_la = src_set_la; | ||
2153 | hw->src_set_pitch = src_set_pitch; | ||
2154 | hw->src_set_dirty = src_set_dirty; | ||
2155 | hw->src_set_clear_zbufs = src_set_clear_zbufs; | ||
2156 | hw->src_set_dirty_all = src_set_dirty_all; | ||
2157 | hw->src_commit_write = src_commit_write; | ||
2158 | hw->src_get_ca = src_get_ca; | ||
2159 | hw->src_get_dirty = src_get_dirty; | ||
2160 | hw->src_dirty_conj_mask = src_dirty_conj_mask; | ||
2161 | hw->src_mgr_enbs_src = src_mgr_enbs_src; | ||
2162 | hw->src_mgr_enb_src = src_mgr_enb_src; | ||
2163 | hw->src_mgr_dsb_src = src_mgr_dsb_src; | ||
2164 | hw->src_mgr_commit_write = src_mgr_commit_write; | ||
2165 | |||
2166 | hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; | ||
2167 | hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; | ||
2168 | hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; | ||
2169 | hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; | ||
2170 | hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; | ||
2171 | hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; | ||
2172 | hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; | ||
2173 | |||
2174 | hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; | ||
2175 | hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; | ||
2176 | hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; | ||
2177 | hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; | ||
2178 | hw->amixer_set_mode = amixer_set_mode; | ||
2179 | hw->amixer_set_iv = amixer_set_iv; | ||
2180 | hw->amixer_set_x = amixer_set_x; | ||
2181 | hw->amixer_set_y = amixer_set_y; | ||
2182 | hw->amixer_set_sadr = amixer_set_sadr; | ||
2183 | hw->amixer_set_se = amixer_set_se; | ||
2184 | hw->amixer_set_dirty = amixer_set_dirty; | ||
2185 | hw->amixer_set_dirty_all = amixer_set_dirty_all; | ||
2186 | hw->amixer_commit_write = amixer_commit_write; | ||
2187 | hw->amixer_get_y = amixer_get_y; | ||
2188 | hw->amixer_get_dirty = amixer_get_dirty; | ||
2189 | |||
2190 | hw->dai_get_ctrl_blk = dai_get_ctrl_blk; | ||
2191 | hw->dai_put_ctrl_blk = dai_put_ctrl_blk; | ||
2192 | hw->dai_srt_set_srco = dai_srt_set_srcr; | ||
2193 | hw->dai_srt_set_srcm = dai_srt_set_srcl; | ||
2194 | hw->dai_srt_set_rsr = dai_srt_set_rsr; | ||
2195 | hw->dai_srt_set_drat = dai_srt_set_drat; | ||
2196 | hw->dai_srt_set_ec = dai_srt_set_ec; | ||
2197 | hw->dai_srt_set_et = dai_srt_set_et; | ||
2198 | hw->dai_commit_write = dai_commit_write; | ||
2199 | |||
2200 | hw->dao_get_ctrl_blk = dao_get_ctrl_blk; | ||
2201 | hw->dao_put_ctrl_blk = dao_put_ctrl_blk; | ||
2202 | hw->dao_set_spos = dao_set_spos; | ||
2203 | hw->dao_commit_write = dao_commit_write; | ||
2204 | hw->dao_get_spos = dao_get_spos; | ||
2205 | |||
2206 | hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; | ||
2207 | hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; | ||
2208 | hw->daio_mgr_enb_dai = daio_mgr_enb_dai; | ||
2209 | hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; | ||
2210 | hw->daio_mgr_enb_dao = daio_mgr_enb_dao; | ||
2211 | hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; | ||
2212 | hw->daio_mgr_dao_init = daio_mgr_dao_init; | ||
2213 | hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; | ||
2214 | hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; | ||
2215 | hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; | ||
2216 | hw->daio_mgr_commit_write = daio_mgr_commit_write; | ||
2217 | |||
2218 | *rhw = hw; | ||
2219 | |||
2220 | return 0; | ||
2221 | } | ||
2222 | |||
2223 | int destroy_20k1_hw_obj(struct hw *hw) | ||
2224 | { | ||
2225 | if (hw->io_base) | ||
2226 | hw_card_shutdown(hw); | ||
2227 | |||
2228 | kfree(container_of(hw, struct hw20k1, hw)); | ||
2229 | return 0; | ||
2230 | } | ||
diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h new file mode 100644 index 000000000000..02f72fb448a6 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File cthw20k1.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of hardware access methord. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date May 13 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef CTHW20K1_H | ||
19 | #define CTHW20K1_H | ||
20 | |||
21 | #include "cthardware.h" | ||
22 | |||
23 | int create_20k1_hw_obj(struct hw **rhw); | ||
24 | int destroy_20k1_hw_obj(struct hw *hw); | ||
25 | |||
26 | #endif /* CTHW20K1_H */ | ||
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c new file mode 100644 index 000000000000..cdcb75cc33b6 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.c | |||
@@ -0,0 +1,2133 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File cthw20k2.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of hardware access methord for 20k2. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date May 14 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include "cthw20k2.h" | ||
19 | #include "ct20k2reg.h" | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | |||
28 | #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ | ||
29 | |||
30 | static u32 hw_read_20kx(struct hw *hw, u32 reg); | ||
31 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); | ||
32 | |||
33 | /* | ||
34 | * Type definition block. | ||
35 | * The layout of control structures can be directly applied on 20k2 chip. | ||
36 | */ | ||
37 | |||
38 | /* | ||
39 | * SRC control block definitions. | ||
40 | */ | ||
41 | |||
42 | /* SRC resource control block */ | ||
43 | #define SRCCTL_STATE 0x00000007 | ||
44 | #define SRCCTL_BM 0x00000008 | ||
45 | #define SRCCTL_RSR 0x00000030 | ||
46 | #define SRCCTL_SF 0x000001C0 | ||
47 | #define SRCCTL_WR 0x00000200 | ||
48 | #define SRCCTL_PM 0x00000400 | ||
49 | #define SRCCTL_ROM 0x00001800 | ||
50 | #define SRCCTL_VO 0x00002000 | ||
51 | #define SRCCTL_ST 0x00004000 | ||
52 | #define SRCCTL_IE 0x00008000 | ||
53 | #define SRCCTL_ILSZ 0x000F0000 | ||
54 | #define SRCCTL_BP 0x00100000 | ||
55 | |||
56 | #define SRCCCR_CISZ 0x000007FF | ||
57 | #define SRCCCR_CWA 0x001FF800 | ||
58 | #define SRCCCR_D 0x00200000 | ||
59 | #define SRCCCR_RS 0x01C00000 | ||
60 | #define SRCCCR_NAL 0x3E000000 | ||
61 | #define SRCCCR_RA 0xC0000000 | ||
62 | |||
63 | #define SRCCA_CA 0x0FFFFFFF | ||
64 | #define SRCCA_RS 0xE0000000 | ||
65 | |||
66 | #define SRCSA_SA 0x0FFFFFFF | ||
67 | |||
68 | #define SRCLA_LA 0x0FFFFFFF | ||
69 | |||
70 | /* Mixer Parameter Ring ram Low and Hight register. | ||
71 | * Fixed-point value in 8.24 format for parameter channel */ | ||
72 | #define MPRLH_PITCH 0xFFFFFFFF | ||
73 | |||
74 | /* SRC resource register dirty flags */ | ||
75 | union src_dirty { | ||
76 | struct { | ||
77 | u16 ctl:1; | ||
78 | u16 ccr:1; | ||
79 | u16 sa:1; | ||
80 | u16 la:1; | ||
81 | u16 ca:1; | ||
82 | u16 mpr:1; | ||
83 | u16 czbfs:1; /* Clear Z-Buffers */ | ||
84 | u16 rsv:9; | ||
85 | } bf; | ||
86 | u16 data; | ||
87 | }; | ||
88 | |||
89 | struct src_rsc_ctrl_blk { | ||
90 | unsigned int ctl; | ||
91 | unsigned int ccr; | ||
92 | unsigned int ca; | ||
93 | unsigned int sa; | ||
94 | unsigned int la; | ||
95 | unsigned int mpr; | ||
96 | union src_dirty dirty; | ||
97 | }; | ||
98 | |||
99 | /* SRC manager control block */ | ||
100 | union src_mgr_dirty { | ||
101 | struct { | ||
102 | u16 enb0:1; | ||
103 | u16 enb1:1; | ||
104 | u16 enb2:1; | ||
105 | u16 enb3:1; | ||
106 | u16 enb4:1; | ||
107 | u16 enb5:1; | ||
108 | u16 enb6:1; | ||
109 | u16 enb7:1; | ||
110 | u16 enbsa:1; | ||
111 | u16 rsv:7; | ||
112 | } bf; | ||
113 | u16 data; | ||
114 | }; | ||
115 | |||
116 | struct src_mgr_ctrl_blk { | ||
117 | unsigned int enbsa; | ||
118 | unsigned int enb[8]; | ||
119 | union src_mgr_dirty dirty; | ||
120 | }; | ||
121 | |||
122 | /* SRCIMP manager control block */ | ||
123 | #define SRCAIM_ARC 0x00000FFF | ||
124 | #define SRCAIM_NXT 0x00FF0000 | ||
125 | #define SRCAIM_SRC 0xFF000000 | ||
126 | |||
127 | struct srcimap { | ||
128 | unsigned int srcaim; | ||
129 | unsigned int idx; | ||
130 | }; | ||
131 | |||
132 | /* SRCIMP manager register dirty flags */ | ||
133 | union srcimp_mgr_dirty { | ||
134 | struct { | ||
135 | u16 srcimap:1; | ||
136 | u16 rsv:15; | ||
137 | } bf; | ||
138 | u16 data; | ||
139 | }; | ||
140 | |||
141 | struct srcimp_mgr_ctrl_blk { | ||
142 | struct srcimap srcimap; | ||
143 | union srcimp_mgr_dirty dirty; | ||
144 | }; | ||
145 | |||
146 | /* | ||
147 | * Function implementation block. | ||
148 | */ | ||
149 | |||
150 | static int src_get_rsc_ctrl_blk(void **rblk) | ||
151 | { | ||
152 | struct src_rsc_ctrl_blk *blk; | ||
153 | |||
154 | *rblk = NULL; | ||
155 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
156 | if (NULL == blk) | ||
157 | return -ENOMEM; | ||
158 | |||
159 | *rblk = blk; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int src_put_rsc_ctrl_blk(void *blk) | ||
165 | { | ||
166 | kfree((struct src_rsc_ctrl_blk *)blk); | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int src_set_state(void *blk, unsigned int state) | ||
172 | { | ||
173 | struct src_rsc_ctrl_blk *ctl = blk; | ||
174 | |||
175 | set_field(&ctl->ctl, SRCCTL_STATE, state); | ||
176 | ctl->dirty.bf.ctl = 1; | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int src_set_bm(void *blk, unsigned int bm) | ||
181 | { | ||
182 | struct src_rsc_ctrl_blk *ctl = blk; | ||
183 | |||
184 | set_field(&ctl->ctl, SRCCTL_BM, bm); | ||
185 | ctl->dirty.bf.ctl = 1; | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int src_set_rsr(void *blk, unsigned int rsr) | ||
190 | { | ||
191 | struct src_rsc_ctrl_blk *ctl = blk; | ||
192 | |||
193 | set_field(&ctl->ctl, SRCCTL_RSR, rsr); | ||
194 | ctl->dirty.bf.ctl = 1; | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int src_set_sf(void *blk, unsigned int sf) | ||
199 | { | ||
200 | struct src_rsc_ctrl_blk *ctl = blk; | ||
201 | |||
202 | set_field(&ctl->ctl, SRCCTL_SF, sf); | ||
203 | ctl->dirty.bf.ctl = 1; | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int src_set_wr(void *blk, unsigned int wr) | ||
208 | { | ||
209 | struct src_rsc_ctrl_blk *ctl = blk; | ||
210 | |||
211 | set_field(&ctl->ctl, SRCCTL_WR, wr); | ||
212 | ctl->dirty.bf.ctl = 1; | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int src_set_pm(void *blk, unsigned int pm) | ||
217 | { | ||
218 | struct src_rsc_ctrl_blk *ctl = blk; | ||
219 | |||
220 | set_field(&ctl->ctl, SRCCTL_PM, pm); | ||
221 | ctl->dirty.bf.ctl = 1; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int src_set_rom(void *blk, unsigned int rom) | ||
226 | { | ||
227 | struct src_rsc_ctrl_blk *ctl = blk; | ||
228 | |||
229 | set_field(&ctl->ctl, SRCCTL_ROM, rom); | ||
230 | ctl->dirty.bf.ctl = 1; | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static int src_set_vo(void *blk, unsigned int vo) | ||
235 | { | ||
236 | struct src_rsc_ctrl_blk *ctl = blk; | ||
237 | |||
238 | set_field(&ctl->ctl, SRCCTL_VO, vo); | ||
239 | ctl->dirty.bf.ctl = 1; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int src_set_st(void *blk, unsigned int st) | ||
244 | { | ||
245 | struct src_rsc_ctrl_blk *ctl = blk; | ||
246 | |||
247 | set_field(&ctl->ctl, SRCCTL_ST, st); | ||
248 | ctl->dirty.bf.ctl = 1; | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static int src_set_ie(void *blk, unsigned int ie) | ||
253 | { | ||
254 | struct src_rsc_ctrl_blk *ctl = blk; | ||
255 | |||
256 | set_field(&ctl->ctl, SRCCTL_IE, ie); | ||
257 | ctl->dirty.bf.ctl = 1; | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int src_set_ilsz(void *blk, unsigned int ilsz) | ||
262 | { | ||
263 | struct src_rsc_ctrl_blk *ctl = blk; | ||
264 | |||
265 | set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); | ||
266 | ctl->dirty.bf.ctl = 1; | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int src_set_bp(void *blk, unsigned int bp) | ||
271 | { | ||
272 | struct src_rsc_ctrl_blk *ctl = blk; | ||
273 | |||
274 | set_field(&ctl->ctl, SRCCTL_BP, bp); | ||
275 | ctl->dirty.bf.ctl = 1; | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int src_set_cisz(void *blk, unsigned int cisz) | ||
280 | { | ||
281 | struct src_rsc_ctrl_blk *ctl = blk; | ||
282 | |||
283 | set_field(&ctl->ccr, SRCCCR_CISZ, cisz); | ||
284 | ctl->dirty.bf.ccr = 1; | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int src_set_ca(void *blk, unsigned int ca) | ||
289 | { | ||
290 | struct src_rsc_ctrl_blk *ctl = blk; | ||
291 | |||
292 | set_field(&ctl->ca, SRCCA_CA, ca); | ||
293 | ctl->dirty.bf.ca = 1; | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int src_set_sa(void *blk, unsigned int sa) | ||
298 | { | ||
299 | struct src_rsc_ctrl_blk *ctl = blk; | ||
300 | |||
301 | set_field(&ctl->sa, SRCSA_SA, sa); | ||
302 | ctl->dirty.bf.sa = 1; | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static int src_set_la(void *blk, unsigned int la) | ||
307 | { | ||
308 | struct src_rsc_ctrl_blk *ctl = blk; | ||
309 | |||
310 | set_field(&ctl->la, SRCLA_LA, la); | ||
311 | ctl->dirty.bf.la = 1; | ||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static int src_set_pitch(void *blk, unsigned int pitch) | ||
316 | { | ||
317 | struct src_rsc_ctrl_blk *ctl = blk; | ||
318 | |||
319 | set_field(&ctl->mpr, MPRLH_PITCH, pitch); | ||
320 | ctl->dirty.bf.mpr = 1; | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int src_set_clear_zbufs(void *blk, unsigned int clear) | ||
325 | { | ||
326 | ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static int src_set_dirty(void *blk, unsigned int flags) | ||
331 | { | ||
332 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | static int src_set_dirty_all(void *blk) | ||
337 | { | ||
338 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | #define AR_SLOT_SIZE 4096 | ||
343 | #define AR_SLOT_BLOCK_SIZE 16 | ||
344 | #define AR_PTS_PITCH 6 | ||
345 | #define AR_PARAM_SRC_OFFSET 0x60 | ||
346 | |||
347 | static unsigned int src_param_pitch_mixer(unsigned int src_idx) | ||
348 | { | ||
349 | return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE | ||
350 | - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; | ||
351 | |||
352 | } | ||
353 | |||
354 | static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
355 | { | ||
356 | struct src_rsc_ctrl_blk *ctl = blk; | ||
357 | int i = 0; | ||
358 | |||
359 | if (ctl->dirty.bf.czbfs) { | ||
360 | /* Clear Z-Buffer registers */ | ||
361 | for (i = 0; i < 8; i++) | ||
362 | hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0); | ||
363 | |||
364 | for (i = 0; i < 4; i++) | ||
365 | hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0); | ||
366 | |||
367 | for (i = 0; i < 8; i++) | ||
368 | hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0); | ||
369 | |||
370 | ctl->dirty.bf.czbfs = 0; | ||
371 | } | ||
372 | if (ctl->dirty.bf.mpr) { | ||
373 | /* Take the parameter mixer resource in the same group as that | ||
374 | * the idx src is in for simplicity. Unlike src, all conjugate | ||
375 | * parameter mixer resources must be programmed for | ||
376 | * corresponding conjugate src resources. */ | ||
377 | unsigned int pm_idx = src_param_pitch_mixer(idx); | ||
378 | hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr); | ||
379 | hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3); | ||
380 | hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0); | ||
381 | ctl->dirty.bf.mpr = 0; | ||
382 | } | ||
383 | if (ctl->dirty.bf.sa) { | ||
384 | hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa); | ||
385 | ctl->dirty.bf.sa = 0; | ||
386 | } | ||
387 | if (ctl->dirty.bf.la) { | ||
388 | hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la); | ||
389 | ctl->dirty.bf.la = 0; | ||
390 | } | ||
391 | if (ctl->dirty.bf.ca) { | ||
392 | hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca); | ||
393 | ctl->dirty.bf.ca = 0; | ||
394 | } | ||
395 | |||
396 | /* Write srccf register */ | ||
397 | hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0); | ||
398 | |||
399 | if (ctl->dirty.bf.ccr) { | ||
400 | hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr); | ||
401 | ctl->dirty.bf.ccr = 0; | ||
402 | } | ||
403 | if (ctl->dirty.bf.ctl) { | ||
404 | hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl); | ||
405 | ctl->dirty.bf.ctl = 0; | ||
406 | } | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) | ||
412 | { | ||
413 | struct src_rsc_ctrl_blk *ctl = blk; | ||
414 | |||
415 | ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100); | ||
416 | ctl->dirty.bf.ca = 0; | ||
417 | |||
418 | return get_field(ctl->ca, SRCCA_CA); | ||
419 | } | ||
420 | |||
421 | static unsigned int src_get_dirty(void *blk) | ||
422 | { | ||
423 | return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; | ||
424 | } | ||
425 | |||
426 | static unsigned int src_dirty_conj_mask(void) | ||
427 | { | ||
428 | return 0x20; | ||
429 | } | ||
430 | |||
431 | static int src_mgr_enbs_src(void *blk, unsigned int idx) | ||
432 | { | ||
433 | ((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4)); | ||
434 | ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; | ||
435 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | static int src_mgr_enb_src(void *blk, unsigned int idx) | ||
440 | { | ||
441 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
442 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static int src_mgr_dsb_src(void *blk, unsigned int idx) | ||
447 | { | ||
448 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); | ||
449 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static int src_mgr_commit_write(struct hw *hw, void *blk) | ||
454 | { | ||
455 | struct src_mgr_ctrl_blk *ctl = blk; | ||
456 | int i = 0; | ||
457 | unsigned int ret = 0; | ||
458 | |||
459 | if (ctl->dirty.bf.enbsa) { | ||
460 | do { | ||
461 | ret = hw_read_20kx(hw, SRC_ENBSTAT); | ||
462 | } while (ret & 0x1); | ||
463 | hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa); | ||
464 | ctl->dirty.bf.enbsa = 0; | ||
465 | } | ||
466 | for (i = 0; i < 8; i++) { | ||
467 | if ((ctl->dirty.data & (0x1 << i))) { | ||
468 | hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]); | ||
469 | ctl->dirty.data &= ~(0x1 << i); | ||
470 | } | ||
471 | } | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static int src_mgr_get_ctrl_blk(void **rblk) | ||
477 | { | ||
478 | struct src_mgr_ctrl_blk *blk; | ||
479 | |||
480 | *rblk = NULL; | ||
481 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
482 | if (NULL == blk) | ||
483 | return -ENOMEM; | ||
484 | |||
485 | *rblk = blk; | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | static int src_mgr_put_ctrl_blk(void *blk) | ||
491 | { | ||
492 | kfree((struct src_mgr_ctrl_blk *)blk); | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static int srcimp_mgr_get_ctrl_blk(void **rblk) | ||
498 | { | ||
499 | struct srcimp_mgr_ctrl_blk *blk; | ||
500 | |||
501 | *rblk = NULL; | ||
502 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
503 | if (NULL == blk) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | *rblk = blk; | ||
507 | |||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static int srcimp_mgr_put_ctrl_blk(void *blk) | ||
512 | { | ||
513 | kfree((struct srcimp_mgr_ctrl_blk *)blk); | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) | ||
519 | { | ||
520 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
521 | |||
522 | set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); | ||
523 | ctl->dirty.bf.srcimap = 1; | ||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) | ||
528 | { | ||
529 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
530 | |||
531 | set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); | ||
532 | ctl->dirty.bf.srcimap = 1; | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) | ||
537 | { | ||
538 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
539 | |||
540 | set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); | ||
541 | ctl->dirty.bf.srcimap = 1; | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
546 | { | ||
547 | ((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr; | ||
548 | ((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1; | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | static int srcimp_mgr_commit_write(struct hw *hw, void *blk) | ||
553 | { | ||
554 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
555 | |||
556 | if (ctl->dirty.bf.srcimap) { | ||
557 | hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100, | ||
558 | ctl->srcimap.srcaim); | ||
559 | ctl->dirty.bf.srcimap = 0; | ||
560 | } | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | /* | ||
566 | * AMIXER control block definitions. | ||
567 | */ | ||
568 | |||
569 | #define AMOPLO_M 0x00000003 | ||
570 | #define AMOPLO_IV 0x00000004 | ||
571 | #define AMOPLO_X 0x0003FFF0 | ||
572 | #define AMOPLO_Y 0xFFFC0000 | ||
573 | |||
574 | #define AMOPHI_SADR 0x000000FF | ||
575 | #define AMOPHI_SE 0x80000000 | ||
576 | |||
577 | /* AMIXER resource register dirty flags */ | ||
578 | union amixer_dirty { | ||
579 | struct { | ||
580 | u16 amoplo:1; | ||
581 | u16 amophi:1; | ||
582 | u16 rsv:14; | ||
583 | } bf; | ||
584 | u16 data; | ||
585 | }; | ||
586 | |||
587 | /* AMIXER resource control block */ | ||
588 | struct amixer_rsc_ctrl_blk { | ||
589 | unsigned int amoplo; | ||
590 | unsigned int amophi; | ||
591 | union amixer_dirty dirty; | ||
592 | }; | ||
593 | |||
594 | static int amixer_set_mode(void *blk, unsigned int mode) | ||
595 | { | ||
596 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
597 | |||
598 | set_field(&ctl->amoplo, AMOPLO_M, mode); | ||
599 | ctl->dirty.bf.amoplo = 1; | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static int amixer_set_iv(void *blk, unsigned int iv) | ||
604 | { | ||
605 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
606 | |||
607 | set_field(&ctl->amoplo, AMOPLO_IV, iv); | ||
608 | ctl->dirty.bf.amoplo = 1; | ||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | static int amixer_set_x(void *blk, unsigned int x) | ||
613 | { | ||
614 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
615 | |||
616 | set_field(&ctl->amoplo, AMOPLO_X, x); | ||
617 | ctl->dirty.bf.amoplo = 1; | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static int amixer_set_y(void *blk, unsigned int y) | ||
622 | { | ||
623 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
624 | |||
625 | set_field(&ctl->amoplo, AMOPLO_Y, y); | ||
626 | ctl->dirty.bf.amoplo = 1; | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int amixer_set_sadr(void *blk, unsigned int sadr) | ||
631 | { | ||
632 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
633 | |||
634 | set_field(&ctl->amophi, AMOPHI_SADR, sadr); | ||
635 | ctl->dirty.bf.amophi = 1; | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int amixer_set_se(void *blk, unsigned int se) | ||
640 | { | ||
641 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
642 | |||
643 | set_field(&ctl->amophi, AMOPHI_SE, se); | ||
644 | ctl->dirty.bf.amophi = 1; | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int amixer_set_dirty(void *blk, unsigned int flags) | ||
649 | { | ||
650 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static int amixer_set_dirty_all(void *blk) | ||
655 | { | ||
656 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
661 | { | ||
662 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
663 | |||
664 | if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { | ||
665 | hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo); | ||
666 | ctl->dirty.bf.amoplo = 0; | ||
667 | hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi); | ||
668 | ctl->dirty.bf.amophi = 0; | ||
669 | } | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static int amixer_get_y(void *blk) | ||
675 | { | ||
676 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
677 | |||
678 | return get_field(ctl->amoplo, AMOPLO_Y); | ||
679 | } | ||
680 | |||
681 | static unsigned int amixer_get_dirty(void *blk) | ||
682 | { | ||
683 | return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; | ||
684 | } | ||
685 | |||
686 | static int amixer_rsc_get_ctrl_blk(void **rblk) | ||
687 | { | ||
688 | struct amixer_rsc_ctrl_blk *blk; | ||
689 | |||
690 | *rblk = NULL; | ||
691 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
692 | if (NULL == blk) | ||
693 | return -ENOMEM; | ||
694 | |||
695 | *rblk = blk; | ||
696 | |||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | static int amixer_rsc_put_ctrl_blk(void *blk) | ||
701 | { | ||
702 | kfree((struct amixer_rsc_ctrl_blk *)blk); | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static int amixer_mgr_get_ctrl_blk(void **rblk) | ||
708 | { | ||
709 | *rblk = NULL; | ||
710 | |||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static int amixer_mgr_put_ctrl_blk(void *blk) | ||
715 | { | ||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | /* | ||
720 | * DAIO control block definitions. | ||
721 | */ | ||
722 | |||
723 | /* Receiver Sample Rate Tracker Control register */ | ||
724 | #define SRTCTL_SRCO 0x000000FF | ||
725 | #define SRTCTL_SRCM 0x0000FF00 | ||
726 | #define SRTCTL_RSR 0x00030000 | ||
727 | #define SRTCTL_DRAT 0x00300000 | ||
728 | #define SRTCTL_EC 0x01000000 | ||
729 | #define SRTCTL_ET 0x10000000 | ||
730 | |||
731 | /* DAIO Receiver register dirty flags */ | ||
732 | union dai_dirty { | ||
733 | struct { | ||
734 | u16 srt:1; | ||
735 | u16 rsv:15; | ||
736 | } bf; | ||
737 | u16 data; | ||
738 | }; | ||
739 | |||
740 | /* DAIO Receiver control block */ | ||
741 | struct dai_ctrl_blk { | ||
742 | unsigned int srt; | ||
743 | union dai_dirty dirty; | ||
744 | }; | ||
745 | |||
746 | /* Audio Input Mapper RAM */ | ||
747 | #define AIM_ARC 0x00000FFF | ||
748 | #define AIM_NXT 0x007F0000 | ||
749 | |||
750 | struct daoimap { | ||
751 | unsigned int aim; | ||
752 | unsigned int idx; | ||
753 | }; | ||
754 | |||
755 | /* Audio Transmitter Control and Status register */ | ||
756 | #define ATXCTL_EN 0x00000001 | ||
757 | #define ATXCTL_MODE 0x00000010 | ||
758 | #define ATXCTL_CD 0x00000020 | ||
759 | #define ATXCTL_RAW 0x00000100 | ||
760 | #define ATXCTL_MT 0x00000200 | ||
761 | #define ATXCTL_NUC 0x00003000 | ||
762 | #define ATXCTL_BEN 0x00010000 | ||
763 | #define ATXCTL_BMUX 0x00700000 | ||
764 | #define ATXCTL_B24 0x01000000 | ||
765 | #define ATXCTL_CPF 0x02000000 | ||
766 | #define ATXCTL_RIV 0x10000000 | ||
767 | #define ATXCTL_LIV 0x20000000 | ||
768 | #define ATXCTL_RSAT 0x40000000 | ||
769 | #define ATXCTL_LSAT 0x80000000 | ||
770 | |||
771 | /* XDIF Transmitter register dirty flags */ | ||
772 | union dao_dirty { | ||
773 | struct { | ||
774 | u16 atxcsl:1; | ||
775 | u16 rsv:15; | ||
776 | } bf; | ||
777 | u16 data; | ||
778 | }; | ||
779 | |||
780 | /* XDIF Transmitter control block */ | ||
781 | struct dao_ctrl_blk { | ||
782 | /* XDIF Transmitter Channel Status Low Register */ | ||
783 | unsigned int atxcsl; | ||
784 | union dao_dirty dirty; | ||
785 | }; | ||
786 | |||
787 | /* Audio Receiver Control register */ | ||
788 | #define ARXCTL_EN 0x00000001 | ||
789 | |||
790 | /* DAIO manager register dirty flags */ | ||
791 | union daio_mgr_dirty { | ||
792 | struct { | ||
793 | u32 atxctl:8; | ||
794 | u32 arxctl:8; | ||
795 | u32 daoimap:1; | ||
796 | u32 rsv:15; | ||
797 | } bf; | ||
798 | u32 data; | ||
799 | }; | ||
800 | |||
801 | /* DAIO manager control block */ | ||
802 | struct daio_mgr_ctrl_blk { | ||
803 | struct daoimap daoimap; | ||
804 | unsigned int txctl[8]; | ||
805 | unsigned int rxctl[8]; | ||
806 | union daio_mgr_dirty dirty; | ||
807 | }; | ||
808 | |||
809 | static int dai_srt_set_srco(void *blk, unsigned int src) | ||
810 | { | ||
811 | struct dai_ctrl_blk *ctl = blk; | ||
812 | |||
813 | set_field(&ctl->srt, SRTCTL_SRCO, src); | ||
814 | ctl->dirty.bf.srt = 1; | ||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | static int dai_srt_set_srcm(void *blk, unsigned int src) | ||
819 | { | ||
820 | struct dai_ctrl_blk *ctl = blk; | ||
821 | |||
822 | set_field(&ctl->srt, SRTCTL_SRCM, src); | ||
823 | ctl->dirty.bf.srt = 1; | ||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | static int dai_srt_set_rsr(void *blk, unsigned int rsr) | ||
828 | { | ||
829 | struct dai_ctrl_blk *ctl = blk; | ||
830 | |||
831 | set_field(&ctl->srt, SRTCTL_RSR, rsr); | ||
832 | ctl->dirty.bf.srt = 1; | ||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | static int dai_srt_set_drat(void *blk, unsigned int drat) | ||
837 | { | ||
838 | struct dai_ctrl_blk *ctl = blk; | ||
839 | |||
840 | set_field(&ctl->srt, SRTCTL_DRAT, drat); | ||
841 | ctl->dirty.bf.srt = 1; | ||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | static int dai_srt_set_ec(void *blk, unsigned int ec) | ||
846 | { | ||
847 | struct dai_ctrl_blk *ctl = blk; | ||
848 | |||
849 | set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0); | ||
850 | ctl->dirty.bf.srt = 1; | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int dai_srt_set_et(void *blk, unsigned int et) | ||
855 | { | ||
856 | struct dai_ctrl_blk *ctl = blk; | ||
857 | |||
858 | set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0); | ||
859 | ctl->dirty.bf.srt = 1; | ||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
864 | { | ||
865 | struct dai_ctrl_blk *ctl = blk; | ||
866 | |||
867 | if (ctl->dirty.bf.srt) { | ||
868 | hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt); | ||
869 | ctl->dirty.bf.srt = 0; | ||
870 | } | ||
871 | |||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | static int dai_get_ctrl_blk(void **rblk) | ||
876 | { | ||
877 | struct dai_ctrl_blk *blk; | ||
878 | |||
879 | *rblk = NULL; | ||
880 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
881 | if (NULL == blk) | ||
882 | return -ENOMEM; | ||
883 | |||
884 | *rblk = blk; | ||
885 | |||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | static int dai_put_ctrl_blk(void *blk) | ||
890 | { | ||
891 | kfree((struct dai_ctrl_blk *)blk); | ||
892 | |||
893 | return 0; | ||
894 | } | ||
895 | |||
896 | static int dao_set_spos(void *blk, unsigned int spos) | ||
897 | { | ||
898 | ((struct dao_ctrl_blk *)blk)->atxcsl = spos; | ||
899 | ((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1; | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
904 | { | ||
905 | struct dao_ctrl_blk *ctl = blk; | ||
906 | |||
907 | if (ctl->dirty.bf.atxcsl) { | ||
908 | if (idx < 4) { | ||
909 | /* S/PDIF SPOSx */ | ||
910 | hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx, | ||
911 | ctl->atxcsl); | ||
912 | } | ||
913 | ctl->dirty.bf.atxcsl = 0; | ||
914 | } | ||
915 | |||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | static int dao_get_spos(void *blk, unsigned int *spos) | ||
920 | { | ||
921 | *spos = ((struct dao_ctrl_blk *)blk)->atxcsl; | ||
922 | return 0; | ||
923 | } | ||
924 | |||
925 | static int dao_get_ctrl_blk(void **rblk) | ||
926 | { | ||
927 | struct dao_ctrl_blk *blk; | ||
928 | |||
929 | *rblk = NULL; | ||
930 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
931 | if (NULL == blk) | ||
932 | return -ENOMEM; | ||
933 | |||
934 | *rblk = blk; | ||
935 | |||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | static int dao_put_ctrl_blk(void *blk) | ||
940 | { | ||
941 | kfree((struct dao_ctrl_blk *)blk); | ||
942 | |||
943 | return 0; | ||
944 | } | ||
945 | |||
946 | static int daio_mgr_enb_dai(void *blk, unsigned int idx) | ||
947 | { | ||
948 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
949 | |||
950 | set_field(&ctl->rxctl[idx], ARXCTL_EN, 1); | ||
951 | ctl->dirty.bf.arxctl |= (0x1 << idx); | ||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | static int daio_mgr_dsb_dai(void *blk, unsigned int idx) | ||
956 | { | ||
957 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
958 | |||
959 | set_field(&ctl->rxctl[idx], ARXCTL_EN, 0); | ||
960 | |||
961 | ctl->dirty.bf.arxctl |= (0x1 << idx); | ||
962 | return 0; | ||
963 | } | ||
964 | |||
965 | static int daio_mgr_enb_dao(void *blk, unsigned int idx) | ||
966 | { | ||
967 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
968 | |||
969 | set_field(&ctl->txctl[idx], ATXCTL_EN, 1); | ||
970 | ctl->dirty.bf.atxctl |= (0x1 << idx); | ||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int daio_mgr_dsb_dao(void *blk, unsigned int idx) | ||
975 | { | ||
976 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
977 | |||
978 | set_field(&ctl->txctl[idx], ATXCTL_EN, 0); | ||
979 | ctl->dirty.bf.atxctl |= (0x1 << idx); | ||
980 | return 0; | ||
981 | } | ||
982 | |||
983 | static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) | ||
984 | { | ||
985 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
986 | |||
987 | if (idx < 4) { | ||
988 | /* S/PDIF output */ | ||
989 | switch ((conf & 0x7)) { | ||
990 | case 1: | ||
991 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 0); | ||
992 | break; | ||
993 | case 2: | ||
994 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 1); | ||
995 | break; | ||
996 | case 4: | ||
997 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 2); | ||
998 | break; | ||
999 | case 8: | ||
1000 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 3); | ||
1001 | break; | ||
1002 | default: | ||
1003 | break; | ||
1004 | } | ||
1005 | /* CDIF */ | ||
1006 | set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7))); | ||
1007 | /* Non-audio */ | ||
1008 | set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1); | ||
1009 | /* Non-audio */ | ||
1010 | set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1); | ||
1011 | set_field(&ctl->txctl[idx], ATXCTL_RAW, | ||
1012 | ((conf >> 3) & 0x1) ? 0 : 0); | ||
1013 | ctl->dirty.bf.atxctl |= (0x1 << idx); | ||
1014 | } else { | ||
1015 | /* I2S output */ | ||
1016 | /*idx %= 4; */ | ||
1017 | } | ||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | static int daio_mgr_set_imaparc(void *blk, unsigned int slot) | ||
1022 | { | ||
1023 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1024 | |||
1025 | set_field(&ctl->daoimap.aim, AIM_ARC, slot); | ||
1026 | ctl->dirty.bf.daoimap = 1; | ||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | static int daio_mgr_set_imapnxt(void *blk, unsigned int next) | ||
1031 | { | ||
1032 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1033 | |||
1034 | set_field(&ctl->daoimap.aim, AIM_NXT, next); | ||
1035 | ctl->dirty.bf.daoimap = 1; | ||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
1039 | static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
1040 | { | ||
1041 | ((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr; | ||
1042 | ((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1; | ||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | static int daio_mgr_commit_write(struct hw *hw, void *blk) | ||
1047 | { | ||
1048 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1049 | unsigned int data = 0; | ||
1050 | int i = 0; | ||
1051 | |||
1052 | for (i = 0; i < 8; i++) { | ||
1053 | if ((ctl->dirty.bf.atxctl & (0x1 << i))) { | ||
1054 | data = ctl->txctl[i]; | ||
1055 | hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); | ||
1056 | ctl->dirty.bf.atxctl &= ~(0x1 << i); | ||
1057 | mdelay(1); | ||
1058 | } | ||
1059 | if ((ctl->dirty.bf.arxctl & (0x1 << i))) { | ||
1060 | data = ctl->rxctl[i]; | ||
1061 | hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); | ||
1062 | ctl->dirty.bf.arxctl &= ~(0x1 << i); | ||
1063 | mdelay(1); | ||
1064 | } | ||
1065 | } | ||
1066 | if (ctl->dirty.bf.daoimap) { | ||
1067 | hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4, | ||
1068 | ctl->daoimap.aim); | ||
1069 | ctl->dirty.bf.daoimap = 0; | ||
1070 | } | ||
1071 | |||
1072 | return 0; | ||
1073 | } | ||
1074 | |||
1075 | static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) | ||
1076 | { | ||
1077 | struct daio_mgr_ctrl_blk *blk; | ||
1078 | int i = 0; | ||
1079 | |||
1080 | *rblk = NULL; | ||
1081 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
1082 | if (NULL == blk) | ||
1083 | return -ENOMEM; | ||
1084 | |||
1085 | for (i = 0; i < 8; i++) { | ||
1086 | blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i)); | ||
1087 | blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i)); | ||
1088 | } | ||
1089 | |||
1090 | *rblk = blk; | ||
1091 | |||
1092 | return 0; | ||
1093 | } | ||
1094 | |||
1095 | static int daio_mgr_put_ctrl_blk(void *blk) | ||
1096 | { | ||
1097 | kfree((struct daio_mgr_ctrl_blk *)blk); | ||
1098 | |||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
1102 | /* Card hardware initialization block */ | ||
1103 | struct dac_conf { | ||
1104 | unsigned int msr; /* master sample rate in rsrs */ | ||
1105 | }; | ||
1106 | |||
1107 | struct adc_conf { | ||
1108 | unsigned int msr; /* master sample rate in rsrs */ | ||
1109 | unsigned char input; /* the input source of ADC */ | ||
1110 | unsigned char mic20db; /* boost mic by 20db if input is microphone */ | ||
1111 | }; | ||
1112 | |||
1113 | struct daio_conf { | ||
1114 | unsigned int msr; /* master sample rate in rsrs */ | ||
1115 | }; | ||
1116 | |||
1117 | struct trn_conf { | ||
1118 | unsigned long vm_pgt_phys; | ||
1119 | }; | ||
1120 | |||
1121 | static int hw_daio_init(struct hw *hw, const struct daio_conf *info) | ||
1122 | { | ||
1123 | u32 dwData = 0; | ||
1124 | int i; | ||
1125 | |||
1126 | /* Program I2S with proper sample rate and enable the correct I2S | ||
1127 | * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */ | ||
1128 | if (1 == info->msr) { | ||
1129 | hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101); | ||
1130 | hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101); | ||
1131 | hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); | ||
1132 | } else if (2 == info->msr) { | ||
1133 | hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); | ||
1134 | /* Specify all playing 96khz | ||
1135 | * EA [0] - Enabled | ||
1136 | * RTA [4:5] - 96kHz | ||
1137 | * EB [8] - Enabled | ||
1138 | * RTB [12:13] - 96kHz | ||
1139 | * EC [16] - Enabled | ||
1140 | * RTC [20:21] - 96kHz | ||
1141 | * ED [24] - Enabled | ||
1142 | * RTD [28:29] - 96kHz */ | ||
1143 | hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); | ||
1144 | hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); | ||
1145 | } else { | ||
1146 | printk(KERN_ALERT "ERROR!!! Invalid sampling rate!!!\n"); | ||
1147 | return -EINVAL; | ||
1148 | } | ||
1149 | |||
1150 | for (i = 0; i < 8; i++) { | ||
1151 | if (i <= 3) { | ||
1152 | /* 1st 3 channels are SPDIFs (SB0960) */ | ||
1153 | if (i == 3) | ||
1154 | dwData = 0x1001001; | ||
1155 | else | ||
1156 | dwData = 0x1000001; | ||
1157 | |||
1158 | hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), dwData); | ||
1159 | hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), dwData); | ||
1160 | |||
1161 | /* Initialize the SPDIF Out Channel status registers. | ||
1162 | * The value specified here is based on the typical | ||
1163 | * values provided in the specification, namely: Clock | ||
1164 | * Accuracy of 1000ppm, Sample Rate of 48KHz, | ||
1165 | * unspecified source number, Generation status = 1, | ||
1166 | * Category code = 0x12 (Digital Signal Mixer), | ||
1167 | * Mode = 0, Emph = 0, Copy Permitted, AN = 0 | ||
1168 | * (indicating that we're transmitting digital audio, | ||
1169 | * and the Professional Use bit is 0. */ | ||
1170 | |||
1171 | hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i), | ||
1172 | 0x02109204); /* Default to 48kHz */ | ||
1173 | |||
1174 | hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); | ||
1175 | } else { | ||
1176 | /* Next 5 channels are I2S (SB0960) */ | ||
1177 | dwData = 0x11; | ||
1178 | hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), dwData); | ||
1179 | if (2 == info->msr) { | ||
1180 | /* Four channels per sample period */ | ||
1181 | dwData |= 0x1000; | ||
1182 | } | ||
1183 | hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), dwData); | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | return 0; | ||
1188 | } | ||
1189 | |||
1190 | /* TRANSPORT operations */ | ||
1191 | static int hw_trn_init(struct hw *hw, const struct trn_conf *info) | ||
1192 | { | ||
1193 | u32 vmctl = 0, data = 0; | ||
1194 | unsigned long ptp_phys_low = 0, ptp_phys_high = 0; | ||
1195 | int i = 0; | ||
1196 | |||
1197 | /* Set up device page table */ | ||
1198 | if ((~0UL) == info->vm_pgt_phys) { | ||
1199 | printk(KERN_ALERT "Wrong device page table page address!!!\n"); | ||
1200 | return -1; | ||
1201 | } | ||
1202 | |||
1203 | vmctl = 0x80000C0F; /* 32-bit, 4k-size page */ | ||
1204 | #if BITS_PER_LONG == 64 | ||
1205 | ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); | ||
1206 | ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); | ||
1207 | vmctl |= (3<<8); | ||
1208 | #elif BITS_PER_LONG == 32 | ||
1209 | ptp_phys_low = info->vm_pgt_phys & (~0UL); | ||
1210 | ptp_phys_high = 0; | ||
1211 | #else | ||
1212 | # error "Unknown BITS_PER_LONG!" | ||
1213 | #endif | ||
1214 | #if PAGE_SIZE == 8192 | ||
1215 | # error "Don't support 8k-page!" | ||
1216 | #endif | ||
1217 | /* Write page table physical address to all PTPAL registers */ | ||
1218 | for (i = 0; i < 64; i++) { | ||
1219 | hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low); | ||
1220 | hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high); | ||
1221 | } | ||
1222 | /* Enable virtual memory transfer */ | ||
1223 | hw_write_20kx(hw, VMEM_CTL, vmctl); | ||
1224 | /* Enable transport bus master and queueing of request */ | ||
1225 | hw_write_20kx(hw, TRANSPORT_CTL, 0x03); | ||
1226 | hw_write_20kx(hw, TRANSPORT_INT, 0x200c01); | ||
1227 | /* Enable transport ring */ | ||
1228 | data = hw_read_20kx(hw, TRANSPORT_ENB); | ||
1229 | hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03)); | ||
1230 | |||
1231 | return 0; | ||
1232 | } | ||
1233 | |||
1234 | /* Card initialization */ | ||
1235 | #define GCTL_AIE 0x00000001 | ||
1236 | #define GCTL_UAA 0x00000002 | ||
1237 | #define GCTL_DPC 0x00000004 | ||
1238 | #define GCTL_DBP 0x00000008 | ||
1239 | #define GCTL_ABP 0x00000010 | ||
1240 | #define GCTL_TBP 0x00000020 | ||
1241 | #define GCTL_SBP 0x00000040 | ||
1242 | #define GCTL_FBP 0x00000080 | ||
1243 | #define GCTL_ME 0x00000100 | ||
1244 | #define GCTL_AID 0x00001000 | ||
1245 | |||
1246 | #define PLLCTL_SRC 0x00000007 | ||
1247 | #define PLLCTL_SPE 0x00000008 | ||
1248 | #define PLLCTL_RD 0x000000F0 | ||
1249 | #define PLLCTL_FD 0x0001FF00 | ||
1250 | #define PLLCTL_OD 0x00060000 | ||
1251 | #define PLLCTL_B 0x00080000 | ||
1252 | #define PLLCTL_AS 0x00100000 | ||
1253 | #define PLLCTL_LF 0x03E00000 | ||
1254 | #define PLLCTL_SPS 0x1C000000 | ||
1255 | #define PLLCTL_AD 0x60000000 | ||
1256 | |||
1257 | #define PLLSTAT_CCS 0x00000007 | ||
1258 | #define PLLSTAT_SPL 0x00000008 | ||
1259 | #define PLLSTAT_CRD 0x000000F0 | ||
1260 | #define PLLSTAT_CFD 0x0001FF00 | ||
1261 | #define PLLSTAT_SL 0x00020000 | ||
1262 | #define PLLSTAT_FAS 0x00040000 | ||
1263 | #define PLLSTAT_B 0x00080000 | ||
1264 | #define PLLSTAT_PD 0x00100000 | ||
1265 | #define PLLSTAT_OCA 0x00200000 | ||
1266 | #define PLLSTAT_NCA 0x00400000 | ||
1267 | |||
1268 | static int hw_pll_init(struct hw *hw, unsigned int rsr) | ||
1269 | { | ||
1270 | unsigned int pllenb; | ||
1271 | unsigned int pllctl; | ||
1272 | unsigned int pllstat; | ||
1273 | int i; | ||
1274 | |||
1275 | pllenb = 0xB; | ||
1276 | hw_write_20kx(hw, PLL_ENB, pllenb); | ||
1277 | pllctl = 0x20D00000; | ||
1278 | set_field(&pllctl, PLLCTL_FD, 16 - 4); | ||
1279 | hw_write_20kx(hw, PLL_CTL, pllctl); | ||
1280 | mdelay(40); | ||
1281 | pllctl = hw_read_20kx(hw, PLL_CTL); | ||
1282 | set_field(&pllctl, PLLCTL_B, 0); | ||
1283 | if (48000 == rsr) { | ||
1284 | set_field(&pllctl, PLLCTL_FD, 16 - 2); | ||
1285 | set_field(&pllctl, PLLCTL_RD, 1 - 1); | ||
1286 | } else { /* 44100 */ | ||
1287 | set_field(&pllctl, PLLCTL_FD, 147 - 2); | ||
1288 | set_field(&pllctl, PLLCTL_RD, 10 - 1); | ||
1289 | } | ||
1290 | hw_write_20kx(hw, PLL_CTL, pllctl); | ||
1291 | mdelay(40); | ||
1292 | for (i = 0; i < 1000; i++) { | ||
1293 | pllstat = hw_read_20kx(hw, PLL_STAT); | ||
1294 | if (get_field(pllstat, PLLSTAT_PD)) | ||
1295 | continue; | ||
1296 | |||
1297 | if (get_field(pllstat, PLLSTAT_B) != | ||
1298 | get_field(pllctl, PLLCTL_B)) | ||
1299 | continue; | ||
1300 | |||
1301 | if (get_field(pllstat, PLLSTAT_CCS) != | ||
1302 | get_field(pllctl, PLLCTL_SRC)) | ||
1303 | continue; | ||
1304 | |||
1305 | if (get_field(pllstat, PLLSTAT_CRD) != | ||
1306 | get_field(pllctl, PLLCTL_RD)) | ||
1307 | continue; | ||
1308 | |||
1309 | if (get_field(pllstat, PLLSTAT_CFD) != | ||
1310 | get_field(pllctl, PLLCTL_FD)) | ||
1311 | continue; | ||
1312 | |||
1313 | break; | ||
1314 | } | ||
1315 | if (i >= 1000) { | ||
1316 | printk(KERN_ALERT "PLL initialization failed!!!\n"); | ||
1317 | return -EBUSY; | ||
1318 | } | ||
1319 | |||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | static int hw_auto_init(struct hw *hw) | ||
1324 | { | ||
1325 | unsigned int gctl; | ||
1326 | int i; | ||
1327 | |||
1328 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1329 | set_field(&gctl, GCTL_AIE, 0); | ||
1330 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1331 | set_field(&gctl, GCTL_AIE, 1); | ||
1332 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1333 | mdelay(10); | ||
1334 | for (i = 0; i < 400000; i++) { | ||
1335 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1336 | if (get_field(gctl, GCTL_AID)) | ||
1337 | break; | ||
1338 | } | ||
1339 | if (!get_field(gctl, GCTL_AID)) { | ||
1340 | printk(KERN_ALERT "Card Auto-init failed!!!\n"); | ||
1341 | return -EBUSY; | ||
1342 | } | ||
1343 | |||
1344 | return 0; | ||
1345 | } | ||
1346 | |||
1347 | /* DAC operations */ | ||
1348 | |||
1349 | #define CS4382_MC1 0x1 | ||
1350 | #define CS4382_MC2 0x2 | ||
1351 | #define CS4382_MC3 0x3 | ||
1352 | #define CS4382_FC 0x4 | ||
1353 | #define CS4382_IC 0x5 | ||
1354 | #define CS4382_XC1 0x6 | ||
1355 | #define CS4382_VCA1 0x7 | ||
1356 | #define CS4382_VCB1 0x8 | ||
1357 | #define CS4382_XC2 0x9 | ||
1358 | #define CS4382_VCA2 0xA | ||
1359 | #define CS4382_VCB2 0xB | ||
1360 | #define CS4382_XC3 0xC | ||
1361 | #define CS4382_VCA3 0xD | ||
1362 | #define CS4382_VCB3 0xE | ||
1363 | #define CS4382_XC4 0xF | ||
1364 | #define CS4382_VCA4 0x10 | ||
1365 | #define CS4382_VCB4 0x11 | ||
1366 | #define CS4382_CREV 0x12 | ||
1367 | |||
1368 | /* I2C status */ | ||
1369 | #define STATE_LOCKED 0x00 | ||
1370 | #define STATE_UNLOCKED 0xAA | ||
1371 | #define DATA_READY 0x800000 /* Used with I2C_IF_STATUS */ | ||
1372 | #define DATA_ABORT 0x10000 /* Used with I2C_IF_STATUS */ | ||
1373 | |||
1374 | #define I2C_STATUS_DCM 0x00000001 | ||
1375 | #define I2C_STATUS_BC 0x00000006 | ||
1376 | #define I2C_STATUS_APD 0x00000008 | ||
1377 | #define I2C_STATUS_AB 0x00010000 | ||
1378 | #define I2C_STATUS_DR 0x00800000 | ||
1379 | |||
1380 | #define I2C_ADDRESS_PTAD 0x0000FFFF | ||
1381 | #define I2C_ADDRESS_SLAD 0x007F0000 | ||
1382 | |||
1383 | struct REGS_CS4382 { | ||
1384 | u32 dwModeControl_1; | ||
1385 | u32 dwModeControl_2; | ||
1386 | u32 dwModeControl_3; | ||
1387 | |||
1388 | u32 dwFilterControl; | ||
1389 | u32 dwInvertControl; | ||
1390 | |||
1391 | u32 dwMixControl_P1; | ||
1392 | u32 dwVolControl_A1; | ||
1393 | u32 dwVolControl_B1; | ||
1394 | |||
1395 | u32 dwMixControl_P2; | ||
1396 | u32 dwVolControl_A2; | ||
1397 | u32 dwVolControl_B2; | ||
1398 | |||
1399 | u32 dwMixControl_P3; | ||
1400 | u32 dwVolControl_A3; | ||
1401 | u32 dwVolControl_B3; | ||
1402 | |||
1403 | u32 dwMixControl_P4; | ||
1404 | u32 dwVolControl_A4; | ||
1405 | u32 dwVolControl_B4; | ||
1406 | }; | ||
1407 | |||
1408 | static u8 m_bAddressSize, m_bDataSize, m_bDeviceID; | ||
1409 | |||
1410 | static int I2CUnlockFullAccess(struct hw *hw) | ||
1411 | { | ||
1412 | u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] = {0xB3, 0xD4}; | ||
1413 | |||
1414 | /* Send keys for forced BIOS mode */ | ||
1415 | hw_write_20kx(hw, I2C_IF_WLOCK, | ||
1416 | UnlockKeySequence_FLASH_FULLACCESS_MODE[0]); | ||
1417 | hw_write_20kx(hw, I2C_IF_WLOCK, | ||
1418 | UnlockKeySequence_FLASH_FULLACCESS_MODE[1]); | ||
1419 | /* Check whether the chip is unlocked */ | ||
1420 | if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED) | ||
1421 | return 0; | ||
1422 | |||
1423 | return -1; | ||
1424 | } | ||
1425 | |||
1426 | static int I2CLockChip(struct hw *hw) | ||
1427 | { | ||
1428 | /* Write twice */ | ||
1429 | hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); | ||
1430 | hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); | ||
1431 | if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED) | ||
1432 | return 0; | ||
1433 | |||
1434 | return -1; | ||
1435 | } | ||
1436 | |||
1437 | static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) | ||
1438 | { | ||
1439 | int err = 0; | ||
1440 | unsigned int RegI2CStatus; | ||
1441 | unsigned int RegI2CAddress; | ||
1442 | |||
1443 | err = I2CUnlockFullAccess(hw); | ||
1444 | if (err < 0) | ||
1445 | return err; | ||
1446 | |||
1447 | m_bAddressSize = bAddressSize; | ||
1448 | m_bDataSize = bDataSize; | ||
1449 | m_bDeviceID = bDeviceID; | ||
1450 | |||
1451 | RegI2CAddress = 0; | ||
1452 | set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, bDeviceID); | ||
1453 | |||
1454 | hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); | ||
1455 | |||
1456 | RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1457 | |||
1458 | set_field(&RegI2CStatus, I2C_STATUS_DCM, 1); /* Direct control mode */ | ||
1459 | |||
1460 | hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); | ||
1461 | |||
1462 | return 0; | ||
1463 | } | ||
1464 | |||
1465 | static int I2CUninit(struct hw *hw) | ||
1466 | { | ||
1467 | unsigned int RegI2CStatus; | ||
1468 | unsigned int RegI2CAddress; | ||
1469 | |||
1470 | RegI2CAddress = 0; | ||
1471 | set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ | ||
1472 | |||
1473 | hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); | ||
1474 | |||
1475 | RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1476 | |||
1477 | set_field(&RegI2CStatus, I2C_STATUS_DCM, 0); /* I2C mode */ | ||
1478 | |||
1479 | hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); | ||
1480 | |||
1481 | return I2CLockChip(hw); | ||
1482 | } | ||
1483 | |||
1484 | static int I2CWaitDataReady(struct hw *hw) | ||
1485 | { | ||
1486 | int i = 0x400000; | ||
1487 | unsigned int ret = 0; | ||
1488 | |||
1489 | do { | ||
1490 | ret = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1491 | } while ((!(ret & DATA_READY)) && --i); | ||
1492 | |||
1493 | return i; | ||
1494 | } | ||
1495 | |||
1496 | static int I2CRead(struct hw *hw, u16 wAddress, u32 *pdwData) | ||
1497 | { | ||
1498 | unsigned int RegI2CStatus; | ||
1499 | |||
1500 | RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1501 | set_field(&RegI2CStatus, I2C_STATUS_BC, | ||
1502 | (4 == m_bAddressSize) ? 0 : m_bAddressSize); | ||
1503 | hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); | ||
1504 | if (!I2CWaitDataReady(hw)) | ||
1505 | return -1; | ||
1506 | |||
1507 | hw_write_20kx(hw, I2C_IF_WDATA, (u32)wAddress); | ||
1508 | if (!I2CWaitDataReady(hw)) | ||
1509 | return -1; | ||
1510 | |||
1511 | /* Force a read operation */ | ||
1512 | hw_write_20kx(hw, I2C_IF_RDATA, 0); | ||
1513 | if (!I2CWaitDataReady(hw)) | ||
1514 | return -1; | ||
1515 | |||
1516 | *pdwData = hw_read_20kx(hw, I2C_IF_RDATA); | ||
1517 | |||
1518 | return 0; | ||
1519 | } | ||
1520 | |||
1521 | static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) | ||
1522 | { | ||
1523 | unsigned int dwI2CData = (dwData << (m_bAddressSize * 8)) | wAddress; | ||
1524 | unsigned int RegI2CStatus; | ||
1525 | |||
1526 | RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1527 | |||
1528 | set_field(&RegI2CStatus, I2C_STATUS_BC, | ||
1529 | (4 == (m_bAddressSize + m_bDataSize)) ? | ||
1530 | 0 : (m_bAddressSize + m_bDataSize)); | ||
1531 | |||
1532 | hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); | ||
1533 | I2CWaitDataReady(hw); | ||
1534 | /* Dummy write to trigger the write oprtation */ | ||
1535 | hw_write_20kx(hw, I2C_IF_WDATA, 0); | ||
1536 | I2CWaitDataReady(hw); | ||
1537 | |||
1538 | /* This is the real data */ | ||
1539 | hw_write_20kx(hw, I2C_IF_WDATA, dwI2CData); | ||
1540 | I2CWaitDataReady(hw); | ||
1541 | |||
1542 | return 0; | ||
1543 | } | ||
1544 | |||
1545 | static int hw_dac_init(struct hw *hw, const struct dac_conf *info) | ||
1546 | { | ||
1547 | int err = 0; | ||
1548 | u32 dwData = 0; | ||
1549 | int i = 0; | ||
1550 | struct REGS_CS4382 cs4382_Read = {0}; | ||
1551 | struct REGS_CS4382 cs4382_Def = { | ||
1552 | 0x00000001, /* Mode Control 1 */ | ||
1553 | 0x00000000, /* Mode Control 2 */ | ||
1554 | 0x00000084, /* Mode Control 3 */ | ||
1555 | 0x00000000, /* Filter Control */ | ||
1556 | 0x00000000, /* Invert Control */ | ||
1557 | 0x00000024, /* Mixing Control Pair 1 */ | ||
1558 | 0x00000000, /* Vol Control A1 */ | ||
1559 | 0x00000000, /* Vol Control B1 */ | ||
1560 | 0x00000024, /* Mixing Control Pair 2 */ | ||
1561 | 0x00000000, /* Vol Control A2 */ | ||
1562 | 0x00000000, /* Vol Control B2 */ | ||
1563 | 0x00000024, /* Mixing Control Pair 3 */ | ||
1564 | 0x00000000, /* Vol Control A3 */ | ||
1565 | 0x00000000, /* Vol Control B3 */ | ||
1566 | 0x00000024, /* Mixing Control Pair 4 */ | ||
1567 | 0x00000000, /* Vol Control A4 */ | ||
1568 | 0x00000000 /* Vol Control B4 */ | ||
1569 | }; | ||
1570 | |||
1571 | /* Set DAC reset bit as output */ | ||
1572 | dwData = hw_read_20kx(hw, GPIO_CTRL); | ||
1573 | dwData |= 0x02; | ||
1574 | hw_write_20kx(hw, GPIO_CTRL, dwData); | ||
1575 | |||
1576 | err = I2CInit(hw, 0x18, 1, 1); | ||
1577 | if (err < 0) | ||
1578 | goto End; | ||
1579 | |||
1580 | for (i = 0; i < 2; i++) { | ||
1581 | /* Reset DAC twice just in-case the chip | ||
1582 | * didn't initialized properly */ | ||
1583 | dwData = hw_read_20kx(hw, GPIO_DATA); | ||
1584 | /* GPIO data bit 1 */ | ||
1585 | dwData &= 0xFFFFFFFD; | ||
1586 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1587 | mdelay(10); | ||
1588 | dwData |= 0x2; | ||
1589 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1590 | mdelay(50); | ||
1591 | |||
1592 | /* Reset the 2nd time */ | ||
1593 | dwData &= 0xFFFFFFFD; | ||
1594 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1595 | mdelay(10); | ||
1596 | dwData |= 0x2; | ||
1597 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1598 | mdelay(50); | ||
1599 | |||
1600 | if (I2CRead(hw, CS4382_MC1, &cs4382_Read.dwModeControl_1)) | ||
1601 | continue; | ||
1602 | |||
1603 | if (I2CRead(hw, CS4382_MC2, &cs4382_Read.dwModeControl_2)) | ||
1604 | continue; | ||
1605 | |||
1606 | if (I2CRead(hw, CS4382_MC3, &cs4382_Read.dwModeControl_3)) | ||
1607 | continue; | ||
1608 | |||
1609 | if (I2CRead(hw, CS4382_FC, &cs4382_Read.dwFilterControl)) | ||
1610 | continue; | ||
1611 | |||
1612 | if (I2CRead(hw, CS4382_IC, &cs4382_Read.dwInvertControl)) | ||
1613 | continue; | ||
1614 | |||
1615 | if (I2CRead(hw, CS4382_XC1, &cs4382_Read.dwMixControl_P1)) | ||
1616 | continue; | ||
1617 | |||
1618 | if (I2CRead(hw, CS4382_VCA1, &cs4382_Read.dwVolControl_A1)) | ||
1619 | continue; | ||
1620 | |||
1621 | if (I2CRead(hw, CS4382_VCB1, &cs4382_Read.dwVolControl_B1)) | ||
1622 | continue; | ||
1623 | |||
1624 | if (I2CRead(hw, CS4382_XC2, &cs4382_Read.dwMixControl_P2)) | ||
1625 | continue; | ||
1626 | |||
1627 | if (I2CRead(hw, CS4382_VCA2, &cs4382_Read.dwVolControl_A2)) | ||
1628 | continue; | ||
1629 | |||
1630 | if (I2CRead(hw, CS4382_VCB2, &cs4382_Read.dwVolControl_B2)) | ||
1631 | continue; | ||
1632 | |||
1633 | if (I2CRead(hw, CS4382_XC3, &cs4382_Read.dwMixControl_P3)) | ||
1634 | continue; | ||
1635 | |||
1636 | if (I2CRead(hw, CS4382_VCA3, &cs4382_Read.dwVolControl_A3)) | ||
1637 | continue; | ||
1638 | |||
1639 | if (I2CRead(hw, CS4382_VCB3, &cs4382_Read.dwVolControl_B3)) | ||
1640 | continue; | ||
1641 | |||
1642 | if (I2CRead(hw, CS4382_XC4, &cs4382_Read.dwMixControl_P4)) | ||
1643 | continue; | ||
1644 | |||
1645 | if (I2CRead(hw, CS4382_VCA4, &cs4382_Read.dwVolControl_A4)) | ||
1646 | continue; | ||
1647 | |||
1648 | if (I2CRead(hw, CS4382_VCB4, &cs4382_Read.dwVolControl_B4)) | ||
1649 | continue; | ||
1650 | |||
1651 | if (memcmp(&cs4382_Read, &cs4382_Def, | ||
1652 | sizeof(struct REGS_CS4382))) | ||
1653 | continue; | ||
1654 | else | ||
1655 | break; | ||
1656 | } | ||
1657 | |||
1658 | if (i >= 2) | ||
1659 | goto End; | ||
1660 | |||
1661 | /* Note: Every I2C write must have some delay. | ||
1662 | * This is not a requirement but the delay works here... */ | ||
1663 | I2CWrite(hw, CS4382_MC1, 0x80); | ||
1664 | I2CWrite(hw, CS4382_MC2, 0x10); | ||
1665 | if (1 == info->msr) { | ||
1666 | I2CWrite(hw, CS4382_XC1, 0x24); | ||
1667 | I2CWrite(hw, CS4382_XC2, 0x24); | ||
1668 | I2CWrite(hw, CS4382_XC3, 0x24); | ||
1669 | I2CWrite(hw, CS4382_XC4, 0x24); | ||
1670 | } else if (2 == info->msr) { | ||
1671 | I2CWrite(hw, CS4382_XC1, 0x25); | ||
1672 | I2CWrite(hw, CS4382_XC2, 0x25); | ||
1673 | I2CWrite(hw, CS4382_XC3, 0x25); | ||
1674 | I2CWrite(hw, CS4382_XC4, 0x25); | ||
1675 | } else { | ||
1676 | I2CWrite(hw, CS4382_XC1, 0x26); | ||
1677 | I2CWrite(hw, CS4382_XC2, 0x26); | ||
1678 | I2CWrite(hw, CS4382_XC3, 0x26); | ||
1679 | I2CWrite(hw, CS4382_XC4, 0x26); | ||
1680 | } | ||
1681 | |||
1682 | return 0; | ||
1683 | End: | ||
1684 | |||
1685 | I2CUninit(hw); | ||
1686 | return -1; | ||
1687 | } | ||
1688 | |||
1689 | /* ADC operations */ | ||
1690 | #define MAKE_WM8775_ADDR(addr, data) (u32)(((addr<<1)&0xFE)|((data>>8)&0x1)) | ||
1691 | #define MAKE_WM8775_DATA(data) (u32)(data&0xFF) | ||
1692 | |||
1693 | #define WM8775_IC 0x0B | ||
1694 | #define WM8775_MMC 0x0C | ||
1695 | #define WM8775_AADCL 0x0E | ||
1696 | #define WM8775_AADCR 0x0F | ||
1697 | #define WM8775_ADCMC 0x15 | ||
1698 | #define WM8775_RESET 0x17 | ||
1699 | |||
1700 | static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) | ||
1701 | { | ||
1702 | u32 data = 0; | ||
1703 | |||
1704 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1705 | switch (type) { | ||
1706 | case ADC_MICIN: | ||
1707 | data = (data & (0x1 << 14)) ? 1 : 0; | ||
1708 | break; | ||
1709 | case ADC_LINEIN: | ||
1710 | data = (data & (0x1 << 14)) ? 0 : 1; | ||
1711 | break; | ||
1712 | default: | ||
1713 | data = 0; | ||
1714 | } | ||
1715 | return data; | ||
1716 | } | ||
1717 | |||
1718 | static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) | ||
1719 | { | ||
1720 | u32 data = 0; | ||
1721 | |||
1722 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1723 | switch (type) { | ||
1724 | case ADC_MICIN: | ||
1725 | data |= (0x1 << 14); | ||
1726 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1727 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), | ||
1728 | MAKE_WM8775_DATA(0x101)); /* Mic-in */ | ||
1729 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), | ||
1730 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1731 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), | ||
1732 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1733 | break; | ||
1734 | case ADC_LINEIN: | ||
1735 | data &= ~(0x1 << 14); | ||
1736 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1737 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), | ||
1738 | MAKE_WM8775_DATA(0x102)); /* Line-in */ | ||
1739 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), | ||
1740 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1741 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), | ||
1742 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1743 | break; | ||
1744 | default: | ||
1745 | break; | ||
1746 | } | ||
1747 | |||
1748 | return 0; | ||
1749 | } | ||
1750 | |||
1751 | static int hw_adc_init(struct hw *hw, const struct adc_conf *info) | ||
1752 | { | ||
1753 | int err = 0; | ||
1754 | u32 dwMux = 2, dwData = 0, dwCtl = 0; | ||
1755 | |||
1756 | /* Set ADC reset bit as output */ | ||
1757 | dwData = hw_read_20kx(hw, GPIO_CTRL); | ||
1758 | dwData |= (0x1 << 15); | ||
1759 | hw_write_20kx(hw, GPIO_CTRL, dwData); | ||
1760 | |||
1761 | /* Initialize I2C */ | ||
1762 | err = I2CInit(hw, 0x1A, 1, 1); | ||
1763 | if (err < 0) { | ||
1764 | printk(KERN_ALERT "Failure to acquire I2C!!!\n"); | ||
1765 | goto error; | ||
1766 | } | ||
1767 | |||
1768 | /* Make ADC in normal operation */ | ||
1769 | dwData = hw_read_20kx(hw, GPIO_DATA); | ||
1770 | dwData &= ~(0x1 << 15); | ||
1771 | mdelay(10); | ||
1772 | dwData |= (0x1 << 15); | ||
1773 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1774 | mdelay(50); | ||
1775 | |||
1776 | /* Set the master mode (256fs) */ | ||
1777 | if (1 == info->msr) { | ||
1778 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), | ||
1779 | MAKE_WM8775_DATA(0x02)); | ||
1780 | } else if (2 == info->msr) { | ||
1781 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), | ||
1782 | MAKE_WM8775_DATA(0x0A)); | ||
1783 | } else { | ||
1784 | printk(KERN_ALERT "Invalid master sampling " | ||
1785 | "rate (msr %d)!!!\n", info->msr); | ||
1786 | err = -EINVAL; | ||
1787 | goto error; | ||
1788 | } | ||
1789 | |||
1790 | /* Configure GPIO bit 14 change to line-in/mic-in */ | ||
1791 | dwCtl = hw_read_20kx(hw, GPIO_CTRL); | ||
1792 | dwCtl |= 0x1<<14; | ||
1793 | hw_write_20kx(hw, GPIO_CTRL, dwCtl); | ||
1794 | |||
1795 | /* Check using Mic-in or Line-in */ | ||
1796 | dwData = hw_read_20kx(hw, GPIO_DATA); | ||
1797 | |||
1798 | if (dwMux == 1) { | ||
1799 | /* Configures GPIO data to select Mic-in */ | ||
1800 | dwData |= 0x1<<14; | ||
1801 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1802 | |||
1803 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), | ||
1804 | MAKE_WM8775_DATA(0x101)); /* Mic-in */ | ||
1805 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), | ||
1806 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1807 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), | ||
1808 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1809 | } else if (dwMux == 2) { | ||
1810 | /* Configures GPIO data to select Line-in */ | ||
1811 | dwData &= ~(0x1<<14); | ||
1812 | hw_write_20kx(hw, GPIO_DATA, dwData); | ||
1813 | |||
1814 | /* Setup ADC */ | ||
1815 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), | ||
1816 | MAKE_WM8775_DATA(0x102)); /* Line-in */ | ||
1817 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), | ||
1818 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1819 | I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), | ||
1820 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1821 | } else { | ||
1822 | printk(KERN_ALERT "ERROR!!! Invalid input mux!!!\n"); | ||
1823 | err = -EINVAL; | ||
1824 | goto error; | ||
1825 | } | ||
1826 | |||
1827 | return 0; | ||
1828 | |||
1829 | error: | ||
1830 | I2CUninit(hw); | ||
1831 | return err; | ||
1832 | } | ||
1833 | |||
1834 | static int hw_have_digit_io_switch(struct hw *hw) | ||
1835 | { | ||
1836 | return 0; | ||
1837 | } | ||
1838 | |||
1839 | static int hw_card_start(struct hw *hw) | ||
1840 | { | ||
1841 | int err = 0; | ||
1842 | struct pci_dev *pci = hw->pci; | ||
1843 | unsigned int gctl; | ||
1844 | unsigned int dma_mask = 0; | ||
1845 | |||
1846 | err = pci_enable_device(pci); | ||
1847 | if (err < 0) | ||
1848 | return err; | ||
1849 | |||
1850 | /* Set DMA transfer mask */ | ||
1851 | dma_mask = CT_XFI_DMA_MASK; | ||
1852 | if (pci_set_dma_mask(pci, dma_mask) < 0 || | ||
1853 | pci_set_consistent_dma_mask(pci, dma_mask) < 0) { | ||
1854 | printk(KERN_ERR "architecture does not support PCI " | ||
1855 | "busmaster DMA with mask 0x%x\n", dma_mask); | ||
1856 | err = -ENXIO; | ||
1857 | goto error1; | ||
1858 | } | ||
1859 | |||
1860 | err = pci_request_regions(pci, "XFi"); | ||
1861 | if (err < 0) | ||
1862 | goto error1; | ||
1863 | |||
1864 | hw->io_base = pci_resource_start(hw->pci, 2); | ||
1865 | hw->mem_base = (unsigned long)ioremap(hw->io_base, | ||
1866 | pci_resource_len(hw->pci, 2)); | ||
1867 | if (NULL == (void *)hw->mem_base) { | ||
1868 | err = -ENOENT; | ||
1869 | goto error2; | ||
1870 | } | ||
1871 | |||
1872 | /* Switch to 20k2 mode from UAA mode. */ | ||
1873 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1874 | set_field(&gctl, GCTL_UAA, 0); | ||
1875 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1876 | |||
1877 | /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, | ||
1878 | atc->chip_details->nm_card, hw))) { | ||
1879 | goto error3; | ||
1880 | } | ||
1881 | hw->irq = pci->irq; | ||
1882 | */ | ||
1883 | |||
1884 | pci_set_master(pci); | ||
1885 | |||
1886 | return 0; | ||
1887 | |||
1888 | /*error3: | ||
1889 | iounmap((void *)hw->mem_base); | ||
1890 | hw->mem_base = (unsigned long)NULL;*/ | ||
1891 | error2: | ||
1892 | pci_release_regions(pci); | ||
1893 | hw->io_base = 0; | ||
1894 | error1: | ||
1895 | pci_disable_device(pci); | ||
1896 | return err; | ||
1897 | } | ||
1898 | |||
1899 | static int hw_card_stop(struct hw *hw) | ||
1900 | { | ||
1901 | /* TODO: Disable interrupt and so on... */ | ||
1902 | return 0; | ||
1903 | } | ||
1904 | |||
1905 | static int hw_card_shutdown(struct hw *hw) | ||
1906 | { | ||
1907 | if (hw->irq >= 0) | ||
1908 | free_irq(hw->irq, hw); | ||
1909 | |||
1910 | hw->irq = -1; | ||
1911 | |||
1912 | if (NULL != ((void *)hw->mem_base)) | ||
1913 | iounmap((void *)hw->mem_base); | ||
1914 | |||
1915 | hw->mem_base = (unsigned long)NULL; | ||
1916 | |||
1917 | if (hw->io_base) | ||
1918 | pci_release_regions(hw->pci); | ||
1919 | |||
1920 | hw->io_base = 0; | ||
1921 | |||
1922 | pci_disable_device(hw->pci); | ||
1923 | |||
1924 | return 0; | ||
1925 | } | ||
1926 | |||
1927 | static int hw_card_init(struct hw *hw, struct card_conf *info) | ||
1928 | { | ||
1929 | int err; | ||
1930 | unsigned int gctl; | ||
1931 | u32 data = 0; | ||
1932 | struct dac_conf dac_info = {0}; | ||
1933 | struct adc_conf adc_info = {0}; | ||
1934 | struct daio_conf daio_info = {0}; | ||
1935 | struct trn_conf trn_info = {0}; | ||
1936 | |||
1937 | /* Get PCI io port/memory base address and | ||
1938 | * do 20kx core switch if needed. */ | ||
1939 | if (!hw->io_base) { | ||
1940 | err = hw_card_start(hw); | ||
1941 | if (err) | ||
1942 | return err; | ||
1943 | } | ||
1944 | |||
1945 | /* PLL init */ | ||
1946 | err = hw_pll_init(hw, info->rsr); | ||
1947 | if (err < 0) | ||
1948 | return err; | ||
1949 | |||
1950 | /* kick off auto-init */ | ||
1951 | err = hw_auto_init(hw); | ||
1952 | if (err < 0) | ||
1953 | return err; | ||
1954 | |||
1955 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1956 | set_field(&gctl, GCTL_DBP, 1); | ||
1957 | set_field(&gctl, GCTL_TBP, 1); | ||
1958 | set_field(&gctl, GCTL_FBP, 1); | ||
1959 | set_field(&gctl, GCTL_DPC, 0); | ||
1960 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1961 | |||
1962 | /* Reset all global pending interrupts */ | ||
1963 | hw_write_20kx(hw, INTERRUPT_GIE, 0); | ||
1964 | /* Reset all SRC pending interrupts */ | ||
1965 | hw_write_20kx(hw, SRC_IP, 0); | ||
1966 | |||
1967 | /* TODO: detect the card ID and configure GPIO accordingly. */ | ||
1968 | /* Configures GPIO (0xD802 0x98028) */ | ||
1969 | /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ | ||
1970 | /* Configures GPIO (SB0880) */ | ||
1971 | /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ | ||
1972 | hw_write_20kx(hw, GPIO_CTRL, 0xD802); | ||
1973 | |||
1974 | /* Enable audio ring */ | ||
1975 | hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); | ||
1976 | |||
1977 | trn_info.vm_pgt_phys = info->vm_pgt_phys; | ||
1978 | err = hw_trn_init(hw, &trn_info); | ||
1979 | if (err < 0) | ||
1980 | return err; | ||
1981 | |||
1982 | daio_info.msr = info->msr; | ||
1983 | err = hw_daio_init(hw, &daio_info); | ||
1984 | if (err < 0) | ||
1985 | return err; | ||
1986 | |||
1987 | dac_info.msr = info->msr; | ||
1988 | err = hw_dac_init(hw, &dac_info); | ||
1989 | if (err < 0) | ||
1990 | return err; | ||
1991 | |||
1992 | adc_info.msr = info->msr; | ||
1993 | adc_info.input = ADC_LINEIN; | ||
1994 | adc_info.mic20db = 0; | ||
1995 | err = hw_adc_init(hw, &adc_info); | ||
1996 | if (err < 0) | ||
1997 | return err; | ||
1998 | |||
1999 | data = hw_read_20kx(hw, SRC_MCTL); | ||
2000 | data |= 0x1; /* Enables input from the audio ring */ | ||
2001 | hw_write_20kx(hw, SRC_MCTL, data); | ||
2002 | |||
2003 | return 0; | ||
2004 | } | ||
2005 | |||
2006 | static u32 hw_read_20kx(struct hw *hw, u32 reg) | ||
2007 | { | ||
2008 | return readl((void *)(hw->mem_base + reg)); | ||
2009 | } | ||
2010 | |||
2011 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) | ||
2012 | { | ||
2013 | writel(data, (void *)(hw->mem_base + reg)); | ||
2014 | } | ||
2015 | |||
2016 | int create_20k2_hw_obj(struct hw **rhw) | ||
2017 | { | ||
2018 | struct hw *hw; | ||
2019 | |||
2020 | *rhw = NULL; | ||
2021 | hw = kzalloc(sizeof(*hw), GFP_KERNEL); | ||
2022 | if (NULL == hw) | ||
2023 | return -ENOMEM; | ||
2024 | |||
2025 | hw->io_base = 0; | ||
2026 | hw->mem_base = (unsigned long)NULL; | ||
2027 | hw->irq = -1; | ||
2028 | |||
2029 | hw->card_init = hw_card_init; | ||
2030 | hw->card_stop = hw_card_stop; | ||
2031 | hw->pll_init = hw_pll_init; | ||
2032 | hw->is_adc_source_selected = hw_is_adc_input_selected; | ||
2033 | hw->select_adc_source = hw_adc_input_select; | ||
2034 | hw->have_digit_io_switch = hw_have_digit_io_switch; | ||
2035 | |||
2036 | hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; | ||
2037 | hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; | ||
2038 | hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; | ||
2039 | hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; | ||
2040 | hw->src_set_state = src_set_state; | ||
2041 | hw->src_set_bm = src_set_bm; | ||
2042 | hw->src_set_rsr = src_set_rsr; | ||
2043 | hw->src_set_sf = src_set_sf; | ||
2044 | hw->src_set_wr = src_set_wr; | ||
2045 | hw->src_set_pm = src_set_pm; | ||
2046 | hw->src_set_rom = src_set_rom; | ||
2047 | hw->src_set_vo = src_set_vo; | ||
2048 | hw->src_set_st = src_set_st; | ||
2049 | hw->src_set_ie = src_set_ie; | ||
2050 | hw->src_set_ilsz = src_set_ilsz; | ||
2051 | hw->src_set_bp = src_set_bp; | ||
2052 | hw->src_set_cisz = src_set_cisz; | ||
2053 | hw->src_set_ca = src_set_ca; | ||
2054 | hw->src_set_sa = src_set_sa; | ||
2055 | hw->src_set_la = src_set_la; | ||
2056 | hw->src_set_pitch = src_set_pitch; | ||
2057 | hw->src_set_dirty = src_set_dirty; | ||
2058 | hw->src_set_clear_zbufs = src_set_clear_zbufs; | ||
2059 | hw->src_set_dirty_all = src_set_dirty_all; | ||
2060 | hw->src_commit_write = src_commit_write; | ||
2061 | hw->src_get_ca = src_get_ca; | ||
2062 | hw->src_get_dirty = src_get_dirty; | ||
2063 | hw->src_dirty_conj_mask = src_dirty_conj_mask; | ||
2064 | hw->src_mgr_enbs_src = src_mgr_enbs_src; | ||
2065 | hw->src_mgr_enb_src = src_mgr_enb_src; | ||
2066 | hw->src_mgr_dsb_src = src_mgr_dsb_src; | ||
2067 | hw->src_mgr_commit_write = src_mgr_commit_write; | ||
2068 | |||
2069 | hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; | ||
2070 | hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; | ||
2071 | hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; | ||
2072 | hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; | ||
2073 | hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; | ||
2074 | hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; | ||
2075 | hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; | ||
2076 | |||
2077 | hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; | ||
2078 | hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; | ||
2079 | hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; | ||
2080 | hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; | ||
2081 | hw->amixer_set_mode = amixer_set_mode; | ||
2082 | hw->amixer_set_iv = amixer_set_iv; | ||
2083 | hw->amixer_set_x = amixer_set_x; | ||
2084 | hw->amixer_set_y = amixer_set_y; | ||
2085 | hw->amixer_set_sadr = amixer_set_sadr; | ||
2086 | hw->amixer_set_se = amixer_set_se; | ||
2087 | hw->amixer_set_dirty = amixer_set_dirty; | ||
2088 | hw->amixer_set_dirty_all = amixer_set_dirty_all; | ||
2089 | hw->amixer_commit_write = amixer_commit_write; | ||
2090 | hw->amixer_get_y = amixer_get_y; | ||
2091 | hw->amixer_get_dirty = amixer_get_dirty; | ||
2092 | |||
2093 | hw->dai_get_ctrl_blk = dai_get_ctrl_blk; | ||
2094 | hw->dai_put_ctrl_blk = dai_put_ctrl_blk; | ||
2095 | hw->dai_srt_set_srco = dai_srt_set_srco; | ||
2096 | hw->dai_srt_set_srcm = dai_srt_set_srcm; | ||
2097 | hw->dai_srt_set_rsr = dai_srt_set_rsr; | ||
2098 | hw->dai_srt_set_drat = dai_srt_set_drat; | ||
2099 | hw->dai_srt_set_ec = dai_srt_set_ec; | ||
2100 | hw->dai_srt_set_et = dai_srt_set_et; | ||
2101 | hw->dai_commit_write = dai_commit_write; | ||
2102 | |||
2103 | hw->dao_get_ctrl_blk = dao_get_ctrl_blk; | ||
2104 | hw->dao_put_ctrl_blk = dao_put_ctrl_blk; | ||
2105 | hw->dao_set_spos = dao_set_spos; | ||
2106 | hw->dao_commit_write = dao_commit_write; | ||
2107 | hw->dao_get_spos = dao_get_spos; | ||
2108 | |||
2109 | hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; | ||
2110 | hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; | ||
2111 | hw->daio_mgr_enb_dai = daio_mgr_enb_dai; | ||
2112 | hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; | ||
2113 | hw->daio_mgr_enb_dao = daio_mgr_enb_dao; | ||
2114 | hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; | ||
2115 | hw->daio_mgr_dao_init = daio_mgr_dao_init; | ||
2116 | hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; | ||
2117 | hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; | ||
2118 | hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; | ||
2119 | hw->daio_mgr_commit_write = daio_mgr_commit_write; | ||
2120 | |||
2121 | *rhw = hw; | ||
2122 | |||
2123 | return 0; | ||
2124 | } | ||
2125 | |||
2126 | int destroy_20k2_hw_obj(struct hw *hw) | ||
2127 | { | ||
2128 | if (hw->io_base) | ||
2129 | hw_card_shutdown(hw); | ||
2130 | |||
2131 | kfree(hw); | ||
2132 | return 0; | ||
2133 | } | ||
diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h new file mode 100644 index 000000000000..d2b7daab6815 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File cthw20k2.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of hardware access methord. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date May 13 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef CTHW20K2_H | ||
19 | #define CTHW20K2_H | ||
20 | |||
21 | #include "cthardware.h" | ||
22 | |||
23 | int create_20k2_hw_obj(struct hw **rhw); | ||
24 | int destroy_20k2_hw_obj(struct hw *hw); | ||
25 | |||
26 | #endif /* CTHW20K2_H */ | ||
diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c new file mode 100644 index 000000000000..d34eacd902ce --- /dev/null +++ b/sound/pci/ctxfi/ctimap.c | |||
@@ -0,0 +1,112 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctimap.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of generic input mapper operations | ||
12 | * for input mapper management. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 23 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include "ctimap.h" | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | int input_mapper_add(struct list_head *mappers, struct imapper *entry, | ||
23 | int (*map_op)(void *, struct imapper *), void *data) | ||
24 | { | ||
25 | struct list_head *pos, *pre, *head; | ||
26 | struct imapper *pre_ent, *pos_ent; | ||
27 | |||
28 | head = mappers; | ||
29 | |||
30 | if (list_empty(head)) { | ||
31 | entry->next = entry->addr; | ||
32 | map_op(data, entry); | ||
33 | list_add(&entry->list, head); | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | list_for_each(pos, head) { | ||
38 | pos_ent = list_entry(pos, struct imapper, list); | ||
39 | if (pos_ent->slot > entry->slot) { | ||
40 | /* found a position in list */ | ||
41 | break; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | if (pos != head) { | ||
46 | pre = pos->prev; | ||
47 | if (pre == head) | ||
48 | pre = head->prev; | ||
49 | |||
50 | __list_add(&entry->list, pos->prev, pos); | ||
51 | } else { | ||
52 | pre = head->prev; | ||
53 | pos = head->next; | ||
54 | list_add_tail(&entry->list, head); | ||
55 | } | ||
56 | |||
57 | pre_ent = list_entry(pre, struct imapper, list); | ||
58 | pos_ent = list_entry(pos, struct imapper, list); | ||
59 | |||
60 | entry->next = pos_ent->addr; | ||
61 | map_op(data, entry); | ||
62 | pre_ent->next = entry->addr; | ||
63 | map_op(data, pre_ent); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | int input_mapper_delete(struct list_head *mappers, struct imapper *entry, | ||
69 | int (*map_op)(void *, struct imapper *), void *data) | ||
70 | { | ||
71 | struct list_head *next, *pre, *head; | ||
72 | struct imapper *pre_ent, *next_ent; | ||
73 | |||
74 | head = mappers; | ||
75 | |||
76 | if (list_empty(head)) | ||
77 | return 0; | ||
78 | |||
79 | pre = (entry->list.prev == head) ? head->prev : entry->list.prev; | ||
80 | next = (entry->list.next == head) ? head->next : entry->list.next; | ||
81 | |||
82 | if (pre == &entry->list) { | ||
83 | /* entry is the only one node in mappers list */ | ||
84 | entry->next = entry->addr = entry->user = entry->slot = 0; | ||
85 | map_op(data, entry); | ||
86 | list_del(&entry->list); | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | pre_ent = list_entry(pre, struct imapper, list); | ||
91 | next_ent = list_entry(next, struct imapper, list); | ||
92 | |||
93 | pre_ent->next = next_ent->addr; | ||
94 | map_op(data, pre_ent); | ||
95 | list_del(&entry->list); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | void free_input_mapper_list(struct list_head *head) | ||
101 | { | ||
102 | struct imapper *entry = NULL; | ||
103 | struct list_head *pos = NULL; | ||
104 | |||
105 | while (!list_empty(head)) { | ||
106 | pos = head->next; | ||
107 | list_del(pos); | ||
108 | entry = list_entry(pos, struct imapper, list); | ||
109 | kfree(entry); | ||
110 | } | ||
111 | } | ||
112 | |||
diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h new file mode 100644 index 000000000000..53ccf9be8b68 --- /dev/null +++ b/sound/pci/ctxfi/ctimap.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctimap.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of generic input mapper operations | ||
12 | * for input mapper management. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 23 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef CTIMAP_H | ||
20 | #define CTIMAP_H | ||
21 | |||
22 | #include <linux/list.h> | ||
23 | |||
24 | struct imapper { | ||
25 | unsigned short slot; /* the id of the slot containing input data */ | ||
26 | unsigned short user; /* the id of the user resource consuming data */ | ||
27 | unsigned short addr; /* the input mapper ram id */ | ||
28 | unsigned short next; /* the next input mapper ram id */ | ||
29 | struct list_head list; | ||
30 | }; | ||
31 | |||
32 | int input_mapper_add(struct list_head *mappers, struct imapper *entry, | ||
33 | int (*map_op)(void *, struct imapper *), void *data); | ||
34 | |||
35 | int input_mapper_delete(struct list_head *mappers, struct imapper *entry, | ||
36 | int (*map_op)(void *, struct imapper *), void *data); | ||
37 | |||
38 | void free_input_mapper_list(struct list_head *mappers); | ||
39 | |||
40 | #endif /* CTIMAP_H */ | ||
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c new file mode 100644 index 000000000000..c80d692952f9 --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.c | |||
@@ -0,0 +1,1108 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctmixer.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of alsa mixer device functions. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date May 28 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | |||
19 | #include "ctmixer.h" | ||
20 | #include "ctamixer.h" | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/control.h> | ||
23 | #include <sound/asoundef.h> | ||
24 | #include <sound/pcm.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | enum CT_SUM_CTL { | ||
28 | SUM_IN_F, | ||
29 | SUM_IN_R, | ||
30 | SUM_IN_C, | ||
31 | SUM_IN_S, | ||
32 | SUM_IN_F_C, | ||
33 | |||
34 | NUM_CT_SUMS | ||
35 | }; | ||
36 | |||
37 | enum CT_AMIXER_CTL { | ||
38 | /* volume control mixers */ | ||
39 | AMIXER_MASTER_F, | ||
40 | AMIXER_MASTER_R, | ||
41 | AMIXER_MASTER_C, | ||
42 | AMIXER_MASTER_S, | ||
43 | AMIXER_PCM_F, | ||
44 | AMIXER_PCM_R, | ||
45 | AMIXER_PCM_C, | ||
46 | AMIXER_PCM_S, | ||
47 | AMIXER_SPDIFI, | ||
48 | AMIXER_LINEIN, | ||
49 | AMIXER_MIC, | ||
50 | AMIXER_SPDIFO, | ||
51 | AMIXER_WAVE_F, | ||
52 | AMIXER_WAVE_R, | ||
53 | AMIXER_WAVE_C, | ||
54 | AMIXER_WAVE_S, | ||
55 | AMIXER_MASTER_F_C, | ||
56 | AMIXER_PCM_F_C, | ||
57 | AMIXER_SPDIFI_C, | ||
58 | AMIXER_LINEIN_C, | ||
59 | AMIXER_MIC_C, | ||
60 | |||
61 | /* this should always be the last one */ | ||
62 | NUM_CT_AMIXERS | ||
63 | }; | ||
64 | |||
65 | enum CTALSA_MIXER_CTL { | ||
66 | /* volume control mixers */ | ||
67 | MIXER_MASTER_P, | ||
68 | MIXER_PCM_P, | ||
69 | MIXER_LINEIN_P, | ||
70 | MIXER_MIC_P, | ||
71 | MIXER_SPDIFI_P, | ||
72 | MIXER_SPDIFO_P, | ||
73 | MIXER_WAVEF_P, | ||
74 | MIXER_WAVER_P, | ||
75 | MIXER_WAVEC_P, | ||
76 | MIXER_WAVES_P, | ||
77 | MIXER_MASTER_C, | ||
78 | MIXER_PCM_C, | ||
79 | MIXER_LINEIN_C, | ||
80 | MIXER_MIC_C, | ||
81 | MIXER_SPDIFI_C, | ||
82 | |||
83 | /* switch control mixers */ | ||
84 | MIXER_PCM_C_S, | ||
85 | MIXER_LINEIN_C_S, | ||
86 | MIXER_MIC_C_S, | ||
87 | MIXER_SPDIFI_C_S, | ||
88 | MIXER_LINEIN_P_S, | ||
89 | MIXER_SPDIFO_P_S, | ||
90 | MIXER_SPDIFI_P_S, | ||
91 | MIXER_WAVEF_P_S, | ||
92 | MIXER_WAVER_P_S, | ||
93 | MIXER_WAVEC_P_S, | ||
94 | MIXER_WAVES_P_S, | ||
95 | MIXER_DIGITAL_IO_S, | ||
96 | MIXER_IEC958_MASK, | ||
97 | MIXER_IEC958_DEFAULT, | ||
98 | MIXER_IEC958_STREAM, | ||
99 | |||
100 | /* this should always be the last one */ | ||
101 | NUM_CTALSA_MIXERS | ||
102 | }; | ||
103 | |||
104 | #define VOL_MIXER_START MIXER_MASTER_P | ||
105 | #define VOL_MIXER_END MIXER_SPDIFI_C | ||
106 | #define VOL_MIXER_NUM (VOL_MIXER_END - VOL_MIXER_START + 1) | ||
107 | #define SWH_MIXER_START MIXER_PCM_C_S | ||
108 | #define SWH_MIXER_END MIXER_DIGITAL_IO_S | ||
109 | #define SWH_CAPTURE_START MIXER_PCM_C_S | ||
110 | #define SWH_CAPTURE_END MIXER_SPDIFI_C_S | ||
111 | |||
112 | #define CHN_NUM 2 | ||
113 | |||
114 | struct ct_kcontrol_init { | ||
115 | unsigned char ctl; | ||
116 | char *name; | ||
117 | }; | ||
118 | |||
119 | static struct ct_kcontrol_init | ||
120 | ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { | ||
121 | [MIXER_MASTER_P] = { | ||
122 | .ctl = 1, | ||
123 | .name = "Master Playback Volume", | ||
124 | }, | ||
125 | [MIXER_MASTER_C] = { | ||
126 | .ctl = 1, | ||
127 | .name = "Master Capture Volume", | ||
128 | }, | ||
129 | [MIXER_PCM_P] = { | ||
130 | .ctl = 1, | ||
131 | .name = "PCM Playback Volume", | ||
132 | }, | ||
133 | [MIXER_PCM_C] = { | ||
134 | .ctl = 1, | ||
135 | .name = "PCM Capture Volume", | ||
136 | }, | ||
137 | [MIXER_LINEIN_P] = { | ||
138 | .ctl = 1, | ||
139 | .name = "Line-in Playback Volume", | ||
140 | }, | ||
141 | [MIXER_LINEIN_C] = { | ||
142 | .ctl = 1, | ||
143 | .name = "Line-in Capture Volume", | ||
144 | }, | ||
145 | [MIXER_MIC_P] = { | ||
146 | .ctl = 1, | ||
147 | .name = "Mic Playback Volume", | ||
148 | }, | ||
149 | [MIXER_MIC_C] = { | ||
150 | .ctl = 1, | ||
151 | .name = "Mic Capture Volume", | ||
152 | }, | ||
153 | [MIXER_SPDIFI_P] = { | ||
154 | .ctl = 1, | ||
155 | .name = "S/PDIF-in Playback Volume", | ||
156 | }, | ||
157 | [MIXER_SPDIFI_C] = { | ||
158 | .ctl = 1, | ||
159 | .name = "S/PDIF-in Capture Volume", | ||
160 | }, | ||
161 | [MIXER_SPDIFO_P] = { | ||
162 | .ctl = 1, | ||
163 | .name = "S/PDIF-out Playback Volume", | ||
164 | }, | ||
165 | [MIXER_WAVEF_P] = { | ||
166 | .ctl = 1, | ||
167 | .name = "Front Playback Volume", | ||
168 | }, | ||
169 | [MIXER_WAVES_P] = { | ||
170 | .ctl = 1, | ||
171 | .name = "Surround Playback Volume", | ||
172 | }, | ||
173 | [MIXER_WAVEC_P] = { | ||
174 | .ctl = 1, | ||
175 | .name = "Center/LFE Playback Volume", | ||
176 | }, | ||
177 | [MIXER_WAVER_P] = { | ||
178 | .ctl = 1, | ||
179 | .name = "Rear Playback Volume", | ||
180 | }, | ||
181 | |||
182 | [MIXER_PCM_C_S] = { | ||
183 | .ctl = 1, | ||
184 | .name = "PCM Capture Switch", | ||
185 | }, | ||
186 | [MIXER_LINEIN_C_S] = { | ||
187 | .ctl = 1, | ||
188 | .name = "Line-in Capture Switch", | ||
189 | }, | ||
190 | [MIXER_MIC_C_S] = { | ||
191 | .ctl = 1, | ||
192 | .name = "Mic Capture Switch", | ||
193 | }, | ||
194 | [MIXER_SPDIFI_C_S] = { | ||
195 | .ctl = 1, | ||
196 | .name = "S/PDIF-in Capture Switch", | ||
197 | }, | ||
198 | [MIXER_LINEIN_P_S] = { | ||
199 | .ctl = 1, | ||
200 | .name = "Line-in Playback Switch", | ||
201 | }, | ||
202 | [MIXER_SPDIFO_P_S] = { | ||
203 | .ctl = 1, | ||
204 | .name = "S/PDIF-out Playback Switch", | ||
205 | }, | ||
206 | [MIXER_SPDIFI_P_S] = { | ||
207 | .ctl = 1, | ||
208 | .name = "S/PDIF-in Playback Switch", | ||
209 | }, | ||
210 | [MIXER_WAVEF_P_S] = { | ||
211 | .ctl = 1, | ||
212 | .name = "Front Playback Switch", | ||
213 | }, | ||
214 | [MIXER_WAVES_P_S] = { | ||
215 | .ctl = 1, | ||
216 | .name = "Surround Playback Switch", | ||
217 | }, | ||
218 | [MIXER_WAVEC_P_S] = { | ||
219 | .ctl = 1, | ||
220 | .name = "Center/LFE Playback Switch", | ||
221 | }, | ||
222 | [MIXER_WAVER_P_S] = { | ||
223 | .ctl = 1, | ||
224 | .name = "Rear Playback Switch", | ||
225 | }, | ||
226 | [MIXER_DIGITAL_IO_S] = { | ||
227 | .ctl = 0, | ||
228 | .name = "Digit-IO Playback Switch", | ||
229 | }, | ||
230 | }; | ||
231 | |||
232 | static void | ||
233 | ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); | ||
234 | |||
235 | static void | ||
236 | ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); | ||
237 | |||
238 | static struct snd_kcontrol *kctls[2] = {NULL}; | ||
239 | |||
240 | static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) | ||
241 | { | ||
242 | switch (alsa_index) { | ||
243 | case MIXER_MASTER_P: return AMIXER_MASTER_F; | ||
244 | case MIXER_MASTER_C: return AMIXER_MASTER_F_C; | ||
245 | case MIXER_PCM_P: return AMIXER_PCM_F; | ||
246 | case MIXER_PCM_C: | ||
247 | case MIXER_PCM_C_S: return AMIXER_PCM_F_C; | ||
248 | case MIXER_LINEIN_P: return AMIXER_LINEIN; | ||
249 | case MIXER_LINEIN_C: | ||
250 | case MIXER_LINEIN_C_S: return AMIXER_LINEIN_C; | ||
251 | case MIXER_MIC_P: return AMIXER_MIC; | ||
252 | case MIXER_MIC_C: | ||
253 | case MIXER_MIC_C_S: return AMIXER_MIC_C; | ||
254 | case MIXER_SPDIFI_P: return AMIXER_SPDIFI; | ||
255 | case MIXER_SPDIFI_C: | ||
256 | case MIXER_SPDIFI_C_S: return AMIXER_SPDIFI_C; | ||
257 | case MIXER_SPDIFO_P: return AMIXER_SPDIFO; | ||
258 | case MIXER_WAVEF_P: return AMIXER_WAVE_F; | ||
259 | case MIXER_WAVES_P: return AMIXER_WAVE_S; | ||
260 | case MIXER_WAVEC_P: return AMIXER_WAVE_C; | ||
261 | case MIXER_WAVER_P: return AMIXER_WAVE_R; | ||
262 | default: return NUM_CT_AMIXERS; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index) | ||
267 | { | ||
268 | switch (index) { | ||
269 | case AMIXER_MASTER_F: return AMIXER_MASTER_F_C; | ||
270 | case AMIXER_PCM_F: return AMIXER_PCM_F_C; | ||
271 | case AMIXER_SPDIFI: return AMIXER_SPDIFI_C; | ||
272 | case AMIXER_LINEIN: return AMIXER_LINEIN_C; | ||
273 | case AMIXER_MIC: return AMIXER_MIC_C; | ||
274 | default: return NUM_CT_AMIXERS; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | static unsigned char | ||
279 | get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type) | ||
280 | { | ||
281 | return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START))) | ||
282 | ? 1 : 0; | ||
283 | } | ||
284 | |||
285 | static void | ||
286 | set_switch_state(struct ct_mixer *mixer, | ||
287 | enum CTALSA_MIXER_CTL type, unsigned char state) | ||
288 | { | ||
289 | if (state) | ||
290 | mixer->switch_state |= (0x1 << (type - SWH_MIXER_START)); | ||
291 | else | ||
292 | mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START)); | ||
293 | } | ||
294 | |||
295 | /* Map integer value ranging from 0 to 65535 to 14-bit float value ranging | ||
296 | * from 2^-6 to (1+1023/1024) */ | ||
297 | static unsigned int uint16_to_float14(unsigned int x) | ||
298 | { | ||
299 | unsigned int i = 0; | ||
300 | |||
301 | if (x < 17) | ||
302 | return 0; | ||
303 | |||
304 | x *= 2031; | ||
305 | x /= 65535; | ||
306 | x += 16; | ||
307 | |||
308 | /* i <= 6 */ | ||
309 | for (i = 0; !(x & 0x400); i++) | ||
310 | x <<= 1; | ||
311 | |||
312 | x = (((7 - i) & 0x7) << 10) | (x & 0x3ff); | ||
313 | |||
314 | return x; | ||
315 | } | ||
316 | |||
317 | static unsigned int float14_to_uint16(unsigned int x) | ||
318 | { | ||
319 | unsigned int e = 0; | ||
320 | |||
321 | if (!x) | ||
322 | return x; | ||
323 | |||
324 | e = (x >> 10) & 0x7; | ||
325 | x &= 0x3ff; | ||
326 | x += 1024; | ||
327 | x >>= (7 - e); | ||
328 | x -= 16; | ||
329 | x *= 65535; | ||
330 | x /= 2031; | ||
331 | |||
332 | return x; | ||
333 | } | ||
334 | |||
335 | static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, | ||
336 | struct snd_ctl_elem_info *uinfo) | ||
337 | { | ||
338 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
339 | uinfo->count = 2; | ||
340 | uinfo->value.integer.min = 0; | ||
341 | uinfo->value.integer.max = 43690; | ||
342 | uinfo->value.integer.step = 128; | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol, | ||
348 | struct snd_ctl_elem_value *ucontrol) | ||
349 | { | ||
350 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
351 | enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); | ||
352 | struct amixer *amixer = NULL; | ||
353 | int i = 0; | ||
354 | |||
355 | for (i = 0; i < 2; i++) { | ||
356 | amixer = ((struct ct_mixer *)atc->mixer)-> | ||
357 | amixers[type*CHN_NUM+i]; | ||
358 | /* Convert 14-bit float-point scale to 16-bit integer volume */ | ||
359 | ucontrol->value.integer.value[i] = | ||
360 | (float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xffff); | ||
361 | } | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, | ||
367 | struct snd_ctl_elem_value *ucontrol) | ||
368 | { | ||
369 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
370 | struct ct_mixer *mixer = atc->mixer; | ||
371 | enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); | ||
372 | struct amixer *amixer = NULL; | ||
373 | int i = 0, j = 0, change = 0, val = 0; | ||
374 | |||
375 | for (i = 0; i < 2; i++) { | ||
376 | /* Convert 16-bit integer volume to 14-bit float-point scale */ | ||
377 | val = (ucontrol->value.integer.value[i] & 0xffff); | ||
378 | amixer = mixer->amixers[type*CHN_NUM+i]; | ||
379 | if ((float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xff80) | ||
380 | != (val & 0xff80)) { | ||
381 | val = uint16_to_float14(val); | ||
382 | amixer->ops->set_scale(amixer, val); | ||
383 | amixer->ops->commit_write(amixer); | ||
384 | change = 1; | ||
385 | /* Synchronize Master/PCM playback AMIXERs. */ | ||
386 | if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) { | ||
387 | for (j = 1; j < 4; j++) { | ||
388 | amixer = mixer-> | ||
389 | amixers[(type+j)*CHN_NUM+i]; | ||
390 | amixer->ops->set_scale(amixer, val); | ||
391 | amixer->ops->commit_write(amixer); | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | } | ||
396 | |||
397 | return change; | ||
398 | } | ||
399 | |||
400 | static struct snd_kcontrol_new vol_ctl = { | ||
401 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
402 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
403 | .info = ct_alsa_mix_volume_info, | ||
404 | .get = ct_alsa_mix_volume_get, | ||
405 | .put = ct_alsa_mix_volume_put | ||
406 | }; | ||
407 | |||
408 | static void | ||
409 | do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) | ||
410 | { | ||
411 | |||
412 | if (MIXER_LINEIN_C_S == type) { | ||
413 | atc->select_line_in(atc); | ||
414 | set_switch_state(atc->mixer, MIXER_MIC_C_S, 0); | ||
415 | snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
416 | &kctls[1]->id); | ||
417 | } else if (MIXER_MIC_C_S == type) { | ||
418 | atc->select_mic_in(atc); | ||
419 | set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0); | ||
420 | snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
421 | &kctls[0]->id); | ||
422 | } | ||
423 | } | ||
424 | |||
425 | static void | ||
426 | do_digit_io_switch(struct ct_atc *atc, int state) | ||
427 | { | ||
428 | struct ct_mixer *mixer = atc->mixer; | ||
429 | |||
430 | if (state) { | ||
431 | atc->select_digit_io(atc); | ||
432 | atc->spdif_out_unmute(atc, | ||
433 | get_switch_state(mixer, MIXER_SPDIFO_P_S)); | ||
434 | atc->spdif_in_unmute(atc, 1); | ||
435 | atc->line_in_unmute(atc, 0); | ||
436 | return; | ||
437 | } | ||
438 | |||
439 | if (get_switch_state(mixer, MIXER_LINEIN_C_S)) | ||
440 | atc->select_line_in(atc); | ||
441 | else if (get_switch_state(mixer, MIXER_MIC_C_S)) | ||
442 | atc->select_mic_in(atc); | ||
443 | |||
444 | atc->spdif_out_unmute(atc, 0); | ||
445 | atc->spdif_in_unmute(atc, 0); | ||
446 | atc->line_in_unmute(atc, 1); | ||
447 | return; | ||
448 | } | ||
449 | |||
450 | static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol, | ||
451 | struct snd_ctl_elem_info *uinfo) | ||
452 | { | ||
453 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
454 | uinfo->count = 1; | ||
455 | uinfo->value.integer.min = 0; | ||
456 | uinfo->value.integer.max = 1; | ||
457 | uinfo->value.integer.step = 1; | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol, | ||
463 | struct snd_ctl_elem_value *ucontrol) | ||
464 | { | ||
465 | struct ct_mixer *mixer = | ||
466 | ((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer; | ||
467 | enum CTALSA_MIXER_CTL type = kcontrol->private_value; | ||
468 | |||
469 | ucontrol->value.integer.value[0] = get_switch_state(mixer, type); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol, | ||
474 | struct snd_ctl_elem_value *ucontrol) | ||
475 | { | ||
476 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
477 | struct ct_mixer *mixer = atc->mixer; | ||
478 | enum CTALSA_MIXER_CTL type = kcontrol->private_value; | ||
479 | int state = 0; | ||
480 | |||
481 | state = ucontrol->value.integer.value[0]; | ||
482 | if (get_switch_state(mixer, type) == state) | ||
483 | return 0; | ||
484 | |||
485 | set_switch_state(mixer, type, state); | ||
486 | /* Do changes in mixer. */ | ||
487 | if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { | ||
488 | if (state) { | ||
489 | ct_mixer_recording_select(mixer, | ||
490 | get_amixer_index(type)); | ||
491 | } else { | ||
492 | ct_mixer_recording_unselect(mixer, | ||
493 | get_amixer_index(type)); | ||
494 | } | ||
495 | } | ||
496 | /* Do changes out of mixer. */ | ||
497 | if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) | ||
498 | do_line_mic_switch(atc, type); | ||
499 | else if (MIXER_WAVEF_P_S == type) | ||
500 | atc->line_front_unmute(atc, state); | ||
501 | else if (MIXER_WAVES_P_S == type) | ||
502 | atc->line_surround_unmute(atc, state); | ||
503 | else if (MIXER_WAVEC_P_S == type) | ||
504 | atc->line_clfe_unmute(atc, state); | ||
505 | else if (MIXER_WAVER_P_S == type) | ||
506 | atc->line_rear_unmute(atc, state); | ||
507 | else if (MIXER_LINEIN_P_S == type) | ||
508 | atc->line_in_unmute(atc, state); | ||
509 | else if (MIXER_SPDIFO_P_S == type) | ||
510 | atc->spdif_out_unmute(atc, state); | ||
511 | else if (MIXER_SPDIFI_P_S == type) | ||
512 | atc->spdif_in_unmute(atc, state); | ||
513 | else if (MIXER_DIGITAL_IO_S == type) | ||
514 | do_digit_io_switch(atc, state); | ||
515 | |||
516 | return 1; | ||
517 | } | ||
518 | |||
519 | static struct snd_kcontrol_new swh_ctl = { | ||
520 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
521 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
522 | .info = ct_alsa_mix_switch_info, | ||
523 | .get = ct_alsa_mix_switch_get, | ||
524 | .put = ct_alsa_mix_switch_put | ||
525 | }; | ||
526 | |||
527 | static int ct_spdif_info(struct snd_kcontrol *kcontrol, | ||
528 | struct snd_ctl_elem_info *uinfo) | ||
529 | { | ||
530 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
531 | uinfo->count = 1; | ||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, | ||
536 | struct snd_ctl_elem_value *ucontrol) | ||
537 | { | ||
538 | ucontrol->value.iec958.status[0] = 0xff; | ||
539 | ucontrol->value.iec958.status[1] = 0xff; | ||
540 | ucontrol->value.iec958.status[2] = 0xff; | ||
541 | ucontrol->value.iec958.status[3] = 0xff; | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, | ||
546 | struct snd_ctl_elem_value *ucontrol) | ||
547 | { | ||
548 | unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; | ||
549 | |||
550 | ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; | ||
551 | ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; | ||
552 | ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; | ||
553 | ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static int ct_spdif_get(struct snd_kcontrol *kcontrol, | ||
559 | struct snd_ctl_elem_value *ucontrol) | ||
560 | { | ||
561 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
562 | unsigned int status = 0; | ||
563 | |||
564 | atc->spdif_out_get_status(atc, &status); | ||
565 | ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; | ||
566 | ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; | ||
567 | ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; | ||
568 | ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; | ||
569 | |||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static int ct_spdif_put(struct snd_kcontrol *kcontrol, | ||
574 | struct snd_ctl_elem_value *ucontrol) | ||
575 | { | ||
576 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
577 | int change = 1; | ||
578 | unsigned int status = 0, old_status = 0; | ||
579 | |||
580 | status = (ucontrol->value.iec958.status[0] << 0) | | ||
581 | (ucontrol->value.iec958.status[1] << 8) | | ||
582 | (ucontrol->value.iec958.status[2] << 16) | | ||
583 | (ucontrol->value.iec958.status[3] << 24); | ||
584 | |||
585 | atc->spdif_out_get_status(atc, &old_status); | ||
586 | change = (old_status != status); | ||
587 | if (change) | ||
588 | atc->spdif_out_set_status(atc, status); | ||
589 | |||
590 | return change; | ||
591 | } | ||
592 | |||
593 | static struct snd_kcontrol_new iec958_mask_ctl = { | ||
594 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
595 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
596 | .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), | ||
597 | .count = 1, | ||
598 | .info = ct_spdif_info, | ||
599 | .get = ct_spdif_get_mask, | ||
600 | .private_value = MIXER_IEC958_MASK | ||
601 | }; | ||
602 | |||
603 | static struct snd_kcontrol_new iec958_default_ctl = { | ||
604 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
605 | .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), | ||
606 | .count = 1, | ||
607 | .info = ct_spdif_info, | ||
608 | .get = ct_spdif_default_get, | ||
609 | .put = ct_spdif_put, | ||
610 | .private_value = MIXER_IEC958_DEFAULT | ||
611 | }; | ||
612 | |||
613 | static struct snd_kcontrol_new iec958_ctl = { | ||
614 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
615 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
616 | .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), | ||
617 | .count = 1, | ||
618 | .info = ct_spdif_info, | ||
619 | .get = ct_spdif_get, | ||
620 | .put = ct_spdif_put, | ||
621 | .private_value = MIXER_IEC958_STREAM | ||
622 | }; | ||
623 | |||
624 | #define NUM_IEC958_CTL 3 | ||
625 | |||
626 | static int | ||
627 | ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) | ||
628 | { | ||
629 | struct snd_kcontrol *kctl = NULL; | ||
630 | int err = 0; | ||
631 | |||
632 | kctl = snd_ctl_new1(new, mixer->atc); | ||
633 | if (NULL == kctl) | ||
634 | return -ENOMEM; | ||
635 | |||
636 | if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface) | ||
637 | kctl->id.device = IEC958; | ||
638 | |||
639 | err = snd_ctl_add(mixer->atc->card, kctl); | ||
640 | if (err) | ||
641 | return err; | ||
642 | |||
643 | switch (new->private_value) { | ||
644 | case MIXER_LINEIN_C_S: | ||
645 | kctls[0] = kctl; break; | ||
646 | case MIXER_MIC_C_S: | ||
647 | kctls[1] = kctl; break; | ||
648 | default: | ||
649 | break; | ||
650 | } | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) | ||
656 | { | ||
657 | enum CTALSA_MIXER_CTL type = 0; | ||
658 | struct ct_atc *atc = mixer->atc; | ||
659 | int err = 0; | ||
660 | |||
661 | /* Create snd kcontrol instances on demand */ | ||
662 | for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) { | ||
663 | if (ct_kcontrol_init_table[type].ctl) { | ||
664 | vol_ctl.name = ct_kcontrol_init_table[type].name; | ||
665 | vol_ctl.private_value = (unsigned long)type; | ||
666 | err = ct_mixer_kcontrol_new(mixer, &vol_ctl); | ||
667 | if (err) | ||
668 | return err; | ||
669 | } | ||
670 | } | ||
671 | |||
672 | ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = | ||
673 | atc->have_digit_io_switch(atc); | ||
674 | for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { | ||
675 | if (ct_kcontrol_init_table[type].ctl) { | ||
676 | swh_ctl.name = ct_kcontrol_init_table[type].name; | ||
677 | swh_ctl.private_value = (unsigned long)type; | ||
678 | err = ct_mixer_kcontrol_new(mixer, &swh_ctl); | ||
679 | if (err) | ||
680 | return err; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl); | ||
685 | if (err) | ||
686 | return err; | ||
687 | |||
688 | err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl); | ||
689 | if (err) | ||
690 | return err; | ||
691 | |||
692 | err = ct_mixer_kcontrol_new(mixer, &iec958_ctl); | ||
693 | if (err) | ||
694 | return err; | ||
695 | |||
696 | atc->line_front_unmute(atc, 1); | ||
697 | set_switch_state(mixer, MIXER_WAVEF_P_S, 1); | ||
698 | atc->line_surround_unmute(atc, 0); | ||
699 | set_switch_state(mixer, MIXER_WAVES_P_S, 0); | ||
700 | atc->line_clfe_unmute(atc, 0); | ||
701 | set_switch_state(mixer, MIXER_WAVEC_P_S, 0); | ||
702 | atc->line_rear_unmute(atc, 0); | ||
703 | set_switch_state(mixer, MIXER_WAVER_P_S, 0); | ||
704 | atc->spdif_out_unmute(atc, 0); | ||
705 | set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); | ||
706 | atc->line_in_unmute(atc, 0); | ||
707 | set_switch_state(mixer, MIXER_LINEIN_P_S, 0); | ||
708 | atc->spdif_in_unmute(atc, 0); | ||
709 | set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); | ||
710 | |||
711 | set_switch_state(mixer, MIXER_PCM_C_S, 1); | ||
712 | set_switch_state(mixer, MIXER_LINEIN_C_S, 1); | ||
713 | set_switch_state(mixer, MIXER_SPDIFI_C_S, 1); | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | static void | ||
719 | ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) | ||
720 | { | ||
721 | struct amixer *amix_d = NULL; | ||
722 | struct sum *sum_c = NULL; | ||
723 | int i = 0; | ||
724 | |||
725 | for (i = 0; i < 2; i++) { | ||
726 | amix_d = mixer->amixers[type*CHN_NUM+i]; | ||
727 | sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i]; | ||
728 | amix_d->ops->set_sum(amix_d, sum_c); | ||
729 | amix_d->ops->commit_write(amix_d); | ||
730 | } | ||
731 | } | ||
732 | |||
733 | static void | ||
734 | ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) | ||
735 | { | ||
736 | struct amixer *amix_d = NULL; | ||
737 | int i = 0; | ||
738 | |||
739 | for (i = 0; i < 2; i++) { | ||
740 | amix_d = mixer->amixers[type*CHN_NUM+i]; | ||
741 | amix_d->ops->set_sum(amix_d, NULL); | ||
742 | amix_d->ops->commit_write(amix_d); | ||
743 | } | ||
744 | } | ||
745 | |||
746 | static int ct_mixer_get_resources(struct ct_mixer *mixer) | ||
747 | { | ||
748 | struct sum_mgr *sum_mgr = NULL; | ||
749 | struct sum *sum = NULL; | ||
750 | struct sum_desc sum_desc = {0}; | ||
751 | struct amixer_mgr *amixer_mgr = NULL; | ||
752 | struct amixer *amixer = NULL; | ||
753 | struct amixer_desc am_desc = {0}; | ||
754 | int err = 0; | ||
755 | int i = 0; | ||
756 | |||
757 | /* Allocate sum resources for mixer obj */ | ||
758 | sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; | ||
759 | sum_desc.msr = mixer->atc->msr; | ||
760 | for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { | ||
761 | err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); | ||
762 | if (err) { | ||
763 | printk(KERN_ERR "Failed to get sum resources for " | ||
764 | "front output!\n"); | ||
765 | break; | ||
766 | } | ||
767 | mixer->sums[i] = sum; | ||
768 | } | ||
769 | if (err) | ||
770 | goto error1; | ||
771 | |||
772 | /* Allocate amixer resources for mixer obj */ | ||
773 | amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; | ||
774 | am_desc.msr = mixer->atc->msr; | ||
775 | for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { | ||
776 | err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); | ||
777 | if (err) { | ||
778 | printk(KERN_ERR "Failed to get amixer resources for " | ||
779 | "mixer obj!\n"); | ||
780 | break; | ||
781 | } | ||
782 | mixer->amixers[i] = amixer; | ||
783 | } | ||
784 | if (err) | ||
785 | goto error2; | ||
786 | |||
787 | return 0; | ||
788 | |||
789 | error2: | ||
790 | for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { | ||
791 | if (NULL != mixer->amixers[i]) { | ||
792 | amixer = mixer->amixers[i]; | ||
793 | amixer_mgr->put_amixer(amixer_mgr, amixer); | ||
794 | mixer->amixers[i] = NULL; | ||
795 | } | ||
796 | } | ||
797 | error1: | ||
798 | for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { | ||
799 | if (NULL != mixer->sums[i]) { | ||
800 | sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); | ||
801 | mixer->sums[i] = NULL; | ||
802 | } | ||
803 | } | ||
804 | |||
805 | return err; | ||
806 | } | ||
807 | |||
808 | static int ct_mixer_get_mem(struct ct_mixer **rmixer) | ||
809 | { | ||
810 | struct ct_mixer *mixer = NULL; | ||
811 | int err = 0; | ||
812 | |||
813 | *rmixer = NULL; | ||
814 | /* Allocate mem for mixer obj */ | ||
815 | mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); | ||
816 | if (NULL == mixer) | ||
817 | return -ENOMEM; | ||
818 | |||
819 | mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), | ||
820 | GFP_KERNEL); | ||
821 | if (NULL == mixer->amixers) { | ||
822 | err = -ENOMEM; | ||
823 | goto error1; | ||
824 | } | ||
825 | mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); | ||
826 | if (NULL == mixer->sums) { | ||
827 | err = -ENOMEM; | ||
828 | goto error2; | ||
829 | } | ||
830 | |||
831 | *rmixer = mixer; | ||
832 | return 0; | ||
833 | |||
834 | error2: | ||
835 | kfree(mixer->amixers); | ||
836 | error1: | ||
837 | kfree(mixer); | ||
838 | return err; | ||
839 | } | ||
840 | |||
841 | static int ct_mixer_topology_build(struct ct_mixer *mixer) | ||
842 | { | ||
843 | struct sum *sum = NULL; | ||
844 | struct amixer *amix_d = NULL, *amix_s = NULL; | ||
845 | enum CT_AMIXER_CTL i = 0, j = 0; | ||
846 | |||
847 | /* Build topology from destination to source */ | ||
848 | |||
849 | /* Set up Master mixer */ | ||
850 | for (i = AMIXER_MASTER_F, j = SUM_IN_F; | ||
851 | i <= AMIXER_MASTER_S; i++, j++) { | ||
852 | amix_d = mixer->amixers[i*CHN_NUM]; | ||
853 | sum = mixer->sums[j*CHN_NUM]; | ||
854 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
855 | amix_d = mixer->amixers[i*CHN_NUM+1]; | ||
856 | sum = mixer->sums[j*CHN_NUM+1]; | ||
857 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
858 | } | ||
859 | |||
860 | /* Set up Wave-out mixer */ | ||
861 | for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F; | ||
862 | i <= AMIXER_WAVE_S; i++, j++) { | ||
863 | amix_d = mixer->amixers[i*CHN_NUM]; | ||
864 | amix_s = mixer->amixers[j*CHN_NUM]; | ||
865 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
866 | amix_d = mixer->amixers[i*CHN_NUM+1]; | ||
867 | amix_s = mixer->amixers[j*CHN_NUM+1]; | ||
868 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
869 | } | ||
870 | |||
871 | /* Set up S/PDIF-out mixer */ | ||
872 | amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM]; | ||
873 | amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM]; | ||
874 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
875 | amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1]; | ||
876 | amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1]; | ||
877 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
878 | |||
879 | /* Set up PCM-in mixer */ | ||
880 | for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) { | ||
881 | amix_d = mixer->amixers[i*CHN_NUM]; | ||
882 | sum = mixer->sums[j*CHN_NUM]; | ||
883 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
884 | amix_d = mixer->amixers[i*CHN_NUM+1]; | ||
885 | sum = mixer->sums[j*CHN_NUM+1]; | ||
886 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
887 | } | ||
888 | |||
889 | /* Set up Line-in mixer */ | ||
890 | amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM]; | ||
891 | sum = mixer->sums[SUM_IN_F*CHN_NUM]; | ||
892 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
893 | amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1]; | ||
894 | sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; | ||
895 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
896 | |||
897 | /* Set up Mic-in mixer */ | ||
898 | amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM]; | ||
899 | sum = mixer->sums[SUM_IN_F*CHN_NUM]; | ||
900 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
901 | amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1]; | ||
902 | sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; | ||
903 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
904 | |||
905 | /* Set up S/PDIF-in mixer */ | ||
906 | amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM]; | ||
907 | sum = mixer->sums[SUM_IN_F*CHN_NUM]; | ||
908 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
909 | amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1]; | ||
910 | sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; | ||
911 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
912 | |||
913 | /* Set up Master recording mixer */ | ||
914 | amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM]; | ||
915 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
916 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
917 | amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1]; | ||
918 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
919 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
920 | |||
921 | /* Set up PCM-in recording mixer */ | ||
922 | amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM]; | ||
923 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
924 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
925 | amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1]; | ||
926 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
927 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
928 | |||
929 | /* Set up Line-in recording mixer */ | ||
930 | amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM]; | ||
931 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
932 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
933 | amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1]; | ||
934 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
935 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
936 | |||
937 | /* Set up Mic-in recording mixer */ | ||
938 | amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM]; | ||
939 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
940 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
941 | amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1]; | ||
942 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
943 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
944 | |||
945 | /* Set up S/PDIF-in recording mixer */ | ||
946 | amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM]; | ||
947 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
948 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
949 | amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1]; | ||
950 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
951 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
952 | |||
953 | return 0; | ||
954 | } | ||
955 | |||
956 | static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc) | ||
957 | { | ||
958 | amixer->ops->set_input(amixer, rsc); | ||
959 | amixer->ops->commit_write(amixer); | ||
960 | |||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type) | ||
965 | { | ||
966 | switch (type) { | ||
967 | case MIX_WAVE_FRONT: return AMIXER_WAVE_F; | ||
968 | case MIX_WAVE_SURROUND: return AMIXER_WAVE_S; | ||
969 | case MIX_WAVE_CENTLFE: return AMIXER_WAVE_C; | ||
970 | case MIX_WAVE_REAR: return AMIXER_WAVE_R; | ||
971 | case MIX_PCMO_FRONT: return AMIXER_MASTER_F_C; | ||
972 | case MIX_SPDIF_OUT: return AMIXER_SPDIFO; | ||
973 | case MIX_LINE_IN: return AMIXER_LINEIN; | ||
974 | case MIX_MIC_IN: return AMIXER_MIC; | ||
975 | case MIX_SPDIF_IN: return AMIXER_SPDIFI; | ||
976 | case MIX_PCMI_FRONT: return AMIXER_PCM_F; | ||
977 | case MIX_PCMI_SURROUND: return AMIXER_PCM_S; | ||
978 | case MIX_PCMI_CENTLFE: return AMIXER_PCM_C; | ||
979 | case MIX_PCMI_REAR: return AMIXER_PCM_R; | ||
980 | default: return 0; | ||
981 | } | ||
982 | } | ||
983 | |||
984 | static int mixer_get_output_ports(struct ct_mixer *mixer, | ||
985 | enum MIXER_PORT_T type, | ||
986 | struct rsc **rleft, struct rsc **rright) | ||
987 | { | ||
988 | enum CT_AMIXER_CTL amix = port_to_amixer(type); | ||
989 | |||
990 | if (NULL != rleft) | ||
991 | *rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc; | ||
992 | |||
993 | if (NULL != rright) | ||
994 | *rright = | ||
995 | &((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc; | ||
996 | |||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | static int mixer_set_input_left(struct ct_mixer *mixer, | ||
1001 | enum MIXER_PORT_T type, struct rsc *rsc) | ||
1002 | { | ||
1003 | enum CT_AMIXER_CTL amix = port_to_amixer(type); | ||
1004 | |||
1005 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); | ||
1006 | amix = get_recording_amixer(amix); | ||
1007 | if (amix < NUM_CT_AMIXERS) | ||
1008 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); | ||
1009 | |||
1010 | return 0; | ||
1011 | } | ||
1012 | |||
1013 | static int | ||
1014 | mixer_set_input_right(struct ct_mixer *mixer, | ||
1015 | enum MIXER_PORT_T type, struct rsc *rsc) | ||
1016 | { | ||
1017 | enum CT_AMIXER_CTL amix = port_to_amixer(type); | ||
1018 | |||
1019 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); | ||
1020 | amix = get_recording_amixer(amix); | ||
1021 | if (amix < NUM_CT_AMIXERS) | ||
1022 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | int ct_mixer_destroy(struct ct_mixer *mixer) | ||
1028 | { | ||
1029 | struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; | ||
1030 | struct amixer_mgr *amixer_mgr = | ||
1031 | (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; | ||
1032 | struct amixer *amixer = NULL; | ||
1033 | int i = 0; | ||
1034 | |||
1035 | /* Release amixer resources */ | ||
1036 | for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { | ||
1037 | if (NULL != mixer->amixers[i]) { | ||
1038 | amixer = mixer->amixers[i]; | ||
1039 | amixer_mgr->put_amixer(amixer_mgr, amixer); | ||
1040 | } | ||
1041 | } | ||
1042 | |||
1043 | /* Release sum resources */ | ||
1044 | for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { | ||
1045 | if (NULL != mixer->sums[i]) | ||
1046 | sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); | ||
1047 | } | ||
1048 | |||
1049 | /* Release mem assigned to mixer object */ | ||
1050 | kfree(mixer->sums); | ||
1051 | kfree(mixer->amixers); | ||
1052 | kfree(mixer); | ||
1053 | |||
1054 | return 0; | ||
1055 | } | ||
1056 | |||
1057 | int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer) | ||
1058 | { | ||
1059 | struct ct_mixer *mixer = NULL; | ||
1060 | int err = 0; | ||
1061 | |||
1062 | *rmixer = NULL; | ||
1063 | |||
1064 | /* Allocate mem for mixer obj */ | ||
1065 | err = ct_mixer_get_mem(&mixer); | ||
1066 | if (err) | ||
1067 | return err; | ||
1068 | |||
1069 | mixer->switch_state = 0; | ||
1070 | mixer->atc = atc; | ||
1071 | /* Set operations */ | ||
1072 | mixer->get_output_ports = mixer_get_output_ports; | ||
1073 | mixer->set_input_left = mixer_set_input_left; | ||
1074 | mixer->set_input_right = mixer_set_input_right; | ||
1075 | |||
1076 | /* Allocate chip resources for mixer obj */ | ||
1077 | err = ct_mixer_get_resources(mixer); | ||
1078 | if (err) | ||
1079 | goto error; | ||
1080 | |||
1081 | /* Build internal mixer topology */ | ||
1082 | ct_mixer_topology_build(mixer); | ||
1083 | |||
1084 | *rmixer = mixer; | ||
1085 | |||
1086 | return 0; | ||
1087 | |||
1088 | error: | ||
1089 | ct_mixer_destroy(mixer); | ||
1090 | return err; | ||
1091 | } | ||
1092 | |||
1093 | int ct_alsa_mix_create(struct ct_atc *atc, | ||
1094 | enum CTALSADEVS device, | ||
1095 | const char *device_name) | ||
1096 | { | ||
1097 | int err = 0; | ||
1098 | |||
1099 | /* Create snd kcontrol instances on demand */ | ||
1100 | vol_ctl.device = swh_ctl.device = device; | ||
1101 | err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer); | ||
1102 | if (err) | ||
1103 | return err; | ||
1104 | |||
1105 | strcpy(atc->card->mixername, device_name); | ||
1106 | |||
1107 | return 0; | ||
1108 | } | ||
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h new file mode 100644 index 000000000000..e2d96ebde746 --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.h | |||
@@ -0,0 +1,67 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctmixer.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the mixer device functions. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Mar 28 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef CTMIXER_H | ||
19 | #define CTMIXER_H | ||
20 | |||
21 | #include "ctatc.h" | ||
22 | #include "ctresource.h" | ||
23 | |||
24 | #define INIT_VOL 0x1c00 | ||
25 | |||
26 | enum MIXER_PORT_T { | ||
27 | MIX_WAVE_FRONT, | ||
28 | MIX_WAVE_REAR, | ||
29 | MIX_WAVE_CENTLFE, | ||
30 | MIX_WAVE_SURROUND, | ||
31 | MIX_SPDIF_OUT, | ||
32 | MIX_PCMO_FRONT, | ||
33 | MIX_MIC_IN, | ||
34 | MIX_LINE_IN, | ||
35 | MIX_SPDIF_IN, | ||
36 | MIX_PCMI_FRONT, | ||
37 | MIX_PCMI_REAR, | ||
38 | MIX_PCMI_CENTLFE, | ||
39 | MIX_PCMI_SURROUND, | ||
40 | |||
41 | NUM_MIX_PORTS | ||
42 | }; | ||
43 | |||
44 | /* alsa mixer descriptor */ | ||
45 | struct ct_mixer { | ||
46 | struct ct_atc *atc; | ||
47 | |||
48 | void **amixers; /* amixer resources for volume control */ | ||
49 | void **sums; /* sum resources for signal collection */ | ||
50 | unsigned int switch_state; /* A bit-map to indicate state of switches */ | ||
51 | |||
52 | int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type, | ||
53 | struct rsc **rleft, struct rsc **rright); | ||
54 | |||
55 | int (*set_input_left)(struct ct_mixer *mixer, | ||
56 | enum MIXER_PORT_T type, struct rsc *rsc); | ||
57 | int (*set_input_right)(struct ct_mixer *mixer, | ||
58 | enum MIXER_PORT_T type, struct rsc *rsc); | ||
59 | }; | ||
60 | |||
61 | int ct_alsa_mix_create(struct ct_atc *atc, | ||
62 | enum CTALSADEVS device, | ||
63 | const char *device_name); | ||
64 | int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer); | ||
65 | int ct_mixer_destroy(struct ct_mixer *mixer); | ||
66 | |||
67 | #endif /* CTMIXER_H */ | ||
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c new file mode 100644 index 000000000000..73d4fdbbb9f4 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.c | |||
@@ -0,0 +1,499 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctpcm.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the pcm device functions. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Apr 2 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include "ctpcm.h" | ||
19 | #include <sound/pcm.h> | ||
20 | |||
21 | /* Hardware descriptions for playback */ | ||
22 | static struct snd_pcm_hardware ct_pcm_playback_hw = { | ||
23 | .info = (SNDRV_PCM_INFO_MMAP | | ||
24 | SNDRV_PCM_INFO_INTERLEAVED | | ||
25 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
26 | SNDRV_PCM_INFO_MMAP_VALID | | ||
27 | SNDRV_PCM_INFO_PAUSE), | ||
28 | .formats = (SNDRV_PCM_FMTBIT_U8 | | ||
29 | SNDRV_PCM_FMTBIT_S8 | | ||
30 | SNDRV_PCM_FMTBIT_S16_LE | | ||
31 | SNDRV_PCM_FMTBIT_U16_LE | | ||
32 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
33 | SNDRV_PCM_FMTBIT_S24_LE | | ||
34 | SNDRV_PCM_FMTBIT_S32_LE), | ||
35 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | | ||
36 | SNDRV_PCM_RATE_8000_192000), | ||
37 | .rate_min = 8000, | ||
38 | .rate_max = 192000, | ||
39 | .channels_min = 1, | ||
40 | .channels_max = 2, | ||
41 | .buffer_bytes_max = (128*1024), | ||
42 | .period_bytes_min = (64), | ||
43 | .period_bytes_max = (128*1024), | ||
44 | .periods_min = 1, | ||
45 | .periods_max = 1024, | ||
46 | .fifo_size = 0, | ||
47 | }; | ||
48 | |||
49 | static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { | ||
50 | .info = (SNDRV_PCM_INFO_MMAP | | ||
51 | SNDRV_PCM_INFO_INTERLEAVED | | ||
52 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
53 | SNDRV_PCM_INFO_MMAP_VALID | | ||
54 | SNDRV_PCM_INFO_PAUSE), | ||
55 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | ||
56 | SNDRV_PCM_FMTBIT_U16_LE), | ||
57 | .rates = (SNDRV_PCM_RATE_48000 | | ||
58 | SNDRV_PCM_RATE_44100 | | ||
59 | SNDRV_PCM_RATE_32000), | ||
60 | .rate_min = 32000, | ||
61 | .rate_max = 48000, | ||
62 | .channels_min = 2, | ||
63 | .channels_max = 2, | ||
64 | .buffer_bytes_max = (128*1024), | ||
65 | .period_bytes_min = (64), | ||
66 | .period_bytes_max = (128*1024), | ||
67 | .periods_min = 1, | ||
68 | .periods_max = 1024, | ||
69 | .fifo_size = 0, | ||
70 | }; | ||
71 | |||
72 | /* Hardware descriptions for capture */ | ||
73 | static struct snd_pcm_hardware ct_pcm_capture_hw = { | ||
74 | .info = (SNDRV_PCM_INFO_MMAP | | ||
75 | SNDRV_PCM_INFO_INTERLEAVED | | ||
76 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
77 | SNDRV_PCM_INFO_PAUSE | | ||
78 | SNDRV_PCM_INFO_MMAP_VALID), | ||
79 | .formats = (SNDRV_PCM_FMTBIT_U8 | | ||
80 | SNDRV_PCM_FMTBIT_S8 | | ||
81 | SNDRV_PCM_FMTBIT_S16_LE | | ||
82 | SNDRV_PCM_FMTBIT_U16_LE | | ||
83 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
84 | SNDRV_PCM_FMTBIT_S24_LE | | ||
85 | SNDRV_PCM_FMTBIT_S32_LE), | ||
86 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | | ||
87 | SNDRV_PCM_RATE_8000_96000), | ||
88 | .rate_min = 8000, | ||
89 | .rate_max = 96000, | ||
90 | .channels_min = 1, | ||
91 | .channels_max = 2, | ||
92 | .buffer_bytes_max = (128*1024), | ||
93 | .period_bytes_min = (384), | ||
94 | .period_bytes_max = (64*1024), | ||
95 | .periods_min = 2, | ||
96 | .periods_max = 1024, | ||
97 | .fifo_size = 0, | ||
98 | }; | ||
99 | |||
100 | static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm) | ||
101 | { | ||
102 | struct ct_atc_pcm *apcm = atc_pcm; | ||
103 | |||
104 | if (NULL == apcm->substream) | ||
105 | return; | ||
106 | |||
107 | snd_pcm_period_elapsed(apcm->substream); | ||
108 | } | ||
109 | |||
110 | static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) | ||
111 | { | ||
112 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
113 | struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); | ||
114 | |||
115 | atc->pcm_release_resources(atc, apcm); | ||
116 | kfree(apcm); | ||
117 | runtime->private_data = NULL; | ||
118 | } | ||
119 | |||
120 | /* pcm playback operations */ | ||
121 | static int ct_pcm_playback_open(struct snd_pcm_substream *substream) | ||
122 | { | ||
123 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
124 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
125 | struct ct_atc_pcm *apcm; | ||
126 | int err; | ||
127 | |||
128 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||
129 | if (NULL == apcm) | ||
130 | return -ENOMEM; | ||
131 | |||
132 | spin_lock_init(&apcm->timer_lock); | ||
133 | apcm->stop_timer = 0; | ||
134 | apcm->substream = substream; | ||
135 | apcm->interrupt = ct_atc_pcm_interrupt; | ||
136 | runtime->private_data = apcm; | ||
137 | runtime->private_free = ct_atc_pcm_free_substream; | ||
138 | if (IEC958 == substream->pcm->device) { | ||
139 | runtime->hw = ct_spdif_passthru_playback_hw; | ||
140 | atc->spdif_out_passthru(atc, 1); | ||
141 | } else { | ||
142 | runtime->hw = ct_pcm_playback_hw; | ||
143 | if (FRONT == substream->pcm->device) | ||
144 | runtime->hw.channels_max = 8; | ||
145 | } | ||
146 | |||
147 | err = snd_pcm_hw_constraint_integer(runtime, | ||
148 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
149 | if (err < 0) { | ||
150 | kfree(apcm); | ||
151 | return err; | ||
152 | } | ||
153 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
154 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
155 | 1024, UINT_MAX); | ||
156 | if (err < 0) { | ||
157 | kfree(apcm); | ||
158 | return err; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int ct_pcm_playback_close(struct snd_pcm_substream *substream) | ||
165 | { | ||
166 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
167 | |||
168 | /* TODO: Notify mixer inactive. */ | ||
169 | if (IEC958 == substream->pcm->device) | ||
170 | atc->spdif_out_passthru(atc, 0); | ||
171 | |||
172 | /* The ct_atc_pcm object will be freed by runtime->private_free */ | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int ct_pcm_hw_params(struct snd_pcm_substream *substream, | ||
178 | struct snd_pcm_hw_params *hw_params) | ||
179 | { | ||
180 | return snd_pcm_lib_malloc_pages(substream, | ||
181 | params_buffer_bytes(hw_params)); | ||
182 | } | ||
183 | |||
184 | static int ct_pcm_hw_free(struct snd_pcm_substream *substream) | ||
185 | { | ||
186 | /* Free snd-allocated pages */ | ||
187 | return snd_pcm_lib_free_pages(substream); | ||
188 | } | ||
189 | |||
190 | static void ct_pcm_timer_callback(unsigned long data) | ||
191 | { | ||
192 | struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; | ||
193 | struct snd_pcm_substream *substream = apcm->substream; | ||
194 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
195 | unsigned int period_size = runtime->period_size; | ||
196 | unsigned int buffer_size = runtime->buffer_size; | ||
197 | unsigned long flags; | ||
198 | unsigned int position = 0, dist = 0, interval = 0; | ||
199 | |||
200 | position = substream->ops->pointer(substream); | ||
201 | dist = (position + buffer_size - apcm->position) % buffer_size; | ||
202 | if ((dist >= period_size) || | ||
203 | (position/period_size != apcm->position/period_size)) { | ||
204 | apcm->interrupt(apcm); | ||
205 | apcm->position = position; | ||
206 | } | ||
207 | /* Add extra HZ*5/1000 to avoid overrun issue when recording | ||
208 | * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ | ||
209 | interval = ((period_size - (position % period_size)) | ||
210 | * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; | ||
211 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
212 | apcm->timer.expires = jiffies + interval; | ||
213 | if (!apcm->stop_timer) | ||
214 | add_timer(&apcm->timer); | ||
215 | |||
216 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
217 | } | ||
218 | |||
219 | static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) | ||
220 | { | ||
221 | unsigned long flags; | ||
222 | |||
223 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
224 | if (timer_pending(&apcm->timer)) { | ||
225 | /* The timer has already been started. */ | ||
226 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | init_timer(&apcm->timer); | ||
231 | apcm->timer.data = (unsigned long)apcm; | ||
232 | apcm->timer.function = ct_pcm_timer_callback; | ||
233 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
234 | apcm->position = 0; | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) | ||
240 | { | ||
241 | struct snd_pcm_runtime *runtime = apcm->substream->runtime; | ||
242 | unsigned long flags; | ||
243 | |||
244 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
245 | if (timer_pending(&apcm->timer)) { | ||
246 | /* The timer has already been started. */ | ||
247 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | apcm->timer.expires = jiffies + (runtime->period_size * HZ + | ||
252 | (runtime->rate - 1)) / runtime->rate; | ||
253 | apcm->stop_timer = 0; | ||
254 | add_timer(&apcm->timer); | ||
255 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) | ||
261 | { | ||
262 | unsigned long flags; | ||
263 | |||
264 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
265 | apcm->stop_timer = 1; | ||
266 | del_timer(&apcm->timer); | ||
267 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
268 | |||
269 | try_to_del_timer_sync(&apcm->timer); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) | ||
275 | { | ||
276 | int err; | ||
277 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
278 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
279 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
280 | |||
281 | if (IEC958 == substream->pcm->device) | ||
282 | err = atc->spdif_passthru_playback_prepare(atc, apcm); | ||
283 | else | ||
284 | err = atc->pcm_playback_prepare(atc, apcm); | ||
285 | |||
286 | if (err < 0) { | ||
287 | printk(KERN_ERR "Preparing pcm playback failed!!!\n"); | ||
288 | return err; | ||
289 | } | ||
290 | |||
291 | ct_pcm_timer_prepare(apcm); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int | ||
297 | ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
298 | { | ||
299 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
300 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
301 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
302 | |||
303 | switch (cmd) { | ||
304 | case SNDRV_PCM_TRIGGER_START: | ||
305 | case SNDRV_PCM_TRIGGER_RESUME: | ||
306 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
307 | atc->pcm_playback_start(atc, apcm); | ||
308 | ct_pcm_timer_start(apcm); | ||
309 | break; | ||
310 | case SNDRV_PCM_TRIGGER_STOP: | ||
311 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
312 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
313 | ct_pcm_timer_stop(apcm); | ||
314 | atc->pcm_playback_stop(atc, apcm); | ||
315 | break; | ||
316 | default: | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static snd_pcm_uframes_t | ||
324 | ct_pcm_playback_pointer(struct snd_pcm_substream *substream) | ||
325 | { | ||
326 | unsigned long position; | ||
327 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
328 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
329 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
330 | |||
331 | /* Read out playback position */ | ||
332 | position = atc->pcm_playback_position(atc, apcm); | ||
333 | position = bytes_to_frames(runtime, position); | ||
334 | return position; | ||
335 | } | ||
336 | |||
337 | /* pcm capture operations */ | ||
338 | static int ct_pcm_capture_open(struct snd_pcm_substream *substream) | ||
339 | { | ||
340 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
341 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
342 | struct ct_atc_pcm *apcm; | ||
343 | int err; | ||
344 | |||
345 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||
346 | if (NULL == apcm) | ||
347 | return -ENOMEM; | ||
348 | |||
349 | spin_lock_init(&apcm->timer_lock); | ||
350 | apcm->started = 0; | ||
351 | apcm->stop_timer = 0; | ||
352 | apcm->substream = substream; | ||
353 | apcm->interrupt = ct_atc_pcm_interrupt; | ||
354 | runtime->private_data = apcm; | ||
355 | runtime->private_free = ct_atc_pcm_free_substream; | ||
356 | runtime->hw = ct_pcm_capture_hw; | ||
357 | runtime->hw.rate_max = atc->rsr * atc->msr; | ||
358 | |||
359 | err = snd_pcm_hw_constraint_integer(runtime, | ||
360 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
361 | if (err < 0) { | ||
362 | kfree(apcm); | ||
363 | return err; | ||
364 | } | ||
365 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
366 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
367 | 1024, UINT_MAX); | ||
368 | if (err < 0) { | ||
369 | kfree(apcm); | ||
370 | return err; | ||
371 | } | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int ct_pcm_capture_close(struct snd_pcm_substream *substream) | ||
377 | { | ||
378 | /* The ct_atc_pcm object will be freed by runtime->private_free */ | ||
379 | /* TODO: Notify mixer inactive. */ | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) | ||
384 | { | ||
385 | int err; | ||
386 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
387 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
388 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
389 | |||
390 | err = atc->pcm_capture_prepare(atc, apcm); | ||
391 | if (err < 0) { | ||
392 | printk(KERN_ERR "Preparing pcm capture failed!!!\n"); | ||
393 | return err; | ||
394 | } | ||
395 | |||
396 | ct_pcm_timer_prepare(apcm); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int | ||
402 | ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) | ||
403 | { | ||
404 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
405 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
406 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
407 | |||
408 | switch (cmd) { | ||
409 | case SNDRV_PCM_TRIGGER_START: | ||
410 | atc->pcm_capture_start(atc, apcm); | ||
411 | ct_pcm_timer_start(apcm); | ||
412 | break; | ||
413 | case SNDRV_PCM_TRIGGER_STOP: | ||
414 | ct_pcm_timer_stop(apcm); | ||
415 | atc->pcm_capture_stop(atc, apcm); | ||
416 | break; | ||
417 | default: | ||
418 | ct_pcm_timer_stop(apcm); | ||
419 | atc->pcm_capture_stop(atc, apcm); | ||
420 | break; | ||
421 | } | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static snd_pcm_uframes_t | ||
427 | ct_pcm_capture_pointer(struct snd_pcm_substream *substream) | ||
428 | { | ||
429 | unsigned long position; | ||
430 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
431 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
432 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
433 | |||
434 | /* Read out playback position */ | ||
435 | position = atc->pcm_capture_position(atc, apcm); | ||
436 | position = bytes_to_frames(runtime, position); | ||
437 | return position; | ||
438 | } | ||
439 | |||
440 | /* PCM operators for playback */ | ||
441 | static struct snd_pcm_ops ct_pcm_playback_ops = { | ||
442 | .open = ct_pcm_playback_open, | ||
443 | .close = ct_pcm_playback_close, | ||
444 | .ioctl = snd_pcm_lib_ioctl, | ||
445 | .hw_params = ct_pcm_hw_params, | ||
446 | .hw_free = ct_pcm_hw_free, | ||
447 | .prepare = ct_pcm_playback_prepare, | ||
448 | .trigger = ct_pcm_playback_trigger, | ||
449 | .pointer = ct_pcm_playback_pointer, | ||
450 | }; | ||
451 | |||
452 | /* PCM operators for capture */ | ||
453 | static struct snd_pcm_ops ct_pcm_capture_ops = { | ||
454 | .open = ct_pcm_capture_open, | ||
455 | .close = ct_pcm_capture_close, | ||
456 | .ioctl = snd_pcm_lib_ioctl, | ||
457 | .hw_params = ct_pcm_hw_params, | ||
458 | .hw_free = ct_pcm_hw_free, | ||
459 | .prepare = ct_pcm_capture_prepare, | ||
460 | .trigger = ct_pcm_capture_trigger, | ||
461 | .pointer = ct_pcm_capture_pointer, | ||
462 | }; | ||
463 | |||
464 | /* Create ALSA pcm device */ | ||
465 | int ct_alsa_pcm_create(struct ct_atc *atc, | ||
466 | enum CTALSADEVS device, | ||
467 | const char *device_name) | ||
468 | { | ||
469 | struct snd_pcm *pcm; | ||
470 | int err; | ||
471 | int playback_count, capture_count; | ||
472 | char name[128]; | ||
473 | |||
474 | strncpy(name, device_name, sizeof(name)); | ||
475 | playback_count = (IEC958 == device) ? 1 : 8; | ||
476 | capture_count = (FRONT == device) ? 1 : 0; | ||
477 | err = snd_pcm_new(atc->card, name, device, | ||
478 | playback_count, capture_count, &pcm); | ||
479 | if (err < 0) { | ||
480 | printk(KERN_ERR "snd_pcm_new failed!! Err=%d\n", err); | ||
481 | return err; | ||
482 | } | ||
483 | |||
484 | pcm->private_data = atc; | ||
485 | pcm->info_flags = 0; | ||
486 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | ||
487 | strcpy(pcm->name, device_name); | ||
488 | |||
489 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); | ||
490 | |||
491 | if (FRONT == device) | ||
492 | snd_pcm_set_ops(pcm, | ||
493 | SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops); | ||
494 | |||
495 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
496 | snd_dma_pci_data(atc->pci), 128*1024, 128*1024); | ||
497 | |||
498 | return 0; | ||
499 | } | ||
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h new file mode 100644 index 000000000000..178da0dca647 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctpcm.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the pcm device functions. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date Mar 28 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef CTPCM_H | ||
19 | #define CTPCM_H | ||
20 | |||
21 | #include "ctatc.h" | ||
22 | |||
23 | int ct_alsa_pcm_create(struct ct_atc *atc, | ||
24 | enum CTALSADEVS device, | ||
25 | const char *device_name); | ||
26 | |||
27 | #endif /* CTPCM_H */ | ||
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c new file mode 100644 index 000000000000..414fc23267cd --- /dev/null +++ b/sound/pci/ctxfi/ctresource.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctresource.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of some generic helper functions. | ||
12 | * | ||
13 | * @Author Liu Chun | ||
14 | * @Date May 15 2008 | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include "ctresource.h" | ||
19 | #include "cthardware.h" | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #define AUDIO_SLOT_BLOCK_NUM 256 | ||
24 | |||
25 | /* Resource allocation based on bit-map management mechanism */ | ||
26 | static int | ||
27 | get_resource(u8 *rscs, unsigned int amount, | ||
28 | unsigned int multi, unsigned int *ridx) | ||
29 | { | ||
30 | int i = 0, j = 0, k = 0, n = 0; | ||
31 | |||
32 | /* Check whether there are sufficient resources to meet request. */ | ||
33 | for (i = 0, n = multi; i < amount; i++) { | ||
34 | j = i / 8; | ||
35 | k = i % 8; | ||
36 | if (rscs[j] & ((u8)1 << k)) { | ||
37 | n = multi; | ||
38 | continue; | ||
39 | } | ||
40 | if (!(--n)) | ||
41 | break; /* found sufficient contiguous resources */ | ||
42 | } | ||
43 | |||
44 | if (i >= amount) { | ||
45 | /* Can not find sufficient contiguous resources */ | ||
46 | return -ENOENT; | ||
47 | } | ||
48 | |||
49 | /* Mark the contiguous bits in resource bit-map as used */ | ||
50 | for (n = multi; n > 0; n--) { | ||
51 | j = i / 8; | ||
52 | k = i % 8; | ||
53 | rscs[j] |= ((u8)1 << k); | ||
54 | i--; | ||
55 | } | ||
56 | |||
57 | *ridx = i + 1; | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) | ||
63 | { | ||
64 | unsigned int i = 0, j = 0, k = 0, n = 0; | ||
65 | |||
66 | /* Mark the contiguous bits in resource bit-map as used */ | ||
67 | for (n = multi, i = idx; n > 0; n--) { | ||
68 | j = i / 8; | ||
69 | k = i % 8; | ||
70 | rscs[j] &= ~((u8)1 << k); | ||
71 | i++; | ||
72 | } | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx) | ||
78 | { | ||
79 | int err = 0; | ||
80 | |||
81 | if (n > mgr->avail) | ||
82 | return -ENOENT; | ||
83 | |||
84 | err = get_resource(mgr->rscs, mgr->amount, n, ridx); | ||
85 | if (!err) | ||
86 | mgr->avail -= n; | ||
87 | |||
88 | return err; | ||
89 | } | ||
90 | |||
91 | int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx) | ||
92 | { | ||
93 | put_resource(mgr->rscs, n, idx); | ||
94 | mgr->avail += n; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = { | ||
100 | /* SRC channel is at Audio Ring slot 1 every 16 slots. */ | ||
101 | [SRC] = 0x1, | ||
102 | [AMIXER] = 0x4, | ||
103 | [SUM] = 0xc, | ||
104 | }; | ||
105 | |||
106 | static int rsc_index(const struct rsc *rsc) | ||
107 | { | ||
108 | return rsc->conj; | ||
109 | } | ||
110 | |||
111 | static int audio_ring_slot(const struct rsc *rsc) | ||
112 | { | ||
113 | return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type]; | ||
114 | } | ||
115 | |||
116 | static int rsc_next_conj(struct rsc *rsc) | ||
117 | { | ||
118 | unsigned int i; | ||
119 | for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); ) | ||
120 | i++; | ||
121 | rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i); | ||
122 | return rsc->conj; | ||
123 | } | ||
124 | |||
125 | static int rsc_master(struct rsc *rsc) | ||
126 | { | ||
127 | return rsc->conj = rsc->idx; | ||
128 | } | ||
129 | |||
130 | static struct rsc_ops rsc_generic_ops = { | ||
131 | .index = rsc_index, | ||
132 | .output_slot = audio_ring_slot, | ||
133 | .master = rsc_master, | ||
134 | .next_conj = rsc_next_conj, | ||
135 | }; | ||
136 | |||
137 | int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) | ||
138 | { | ||
139 | int err = 0; | ||
140 | |||
141 | rsc->idx = idx; | ||
142 | rsc->conj = idx; | ||
143 | rsc->type = type; | ||
144 | rsc->msr = msr; | ||
145 | rsc->hw = hw; | ||
146 | rsc->ops = &rsc_generic_ops; | ||
147 | if (NULL == hw) { | ||
148 | rsc->ctrl_blk = NULL; | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | switch (type) { | ||
153 | case SRC: | ||
154 | err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk); | ||
155 | break; | ||
156 | case AMIXER: | ||
157 | err = ((struct hw *)hw)-> | ||
158 | amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk); | ||
159 | break; | ||
160 | case SRCIMP: | ||
161 | case SUM: | ||
162 | case DAIO: | ||
163 | break; | ||
164 | default: | ||
165 | printk(KERN_ERR "Invalid resource type value %d!\n", type); | ||
166 | return -EINVAL; | ||
167 | } | ||
168 | |||
169 | if (err) { | ||
170 | printk(KERN_ERR "Failed to get resource control block!\n"); | ||
171 | return err; | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | int rsc_uninit(struct rsc *rsc) | ||
178 | { | ||
179 | if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) { | ||
180 | switch (rsc->type) { | ||
181 | case SRC: | ||
182 | ((struct hw *)rsc->hw)-> | ||
183 | src_rsc_put_ctrl_blk(rsc->ctrl_blk); | ||
184 | break; | ||
185 | case AMIXER: | ||
186 | ((struct hw *)rsc->hw)-> | ||
187 | amixer_rsc_put_ctrl_blk(rsc->ctrl_blk); | ||
188 | break; | ||
189 | case SUM: | ||
190 | case DAIO: | ||
191 | break; | ||
192 | default: | ||
193 | printk(KERN_ERR "Invalid resource type value %d!\n", | ||
194 | rsc->type); | ||
195 | break; | ||
196 | } | ||
197 | |||
198 | rsc->hw = rsc->ctrl_blk = NULL; | ||
199 | } | ||
200 | |||
201 | rsc->idx = rsc->conj = 0; | ||
202 | rsc->type = NUM_RSCTYP; | ||
203 | rsc->msr = 0; | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, | ||
209 | unsigned int amount, void *hw_obj) | ||
210 | { | ||
211 | int err = 0; | ||
212 | struct hw *hw = hw_obj; | ||
213 | |||
214 | mgr->type = NUM_RSCTYP; | ||
215 | |||
216 | mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL); | ||
217 | if (NULL == mgr->rscs) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | switch (type) { | ||
221 | case SRC: | ||
222 | err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk); | ||
223 | break; | ||
224 | case SRCIMP: | ||
225 | err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk); | ||
226 | break; | ||
227 | case AMIXER: | ||
228 | err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk); | ||
229 | break; | ||
230 | case DAIO: | ||
231 | err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk); | ||
232 | break; | ||
233 | case SUM: | ||
234 | break; | ||
235 | default: | ||
236 | printk(KERN_ERR "Invalid resource type value %d!\n", type); | ||
237 | err = -EINVAL; | ||
238 | goto error; | ||
239 | } | ||
240 | |||
241 | if (err) { | ||
242 | printk(KERN_ERR "Failed to get manager control block!\n"); | ||
243 | goto error; | ||
244 | } | ||
245 | |||
246 | mgr->type = type; | ||
247 | mgr->avail = mgr->amount = amount; | ||
248 | mgr->hw = hw; | ||
249 | |||
250 | return 0; | ||
251 | |||
252 | error: | ||
253 | kfree(mgr->rscs); | ||
254 | return err; | ||
255 | } | ||
256 | |||
257 | int rsc_mgr_uninit(struct rsc_mgr *mgr) | ||
258 | { | ||
259 | if (NULL != mgr->rscs) { | ||
260 | kfree(mgr->rscs); | ||
261 | mgr->rscs = NULL; | ||
262 | } | ||
263 | |||
264 | if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) { | ||
265 | switch (mgr->type) { | ||
266 | case SRC: | ||
267 | ((struct hw *)mgr->hw)-> | ||
268 | src_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
269 | break; | ||
270 | case SRCIMP: | ||
271 | ((struct hw *)mgr->hw)-> | ||
272 | srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
273 | break; | ||
274 | case AMIXER: | ||
275 | ((struct hw *)mgr->hw)-> | ||
276 | amixer_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
277 | break; | ||
278 | case DAIO: | ||
279 | ((struct hw *)mgr->hw)-> | ||
280 | daio_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
281 | break; | ||
282 | case SUM: | ||
283 | break; | ||
284 | default: | ||
285 | printk(KERN_ERR "Invalid resource type value %d!\n", | ||
286 | mgr->type); | ||
287 | break; | ||
288 | } | ||
289 | |||
290 | mgr->hw = mgr->ctrl_blk = NULL; | ||
291 | } | ||
292 | |||
293 | mgr->type = NUM_RSCTYP; | ||
294 | mgr->avail = mgr->amount = 0; | ||
295 | |||
296 | return 0; | ||
297 | } | ||
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h new file mode 100644 index 000000000000..0838c2e84f8b --- /dev/null +++ b/sound/pci/ctxfi/ctresource.h | |||
@@ -0,0 +1,72 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctresource.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of generic hardware resources for | ||
12 | * resource management. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 13 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef CTRESOURCE_H | ||
20 | #define CTRESOURCE_H | ||
21 | |||
22 | #include <linux/types.h> | ||
23 | |||
24 | enum RSCTYP { | ||
25 | SRC, | ||
26 | SRCIMP, | ||
27 | AMIXER, | ||
28 | SUM, | ||
29 | DAIO, | ||
30 | NUM_RSCTYP /* This must be the last one and less than 16 */ | ||
31 | }; | ||
32 | |||
33 | struct rsc_ops; | ||
34 | |||
35 | struct rsc { | ||
36 | u32 idx:12; /* The index of a resource */ | ||
37 | u32 type:4; /* The type (RSCTYP) of a resource */ | ||
38 | u32 conj:12; /* Current conjugate index */ | ||
39 | u32 msr:4; /* The Master Sample Rate a resource working on */ | ||
40 | void *ctrl_blk; /* Chip specific control info block for a resource */ | ||
41 | void *hw; /* Chip specific object for hardware access means */ | ||
42 | struct rsc_ops *ops; /* Generic resource operations */ | ||
43 | }; | ||
44 | |||
45 | struct rsc_ops { | ||
46 | int (*master)(struct rsc *rsc); /* Move to master resource */ | ||
47 | int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */ | ||
48 | int (*index)(const struct rsc *rsc); /* Return the index of resource */ | ||
49 | /* Return the output slot number */ | ||
50 | int (*output_slot)(const struct rsc *rsc); | ||
51 | }; | ||
52 | |||
53 | int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw); | ||
54 | int rsc_uninit(struct rsc *rsc); | ||
55 | |||
56 | struct rsc_mgr { | ||
57 | enum RSCTYP type; /* The type (RSCTYP) of resource to manage */ | ||
58 | unsigned int amount; /* The total amount of a kind of resource */ | ||
59 | unsigned int avail; /* The amount of currently available resources */ | ||
60 | unsigned char *rscs; /* The bit-map for resource allocation */ | ||
61 | void *ctrl_blk; /* Chip specific control info block */ | ||
62 | void *hw; /* Chip specific object for hardware access */ | ||
63 | }; | ||
64 | |||
65 | /* Resource management is based on bit-map mechanism */ | ||
66 | int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, | ||
67 | unsigned int amount, void *hw); | ||
68 | int rsc_mgr_uninit(struct rsc_mgr *mgr); | ||
69 | int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx); | ||
70 | int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx); | ||
71 | |||
72 | #endif /* CTRESOURCE_H */ | ||
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c new file mode 100644 index 000000000000..d3e0ad5ed9fb --- /dev/null +++ b/sound/pci/ctxfi/ctsrc.c | |||
@@ -0,0 +1,886 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctsrc.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of the Sample Rate Convertor | ||
12 | * resource management object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 13 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include "ctsrc.h" | ||
20 | #include "cthardware.h" | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #define SRC_RESOURCE_NUM 64 | ||
24 | #define SRCIMP_RESOURCE_NUM 256 | ||
25 | |||
26 | static unsigned int conj_mask; | ||
27 | |||
28 | static int src_default_config_memrd(struct src *src); | ||
29 | static int src_default_config_memwr(struct src *src); | ||
30 | static int src_default_config_arcrw(struct src *src); | ||
31 | |||
32 | static int (*src_default_config[3])(struct src *) = { | ||
33 | [MEMRD] = src_default_config_memrd, | ||
34 | [MEMWR] = src_default_config_memwr, | ||
35 | [ARCRW] = src_default_config_arcrw | ||
36 | }; | ||
37 | |||
38 | static int src_set_state(struct src *src, unsigned int state) | ||
39 | { | ||
40 | struct hw *hw = NULL; | ||
41 | |||
42 | hw = (struct hw *)src->rsc.hw; | ||
43 | hw->src_set_state(src->rsc.ctrl_blk, state); | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int src_set_bm(struct src *src, unsigned int bm) | ||
49 | { | ||
50 | struct hw *hw = NULL; | ||
51 | |||
52 | hw = (struct hw *)src->rsc.hw; | ||
53 | hw->src_set_bm(src->rsc.ctrl_blk, bm); | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int src_set_sf(struct src *src, unsigned int sf) | ||
59 | { | ||
60 | struct hw *hw = NULL; | ||
61 | |||
62 | hw = (struct hw *)src->rsc.hw; | ||
63 | hw->src_set_sf(src->rsc.ctrl_blk, sf); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int src_set_pm(struct src *src, unsigned int pm) | ||
69 | { | ||
70 | struct hw *hw = NULL; | ||
71 | |||
72 | hw = (struct hw *)src->rsc.hw; | ||
73 | hw->src_set_pm(src->rsc.ctrl_blk, pm); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int src_set_rom(struct src *src, unsigned int rom) | ||
79 | { | ||
80 | struct hw *hw = NULL; | ||
81 | |||
82 | hw = (struct hw *)src->rsc.hw; | ||
83 | hw->src_set_rom(src->rsc.ctrl_blk, rom); | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int src_set_vo(struct src *src, unsigned int vo) | ||
89 | { | ||
90 | struct hw *hw = NULL; | ||
91 | |||
92 | hw = (struct hw *)src->rsc.hw; | ||
93 | hw->src_set_vo(src->rsc.ctrl_blk, vo); | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int src_set_st(struct src *src, unsigned int st) | ||
99 | { | ||
100 | struct hw *hw = NULL; | ||
101 | |||
102 | hw = (struct hw *)src->rsc.hw; | ||
103 | hw->src_set_st(src->rsc.ctrl_blk, st); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int src_set_bp(struct src *src, unsigned int bp) | ||
109 | { | ||
110 | struct hw *hw = NULL; | ||
111 | |||
112 | hw = (struct hw *)src->rsc.hw; | ||
113 | hw->src_set_bp(src->rsc.ctrl_blk, bp); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int src_set_cisz(struct src *src, unsigned int cisz) | ||
119 | { | ||
120 | struct hw *hw = NULL; | ||
121 | |||
122 | hw = (struct hw *)src->rsc.hw; | ||
123 | hw->src_set_cisz(src->rsc.ctrl_blk, cisz); | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int src_set_ca(struct src *src, unsigned int ca) | ||
129 | { | ||
130 | struct hw *hw = NULL; | ||
131 | |||
132 | hw = (struct hw *)src->rsc.hw; | ||
133 | hw->src_set_ca(src->rsc.ctrl_blk, ca); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int src_set_sa(struct src *src, unsigned int sa) | ||
139 | { | ||
140 | struct hw *hw = NULL; | ||
141 | |||
142 | hw = (struct hw *)src->rsc.hw; | ||
143 | hw->src_set_sa(src->rsc.ctrl_blk, sa); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static int src_set_la(struct src *src, unsigned int la) | ||
149 | { | ||
150 | struct hw *hw = NULL; | ||
151 | |||
152 | hw = (struct hw *)src->rsc.hw; | ||
153 | hw->src_set_la(src->rsc.ctrl_blk, la); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int src_set_pitch(struct src *src, unsigned int pitch) | ||
159 | { | ||
160 | struct hw *hw = NULL; | ||
161 | |||
162 | hw = (struct hw *)src->rsc.hw; | ||
163 | hw->src_set_pitch(src->rsc.ctrl_blk, pitch); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int src_set_clear_zbufs(struct src *src) | ||
169 | { | ||
170 | struct hw *hw = NULL; | ||
171 | |||
172 | hw = (struct hw *)src->rsc.hw; | ||
173 | hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int src_commit_write(struct src *src) | ||
179 | { | ||
180 | struct hw *hw = NULL; | ||
181 | int i = 0; | ||
182 | unsigned int dirty = 0; | ||
183 | |||
184 | hw = (struct hw *)src->rsc.hw; | ||
185 | src->rsc.ops->master(&src->rsc); | ||
186 | if (src->rsc.msr > 1) { | ||
187 | /* Save dirty flags for conjugate resource programming */ | ||
188 | dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask; | ||
189 | } | ||
190 | hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), | ||
191 | src->rsc.ctrl_blk); | ||
192 | |||
193 | /* Program conjugate parameter mixer resources */ | ||
194 | if (MEMWR == src->mode) | ||
195 | return 0; | ||
196 | |||
197 | for (i = 1; i < src->rsc.msr; i++) { | ||
198 | src->rsc.ops->next_conj(&src->rsc); | ||
199 | hw->src_set_dirty(src->rsc.ctrl_blk, dirty); | ||
200 | hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), | ||
201 | src->rsc.ctrl_blk); | ||
202 | } | ||
203 | src->rsc.ops->master(&src->rsc); | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int src_get_ca(struct src *src) | ||
209 | { | ||
210 | struct hw *hw = NULL; | ||
211 | |||
212 | hw = (struct hw *)src->rsc.hw; | ||
213 | return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc), | ||
214 | src->rsc.ctrl_blk); | ||
215 | } | ||
216 | |||
217 | static int src_init(struct src *src) | ||
218 | { | ||
219 | src_default_config[src->mode](src); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static struct src *src_next_interleave(struct src *src) | ||
225 | { | ||
226 | return src->intlv; | ||
227 | } | ||
228 | |||
229 | static int src_default_config_memrd(struct src *src) | ||
230 | { | ||
231 | struct hw *hw = src->rsc.hw; | ||
232 | unsigned int rsr = 0, msr = 0; | ||
233 | |||
234 | hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); | ||
235 | hw->src_set_bm(src->rsc.ctrl_blk, 1); | ||
236 | for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1) | ||
237 | rsr++; | ||
238 | |||
239 | hw->src_set_rsr(src->rsc.ctrl_blk, rsr); | ||
240 | hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16); | ||
241 | hw->src_set_wr(src->rsc.ctrl_blk, 0); | ||
242 | hw->src_set_pm(src->rsc.ctrl_blk, 0); | ||
243 | hw->src_set_rom(src->rsc.ctrl_blk, 0); | ||
244 | hw->src_set_vo(src->rsc.ctrl_blk, 0); | ||
245 | hw->src_set_st(src->rsc.ctrl_blk, 0); | ||
246 | hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1); | ||
247 | hw->src_set_cisz(src->rsc.ctrl_blk, 0x80); | ||
248 | hw->src_set_sa(src->rsc.ctrl_blk, 0x0); | ||
249 | hw->src_set_la(src->rsc.ctrl_blk, 0x1000); | ||
250 | hw->src_set_ca(src->rsc.ctrl_blk, 0x80); | ||
251 | hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); | ||
252 | hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); | ||
253 | |||
254 | src->rsc.ops->master(&src->rsc); | ||
255 | hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), | ||
256 | src->rsc.ctrl_blk); | ||
257 | |||
258 | for (msr = 1; msr < src->rsc.msr; msr++) { | ||
259 | src->rsc.ops->next_conj(&src->rsc); | ||
260 | hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); | ||
261 | hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), | ||
262 | src->rsc.ctrl_blk); | ||
263 | } | ||
264 | src->rsc.ops->master(&src->rsc); | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static int src_default_config_memwr(struct src *src) | ||
270 | { | ||
271 | struct hw *hw = src->rsc.hw; | ||
272 | |||
273 | hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); | ||
274 | hw->src_set_bm(src->rsc.ctrl_blk, 1); | ||
275 | hw->src_set_rsr(src->rsc.ctrl_blk, 0); | ||
276 | hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16); | ||
277 | hw->src_set_wr(src->rsc.ctrl_blk, 1); | ||
278 | hw->src_set_pm(src->rsc.ctrl_blk, 0); | ||
279 | hw->src_set_rom(src->rsc.ctrl_blk, 0); | ||
280 | hw->src_set_vo(src->rsc.ctrl_blk, 0); | ||
281 | hw->src_set_st(src->rsc.ctrl_blk, 0); | ||
282 | hw->src_set_ilsz(src->rsc.ctrl_blk, 0); | ||
283 | hw->src_set_cisz(src->rsc.ctrl_blk, 0x80); | ||
284 | hw->src_set_sa(src->rsc.ctrl_blk, 0x0); | ||
285 | hw->src_set_la(src->rsc.ctrl_blk, 0x1000); | ||
286 | hw->src_set_ca(src->rsc.ctrl_blk, 0x80); | ||
287 | hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); | ||
288 | hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); | ||
289 | |||
290 | src->rsc.ops->master(&src->rsc); | ||
291 | hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), | ||
292 | src->rsc.ctrl_blk); | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int src_default_config_arcrw(struct src *src) | ||
298 | { | ||
299 | struct hw *hw = src->rsc.hw; | ||
300 | unsigned int rsr = 0, msr = 0; | ||
301 | unsigned int dirty; | ||
302 | |||
303 | hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); | ||
304 | hw->src_set_bm(src->rsc.ctrl_blk, 0); | ||
305 | for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1) | ||
306 | rsr++; | ||
307 | |||
308 | hw->src_set_rsr(src->rsc.ctrl_blk, rsr); | ||
309 | hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32); | ||
310 | hw->src_set_wr(src->rsc.ctrl_blk, 0); | ||
311 | hw->src_set_pm(src->rsc.ctrl_blk, 0); | ||
312 | hw->src_set_rom(src->rsc.ctrl_blk, 0); | ||
313 | hw->src_set_vo(src->rsc.ctrl_blk, 0); | ||
314 | hw->src_set_st(src->rsc.ctrl_blk, 0); | ||
315 | hw->src_set_ilsz(src->rsc.ctrl_blk, 0); | ||
316 | hw->src_set_cisz(src->rsc.ctrl_blk, 0x80); | ||
317 | hw->src_set_sa(src->rsc.ctrl_blk, 0x0); | ||
318 | /*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/ | ||
319 | hw->src_set_la(src->rsc.ctrl_blk, 0x1000); | ||
320 | /*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/ | ||
321 | hw->src_set_ca(src->rsc.ctrl_blk, 0x80); | ||
322 | hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); | ||
323 | hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); | ||
324 | |||
325 | dirty = hw->src_get_dirty(src->rsc.ctrl_blk); | ||
326 | src->rsc.ops->master(&src->rsc); | ||
327 | for (msr = 0; msr < src->rsc.msr; msr++) { | ||
328 | hw->src_set_dirty(src->rsc.ctrl_blk, dirty); | ||
329 | hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), | ||
330 | src->rsc.ctrl_blk); | ||
331 | src->rsc.ops->next_conj(&src->rsc); | ||
332 | } | ||
333 | src->rsc.ops->master(&src->rsc); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static struct src_rsc_ops src_rsc_ops = { | ||
339 | .set_state = src_set_state, | ||
340 | .set_bm = src_set_bm, | ||
341 | .set_sf = src_set_sf, | ||
342 | .set_pm = src_set_pm, | ||
343 | .set_rom = src_set_rom, | ||
344 | .set_vo = src_set_vo, | ||
345 | .set_st = src_set_st, | ||
346 | .set_bp = src_set_bp, | ||
347 | .set_cisz = src_set_cisz, | ||
348 | .set_ca = src_set_ca, | ||
349 | .set_sa = src_set_sa, | ||
350 | .set_la = src_set_la, | ||
351 | .set_pitch = src_set_pitch, | ||
352 | .set_clr_zbufs = src_set_clear_zbufs, | ||
353 | .commit_write = src_commit_write, | ||
354 | .get_ca = src_get_ca, | ||
355 | .init = src_init, | ||
356 | .next_interleave = src_next_interleave, | ||
357 | }; | ||
358 | |||
359 | static int | ||
360 | src_rsc_init(struct src *src, u32 idx, | ||
361 | const struct src_desc *desc, struct src_mgr *mgr) | ||
362 | { | ||
363 | int err = 0; | ||
364 | int i = 0, n = 0; | ||
365 | struct src *p; | ||
366 | |||
367 | n = (MEMRD == desc->mode) ? desc->multi : 1; | ||
368 | for (i = 0, p = src; i < n; i++, p++) { | ||
369 | err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw); | ||
370 | if (err) | ||
371 | goto error1; | ||
372 | |||
373 | /* Initialize src specific rsc operations */ | ||
374 | p->ops = &src_rsc_ops; | ||
375 | p->multi = (0 == i) ? desc->multi : 1; | ||
376 | p->mode = desc->mode; | ||
377 | src_default_config[desc->mode](p); | ||
378 | mgr->src_enable(mgr, p); | ||
379 | p->intlv = p + 1; | ||
380 | } | ||
381 | (--p)->intlv = NULL; /* Set @intlv of the last SRC to NULL */ | ||
382 | |||
383 | mgr->commit_write(mgr); | ||
384 | |||
385 | return 0; | ||
386 | |||
387 | error1: | ||
388 | for (i--, p--; i >= 0; i--, p--) { | ||
389 | mgr->src_disable(mgr, p); | ||
390 | rsc_uninit(&p->rsc); | ||
391 | } | ||
392 | mgr->commit_write(mgr); | ||
393 | return err; | ||
394 | } | ||
395 | |||
396 | static int src_rsc_uninit(struct src *src, struct src_mgr *mgr) | ||
397 | { | ||
398 | int i = 0, n = 0; | ||
399 | struct src *p; | ||
400 | |||
401 | n = (MEMRD == src->mode) ? src->multi : 1; | ||
402 | for (i = 0, p = src; i < n; i++, p++) { | ||
403 | mgr->src_disable(mgr, p); | ||
404 | rsc_uninit(&p->rsc); | ||
405 | p->multi = 0; | ||
406 | p->ops = NULL; | ||
407 | p->mode = NUM_SRCMODES; | ||
408 | p->intlv = NULL; | ||
409 | } | ||
410 | mgr->commit_write(mgr); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int | ||
416 | get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) | ||
417 | { | ||
418 | unsigned int idx = SRC_RESOURCE_NUM; | ||
419 | int err = 0; | ||
420 | struct src *src = NULL; | ||
421 | unsigned long flags; | ||
422 | |||
423 | *rsrc = NULL; | ||
424 | |||
425 | /* Check whether there are sufficient src resources to meet request. */ | ||
426 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
427 | if (MEMRD == desc->mode) | ||
428 | err = mgr_get_resource(&mgr->mgr, desc->multi, &idx); | ||
429 | else | ||
430 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | ||
431 | |||
432 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
433 | if (err) { | ||
434 | printk(KERN_ERR "Can't meet SRC resource request!\n"); | ||
435 | return err; | ||
436 | } | ||
437 | |||
438 | /* Allocate mem for master src resource */ | ||
439 | if (MEMRD == desc->mode) | ||
440 | src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL); | ||
441 | else | ||
442 | src = kzalloc(sizeof(*src), GFP_KERNEL); | ||
443 | |||
444 | if (NULL == src) { | ||
445 | err = -ENOMEM; | ||
446 | goto error1; | ||
447 | } | ||
448 | |||
449 | err = src_rsc_init(src, idx, desc, mgr); | ||
450 | if (err) | ||
451 | goto error2; | ||
452 | |||
453 | *rsrc = src; | ||
454 | |||
455 | return 0; | ||
456 | |||
457 | error2: | ||
458 | kfree(src); | ||
459 | error1: | ||
460 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
461 | if (MEMRD == desc->mode) | ||
462 | mgr_put_resource(&mgr->mgr, desc->multi, idx); | ||
463 | else | ||
464 | mgr_put_resource(&mgr->mgr, 1, idx); | ||
465 | |||
466 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
467 | return err; | ||
468 | } | ||
469 | |||
470 | static int put_src_rsc(struct src_mgr *mgr, struct src *src) | ||
471 | { | ||
472 | unsigned long flags; | ||
473 | |||
474 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
475 | src->rsc.ops->master(&src->rsc); | ||
476 | if (MEMRD == src->mode) | ||
477 | mgr_put_resource(&mgr->mgr, src->multi, | ||
478 | src->rsc.ops->index(&src->rsc)); | ||
479 | else | ||
480 | mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc)); | ||
481 | |||
482 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
483 | src_rsc_uninit(src, mgr); | ||
484 | kfree(src); | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int src_enable_s(struct src_mgr *mgr, struct src *src) | ||
490 | { | ||
491 | struct hw *hw = mgr->mgr.hw; | ||
492 | int i = 0; | ||
493 | |||
494 | src->rsc.ops->master(&src->rsc); | ||
495 | for (i = 0; i < src->rsc.msr; i++) { | ||
496 | hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk, | ||
497 | src->rsc.ops->index(&src->rsc)); | ||
498 | src->rsc.ops->next_conj(&src->rsc); | ||
499 | } | ||
500 | src->rsc.ops->master(&src->rsc); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int src_enable(struct src_mgr *mgr, struct src *src) | ||
506 | { | ||
507 | struct hw *hw = mgr->mgr.hw; | ||
508 | int i = 0; | ||
509 | |||
510 | src->rsc.ops->master(&src->rsc); | ||
511 | for (i = 0; i < src->rsc.msr; i++) { | ||
512 | hw->src_mgr_enb_src(mgr->mgr.ctrl_blk, | ||
513 | src->rsc.ops->index(&src->rsc)); | ||
514 | src->rsc.ops->next_conj(&src->rsc); | ||
515 | } | ||
516 | src->rsc.ops->master(&src->rsc); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int src_disable(struct src_mgr *mgr, struct src *src) | ||
522 | { | ||
523 | struct hw *hw = mgr->mgr.hw; | ||
524 | int i = 0; | ||
525 | |||
526 | src->rsc.ops->master(&src->rsc); | ||
527 | for (i = 0; i < src->rsc.msr; i++) { | ||
528 | hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk, | ||
529 | src->rsc.ops->index(&src->rsc)); | ||
530 | src->rsc.ops->next_conj(&src->rsc); | ||
531 | } | ||
532 | src->rsc.ops->master(&src->rsc); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int src_mgr_commit_write(struct src_mgr *mgr) | ||
538 | { | ||
539 | struct hw *hw = mgr->mgr.hw; | ||
540 | |||
541 | hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk); | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) | ||
547 | { | ||
548 | int err = 0, i = 0; | ||
549 | struct src_mgr *src_mgr; | ||
550 | |||
551 | *rsrc_mgr = NULL; | ||
552 | src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL); | ||
553 | if (NULL == src_mgr) | ||
554 | return -ENOMEM; | ||
555 | |||
556 | err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw); | ||
557 | if (err) | ||
558 | goto error1; | ||
559 | |||
560 | spin_lock_init(&src_mgr->mgr_lock); | ||
561 | conj_mask = ((struct hw *)hw)->src_dirty_conj_mask(); | ||
562 | |||
563 | src_mgr->get_src = get_src_rsc; | ||
564 | src_mgr->put_src = put_src_rsc; | ||
565 | src_mgr->src_enable_s = src_enable_s; | ||
566 | src_mgr->src_enable = src_enable; | ||
567 | src_mgr->src_disable = src_disable; | ||
568 | src_mgr->commit_write = src_mgr_commit_write; | ||
569 | |||
570 | /* Disable all SRC resources. */ | ||
571 | for (i = 0; i < 256; i++) | ||
572 | ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i); | ||
573 | |||
574 | ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk); | ||
575 | |||
576 | *rsrc_mgr = src_mgr; | ||
577 | |||
578 | return 0; | ||
579 | |||
580 | error1: | ||
581 | kfree(src_mgr); | ||
582 | return err; | ||
583 | } | ||
584 | |||
585 | int src_mgr_destroy(struct src_mgr *src_mgr) | ||
586 | { | ||
587 | rsc_mgr_uninit(&src_mgr->mgr); | ||
588 | kfree(src_mgr); | ||
589 | |||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | /* SRCIMP resource manager operations */ | ||
594 | |||
595 | static int srcimp_master(struct rsc *rsc) | ||
596 | { | ||
597 | rsc->conj = 0; | ||
598 | return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0]; | ||
599 | } | ||
600 | |||
601 | static int srcimp_next_conj(struct rsc *rsc) | ||
602 | { | ||
603 | rsc->conj++; | ||
604 | return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj]; | ||
605 | } | ||
606 | |||
607 | static int srcimp_index(const struct rsc *rsc) | ||
608 | { | ||
609 | return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj]; | ||
610 | } | ||
611 | |||
612 | static struct rsc_ops srcimp_basic_rsc_ops = { | ||
613 | .master = srcimp_master, | ||
614 | .next_conj = srcimp_next_conj, | ||
615 | .index = srcimp_index, | ||
616 | .output_slot = NULL, | ||
617 | }; | ||
618 | |||
619 | static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input) | ||
620 | { | ||
621 | struct imapper *entry = NULL; | ||
622 | int i = 0; | ||
623 | |||
624 | srcimp->rsc.ops->master(&srcimp->rsc); | ||
625 | src->rsc.ops->master(&src->rsc); | ||
626 | input->ops->master(input); | ||
627 | |||
628 | /* Program master and conjugate resources */ | ||
629 | for (i = 0; i < srcimp->rsc.msr; i++) { | ||
630 | entry = &srcimp->imappers[i]; | ||
631 | entry->slot = input->ops->output_slot(input); | ||
632 | entry->user = src->rsc.ops->index(&src->rsc); | ||
633 | entry->addr = srcimp->rsc.ops->index(&srcimp->rsc); | ||
634 | srcimp->mgr->imap_add(srcimp->mgr, entry); | ||
635 | srcimp->mapped |= (0x1 << i); | ||
636 | |||
637 | srcimp->rsc.ops->next_conj(&srcimp->rsc); | ||
638 | input->ops->next_conj(input); | ||
639 | } | ||
640 | |||
641 | srcimp->rsc.ops->master(&srcimp->rsc); | ||
642 | input->ops->master(input); | ||
643 | |||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static int srcimp_unmap(struct srcimp *srcimp) | ||
648 | { | ||
649 | int i = 0; | ||
650 | |||
651 | /* Program master and conjugate resources */ | ||
652 | for (i = 0; i < srcimp->rsc.msr; i++) { | ||
653 | if (srcimp->mapped & (0x1 << i)) { | ||
654 | srcimp->mgr->imap_delete(srcimp->mgr, | ||
655 | &srcimp->imappers[i]); | ||
656 | srcimp->mapped &= ~(0x1 << i); | ||
657 | } | ||
658 | } | ||
659 | |||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | static struct srcimp_rsc_ops srcimp_ops = { | ||
664 | .map = srcimp_map, | ||
665 | .unmap = srcimp_unmap | ||
666 | }; | ||
667 | |||
668 | static int srcimp_rsc_init(struct srcimp *srcimp, | ||
669 | const struct srcimp_desc *desc, | ||
670 | struct srcimp_mgr *mgr) | ||
671 | { | ||
672 | int err = 0; | ||
673 | |||
674 | err = rsc_init(&srcimp->rsc, srcimp->idx[0], | ||
675 | SRCIMP, desc->msr, mgr->mgr.hw); | ||
676 | if (err) | ||
677 | return err; | ||
678 | |||
679 | /* Reserve memory for imapper nodes */ | ||
680 | srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr, | ||
681 | GFP_KERNEL); | ||
682 | if (NULL == srcimp->imappers) { | ||
683 | err = -ENOMEM; | ||
684 | goto error1; | ||
685 | } | ||
686 | |||
687 | /* Set srcimp specific operations */ | ||
688 | srcimp->rsc.ops = &srcimp_basic_rsc_ops; | ||
689 | srcimp->ops = &srcimp_ops; | ||
690 | srcimp->mgr = mgr; | ||
691 | |||
692 | srcimp->rsc.ops->master(&srcimp->rsc); | ||
693 | |||
694 | return 0; | ||
695 | |||
696 | error1: | ||
697 | rsc_uninit(&srcimp->rsc); | ||
698 | return err; | ||
699 | } | ||
700 | |||
701 | static int srcimp_rsc_uninit(struct srcimp *srcimp) | ||
702 | { | ||
703 | if (NULL != srcimp->imappers) { | ||
704 | kfree(srcimp->imappers); | ||
705 | srcimp->imappers = NULL; | ||
706 | } | ||
707 | srcimp->ops = NULL; | ||
708 | srcimp->mgr = NULL; | ||
709 | rsc_uninit(&srcimp->rsc); | ||
710 | |||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static int get_srcimp_rsc(struct srcimp_mgr *mgr, | ||
715 | const struct srcimp_desc *desc, | ||
716 | struct srcimp **rsrcimp) | ||
717 | { | ||
718 | int err = 0, i = 0; | ||
719 | unsigned int idx = 0; | ||
720 | struct srcimp *srcimp = NULL; | ||
721 | unsigned long flags; | ||
722 | |||
723 | *rsrcimp = NULL; | ||
724 | |||
725 | /* Allocate mem for SRCIMP resource */ | ||
726 | srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL); | ||
727 | if (NULL == srcimp) { | ||
728 | err = -ENOMEM; | ||
729 | return err; | ||
730 | } | ||
731 | |||
732 | /* Check whether there are sufficient SRCIMP resources. */ | ||
733 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
734 | for (i = 0; i < desc->msr; i++) { | ||
735 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | ||
736 | if (err) | ||
737 | break; | ||
738 | |||
739 | srcimp->idx[i] = idx; | ||
740 | } | ||
741 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
742 | if (err) { | ||
743 | printk(KERN_ERR "Can't meet SRCIMP resource request!\n"); | ||
744 | goto error1; | ||
745 | } | ||
746 | |||
747 | err = srcimp_rsc_init(srcimp, desc, mgr); | ||
748 | if (err) | ||
749 | goto error1; | ||
750 | |||
751 | *rsrcimp = srcimp; | ||
752 | |||
753 | return 0; | ||
754 | |||
755 | error1: | ||
756 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
757 | for (i--; i >= 0; i--) | ||
758 | mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]); | ||
759 | |||
760 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
761 | kfree(srcimp); | ||
762 | return err; | ||
763 | } | ||
764 | |||
765 | static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp) | ||
766 | { | ||
767 | unsigned long flags; | ||
768 | int i = 0; | ||
769 | |||
770 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
771 | for (i = 0; i < srcimp->rsc.msr; i++) | ||
772 | mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]); | ||
773 | |||
774 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
775 | srcimp_rsc_uninit(srcimp); | ||
776 | kfree(srcimp); | ||
777 | |||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | static int srcimp_map_op(void *data, struct imapper *entry) | ||
782 | { | ||
783 | struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr; | ||
784 | struct hw *hw = mgr->hw; | ||
785 | |||
786 | hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot); | ||
787 | hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user); | ||
788 | hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next); | ||
789 | hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr); | ||
790 | hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk); | ||
791 | |||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry) | ||
796 | { | ||
797 | unsigned long flags; | ||
798 | int err = 0; | ||
799 | |||
800 | spin_lock_irqsave(&mgr->imap_lock, flags); | ||
801 | if ((0 == entry->addr) && (mgr->init_imap_added)) { | ||
802 | input_mapper_delete(&mgr->imappers, | ||
803 | mgr->init_imap, srcimp_map_op, mgr); | ||
804 | mgr->init_imap_added = 0; | ||
805 | } | ||
806 | err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr); | ||
807 | spin_unlock_irqrestore(&mgr->imap_lock, flags); | ||
808 | |||
809 | return err; | ||
810 | } | ||
811 | |||
812 | static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) | ||
813 | { | ||
814 | unsigned long flags; | ||
815 | int err = 0; | ||
816 | |||
817 | spin_lock_irqsave(&mgr->imap_lock, flags); | ||
818 | err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr); | ||
819 | if (list_empty(&mgr->imappers)) { | ||
820 | input_mapper_add(&mgr->imappers, mgr->init_imap, | ||
821 | srcimp_map_op, mgr); | ||
822 | mgr->init_imap_added = 1; | ||
823 | } | ||
824 | spin_unlock_irqrestore(&mgr->imap_lock, flags); | ||
825 | |||
826 | return err; | ||
827 | } | ||
828 | |||
829 | int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) | ||
830 | { | ||
831 | int err = 0; | ||
832 | struct srcimp_mgr *srcimp_mgr; | ||
833 | struct imapper *entry; | ||
834 | |||
835 | *rsrcimp_mgr = NULL; | ||
836 | srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL); | ||
837 | if (NULL == srcimp_mgr) | ||
838 | return -ENOMEM; | ||
839 | |||
840 | err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw); | ||
841 | if (err) | ||
842 | goto error1; | ||
843 | |||
844 | spin_lock_init(&srcimp_mgr->mgr_lock); | ||
845 | spin_lock_init(&srcimp_mgr->imap_lock); | ||
846 | INIT_LIST_HEAD(&srcimp_mgr->imappers); | ||
847 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
848 | if (NULL == entry) { | ||
849 | err = -ENOMEM; | ||
850 | goto error2; | ||
851 | } | ||
852 | entry->slot = entry->addr = entry->next = entry->user = 0; | ||
853 | list_add(&entry->list, &srcimp_mgr->imappers); | ||
854 | srcimp_mgr->init_imap = entry; | ||
855 | srcimp_mgr->init_imap_added = 1; | ||
856 | |||
857 | srcimp_mgr->get_srcimp = get_srcimp_rsc; | ||
858 | srcimp_mgr->put_srcimp = put_srcimp_rsc; | ||
859 | srcimp_mgr->imap_add = srcimp_imap_add; | ||
860 | srcimp_mgr->imap_delete = srcimp_imap_delete; | ||
861 | |||
862 | *rsrcimp_mgr = srcimp_mgr; | ||
863 | |||
864 | return 0; | ||
865 | |||
866 | error2: | ||
867 | rsc_mgr_uninit(&srcimp_mgr->mgr); | ||
868 | error1: | ||
869 | kfree(srcimp_mgr); | ||
870 | return err; | ||
871 | } | ||
872 | |||
873 | int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr) | ||
874 | { | ||
875 | unsigned long flags; | ||
876 | |||
877 | /* free src input mapper list */ | ||
878 | spin_lock_irqsave(&srcimp_mgr->imap_lock, flags); | ||
879 | free_input_mapper_list(&srcimp_mgr->imappers); | ||
880 | spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags); | ||
881 | |||
882 | rsc_mgr_uninit(&srcimp_mgr->mgr); | ||
883 | kfree(srcimp_mgr); | ||
884 | |||
885 | return 0; | ||
886 | } | ||
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h new file mode 100644 index 000000000000..259366aabcac --- /dev/null +++ b/sound/pci/ctxfi/ctsrc.h | |||
@@ -0,0 +1,149 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctsrc.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of the Sample Rate Convertor | ||
12 | * resource management object. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date May 13 2008 | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef CTSRC_H | ||
20 | #define CTSRC_H | ||
21 | |||
22 | #include "ctresource.h" | ||
23 | #include "ctimap.h" | ||
24 | #include <linux/spinlock.h> | ||
25 | #include <linux/list.h> | ||
26 | |||
27 | #define SRC_STATE_OFF 0x0 | ||
28 | #define SRC_STATE_INIT 0x4 | ||
29 | #define SRC_STATE_RUN 0x5 | ||
30 | |||
31 | #define SRC_SF_U8 0x0 | ||
32 | #define SRC_SF_S16 0x1 | ||
33 | #define SRC_SF_S24 0x2 | ||
34 | #define SRC_SF_S32 0x3 | ||
35 | #define SRC_SF_F32 0x4 | ||
36 | |||
37 | /* Define the descriptor of a src resource */ | ||
38 | enum SRCMODE { | ||
39 | MEMRD, /* Read data from host memory */ | ||
40 | MEMWR, /* Write data to host memory */ | ||
41 | ARCRW, /* Read from and write to audio ring channel */ | ||
42 | NUM_SRCMODES | ||
43 | }; | ||
44 | |||
45 | struct src_rsc_ops; | ||
46 | |||
47 | struct src { | ||
48 | struct rsc rsc; /* Basic resource info */ | ||
49 | struct src *intlv; /* Pointer to next interleaved SRC in a series */ | ||
50 | struct src_rsc_ops *ops; /* SRC specific operations */ | ||
51 | /* Number of contiguous srcs for interleaved usage */ | ||
52 | unsigned char multi; | ||
53 | unsigned char mode; /* Working mode of this SRC resource */ | ||
54 | }; | ||
55 | |||
56 | struct src_rsc_ops { | ||
57 | int (*set_state)(struct src *src, unsigned int state); | ||
58 | int (*set_bm)(struct src *src, unsigned int bm); | ||
59 | int (*set_sf)(struct src *src, unsigned int sf); | ||
60 | int (*set_pm)(struct src *src, unsigned int pm); | ||
61 | int (*set_rom)(struct src *src, unsigned int rom); | ||
62 | int (*set_vo)(struct src *src, unsigned int vo); | ||
63 | int (*set_st)(struct src *src, unsigned int st); | ||
64 | int (*set_bp)(struct src *src, unsigned int bp); | ||
65 | int (*set_cisz)(struct src *src, unsigned int cisz); | ||
66 | int (*set_ca)(struct src *src, unsigned int ca); | ||
67 | int (*set_sa)(struct src *src, unsigned int sa); | ||
68 | int (*set_la)(struct src *src, unsigned int la); | ||
69 | int (*set_pitch)(struct src *src, unsigned int pitch); | ||
70 | int (*set_clr_zbufs)(struct src *src); | ||
71 | int (*commit_write)(struct src *src); | ||
72 | int (*get_ca)(struct src *src); | ||
73 | int (*init)(struct src *src); | ||
74 | struct src* (*next_interleave)(struct src *src); | ||
75 | }; | ||
76 | |||
77 | /* Define src resource request description info */ | ||
78 | struct src_desc { | ||
79 | /* Number of contiguous master srcs for interleaved usage */ | ||
80 | unsigned char multi; | ||
81 | unsigned char msr; | ||
82 | unsigned char mode; /* Working mode of the requested srcs */ | ||
83 | }; | ||
84 | |||
85 | /* Define src manager object */ | ||
86 | struct src_mgr { | ||
87 | struct rsc_mgr mgr; /* Basic resource manager info */ | ||
88 | spinlock_t mgr_lock; | ||
89 | |||
90 | /* request src resource */ | ||
91 | int (*get_src)(struct src_mgr *mgr, | ||
92 | const struct src_desc *desc, struct src **rsrc); | ||
93 | /* return src resource */ | ||
94 | int (*put_src)(struct src_mgr *mgr, struct src *src); | ||
95 | int (*src_enable_s)(struct src_mgr *mgr, struct src *src); | ||
96 | int (*src_enable)(struct src_mgr *mgr, struct src *src); | ||
97 | int (*src_disable)(struct src_mgr *mgr, struct src *src); | ||
98 | int (*commit_write)(struct src_mgr *mgr); | ||
99 | }; | ||
100 | |||
101 | /* Define the descriptor of a SRC Input Mapper resource */ | ||
102 | struct srcimp_mgr; | ||
103 | struct srcimp_rsc_ops; | ||
104 | |||
105 | struct srcimp { | ||
106 | struct rsc rsc; | ||
107 | unsigned char idx[8]; | ||
108 | struct imapper *imappers; | ||
109 | unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */ | ||
110 | struct srcimp_mgr *mgr; | ||
111 | struct srcimp_rsc_ops *ops; | ||
112 | }; | ||
113 | |||
114 | struct srcimp_rsc_ops { | ||
115 | int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input); | ||
116 | int (*unmap)(struct srcimp *srcimp); | ||
117 | }; | ||
118 | |||
119 | /* Define SRCIMP resource request description info */ | ||
120 | struct srcimp_desc { | ||
121 | unsigned int msr; | ||
122 | }; | ||
123 | |||
124 | struct srcimp_mgr { | ||
125 | struct rsc_mgr mgr; /* Basic resource manager info */ | ||
126 | spinlock_t mgr_lock; | ||
127 | spinlock_t imap_lock; | ||
128 | struct list_head imappers; | ||
129 | struct imapper *init_imap; | ||
130 | unsigned int init_imap_added; | ||
131 | |||
132 | /* request srcimp resource */ | ||
133 | int (*get_srcimp)(struct srcimp_mgr *mgr, | ||
134 | const struct srcimp_desc *desc, | ||
135 | struct srcimp **rsrcimp); | ||
136 | /* return srcimp resource */ | ||
137 | int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp); | ||
138 | int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry); | ||
139 | int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry); | ||
140 | }; | ||
141 | |||
142 | /* Constructor and destructor of SRC resource manager */ | ||
143 | int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr); | ||
144 | int src_mgr_destroy(struct src_mgr *src_mgr); | ||
145 | /* Constructor and destructor of SRCIMP resource manager */ | ||
146 | int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr); | ||
147 | int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr); | ||
148 | |||
149 | #endif /* CTSRC_H */ | ||
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c new file mode 100644 index 000000000000..46ca04ce9210 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctvmem.c | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the implementation of virtual memory management object | ||
12 | * for card device. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date Apr 1 2008 | ||
16 | */ | ||
17 | |||
18 | #include "ctvmem.h" | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/mm.h> | ||
21 | #include <asm/page.h> /* for PAGE_SIZE macro definition */ | ||
22 | #include <linux/io.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | |||
25 | #define CT_PTES_PER_PAGE (PAGE_SIZE / sizeof(void *)) | ||
26 | #define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * PAGE_SIZE) | ||
27 | |||
28 | /* * | ||
29 | * Find or create vm block based on requested @size. | ||
30 | * @size must be page aligned. | ||
31 | * */ | ||
32 | static struct ct_vm_block * | ||
33 | get_vm_block(struct ct_vm *vm, unsigned int size) | ||
34 | { | ||
35 | struct ct_vm_block *block = NULL, *entry = NULL; | ||
36 | struct list_head *pos = NULL; | ||
37 | |||
38 | list_for_each(pos, &vm->unused) { | ||
39 | entry = list_entry(pos, struct ct_vm_block, list); | ||
40 | if (entry->size >= size) | ||
41 | break; /* found a block that is big enough */ | ||
42 | } | ||
43 | if (pos == &vm->unused) | ||
44 | return NULL; | ||
45 | |||
46 | if (entry->size == size) { | ||
47 | /* Move the vm node from unused list to used list directly */ | ||
48 | list_del(&entry->list); | ||
49 | list_add(&entry->list, &vm->used); | ||
50 | vm->size -= size; | ||
51 | return entry; | ||
52 | } | ||
53 | |||
54 | block = kzalloc(sizeof(*block), GFP_KERNEL); | ||
55 | if (NULL == block) | ||
56 | return NULL; | ||
57 | |||
58 | block->addr = entry->addr; | ||
59 | block->size = size; | ||
60 | list_add(&block->list, &vm->used); | ||
61 | entry->addr += size; | ||
62 | entry->size -= size; | ||
63 | vm->size -= size; | ||
64 | |||
65 | return block; | ||
66 | } | ||
67 | |||
68 | static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) | ||
69 | { | ||
70 | struct ct_vm_block *entry = NULL, *pre_ent = NULL; | ||
71 | struct list_head *pos = NULL, *pre = NULL; | ||
72 | |||
73 | list_del(&block->list); | ||
74 | vm->size += block->size; | ||
75 | |||
76 | list_for_each(pos, &vm->unused) { | ||
77 | entry = list_entry(pos, struct ct_vm_block, list); | ||
78 | if (entry->addr >= (block->addr + block->size)) | ||
79 | break; /* found a position */ | ||
80 | } | ||
81 | if (pos == &vm->unused) { | ||
82 | list_add_tail(&block->list, &vm->unused); | ||
83 | entry = block; | ||
84 | } else { | ||
85 | if ((block->addr + block->size) == entry->addr) { | ||
86 | entry->addr = block->addr; | ||
87 | entry->size += block->size; | ||
88 | kfree(block); | ||
89 | } else { | ||
90 | __list_add(&block->list, pos->prev, pos); | ||
91 | entry = block; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | pos = &entry->list; | ||
96 | pre = pos->prev; | ||
97 | while (pre != &vm->unused) { | ||
98 | entry = list_entry(pos, struct ct_vm_block, list); | ||
99 | pre_ent = list_entry(pre, struct ct_vm_block, list); | ||
100 | if ((pre_ent->addr + pre_ent->size) > entry->addr) | ||
101 | break; | ||
102 | |||
103 | pre_ent->size += entry->size; | ||
104 | list_del(pos); | ||
105 | kfree(entry); | ||
106 | pos = pre; | ||
107 | pre = pos->prev; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* Map host addr (kmalloced/vmalloced) to device logical addr. */ | ||
112 | static struct ct_vm_block * | ||
113 | ct_vm_map(struct ct_vm *vm, void *host_addr, int size) | ||
114 | { | ||
115 | struct ct_vm_block *block = NULL; | ||
116 | unsigned long pte_start; | ||
117 | unsigned long i; | ||
118 | unsigned long pages; | ||
119 | unsigned long start_phys; | ||
120 | unsigned long *ptp; | ||
121 | |||
122 | /* do mapping */ | ||
123 | if ((unsigned long)host_addr >= VMALLOC_START) { | ||
124 | printk(KERN_ERR "Fail! Not support vmalloced addr now!\n"); | ||
125 | return NULL; | ||
126 | } | ||
127 | |||
128 | if (size > vm->size) { | ||
129 | printk(KERN_ERR "Fail! No sufficient device virtural " | ||
130 | "memory space available!\n"); | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
134 | start_phys = (virt_to_phys(host_addr) & PAGE_MASK); | ||
135 | pages = (PAGE_ALIGN(virt_to_phys(host_addr) + size) | ||
136 | - start_phys) >> PAGE_SHIFT; | ||
137 | |||
138 | ptp = vm->ptp[0]; | ||
139 | |||
140 | block = get_vm_block(vm, (pages << PAGE_SHIFT)); | ||
141 | if (block == NULL) { | ||
142 | printk(KERN_ERR "No virtual memory block that is big " | ||
143 | "enough to allocate!\n"); | ||
144 | return NULL; | ||
145 | } | ||
146 | |||
147 | pte_start = (block->addr >> PAGE_SHIFT); | ||
148 | for (i = 0; i < pages; i++) | ||
149 | ptp[pte_start+i] = start_phys + (i << PAGE_SHIFT); | ||
150 | |||
151 | block->addr += (virt_to_phys(host_addr) & (~PAGE_MASK)); | ||
152 | block->size = size; | ||
153 | |||
154 | return block; | ||
155 | } | ||
156 | |||
157 | static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) | ||
158 | { | ||
159 | /* do unmapping */ | ||
160 | block->size = ((block->addr + block->size + PAGE_SIZE - 1) | ||
161 | & PAGE_MASK) - (block->addr & PAGE_MASK); | ||
162 | block->addr &= PAGE_MASK; | ||
163 | put_vm_block(vm, block); | ||
164 | } | ||
165 | |||
166 | /* * | ||
167 | * return the host (kmalloced) addr of the @index-th device | ||
168 | * page talbe page on success, or NULL on failure. | ||
169 | * The first returned NULL indicates the termination. | ||
170 | * */ | ||
171 | static void * | ||
172 | ct_get_ptp_virt(struct ct_vm *vm, int index) | ||
173 | { | ||
174 | void *addr; | ||
175 | |||
176 | addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index]; | ||
177 | |||
178 | return addr; | ||
179 | } | ||
180 | |||
181 | int ct_vm_create(struct ct_vm **rvm) | ||
182 | { | ||
183 | struct ct_vm *vm; | ||
184 | struct ct_vm_block *block; | ||
185 | int i; | ||
186 | |||
187 | *rvm = NULL; | ||
188 | |||
189 | vm = kzalloc(sizeof(*vm), GFP_KERNEL); | ||
190 | if (NULL == vm) | ||
191 | return -ENOMEM; | ||
192 | |||
193 | /* Allocate page table pages */ | ||
194 | for (i = 0; i < CT_PTP_NUM; i++) { | ||
195 | vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
196 | if (NULL == vm->ptp[i]) | ||
197 | break; | ||
198 | } | ||
199 | if (!i) { | ||
200 | /* no page table pages are allocated */ | ||
201 | kfree(vm); | ||
202 | return -ENOMEM; | ||
203 | } | ||
204 | vm->size = CT_ADDRS_PER_PAGE * i; | ||
205 | /* Initialise remaining ptps */ | ||
206 | for (; i < CT_PTP_NUM; i++) | ||
207 | vm->ptp[i] = NULL; | ||
208 | |||
209 | vm->map = ct_vm_map; | ||
210 | vm->unmap = ct_vm_unmap; | ||
211 | vm->get_ptp_virt = ct_get_ptp_virt; | ||
212 | INIT_LIST_HEAD(&vm->unused); | ||
213 | INIT_LIST_HEAD(&vm->used); | ||
214 | block = kzalloc(sizeof(*block), GFP_KERNEL); | ||
215 | if (NULL != block) { | ||
216 | block->addr = 0; | ||
217 | block->size = vm->size; | ||
218 | list_add(&block->list, &vm->unused); | ||
219 | } | ||
220 | |||
221 | *rvm = vm; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | /* The caller must ensure no mapping pages are being used | ||
226 | * by hardware before calling this function */ | ||
227 | void ct_vm_destroy(struct ct_vm *vm) | ||
228 | { | ||
229 | int i; | ||
230 | struct list_head *pos = NULL; | ||
231 | struct ct_vm_block *entry = NULL; | ||
232 | |||
233 | /* free used and unused list nodes */ | ||
234 | while (!list_empty(&vm->used)) { | ||
235 | pos = vm->used.next; | ||
236 | list_del(pos); | ||
237 | entry = list_entry(pos, struct ct_vm_block, list); | ||
238 | kfree(entry); | ||
239 | } | ||
240 | while (!list_empty(&vm->unused)) { | ||
241 | pos = vm->unused.next; | ||
242 | list_del(pos); | ||
243 | entry = list_entry(pos, struct ct_vm_block, list); | ||
244 | kfree(entry); | ||
245 | } | ||
246 | |||
247 | /* free allocated page table pages */ | ||
248 | for (i = 0; i < CT_PTP_NUM; i++) | ||
249 | kfree(vm->ptp[i]); | ||
250 | |||
251 | vm->size = 0; | ||
252 | |||
253 | kfree(vm); | ||
254 | } | ||
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h new file mode 100644 index 000000000000..4eb5bdd5cad4 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /** | ||
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
3 | * | ||
4 | * This source file is released under GPL v2 license (no other versions). | ||
5 | * See the COPYING file included in the main directory of this source | ||
6 | * distribution for the license terms and conditions. | ||
7 | * | ||
8 | * @File ctvmem.h | ||
9 | * | ||
10 | * @Brief | ||
11 | * This file contains the definition of virtual memory management object | ||
12 | * for card device. | ||
13 | * | ||
14 | * @Author Liu Chun | ||
15 | * @Date Mar 28 2008 | ||
16 | */ | ||
17 | |||
18 | #ifndef CTVMEM_H | ||
19 | #define CTVMEM_H | ||
20 | |||
21 | #define CT_PTP_NUM 1 /* num of device page table pages */ | ||
22 | |||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/list.h> | ||
25 | |||
26 | struct ct_vm_block { | ||
27 | unsigned int addr; /* starting logical addr of this block */ | ||
28 | unsigned int size; /* size of this device virtual mem block */ | ||
29 | struct list_head list; | ||
30 | }; | ||
31 | |||
32 | /* Virtual memory management object for card device */ | ||
33 | struct ct_vm { | ||
34 | void *ptp[CT_PTP_NUM]; /* Device page table pages */ | ||
35 | unsigned int size; /* Available addr space in bytes */ | ||
36 | struct list_head unused; /* List of unused blocks */ | ||
37 | struct list_head used; /* List of used blocks */ | ||
38 | |||
39 | /* Map host addr (kmalloced/vmalloced) to device logical addr. */ | ||
40 | struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, int size); | ||
41 | /* Unmap device logical addr area. */ | ||
42 | void (*unmap)(struct ct_vm *, struct ct_vm_block *block); | ||
43 | void *(*get_ptp_virt)(struct ct_vm *vm, int index); | ||
44 | }; | ||
45 | |||
46 | int ct_vm_create(struct ct_vm **rvm); | ||
47 | void ct_vm_destroy(struct ct_vm *vm); | ||
48 | |||
49 | #endif /* CTVMEM_H */ | ||
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c new file mode 100644 index 000000000000..f91144036512 --- /dev/null +++ b/sound/pci/ctxfi/xfi.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * xfi linux driver. | ||
3 | * | ||
4 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | ||
5 | * | ||
6 | * This source file is released under GPL v2 license (no other versions). | ||
7 | * See the COPYING file included in the main directory of this source | ||
8 | * distribution for the license terms and conditions. | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/pci.h> | ||
13 | #include <linux/moduleparam.h> | ||
14 | #include <sound/core.h> | ||
15 | #include <sound/initval.h> | ||
16 | #include "ctatc.h" | ||
17 | #include "ctdrv.h" | ||
18 | |||
19 | MODULE_AUTHOR("Creative Technology Ltd"); | ||
20 | MODULE_DESCRIPTION("X-Fi driver version 1.03"); | ||
21 | MODULE_LICENSE("GPLv2"); | ||
22 | MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); | ||
23 | |||
24 | static unsigned int reference_rate = 48000; | ||
25 | static unsigned int multiple = 2; | ||
26 | module_param(reference_rate, uint, S_IRUGO); | ||
27 | module_param(multiple, uint, S_IRUGO); | ||
28 | |||
29 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
30 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
31 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | ||
32 | |||
33 | static struct pci_device_id ct_pci_dev_ids[] = { | ||
34 | /* only X-Fi is supported, so... */ | ||
35 | { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K1) }, | ||
36 | { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K2) }, | ||
37 | { 0, } | ||
38 | }; | ||
39 | MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); | ||
40 | |||
41 | static int __devinit | ||
42 | ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) | ||
43 | { | ||
44 | static int dev; | ||
45 | struct snd_card *card; | ||
46 | struct ct_atc *atc; | ||
47 | int err; | ||
48 | |||
49 | if (dev >= SNDRV_CARDS) | ||
50 | return -ENODEV; | ||
51 | |||
52 | if (!enable[dev]) { | ||
53 | dev++; | ||
54 | return -ENOENT; | ||
55 | } | ||
56 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); | ||
57 | if (err) | ||
58 | return err; | ||
59 | if ((reference_rate != 48000) && (reference_rate != 44100)) { | ||
60 | printk(KERN_ERR "Invalid reference_rate value %u!!!\n" | ||
61 | "The valid values for reference_rate " | ||
62 | "are 48000 and 44100.\nValue 48000 is " | ||
63 | "assumed.\n", reference_rate); | ||
64 | reference_rate = 48000; | ||
65 | } | ||
66 | if ((multiple != 1) && (multiple != 2)) { | ||
67 | printk(KERN_ERR "Invalid multiple value %u!!!\nThe valid " | ||
68 | "values for multiple are 1 and 2.\nValue 2 " | ||
69 | "is assumed.\n", multiple); | ||
70 | multiple = 2; | ||
71 | } | ||
72 | err = ct_atc_create(card, pci, reference_rate, multiple, &atc); | ||
73 | if (err < 0) | ||
74 | goto error; | ||
75 | |||
76 | card->private_data = atc; | ||
77 | |||
78 | /* Create alsa devices supported by this card */ | ||
79 | err = atc->create_alsa_devs(atc); | ||
80 | if (err < 0) | ||
81 | goto error; | ||
82 | |||
83 | strcpy(card->driver, "SB-XFi"); | ||
84 | strcpy(card->shortname, "Creative X-Fi"); | ||
85 | strcpy(card->longname, "Creative ALSA Driver X-Fi"); | ||
86 | |||
87 | err = snd_card_register(card); | ||
88 | if (err < 0) | ||
89 | goto error; | ||
90 | |||
91 | pci_set_drvdata(pci, card); | ||
92 | dev++; | ||
93 | |||
94 | return 0; | ||
95 | |||
96 | error: | ||
97 | snd_card_free(card); | ||
98 | return err; | ||
99 | } | ||
100 | |||
101 | static void __devexit ct_card_remove(struct pci_dev *pci) | ||
102 | { | ||
103 | snd_card_free(pci_get_drvdata(pci)); | ||
104 | pci_set_drvdata(pci, NULL); | ||
105 | } | ||
106 | |||
107 | static struct pci_driver ct_driver = { | ||
108 | .name = "SB-XFi", | ||
109 | .id_table = ct_pci_dev_ids, | ||
110 | .probe = ct_card_probe, | ||
111 | .remove = __devexit_p(ct_card_remove), | ||
112 | }; | ||
113 | |||
114 | static int __init ct_card_init(void) | ||
115 | { | ||
116 | return pci_register_driver(&ct_driver); | ||
117 | } | ||
118 | |||
119 | static void __exit ct_card_exit(void) | ||
120 | { | ||
121 | pci_unregister_driver(&ct_driver); | ||
122 | } | ||
123 | |||
124 | module_init(ct_card_init) | ||
125 | module_exit(ct_card_exit) | ||