diff options
Diffstat (limited to 'sound/pci/ctxfi')
30 files changed, 12817 insertions, 0 deletions
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile new file mode 100644 index 000000000000..15075f89e98a --- /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 cttimer.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..f2e34e3f27ee --- /dev/null +++ b/sound/pci/ctxfi/ct20k1reg.h | |||
@@ -0,0 +1,636 @@ | |||
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 | # define TIMR_IE (1<<15) | ||
593 | # define TIMR_IP (1<<14) | ||
594 | |||
595 | #define GIP 0x1C6010 | ||
596 | #define GIE 0x1C6014 | ||
597 | #define DIE 0x1C6018 | ||
598 | #define DIC 0x1C601C | ||
599 | #define GPIO 0x1C6020 | ||
600 | #define GPIOCTL 0x1C6024 | ||
601 | #define GPIP 0x1C6028 | ||
602 | #define GPIE 0x1C602C | ||
603 | #define DSPINT0 0x1C6030 | ||
604 | #define DSPEIOC 0x1C6034 | ||
605 | #define MUADAT 0x1C6040 | ||
606 | #define MUACMD 0x1C6044 | ||
607 | #define MUASTAT 0x1C6044 | ||
608 | #define MUBDAT 0x1C6048 | ||
609 | #define MUBCMD 0x1C604C | ||
610 | #define MUBSTAT 0x1C604C | ||
611 | #define UARTCMA 0x1C6050 | ||
612 | #define UARTCMB 0x1C6054 | ||
613 | #define UARTIP 0x1C6058 | ||
614 | #define UARTIE 0x1C605C | ||
615 | #define PLLCTL 0x1C6060 | ||
616 | #define PLLDCD 0x1C6064 | ||
617 | #define GCTL 0x1C6070 | ||
618 | #define ID0 0x1C6080 | ||
619 | #define ID1 0x1C6084 | ||
620 | #define ID2 0x1C6088 | ||
621 | #define ID3 0x1C608C | ||
622 | #define SDRCTL 0x1C7000 | ||
623 | |||
624 | |||
625 | #define I2SA_L 0x0L | ||
626 | #define I2SA_R 0x1L | ||
627 | #define I2SB_L 0x8L | ||
628 | #define I2SB_R 0x9L | ||
629 | #define I2SC_L 0x10L | ||
630 | #define I2SC_R 0x11L | ||
631 | #define I2SD_L 0x18L | ||
632 | #define I2SD_R 0x19L | ||
633 | |||
634 | #endif /* CT20K1REG_H */ | ||
635 | |||
636 | |||
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..a1db51b3ead8 --- /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; | ||
62 | |||
63 | 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; | ||
79 | |||
80 | 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; | ||
89 | |||
90 | 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; | ||
99 | |||
100 | 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; | ||
116 | unsigned int index; | ||
117 | int i; | ||
118 | struct rsc *input; | ||
119 | struct sum *sum; | ||
120 | |||
121 | 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; | ||
162 | unsigned int index; | ||
163 | |||
164 | 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; | ||
174 | |||
175 | 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; | ||
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, i; | ||
237 | unsigned int idx; | ||
238 | struct amixer *amixer; | ||
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 "ctxfi: 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; | ||
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; | ||
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; | ||
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, i; | ||
392 | unsigned int idx; | ||
393 | struct sum *sum; | ||
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 "ctxfi: 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; | ||
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; | ||
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..80fb2baed7a7 --- /dev/null +++ b/sound/pci/ctxfi/ctatc.c | |||
@@ -0,0 +1,1619 @@ | |||
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 "cthardware.h" | ||
22 | #include "ctsrc.h" | ||
23 | #include "ctamixer.h" | ||
24 | #include "ctdaio.h" | ||
25 | #include "cttimer.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 struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { | ||
43 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X), | ||
44 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X), | ||
45 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X), | ||
46 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X), | ||
47 | SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, 0x6000, | ||
48 | "UAA", CTUAA), | ||
49 | SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE, | ||
50 | "Unknown", CT20K1_UNKNOWN), | ||
51 | { } /* terminator */ | ||
52 | }; | ||
53 | |||
54 | static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { | ||
55 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, | ||
56 | "SB0760", CTSB0760), | ||
57 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, | ||
58 | "SB0880", CTSB0880), | ||
59 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, | ||
60 | "SB0880", CTSB0880), | ||
61 | SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, | ||
62 | "SB0880", CTSB0880), | ||
63 | SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, | ||
64 | PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX", | ||
65 | CTHENDRIX), | ||
66 | { } /* terminator */ | ||
67 | }; | ||
68 | |||
69 | static const char *ct_subsys_name[NUM_CTCARDS] = { | ||
70 | [CTSB055X] = "SB055x", | ||
71 | [CTSB073X] = "SB073x", | ||
72 | [CTSB0760] = "SB076x", | ||
73 | [CTUAA] = "UAA", | ||
74 | [CT20K1_UNKNOWN] = "Unknown", | ||
75 | [CTHENDRIX] = "Hendrix", | ||
76 | [CTSB0880] = "SB0880", | ||
77 | }; | ||
78 | |||
79 | static struct { | ||
80 | int (*create)(struct ct_atc *atc, | ||
81 | enum CTALSADEVS device, const char *device_name); | ||
82 | int (*destroy)(void *alsa_dev); | ||
83 | const char *public_name; | ||
84 | } alsa_dev_funcs[NUM_CTALSADEVS] = { | ||
85 | [FRONT] = { .create = ct_alsa_pcm_create, | ||
86 | .destroy = NULL, | ||
87 | .public_name = "Front/WaveIn"}, | ||
88 | [SURROUND] = { .create = ct_alsa_pcm_create, | ||
89 | .destroy = NULL, | ||
90 | .public_name = "Surround"}, | ||
91 | [CLFE] = { .create = ct_alsa_pcm_create, | ||
92 | .destroy = NULL, | ||
93 | .public_name = "Center/LFE"}, | ||
94 | [SIDE] = { .create = ct_alsa_pcm_create, | ||
95 | .destroy = NULL, | ||
96 | .public_name = "Side"}, | ||
97 | [IEC958] = { .create = ct_alsa_pcm_create, | ||
98 | .destroy = NULL, | ||
99 | .public_name = "IEC958 Non-audio"}, | ||
100 | |||
101 | [MIXER] = { .create = ct_alsa_mix_create, | ||
102 | .destroy = NULL, | ||
103 | .public_name = "Mixer"} | ||
104 | }; | ||
105 | |||
106 | typedef int (*create_t)(void *, void **); | ||
107 | typedef int (*destroy_t)(void *); | ||
108 | |||
109 | static struct { | ||
110 | int (*create)(void *hw, void **rmgr); | ||
111 | int (*destroy)(void *mgr); | ||
112 | } rsc_mgr_funcs[NUM_RSCTYP] = { | ||
113 | [SRC] = { .create = (create_t)src_mgr_create, | ||
114 | .destroy = (destroy_t)src_mgr_destroy }, | ||
115 | [SRCIMP] = { .create = (create_t)srcimp_mgr_create, | ||
116 | .destroy = (destroy_t)srcimp_mgr_destroy }, | ||
117 | [AMIXER] = { .create = (create_t)amixer_mgr_create, | ||
118 | .destroy = (destroy_t)amixer_mgr_destroy }, | ||
119 | [SUM] = { .create = (create_t)sum_mgr_create, | ||
120 | .destroy = (destroy_t)sum_mgr_destroy }, | ||
121 | [DAIO] = { .create = (create_t)daio_mgr_create, | ||
122 | .destroy = (destroy_t)daio_mgr_destroy } | ||
123 | }; | ||
124 | |||
125 | static int | ||
126 | atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
127 | |||
128 | /* * | ||
129 | * Only mono and interleaved modes are supported now. | ||
130 | * Always allocates a contiguous channel block. | ||
131 | * */ | ||
132 | |||
133 | static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
134 | { | ||
135 | struct snd_pcm_runtime *runtime; | ||
136 | struct ct_vm *vm; | ||
137 | |||
138 | if (NULL == apcm->substream) | ||
139 | return 0; | ||
140 | |||
141 | runtime = apcm->substream->runtime; | ||
142 | vm = atc->vm; | ||
143 | |||
144 | apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes); | ||
145 | |||
146 | if (NULL == apcm->vm_block) | ||
147 | return -ENOENT; | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
153 | { | ||
154 | struct ct_vm *vm; | ||
155 | |||
156 | if (NULL == apcm->vm_block) | ||
157 | return; | ||
158 | |||
159 | vm = atc->vm; | ||
160 | |||
161 | vm->unmap(vm, apcm->vm_block); | ||
162 | |||
163 | apcm->vm_block = NULL; | ||
164 | } | ||
165 | |||
166 | static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) | ||
167 | { | ||
168 | struct ct_vm *vm; | ||
169 | void *kvirt_addr; | ||
170 | unsigned long phys_addr; | ||
171 | |||
172 | vm = atc->vm; | ||
173 | kvirt_addr = vm->get_ptp_virt(vm, index); | ||
174 | if (kvirt_addr == NULL) | ||
175 | phys_addr = (~0UL); | ||
176 | else | ||
177 | phys_addr = virt_to_phys(kvirt_addr); | ||
178 | |||
179 | return phys_addr; | ||
180 | } | ||
181 | |||
182 | static unsigned int convert_format(snd_pcm_format_t snd_format) | ||
183 | { | ||
184 | switch (snd_format) { | ||
185 | case SNDRV_PCM_FORMAT_U8: | ||
186 | return SRC_SF_U8; | ||
187 | case SNDRV_PCM_FORMAT_S16_LE: | ||
188 | return SRC_SF_S16; | ||
189 | case SNDRV_PCM_FORMAT_S24_3LE: | ||
190 | return SRC_SF_S24; | ||
191 | case SNDRV_PCM_FORMAT_S32_LE: | ||
192 | return SRC_SF_S32; | ||
193 | case SNDRV_PCM_FORMAT_FLOAT_LE: | ||
194 | return SRC_SF_F32; | ||
195 | default: | ||
196 | printk(KERN_ERR "ctxfi: not recognized snd format is %d \n", | ||
197 | snd_format); | ||
198 | return SRC_SF_S16; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | static unsigned int | ||
203 | atc_get_pitch(unsigned int input_rate, unsigned int output_rate) | ||
204 | { | ||
205 | unsigned int pitch; | ||
206 | int b; | ||
207 | |||
208 | /* get pitch and convert to fixed-point 8.24 format. */ | ||
209 | pitch = (input_rate / output_rate) << 24; | ||
210 | input_rate %= output_rate; | ||
211 | input_rate /= 100; | ||
212 | output_rate /= 100; | ||
213 | for (b = 31; ((b >= 0) && !(input_rate >> b)); ) | ||
214 | b--; | ||
215 | |||
216 | if (b >= 0) { | ||
217 | input_rate <<= (31 - b); | ||
218 | input_rate /= output_rate; | ||
219 | b = 24 - (31 - b); | ||
220 | if (b >= 0) | ||
221 | input_rate <<= b; | ||
222 | else | ||
223 | input_rate >>= -b; | ||
224 | |||
225 | pitch |= input_rate; | ||
226 | } | ||
227 | |||
228 | return pitch; | ||
229 | } | ||
230 | |||
231 | static int select_rom(unsigned int pitch) | ||
232 | { | ||
233 | if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) { | ||
234 | /* 0.26 <= pitch <= 1.72 */ | ||
235 | return 1; | ||
236 | } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) { | ||
237 | /* pitch == 1.8375 */ | ||
238 | return 2; | ||
239 | } else if (0x02000000 == pitch) { | ||
240 | /* pitch == 2 */ | ||
241 | return 3; | ||
242 | } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) { | ||
243 | /* 0 <= pitch <= 8 */ | ||
244 | return 0; | ||
245 | } else { | ||
246 | return -ENOENT; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
251 | { | ||
252 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
253 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
254 | struct src_desc desc = {0}; | ||
255 | struct amixer_desc mix_dsc = {0}; | ||
256 | struct src *src; | ||
257 | struct amixer *amixer; | ||
258 | int err; | ||
259 | int n_amixer = apcm->substream->runtime->channels, i = 0; | ||
260 | int device = apcm->substream->pcm->device; | ||
261 | unsigned int pitch; | ||
262 | unsigned long flags; | ||
263 | |||
264 | if (NULL != apcm->src) { | ||
265 | /* Prepared pcm playback */ | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* first release old resources */ | ||
270 | atc->pcm_release_resources(atc, apcm); | ||
271 | |||
272 | /* Get SRC resource */ | ||
273 | desc.multi = apcm->substream->runtime->channels; | ||
274 | desc.msr = atc->msr; | ||
275 | desc.mode = MEMRD; | ||
276 | err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); | ||
277 | if (err) | ||
278 | goto error1; | ||
279 | |||
280 | pitch = atc_get_pitch(apcm->substream->runtime->rate, | ||
281 | (atc->rsr * atc->msr)); | ||
282 | src = apcm->src; | ||
283 | src->ops->set_pitch(src, pitch); | ||
284 | src->ops->set_rom(src, select_rom(pitch)); | ||
285 | src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); | ||
286 | src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); | ||
287 | |||
288 | /* Get AMIXER resource */ | ||
289 | n_amixer = (n_amixer < 2) ? 2 : n_amixer; | ||
290 | apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||
291 | if (NULL == apcm->amixers) { | ||
292 | err = -ENOMEM; | ||
293 | goto error1; | ||
294 | } | ||
295 | mix_dsc.msr = atc->msr; | ||
296 | for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { | ||
297 | err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, | ||
298 | (struct amixer **)&apcm->amixers[i]); | ||
299 | if (err) | ||
300 | goto error1; | ||
301 | |||
302 | apcm->n_amixer++; | ||
303 | } | ||
304 | |||
305 | /* Set up device virtual mem map */ | ||
306 | err = ct_map_audio_buffer(atc, apcm); | ||
307 | if (err < 0) | ||
308 | goto error1; | ||
309 | |||
310 | /* Connect resources */ | ||
311 | src = apcm->src; | ||
312 | for (i = 0; i < n_amixer; i++) { | ||
313 | amixer = apcm->amixers[i]; | ||
314 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
315 | amixer->ops->setup(amixer, &src->rsc, | ||
316 | INIT_VOL, atc->pcm[i+device*2]); | ||
317 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
318 | src = src->ops->next_interleave(src); | ||
319 | if (NULL == src) | ||
320 | src = apcm->src; | ||
321 | } | ||
322 | |||
323 | ct_timer_prepare(apcm->timer); | ||
324 | |||
325 | return 0; | ||
326 | |||
327 | error1: | ||
328 | atc_pcm_release_resources(atc, apcm); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | static int | ||
333 | atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
334 | { | ||
335 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
336 | struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
337 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
338 | struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; | ||
339 | struct srcimp *srcimp; | ||
340 | int i; | ||
341 | |||
342 | if (NULL != apcm->srcimps) { | ||
343 | for (i = 0; i < apcm->n_srcimp; i++) { | ||
344 | srcimp = apcm->srcimps[i]; | ||
345 | srcimp->ops->unmap(srcimp); | ||
346 | srcimp_mgr->put_srcimp(srcimp_mgr, srcimp); | ||
347 | apcm->srcimps[i] = NULL; | ||
348 | } | ||
349 | kfree(apcm->srcimps); | ||
350 | apcm->srcimps = NULL; | ||
351 | } | ||
352 | |||
353 | if (NULL != apcm->srccs) { | ||
354 | for (i = 0; i < apcm->n_srcc; i++) { | ||
355 | src_mgr->put_src(src_mgr, apcm->srccs[i]); | ||
356 | apcm->srccs[i] = NULL; | ||
357 | } | ||
358 | kfree(apcm->srccs); | ||
359 | apcm->srccs = NULL; | ||
360 | } | ||
361 | |||
362 | if (NULL != apcm->amixers) { | ||
363 | for (i = 0; i < apcm->n_amixer; i++) { | ||
364 | amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]); | ||
365 | apcm->amixers[i] = NULL; | ||
366 | } | ||
367 | kfree(apcm->amixers); | ||
368 | apcm->amixers = NULL; | ||
369 | } | ||
370 | |||
371 | if (NULL != apcm->mono) { | ||
372 | sum_mgr->put_sum(sum_mgr, apcm->mono); | ||
373 | apcm->mono = NULL; | ||
374 | } | ||
375 | |||
376 | if (NULL != apcm->src) { | ||
377 | src_mgr->put_src(src_mgr, apcm->src); | ||
378 | apcm->src = NULL; | ||
379 | } | ||
380 | |||
381 | if (NULL != apcm->vm_block) { | ||
382 | /* Undo device virtual mem map */ | ||
383 | ct_unmap_audio_buffer(atc, apcm); | ||
384 | apcm->vm_block = NULL; | ||
385 | } | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
391 | { | ||
392 | unsigned int max_cisz; | ||
393 | struct src *src = apcm->src; | ||
394 | |||
395 | if (apcm->started) | ||
396 | return 0; | ||
397 | apcm->started = 1; | ||
398 | |||
399 | max_cisz = src->multi * src->rsc.msr; | ||
400 | max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8); | ||
401 | |||
402 | src->ops->set_sa(src, apcm->vm_block->addr); | ||
403 | src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); | ||
404 | src->ops->set_ca(src, apcm->vm_block->addr + max_cisz); | ||
405 | src->ops->set_cisz(src, max_cisz); | ||
406 | |||
407 | src->ops->set_bm(src, 1); | ||
408 | src->ops->set_state(src, SRC_STATE_INIT); | ||
409 | src->ops->commit_write(src); | ||
410 | |||
411 | ct_timer_start(apcm->timer); | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
416 | { | ||
417 | struct src *src; | ||
418 | int i; | ||
419 | |||
420 | ct_timer_stop(apcm->timer); | ||
421 | |||
422 | src = apcm->src; | ||
423 | src->ops->set_bm(src, 0); | ||
424 | src->ops->set_state(src, SRC_STATE_OFF); | ||
425 | src->ops->commit_write(src); | ||
426 | |||
427 | if (NULL != apcm->srccs) { | ||
428 | for (i = 0; i < apcm->n_srcc; i++) { | ||
429 | src = apcm->srccs[i]; | ||
430 | src->ops->set_bm(src, 0); | ||
431 | src->ops->set_state(src, SRC_STATE_OFF); | ||
432 | src->ops->commit_write(src); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | apcm->started = 0; | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int | ||
442 | atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
443 | { | ||
444 | struct src *src = apcm->src; | ||
445 | u32 size, max_cisz; | ||
446 | int position; | ||
447 | |||
448 | if (!src) | ||
449 | return 0; | ||
450 | position = src->ops->get_ca(src); | ||
451 | |||
452 | size = apcm->vm_block->size; | ||
453 | max_cisz = src->multi * src->rsc.msr; | ||
454 | max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8); | ||
455 | |||
456 | return (position + size - max_cisz - apcm->vm_block->addr) % size; | ||
457 | } | ||
458 | |||
459 | struct src_node_conf_t { | ||
460 | unsigned int pitch; | ||
461 | unsigned int msr:8; | ||
462 | unsigned int mix_msr:8; | ||
463 | unsigned int imp_msr:8; | ||
464 | unsigned int vo:1; | ||
465 | }; | ||
466 | |||
467 | static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, | ||
468 | struct src_node_conf_t *conf, int *n_srcc) | ||
469 | { | ||
470 | unsigned int pitch; | ||
471 | |||
472 | /* get pitch and convert to fixed-point 8.24 format. */ | ||
473 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
474 | apcm->substream->runtime->rate); | ||
475 | *n_srcc = 0; | ||
476 | |||
477 | if (1 == atc->msr) { | ||
478 | *n_srcc = apcm->substream->runtime->channels; | ||
479 | conf[0].pitch = pitch; | ||
480 | conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; | ||
481 | conf[0].vo = 1; | ||
482 | } else if (2 == atc->msr) { | ||
483 | if (0x8000000 < pitch) { | ||
484 | /* Need two-stage SRCs, SRCIMPs and | ||
485 | * AMIXERs for converting format */ | ||
486 | conf[0].pitch = (atc->msr << 24); | ||
487 | conf[0].msr = conf[0].mix_msr = 1; | ||
488 | conf[0].imp_msr = atc->msr; | ||
489 | conf[0].vo = 0; | ||
490 | conf[1].pitch = atc_get_pitch(atc->rsr, | ||
491 | apcm->substream->runtime->rate); | ||
492 | conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1; | ||
493 | conf[1].vo = 1; | ||
494 | *n_srcc = apcm->substream->runtime->channels * 2; | ||
495 | } else if (0x1000000 < pitch) { | ||
496 | /* Need one-stage SRCs, SRCIMPs and | ||
497 | * AMIXERs for converting format */ | ||
498 | conf[0].pitch = pitch; | ||
499 | conf[0].msr = conf[0].mix_msr | ||
500 | = conf[0].imp_msr = atc->msr; | ||
501 | conf[0].vo = 1; | ||
502 | *n_srcc = apcm->substream->runtime->channels; | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | static int | ||
508 | atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
509 | { | ||
510 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
511 | struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
512 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
513 | struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; | ||
514 | struct src_desc src_dsc = {0}; | ||
515 | struct src *src; | ||
516 | struct srcimp_desc srcimp_dsc = {0}; | ||
517 | struct srcimp *srcimp; | ||
518 | struct amixer_desc mix_dsc = {0}; | ||
519 | struct sum_desc sum_dsc = {0}; | ||
520 | unsigned int pitch; | ||
521 | int multi, err, i; | ||
522 | int n_srcimp, n_amixer, n_srcc, n_sum; | ||
523 | struct src_node_conf_t src_node_conf[2] = {{0} }; | ||
524 | |||
525 | /* first release old resources */ | ||
526 | atc_pcm_release_resources(atc, apcm); | ||
527 | |||
528 | /* The numbers of converting SRCs and SRCIMPs should be determined | ||
529 | * by pitch value. */ | ||
530 | |||
531 | multi = apcm->substream->runtime->channels; | ||
532 | |||
533 | /* get pitch and convert to fixed-point 8.24 format. */ | ||
534 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
535 | apcm->substream->runtime->rate); | ||
536 | |||
537 | setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc); | ||
538 | n_sum = (1 == multi) ? 1 : 0; | ||
539 | n_amixer = n_sum * 2 + n_srcc; | ||
540 | n_srcimp = n_srcc; | ||
541 | if ((multi > 1) && (0x8000000 >= pitch)) { | ||
542 | /* Need extra AMIXERs and SRCIMPs for special treatment | ||
543 | * of interleaved recording of conjugate channels */ | ||
544 | n_amixer += multi * atc->msr; | ||
545 | n_srcimp += multi * atc->msr; | ||
546 | } else { | ||
547 | n_srcimp += multi; | ||
548 | } | ||
549 | |||
550 | if (n_srcc) { | ||
551 | apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); | ||
552 | if (NULL == apcm->srccs) | ||
553 | return -ENOMEM; | ||
554 | } | ||
555 | if (n_amixer) { | ||
556 | apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||
557 | if (NULL == apcm->amixers) { | ||
558 | err = -ENOMEM; | ||
559 | goto error1; | ||
560 | } | ||
561 | } | ||
562 | apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); | ||
563 | if (NULL == apcm->srcimps) { | ||
564 | err = -ENOMEM; | ||
565 | goto error1; | ||
566 | } | ||
567 | |||
568 | /* Allocate SRCs for sample rate conversion if needed */ | ||
569 | src_dsc.multi = 1; | ||
570 | src_dsc.mode = ARCRW; | ||
571 | for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) { | ||
572 | src_dsc.msr = src_node_conf[i/multi].msr; | ||
573 | err = src_mgr->get_src(src_mgr, &src_dsc, | ||
574 | (struct src **)&apcm->srccs[i]); | ||
575 | if (err) | ||
576 | goto error1; | ||
577 | |||
578 | src = apcm->srccs[i]; | ||
579 | pitch = src_node_conf[i/multi].pitch; | ||
580 | src->ops->set_pitch(src, pitch); | ||
581 | src->ops->set_rom(src, select_rom(pitch)); | ||
582 | src->ops->set_vo(src, src_node_conf[i/multi].vo); | ||
583 | |||
584 | apcm->n_srcc++; | ||
585 | } | ||
586 | |||
587 | /* Allocate AMIXERs for routing SRCs of conversion if needed */ | ||
588 | for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { | ||
589 | if (i < (n_sum*2)) | ||
590 | mix_dsc.msr = atc->msr; | ||
591 | else if (i < (n_sum*2+n_srcc)) | ||
592 | mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr; | ||
593 | else | ||
594 | mix_dsc.msr = 1; | ||
595 | |||
596 | err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, | ||
597 | (struct amixer **)&apcm->amixers[i]); | ||
598 | if (err) | ||
599 | goto error1; | ||
600 | |||
601 | apcm->n_amixer++; | ||
602 | } | ||
603 | |||
604 | /* Allocate a SUM resource to mix all input channels together */ | ||
605 | sum_dsc.msr = atc->msr; | ||
606 | err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono); | ||
607 | if (err) | ||
608 | goto error1; | ||
609 | |||
610 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
611 | apcm->substream->runtime->rate); | ||
612 | /* Allocate SRCIMP resources */ | ||
613 | for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) { | ||
614 | if (i < (n_srcc)) | ||
615 | srcimp_dsc.msr = src_node_conf[i/multi].imp_msr; | ||
616 | else if (1 == multi) | ||
617 | srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1; | ||
618 | else | ||
619 | srcimp_dsc.msr = 1; | ||
620 | |||
621 | err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp); | ||
622 | if (err) | ||
623 | goto error1; | ||
624 | |||
625 | apcm->srcimps[i] = srcimp; | ||
626 | apcm->n_srcimp++; | ||
627 | } | ||
628 | |||
629 | /* Allocate a SRC for writing data to host memory */ | ||
630 | src_dsc.multi = apcm->substream->runtime->channels; | ||
631 | src_dsc.msr = 1; | ||
632 | src_dsc.mode = MEMWR; | ||
633 | err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src); | ||
634 | if (err) | ||
635 | goto error1; | ||
636 | |||
637 | src = apcm->src; | ||
638 | src->ops->set_pitch(src, pitch); | ||
639 | |||
640 | /* Set up device virtual mem map */ | ||
641 | err = ct_map_audio_buffer(atc, apcm); | ||
642 | if (err < 0) | ||
643 | goto error1; | ||
644 | |||
645 | return 0; | ||
646 | |||
647 | error1: | ||
648 | atc_pcm_release_resources(atc, apcm); | ||
649 | return err; | ||
650 | } | ||
651 | |||
652 | static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
653 | { | ||
654 | struct src *src; | ||
655 | struct amixer *amixer; | ||
656 | struct srcimp *srcimp; | ||
657 | struct ct_mixer *mixer = atc->mixer; | ||
658 | struct sum *mono; | ||
659 | struct rsc *out_ports[8] = {NULL}; | ||
660 | int err, i, j, n_sum, multi; | ||
661 | unsigned int pitch; | ||
662 | int mix_base = 0, imp_base = 0; | ||
663 | |||
664 | if (NULL != apcm->src) { | ||
665 | /* Prepared pcm capture */ | ||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | /* Get needed resources. */ | ||
670 | err = atc_pcm_capture_get_resources(atc, apcm); | ||
671 | if (err) | ||
672 | return err; | ||
673 | |||
674 | /* Connect resources */ | ||
675 | mixer->get_output_ports(mixer, MIX_PCMO_FRONT, | ||
676 | &out_ports[0], &out_ports[1]); | ||
677 | |||
678 | multi = apcm->substream->runtime->channels; | ||
679 | if (1 == multi) { | ||
680 | mono = apcm->mono; | ||
681 | for (i = 0; i < 2; i++) { | ||
682 | amixer = apcm->amixers[i]; | ||
683 | amixer->ops->setup(amixer, out_ports[i], | ||
684 | MONO_SUM_SCALE, mono); | ||
685 | } | ||
686 | out_ports[0] = &mono->rsc; | ||
687 | n_sum = 1; | ||
688 | mix_base = n_sum * 2; | ||
689 | } | ||
690 | |||
691 | for (i = 0; i < apcm->n_srcc; i++) { | ||
692 | src = apcm->srccs[i]; | ||
693 | srcimp = apcm->srcimps[imp_base+i]; | ||
694 | amixer = apcm->amixers[mix_base+i]; | ||
695 | srcimp->ops->map(srcimp, src, out_ports[i%multi]); | ||
696 | amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); | ||
697 | out_ports[i%multi] = &amixer->rsc; | ||
698 | } | ||
699 | |||
700 | pitch = atc_get_pitch((atc->rsr * atc->msr), | ||
701 | apcm->substream->runtime->rate); | ||
702 | |||
703 | if ((multi > 1) && (pitch <= 0x8000000)) { | ||
704 | /* Special connection for interleaved | ||
705 | * recording with conjugate channels */ | ||
706 | for (i = 0; i < multi; i++) { | ||
707 | out_ports[i]->ops->master(out_ports[i]); | ||
708 | for (j = 0; j < atc->msr; j++) { | ||
709 | amixer = apcm->amixers[apcm->n_srcc+j*multi+i]; | ||
710 | amixer->ops->set_input(amixer, out_ports[i]); | ||
711 | amixer->ops->set_scale(amixer, INIT_VOL); | ||
712 | amixer->ops->set_sum(amixer, NULL); | ||
713 | amixer->ops->commit_raw_write(amixer); | ||
714 | out_ports[i]->ops->next_conj(out_ports[i]); | ||
715 | |||
716 | srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i]; | ||
717 | srcimp->ops->map(srcimp, apcm->src, | ||
718 | &amixer->rsc); | ||
719 | } | ||
720 | } | ||
721 | } else { | ||
722 | for (i = 0; i < multi; i++) { | ||
723 | srcimp = apcm->srcimps[apcm->n_srcc+i]; | ||
724 | srcimp->ops->map(srcimp, apcm->src, out_ports[i]); | ||
725 | } | ||
726 | } | ||
727 | |||
728 | ct_timer_prepare(apcm->timer); | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
734 | { | ||
735 | struct src *src; | ||
736 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
737 | int i, multi; | ||
738 | |||
739 | if (apcm->started) | ||
740 | return 0; | ||
741 | |||
742 | apcm->started = 1; | ||
743 | multi = apcm->substream->runtime->channels; | ||
744 | /* Set up converting SRCs */ | ||
745 | for (i = 0; i < apcm->n_srcc; i++) { | ||
746 | src = apcm->srccs[i]; | ||
747 | src->ops->set_pm(src, ((i%multi) != (multi-1))); | ||
748 | src_mgr->src_disable(src_mgr, src); | ||
749 | } | ||
750 | |||
751 | /* Set up recording SRC */ | ||
752 | src = apcm->src; | ||
753 | src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); | ||
754 | src->ops->set_sa(src, apcm->vm_block->addr); | ||
755 | src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); | ||
756 | src->ops->set_ca(src, apcm->vm_block->addr); | ||
757 | src_mgr->src_disable(src_mgr, src); | ||
758 | |||
759 | /* Disable relevant SRCs firstly */ | ||
760 | src_mgr->commit_write(src_mgr); | ||
761 | |||
762 | /* Enable SRCs respectively */ | ||
763 | for (i = 0; i < apcm->n_srcc; i++) { | ||
764 | src = apcm->srccs[i]; | ||
765 | src->ops->set_state(src, SRC_STATE_RUN); | ||
766 | src->ops->commit_write(src); | ||
767 | src_mgr->src_enable_s(src_mgr, src); | ||
768 | } | ||
769 | src = apcm->src; | ||
770 | src->ops->set_bm(src, 1); | ||
771 | src->ops->set_state(src, SRC_STATE_RUN); | ||
772 | src->ops->commit_write(src); | ||
773 | src_mgr->src_enable_s(src_mgr, src); | ||
774 | |||
775 | /* Enable relevant SRCs synchronously */ | ||
776 | src_mgr->commit_write(src_mgr); | ||
777 | |||
778 | ct_timer_start(apcm->timer); | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | static int | ||
783 | atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
784 | { | ||
785 | struct src *src = apcm->src; | ||
786 | |||
787 | if (!src) | ||
788 | return 0; | ||
789 | return src->ops->get_ca(src) - apcm->vm_block->addr; | ||
790 | } | ||
791 | |||
792 | static int spdif_passthru_playback_get_resources(struct ct_atc *atc, | ||
793 | struct ct_atc_pcm *apcm) | ||
794 | { | ||
795 | struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; | ||
796 | struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; | ||
797 | struct src_desc desc = {0}; | ||
798 | struct amixer_desc mix_dsc = {0}; | ||
799 | struct src *src; | ||
800 | int err; | ||
801 | int n_amixer = apcm->substream->runtime->channels, i; | ||
802 | unsigned int pitch, rsr = atc->pll_rate; | ||
803 | |||
804 | /* first release old resources */ | ||
805 | atc_pcm_release_resources(atc, apcm); | ||
806 | |||
807 | /* Get SRC resource */ | ||
808 | desc.multi = apcm->substream->runtime->channels; | ||
809 | desc.msr = 1; | ||
810 | while (apcm->substream->runtime->rate > (rsr * desc.msr)) | ||
811 | desc.msr <<= 1; | ||
812 | |||
813 | desc.mode = MEMRD; | ||
814 | err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); | ||
815 | if (err) | ||
816 | goto error1; | ||
817 | |||
818 | pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr)); | ||
819 | src = apcm->src; | ||
820 | src->ops->set_pitch(src, pitch); | ||
821 | src->ops->set_rom(src, select_rom(pitch)); | ||
822 | src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); | ||
823 | src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); | ||
824 | src->ops->set_bp(src, 1); | ||
825 | |||
826 | /* Get AMIXER resource */ | ||
827 | n_amixer = (n_amixer < 2) ? 2 : n_amixer; | ||
828 | apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||
829 | if (NULL == apcm->amixers) { | ||
830 | err = -ENOMEM; | ||
831 | goto error1; | ||
832 | } | ||
833 | mix_dsc.msr = desc.msr; | ||
834 | for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { | ||
835 | err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, | ||
836 | (struct amixer **)&apcm->amixers[i]); | ||
837 | if (err) | ||
838 | goto error1; | ||
839 | |||
840 | apcm->n_amixer++; | ||
841 | } | ||
842 | |||
843 | /* Set up device virtual mem map */ | ||
844 | err = ct_map_audio_buffer(atc, apcm); | ||
845 | if (err < 0) | ||
846 | goto error1; | ||
847 | |||
848 | return 0; | ||
849 | |||
850 | error1: | ||
851 | atc_pcm_release_resources(atc, apcm); | ||
852 | return err; | ||
853 | } | ||
854 | |||
855 | static int atc_pll_init(struct ct_atc *atc, int rate) | ||
856 | { | ||
857 | struct hw *hw = atc->hw; | ||
858 | int err; | ||
859 | err = hw->pll_init(hw, rate); | ||
860 | atc->pll_rate = err ? 0 : rate; | ||
861 | return err; | ||
862 | } | ||
863 | |||
864 | static int | ||
865 | spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
866 | { | ||
867 | struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio); | ||
868 | unsigned long flags; | ||
869 | unsigned int rate = apcm->substream->runtime->rate; | ||
870 | unsigned int status; | ||
871 | int err; | ||
872 | unsigned char iec958_con_fs; | ||
873 | |||
874 | switch (rate) { | ||
875 | case 48000: | ||
876 | iec958_con_fs = IEC958_AES3_CON_FS_48000; | ||
877 | break; | ||
878 | case 44100: | ||
879 | iec958_con_fs = IEC958_AES3_CON_FS_44100; | ||
880 | break; | ||
881 | case 32000: | ||
882 | iec958_con_fs = IEC958_AES3_CON_FS_32000; | ||
883 | break; | ||
884 | default: | ||
885 | return -ENOENT; | ||
886 | } | ||
887 | |||
888 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
889 | dao->ops->get_spos(dao, &status); | ||
890 | if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { | ||
891 | status &= ((~IEC958_AES3_CON_FS) << 24); | ||
892 | status |= (iec958_con_fs << 24); | ||
893 | dao->ops->set_spos(dao, status); | ||
894 | dao->ops->commit_write(dao); | ||
895 | } | ||
896 | if ((rate != atc->pll_rate) && (32000 != rate)) | ||
897 | err = atc_pll_init(atc, rate); | ||
898 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
899 | |||
900 | return err; | ||
901 | } | ||
902 | |||
903 | static int | ||
904 | spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | ||
905 | { | ||
906 | struct src *src; | ||
907 | struct amixer *amixer; | ||
908 | struct dao *dao; | ||
909 | int err; | ||
910 | int i; | ||
911 | unsigned long flags; | ||
912 | |||
913 | if (NULL != apcm->src) | ||
914 | return 0; | ||
915 | |||
916 | /* Configure SPDIFOO and PLL to passthrough mode; | ||
917 | * determine pll_rate. */ | ||
918 | err = spdif_passthru_playback_setup(atc, apcm); | ||
919 | if (err) | ||
920 | return err; | ||
921 | |||
922 | /* Get needed resources. */ | ||
923 | err = spdif_passthru_playback_get_resources(atc, apcm); | ||
924 | if (err) | ||
925 | return err; | ||
926 | |||
927 | /* Connect resources */ | ||
928 | src = apcm->src; | ||
929 | for (i = 0; i < apcm->n_amixer; i++) { | ||
930 | amixer = apcm->amixers[i]; | ||
931 | amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); | ||
932 | src = src->ops->next_interleave(src); | ||
933 | if (NULL == src) | ||
934 | src = apcm->src; | ||
935 | } | ||
936 | /* Connect to SPDIFOO */ | ||
937 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
938 | dao = container_of(atc->daios[SPDIFOO], struct dao, daio); | ||
939 | amixer = apcm->amixers[0]; | ||
940 | dao->ops->set_left_input(dao, &amixer->rsc); | ||
941 | amixer = apcm->amixers[1]; | ||
942 | dao->ops->set_right_input(dao, &amixer->rsc); | ||
943 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
944 | |||
945 | ct_timer_prepare(apcm->timer); | ||
946 | |||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | static int atc_select_line_in(struct ct_atc *atc) | ||
951 | { | ||
952 | struct hw *hw = atc->hw; | ||
953 | struct ct_mixer *mixer = atc->mixer; | ||
954 | struct src *src; | ||
955 | |||
956 | if (hw->is_adc_source_selected(hw, ADC_LINEIN)) | ||
957 | return 0; | ||
958 | |||
959 | mixer->set_input_left(mixer, MIX_MIC_IN, NULL); | ||
960 | mixer->set_input_right(mixer, MIX_MIC_IN, NULL); | ||
961 | |||
962 | hw->select_adc_source(hw, ADC_LINEIN); | ||
963 | |||
964 | src = atc->srcs[2]; | ||
965 | mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); | ||
966 | src = atc->srcs[3]; | ||
967 | mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | static int atc_select_mic_in(struct ct_atc *atc) | ||
973 | { | ||
974 | struct hw *hw = atc->hw; | ||
975 | struct ct_mixer *mixer = atc->mixer; | ||
976 | struct src *src; | ||
977 | |||
978 | if (hw->is_adc_source_selected(hw, ADC_MICIN)) | ||
979 | return 0; | ||
980 | |||
981 | mixer->set_input_left(mixer, MIX_LINE_IN, NULL); | ||
982 | mixer->set_input_right(mixer, MIX_LINE_IN, NULL); | ||
983 | |||
984 | hw->select_adc_source(hw, ADC_MICIN); | ||
985 | |||
986 | src = atc->srcs[2]; | ||
987 | mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc); | ||
988 | src = atc->srcs[3]; | ||
989 | mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); | ||
990 | |||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | static int atc_have_digit_io_switch(struct ct_atc *atc) | ||
995 | { | ||
996 | struct hw *hw = atc->hw; | ||
997 | |||
998 | return hw->have_digit_io_switch(hw); | ||
999 | } | ||
1000 | |||
1001 | static int atc_select_digit_io(struct ct_atc *atc) | ||
1002 | { | ||
1003 | struct hw *hw = atc->hw; | ||
1004 | |||
1005 | if (hw->is_adc_source_selected(hw, ADC_NONE)) | ||
1006 | return 0; | ||
1007 | |||
1008 | hw->select_adc_source(hw, ADC_NONE); | ||
1009 | |||
1010 | return 0; | ||
1011 | } | ||
1012 | |||
1013 | static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type) | ||
1014 | { | ||
1015 | struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO]; | ||
1016 | |||
1017 | if (state) | ||
1018 | daio_mgr->daio_enable(daio_mgr, atc->daios[type]); | ||
1019 | else | ||
1020 | daio_mgr->daio_disable(daio_mgr, atc->daios[type]); | ||
1021 | |||
1022 | daio_mgr->commit_write(daio_mgr); | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | static int | ||
1028 | atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type) | ||
1029 | { | ||
1030 | struct dao *dao = container_of(atc->daios[type], struct dao, daio); | ||
1031 | return dao->ops->get_spos(dao, status); | ||
1032 | } | ||
1033 | |||
1034 | static int | ||
1035 | atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type) | ||
1036 | { | ||
1037 | struct dao *dao = container_of(atc->daios[type], struct dao, daio); | ||
1038 | |||
1039 | dao->ops->set_spos(dao, status); | ||
1040 | dao->ops->commit_write(dao); | ||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state) | ||
1045 | { | ||
1046 | return atc_daio_unmute(atc, state, LINEO1); | ||
1047 | } | ||
1048 | |||
1049 | static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state) | ||
1050 | { | ||
1051 | return atc_daio_unmute(atc, state, LINEO4); | ||
1052 | } | ||
1053 | |||
1054 | static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state) | ||
1055 | { | ||
1056 | return atc_daio_unmute(atc, state, LINEO3); | ||
1057 | } | ||
1058 | |||
1059 | static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state) | ||
1060 | { | ||
1061 | return atc_daio_unmute(atc, state, LINEO2); | ||
1062 | } | ||
1063 | |||
1064 | static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) | ||
1065 | { | ||
1066 | return atc_daio_unmute(atc, state, LINEIM); | ||
1067 | } | ||
1068 | |||
1069 | static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) | ||
1070 | { | ||
1071 | return atc_daio_unmute(atc, state, SPDIFOO); | ||
1072 | } | ||
1073 | |||
1074 | static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state) | ||
1075 | { | ||
1076 | return atc_daio_unmute(atc, state, SPDIFIO); | ||
1077 | } | ||
1078 | |||
1079 | static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status) | ||
1080 | { | ||
1081 | return atc_dao_get_status(atc, status, SPDIFOO); | ||
1082 | } | ||
1083 | |||
1084 | static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status) | ||
1085 | { | ||
1086 | return atc_dao_set_status(atc, status, SPDIFOO); | ||
1087 | } | ||
1088 | |||
1089 | static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) | ||
1090 | { | ||
1091 | unsigned long flags; | ||
1092 | struct dao_desc da_dsc = {0}; | ||
1093 | struct dao *dao; | ||
1094 | int err; | ||
1095 | struct ct_mixer *mixer = atc->mixer; | ||
1096 | struct rsc *rscs[2] = {NULL}; | ||
1097 | unsigned int spos = 0; | ||
1098 | |||
1099 | spin_lock_irqsave(&atc->atc_lock, flags); | ||
1100 | dao = container_of(atc->daios[SPDIFOO], struct dao, daio); | ||
1101 | da_dsc.msr = state ? 1 : atc->msr; | ||
1102 | da_dsc.passthru = state ? 1 : 0; | ||
1103 | err = dao->ops->reinit(dao, &da_dsc); | ||
1104 | if (state) { | ||
1105 | spos = IEC958_DEFAULT_CON; | ||
1106 | } else { | ||
1107 | mixer->get_output_ports(mixer, MIX_SPDIF_OUT, | ||
1108 | &rscs[0], &rscs[1]); | ||
1109 | dao->ops->set_left_input(dao, rscs[0]); | ||
1110 | dao->ops->set_right_input(dao, rscs[1]); | ||
1111 | /* Restore PLL to atc->rsr if needed. */ | ||
1112 | if (atc->pll_rate != atc->rsr) | ||
1113 | err = atc_pll_init(atc, atc->rsr); | ||
1114 | } | ||
1115 | dao->ops->set_spos(dao, spos); | ||
1116 | dao->ops->commit_write(dao); | ||
1117 | spin_unlock_irqrestore(&atc->atc_lock, flags); | ||
1118 | |||
1119 | return err; | ||
1120 | } | ||
1121 | |||
1122 | static int ct_atc_destroy(struct ct_atc *atc) | ||
1123 | { | ||
1124 | struct daio_mgr *daio_mgr; | ||
1125 | struct dao *dao; | ||
1126 | struct dai *dai; | ||
1127 | struct daio *daio; | ||
1128 | struct sum_mgr *sum_mgr; | ||
1129 | struct src_mgr *src_mgr; | ||
1130 | struct srcimp_mgr *srcimp_mgr; | ||
1131 | struct srcimp *srcimp; | ||
1132 | struct ct_mixer *mixer; | ||
1133 | int i = 0; | ||
1134 | |||
1135 | if (NULL == atc) | ||
1136 | return 0; | ||
1137 | |||
1138 | if (atc->timer) { | ||
1139 | ct_timer_free(atc->timer); | ||
1140 | atc->timer = NULL; | ||
1141 | } | ||
1142 | |||
1143 | /* Stop hardware and disable all interrupts */ | ||
1144 | if (NULL != atc->hw) | ||
1145 | ((struct hw *)atc->hw)->card_stop(atc->hw); | ||
1146 | |||
1147 | /* Destroy internal mixer objects */ | ||
1148 | if (NULL != atc->mixer) { | ||
1149 | mixer = atc->mixer; | ||
1150 | mixer->set_input_left(mixer, MIX_LINE_IN, NULL); | ||
1151 | mixer->set_input_right(mixer, MIX_LINE_IN, NULL); | ||
1152 | mixer->set_input_left(mixer, MIX_MIC_IN, NULL); | ||
1153 | mixer->set_input_right(mixer, MIX_MIC_IN, NULL); | ||
1154 | mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL); | ||
1155 | mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL); | ||
1156 | ct_mixer_destroy(atc->mixer); | ||
1157 | } | ||
1158 | |||
1159 | if (NULL != atc->daios) { | ||
1160 | daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; | ||
1161 | for (i = 0; i < atc->n_daio; i++) { | ||
1162 | daio = atc->daios[i]; | ||
1163 | if (daio->type < LINEIM) { | ||
1164 | dao = container_of(daio, struct dao, daio); | ||
1165 | dao->ops->clear_left_input(dao); | ||
1166 | dao->ops->clear_right_input(dao); | ||
1167 | } else { | ||
1168 | dai = container_of(daio, struct dai, daio); | ||
1169 | /* some thing to do for dai ... */ | ||
1170 | } | ||
1171 | daio_mgr->put_daio(daio_mgr, daio); | ||
1172 | } | ||
1173 | kfree(atc->daios); | ||
1174 | } | ||
1175 | |||
1176 | if (NULL != atc->pcm) { | ||
1177 | sum_mgr = atc->rsc_mgrs[SUM]; | ||
1178 | for (i = 0; i < atc->n_pcm; i++) | ||
1179 | sum_mgr->put_sum(sum_mgr, atc->pcm[i]); | ||
1180 | |||
1181 | kfree(atc->pcm); | ||
1182 | } | ||
1183 | |||
1184 | if (NULL != atc->srcs) { | ||
1185 | src_mgr = atc->rsc_mgrs[SRC]; | ||
1186 | for (i = 0; i < atc->n_src; i++) | ||
1187 | src_mgr->put_src(src_mgr, atc->srcs[i]); | ||
1188 | |||
1189 | kfree(atc->srcs); | ||
1190 | } | ||
1191 | |||
1192 | if (NULL != atc->srcimps) { | ||
1193 | srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
1194 | for (i = 0; i < atc->n_srcimp; i++) { | ||
1195 | srcimp = atc->srcimps[i]; | ||
1196 | srcimp->ops->unmap(srcimp); | ||
1197 | srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]); | ||
1198 | } | ||
1199 | kfree(atc->srcimps); | ||
1200 | } | ||
1201 | |||
1202 | for (i = 0; i < NUM_RSCTYP; i++) { | ||
1203 | if ((NULL != rsc_mgr_funcs[i].destroy) && | ||
1204 | (NULL != atc->rsc_mgrs[i])) | ||
1205 | rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]); | ||
1206 | |||
1207 | } | ||
1208 | |||
1209 | if (NULL != atc->hw) | ||
1210 | destroy_hw_obj((struct hw *)atc->hw); | ||
1211 | |||
1212 | /* Destroy device virtual memory manager object */ | ||
1213 | if (NULL != atc->vm) { | ||
1214 | ct_vm_destroy(atc->vm); | ||
1215 | atc->vm = NULL; | ||
1216 | } | ||
1217 | |||
1218 | kfree(atc); | ||
1219 | |||
1220 | return 0; | ||
1221 | } | ||
1222 | |||
1223 | static int atc_dev_free(struct snd_device *dev) | ||
1224 | { | ||
1225 | struct ct_atc *atc = dev->device_data; | ||
1226 | return ct_atc_destroy(atc); | ||
1227 | } | ||
1228 | |||
1229 | static int __devinit atc_identify_card(struct ct_atc *atc) | ||
1230 | { | ||
1231 | const struct snd_pci_quirk *p; | ||
1232 | const struct snd_pci_quirk *list; | ||
1233 | |||
1234 | switch (atc->chip_type) { | ||
1235 | case ATC20K1: | ||
1236 | atc->chip_name = "20K1"; | ||
1237 | list = subsys_20k1_list; | ||
1238 | break; | ||
1239 | case ATC20K2: | ||
1240 | atc->chip_name = "20K2"; | ||
1241 | list = subsys_20k2_list; | ||
1242 | break; | ||
1243 | default: | ||
1244 | return -ENOENT; | ||
1245 | } | ||
1246 | p = snd_pci_quirk_lookup(atc->pci, list); | ||
1247 | if (!p) | ||
1248 | return -ENOENT; | ||
1249 | atc->model = p->value; | ||
1250 | atc->model_name = ct_subsys_name[atc->model]; | ||
1251 | snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", | ||
1252 | atc->chip_name, atc->model_name, | ||
1253 | atc->pci->subsystem_vendor, | ||
1254 | atc->pci->subsystem_device); | ||
1255 | return 0; | ||
1256 | } | ||
1257 | |||
1258 | int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) | ||
1259 | { | ||
1260 | enum CTALSADEVS i; | ||
1261 | int err; | ||
1262 | |||
1263 | alsa_dev_funcs[MIXER].public_name = atc->chip_name; | ||
1264 | |||
1265 | for (i = 0; i < NUM_CTALSADEVS; i++) { | ||
1266 | if (NULL == alsa_dev_funcs[i].create) | ||
1267 | continue; | ||
1268 | |||
1269 | err = alsa_dev_funcs[i].create(atc, i, | ||
1270 | alsa_dev_funcs[i].public_name); | ||
1271 | if (err) { | ||
1272 | printk(KERN_ERR "ctxfi: " | ||
1273 | "Creating alsa device %d failed!\n", i); | ||
1274 | return err; | ||
1275 | } | ||
1276 | } | ||
1277 | |||
1278 | return 0; | ||
1279 | } | ||
1280 | |||
1281 | static int __devinit atc_create_hw_devs(struct ct_atc *atc) | ||
1282 | { | ||
1283 | struct hw *hw; | ||
1284 | struct card_conf info = {0}; | ||
1285 | int i, err; | ||
1286 | |||
1287 | err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw); | ||
1288 | if (err) { | ||
1289 | printk(KERN_ERR "Failed to create hw obj!!!\n"); | ||
1290 | return err; | ||
1291 | } | ||
1292 | atc->hw = hw; | ||
1293 | |||
1294 | /* Initialize card hardware. */ | ||
1295 | info.rsr = atc->rsr; | ||
1296 | info.msr = atc->msr; | ||
1297 | info.vm_pgt_phys = atc_get_ptp_phys(atc, 0); | ||
1298 | err = hw->card_init(hw, &info); | ||
1299 | if (err < 0) | ||
1300 | return err; | ||
1301 | |||
1302 | for (i = 0; i < NUM_RSCTYP; i++) { | ||
1303 | if (NULL == rsc_mgr_funcs[i].create) | ||
1304 | continue; | ||
1305 | |||
1306 | err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); | ||
1307 | if (err) { | ||
1308 | printk(KERN_ERR "ctxfi: " | ||
1309 | "Failed to create rsc_mgr %d!!!\n", i); | ||
1310 | return err; | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1314 | return 0; | ||
1315 | } | ||
1316 | |||
1317 | static int __devinit atc_get_resources(struct ct_atc *atc) | ||
1318 | { | ||
1319 | struct daio_desc da_desc = {0}; | ||
1320 | struct daio_mgr *daio_mgr; | ||
1321 | struct src_desc src_dsc = {0}; | ||
1322 | struct src_mgr *src_mgr; | ||
1323 | struct srcimp_desc srcimp_dsc = {0}; | ||
1324 | struct srcimp_mgr *srcimp_mgr; | ||
1325 | struct sum_desc sum_dsc = {0}; | ||
1326 | struct sum_mgr *sum_mgr; | ||
1327 | int err, i; | ||
1328 | |||
1329 | atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); | ||
1330 | if (NULL == atc->daios) | ||
1331 | return -ENOMEM; | ||
1332 | |||
1333 | atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); | ||
1334 | if (NULL == atc->srcs) | ||
1335 | return -ENOMEM; | ||
1336 | |||
1337 | atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); | ||
1338 | if (NULL == atc->srcimps) | ||
1339 | return -ENOMEM; | ||
1340 | |||
1341 | atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); | ||
1342 | if (NULL == atc->pcm) | ||
1343 | return -ENOMEM; | ||
1344 | |||
1345 | daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; | ||
1346 | da_desc.msr = atc->msr; | ||
1347 | for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { | ||
1348 | da_desc.type = i; | ||
1349 | err = daio_mgr->get_daio(daio_mgr, &da_desc, | ||
1350 | (struct daio **)&atc->daios[i]); | ||
1351 | if (err) { | ||
1352 | printk(KERN_ERR "ctxfi: Failed to get DAIO " | ||
1353 | "resource %d!!!\n", i); | ||
1354 | return err; | ||
1355 | } | ||
1356 | atc->n_daio++; | ||
1357 | } | ||
1358 | if (atc->model == CTSB073X) | ||
1359 | da_desc.type = SPDIFI1; | ||
1360 | else | ||
1361 | da_desc.type = SPDIFIO; | ||
1362 | err = daio_mgr->get_daio(daio_mgr, &da_desc, | ||
1363 | (struct daio **)&atc->daios[i]); | ||
1364 | if (err) { | ||
1365 | printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n"); | ||
1366 | return err; | ||
1367 | } | ||
1368 | atc->n_daio++; | ||
1369 | |||
1370 | src_mgr = atc->rsc_mgrs[SRC]; | ||
1371 | src_dsc.multi = 1; | ||
1372 | src_dsc.msr = atc->msr; | ||
1373 | src_dsc.mode = ARCRW; | ||
1374 | for (i = 0, atc->n_src = 0; i < (2*2); i++) { | ||
1375 | err = src_mgr->get_src(src_mgr, &src_dsc, | ||
1376 | (struct src **)&atc->srcs[i]); | ||
1377 | if (err) | ||
1378 | return err; | ||
1379 | |||
1380 | atc->n_src++; | ||
1381 | } | ||
1382 | |||
1383 | srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||
1384 | srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ | ||
1385 | for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { | ||
1386 | err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, | ||
1387 | (struct srcimp **)&atc->srcimps[i]); | ||
1388 | if (err) | ||
1389 | return err; | ||
1390 | |||
1391 | atc->n_srcimp++; | ||
1392 | } | ||
1393 | srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */ | ||
1394 | for (i = 0; i < (2*1); i++) { | ||
1395 | err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, | ||
1396 | (struct srcimp **)&atc->srcimps[2*1+i]); | ||
1397 | if (err) | ||
1398 | return err; | ||
1399 | |||
1400 | atc->n_srcimp++; | ||
1401 | } | ||
1402 | |||
1403 | sum_mgr = atc->rsc_mgrs[SUM]; | ||
1404 | sum_dsc.msr = atc->msr; | ||
1405 | for (i = 0, atc->n_pcm = 0; i < (2*4); i++) { | ||
1406 | err = sum_mgr->get_sum(sum_mgr, &sum_dsc, | ||
1407 | (struct sum **)&atc->pcm[i]); | ||
1408 | if (err) | ||
1409 | return err; | ||
1410 | |||
1411 | atc->n_pcm++; | ||
1412 | } | ||
1413 | |||
1414 | err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); | ||
1415 | if (err) { | ||
1416 | printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n"); | ||
1417 | return err; | ||
1418 | } | ||
1419 | |||
1420 | return 0; | ||
1421 | } | ||
1422 | |||
1423 | static void __devinit | ||
1424 | atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, | ||
1425 | struct src **srcs, struct srcimp **srcimps) | ||
1426 | { | ||
1427 | struct rsc *rscs[2] = {NULL}; | ||
1428 | struct src *src; | ||
1429 | struct srcimp *srcimp; | ||
1430 | int i = 0; | ||
1431 | |||
1432 | rscs[0] = &dai->daio.rscl; | ||
1433 | rscs[1] = &dai->daio.rscr; | ||
1434 | for (i = 0; i < 2; i++) { | ||
1435 | src = srcs[i]; | ||
1436 | srcimp = srcimps[i]; | ||
1437 | srcimp->ops->map(srcimp, src, rscs[i]); | ||
1438 | src_mgr->src_disable(src_mgr, src); | ||
1439 | } | ||
1440 | |||
1441 | src_mgr->commit_write(src_mgr); /* Actually disable SRCs */ | ||
1442 | |||
1443 | src = srcs[0]; | ||
1444 | src->ops->set_pm(src, 1); | ||
1445 | for (i = 0; i < 2; i++) { | ||
1446 | src = srcs[i]; | ||
1447 | src->ops->set_state(src, SRC_STATE_RUN); | ||
1448 | src->ops->commit_write(src); | ||
1449 | src_mgr->src_enable_s(src_mgr, src); | ||
1450 | } | ||
1451 | |||
1452 | dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc)); | ||
1453 | dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc)); | ||
1454 | |||
1455 | dai->ops->set_enb_src(dai, 1); | ||
1456 | dai->ops->set_enb_srt(dai, 1); | ||
1457 | dai->ops->commit_write(dai); | ||
1458 | |||
1459 | src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */ | ||
1460 | } | ||
1461 | |||
1462 | static void __devinit atc_connect_resources(struct ct_atc *atc) | ||
1463 | { | ||
1464 | struct dai *dai; | ||
1465 | struct dao *dao; | ||
1466 | struct src *src; | ||
1467 | struct sum *sum; | ||
1468 | struct ct_mixer *mixer; | ||
1469 | struct rsc *rscs[2] = {NULL}; | ||
1470 | int i, j; | ||
1471 | |||
1472 | mixer = atc->mixer; | ||
1473 | |||
1474 | for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) { | ||
1475 | mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]); | ||
1476 | dao = container_of(atc->daios[j], struct dao, daio); | ||
1477 | dao->ops->set_left_input(dao, rscs[0]); | ||
1478 | dao->ops->set_right_input(dao, rscs[1]); | ||
1479 | } | ||
1480 | |||
1481 | dai = container_of(atc->daios[LINEIM], struct dai, daio); | ||
1482 | atc_connect_dai(atc->rsc_mgrs[SRC], dai, | ||
1483 | (struct src **)&atc->srcs[2], | ||
1484 | (struct srcimp **)&atc->srcimps[2]); | ||
1485 | src = atc->srcs[2]; | ||
1486 | mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); | ||
1487 | src = atc->srcs[3]; | ||
1488 | mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); | ||
1489 | |||
1490 | dai = container_of(atc->daios[SPDIFIO], struct dai, daio); | ||
1491 | atc_connect_dai(atc->rsc_mgrs[SRC], dai, | ||
1492 | (struct src **)&atc->srcs[0], | ||
1493 | (struct srcimp **)&atc->srcimps[0]); | ||
1494 | |||
1495 | src = atc->srcs[0]; | ||
1496 | mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc); | ||
1497 | src = atc->srcs[1]; | ||
1498 | mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc); | ||
1499 | |||
1500 | for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) { | ||
1501 | sum = atc->pcm[j]; | ||
1502 | mixer->set_input_left(mixer, i, &sum->rsc); | ||
1503 | sum = atc->pcm[j+1]; | ||
1504 | mixer->set_input_right(mixer, i, &sum->rsc); | ||
1505 | } | ||
1506 | } | ||
1507 | |||
1508 | static struct ct_atc atc_preset __devinitdata = { | ||
1509 | .map_audio_buffer = ct_map_audio_buffer, | ||
1510 | .unmap_audio_buffer = ct_unmap_audio_buffer, | ||
1511 | .pcm_playback_prepare = atc_pcm_playback_prepare, | ||
1512 | .pcm_release_resources = atc_pcm_release_resources, | ||
1513 | .pcm_playback_start = atc_pcm_playback_start, | ||
1514 | .pcm_playback_stop = atc_pcm_stop, | ||
1515 | .pcm_playback_position = atc_pcm_playback_position, | ||
1516 | .pcm_capture_prepare = atc_pcm_capture_prepare, | ||
1517 | .pcm_capture_start = atc_pcm_capture_start, | ||
1518 | .pcm_capture_stop = atc_pcm_stop, | ||
1519 | .pcm_capture_position = atc_pcm_capture_position, | ||
1520 | .spdif_passthru_playback_prepare = spdif_passthru_playback_prepare, | ||
1521 | .get_ptp_phys = atc_get_ptp_phys, | ||
1522 | .select_line_in = atc_select_line_in, | ||
1523 | .select_mic_in = atc_select_mic_in, | ||
1524 | .select_digit_io = atc_select_digit_io, | ||
1525 | .line_front_unmute = atc_line_front_unmute, | ||
1526 | .line_surround_unmute = atc_line_surround_unmute, | ||
1527 | .line_clfe_unmute = atc_line_clfe_unmute, | ||
1528 | .line_rear_unmute = atc_line_rear_unmute, | ||
1529 | .line_in_unmute = atc_line_in_unmute, | ||
1530 | .spdif_out_unmute = atc_spdif_out_unmute, | ||
1531 | .spdif_in_unmute = atc_spdif_in_unmute, | ||
1532 | .spdif_out_get_status = atc_spdif_out_get_status, | ||
1533 | .spdif_out_set_status = atc_spdif_out_set_status, | ||
1534 | .spdif_out_passthru = atc_spdif_out_passthru, | ||
1535 | .have_digit_io_switch = atc_have_digit_io_switch, | ||
1536 | }; | ||
1537 | |||
1538 | /** | ||
1539 | * ct_atc_create - create and initialize a hardware manager | ||
1540 | * @card: corresponding alsa card object | ||
1541 | * @pci: corresponding kernel pci device object | ||
1542 | * @ratc: return created object address in it | ||
1543 | * | ||
1544 | * Creates and initializes a hardware manager. | ||
1545 | * | ||
1546 | * Creates kmallocated ct_atc structure. Initializes hardware. | ||
1547 | * Returns 0 if suceeds, or negative error code if fails. | ||
1548 | */ | ||
1549 | |||
1550 | int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, | ||
1551 | unsigned int rsr, unsigned int msr, | ||
1552 | int chip_type, struct ct_atc **ratc) | ||
1553 | { | ||
1554 | struct ct_atc *atc; | ||
1555 | static struct snd_device_ops ops = { | ||
1556 | .dev_free = atc_dev_free, | ||
1557 | }; | ||
1558 | int err; | ||
1559 | |||
1560 | *ratc = NULL; | ||
1561 | |||
1562 | atc = kzalloc(sizeof(*atc), GFP_KERNEL); | ||
1563 | if (NULL == atc) | ||
1564 | return -ENOMEM; | ||
1565 | |||
1566 | /* Set operations */ | ||
1567 | *atc = atc_preset; | ||
1568 | |||
1569 | atc->card = card; | ||
1570 | atc->pci = pci; | ||
1571 | atc->rsr = rsr; | ||
1572 | atc->msr = msr; | ||
1573 | atc->chip_type = chip_type; | ||
1574 | |||
1575 | spin_lock_init(&atc->atc_lock); | ||
1576 | |||
1577 | /* Find card model */ | ||
1578 | err = atc_identify_card(atc); | ||
1579 | if (err < 0) { | ||
1580 | printk(KERN_ERR "ctatc: Card not recognised\n"); | ||
1581 | goto error1; | ||
1582 | } | ||
1583 | |||
1584 | /* Set up device virtual memory management object */ | ||
1585 | err = ct_vm_create(&atc->vm); | ||
1586 | if (err < 0) | ||
1587 | goto error1; | ||
1588 | |||
1589 | /* Create all atc hw devices */ | ||
1590 | err = atc_create_hw_devs(atc); | ||
1591 | if (err < 0) | ||
1592 | goto error1; | ||
1593 | |||
1594 | /* Get resources */ | ||
1595 | err = atc_get_resources(atc); | ||
1596 | if (err < 0) | ||
1597 | goto error1; | ||
1598 | |||
1599 | /* Build topology */ | ||
1600 | atc_connect_resources(atc); | ||
1601 | |||
1602 | atc->timer = ct_timer_new(atc); | ||
1603 | if (!atc->timer) | ||
1604 | goto error1; | ||
1605 | |||
1606 | err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); | ||
1607 | if (err < 0) | ||
1608 | goto error1; | ||
1609 | |||
1610 | snd_card_set_dev(card, &pci->dev); | ||
1611 | |||
1612 | *ratc = atc; | ||
1613 | return 0; | ||
1614 | |||
1615 | error1: | ||
1616 | ct_atc_destroy(atc); | ||
1617 | printk(KERN_ERR "ctxfi: Something wrong!!!\n"); | ||
1618 | return err; | ||
1619 | } | ||
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h new file mode 100644 index 000000000000..a03347232e84 --- /dev/null +++ b/sound/pci/ctxfi/ctatc.h | |||
@@ -0,0 +1,147 @@ | |||
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 | SURROUND, | ||
33 | CLFE, | ||
34 | SIDE, | ||
35 | IEC958, | ||
36 | MIXER, | ||
37 | NUM_CTALSADEVS /* This should always be the last */ | ||
38 | }; | ||
39 | |||
40 | struct ct_atc_chip_sub_details { | ||
41 | u16 subsys; | ||
42 | const char *nm_model; | ||
43 | }; | ||
44 | |||
45 | struct ct_atc_chip_details { | ||
46 | u16 vendor; | ||
47 | u16 device; | ||
48 | const struct ct_atc_chip_sub_details *sub_details; | ||
49 | const char *nm_card; | ||
50 | }; | ||
51 | |||
52 | struct ct_atc; | ||
53 | struct ct_timer; | ||
54 | struct ct_timer_instance; | ||
55 | |||
56 | /* alsa pcm stream descriptor */ | ||
57 | struct ct_atc_pcm { | ||
58 | struct snd_pcm_substream *substream; | ||
59 | void (*interrupt)(struct ct_atc_pcm *apcm); | ||
60 | struct ct_timer_instance *timer; | ||
61 | unsigned int started:1; | ||
62 | |||
63 | /* Only mono and interleaved modes are supported now. */ | ||
64 | struct ct_vm_block *vm_block; | ||
65 | void *src; /* SRC for interacting with host memory */ | ||
66 | void **srccs; /* SRCs for sample rate conversion */ | ||
67 | void **srcimps; /* SRC Input Mappers */ | ||
68 | void **amixers; /* AMIXERs for routing converted data */ | ||
69 | void *mono; /* A SUM resource for mixing chs to one */ | ||
70 | unsigned char n_srcc; /* Number of converting SRCs */ | ||
71 | unsigned char n_srcimp; /* Number of SRC Input Mappers */ | ||
72 | unsigned char n_amixer; /* Number of AMIXERs */ | ||
73 | }; | ||
74 | |||
75 | /* Chip resource management object */ | ||
76 | struct ct_atc { | ||
77 | struct pci_dev *pci; | ||
78 | struct snd_card *card; | ||
79 | unsigned int rsr; /* reference sample rate in Hz */ | ||
80 | unsigned int msr; /* master sample rate in rsr */ | ||
81 | unsigned int pll_rate; /* current rate of Phase Lock Loop */ | ||
82 | |||
83 | int chip_type; | ||
84 | int model; | ||
85 | const char *chip_name; | ||
86 | const char *model_name; | ||
87 | |||
88 | struct ct_vm *vm; /* device virtual memory manager for this card */ | ||
89 | int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
90 | void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
91 | unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index); | ||
92 | |||
93 | spinlock_t atc_lock; | ||
94 | |||
95 | int (*pcm_playback_prepare)(struct ct_atc *atc, | ||
96 | struct ct_atc_pcm *apcm); | ||
97 | int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
98 | int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
99 | int (*pcm_playback_position)(struct ct_atc *atc, | ||
100 | struct ct_atc_pcm *apcm); | ||
101 | int (*spdif_passthru_playback_prepare)(struct ct_atc *atc, | ||
102 | struct ct_atc_pcm *apcm); | ||
103 | int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
104 | int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
105 | int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm); | ||
106 | int (*pcm_capture_position)(struct ct_atc *atc, | ||
107 | struct ct_atc_pcm *apcm); | ||
108 | int (*pcm_release_resources)(struct ct_atc *atc, | ||
109 | struct ct_atc_pcm *apcm); | ||
110 | int (*select_line_in)(struct ct_atc *atc); | ||
111 | int (*select_mic_in)(struct ct_atc *atc); | ||
112 | int (*select_digit_io)(struct ct_atc *atc); | ||
113 | int (*line_front_unmute)(struct ct_atc *atc, unsigned char state); | ||
114 | int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state); | ||
115 | int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state); | ||
116 | int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state); | ||
117 | int (*line_in_unmute)(struct ct_atc *atc, unsigned char state); | ||
118 | int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state); | ||
119 | int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state); | ||
120 | int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status); | ||
121 | int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status); | ||
122 | int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state); | ||
123 | int (*have_digit_io_switch)(struct ct_atc *atc); | ||
124 | |||
125 | /* Don't touch! Used for internal object. */ | ||
126 | void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ | ||
127 | void *mixer; /* internal mixer object */ | ||
128 | void *hw; /* chip specific hardware access object */ | ||
129 | void **daios; /* digital audio io resources */ | ||
130 | void **pcm; /* SUMs for collecting all pcm stream */ | ||
131 | void **srcs; /* Sample Rate Converters for input signal */ | ||
132 | void **srcimps; /* input mappers for SRCs */ | ||
133 | unsigned char n_daio; | ||
134 | unsigned char n_src; | ||
135 | unsigned char n_srcimp; | ||
136 | unsigned char n_pcm; | ||
137 | |||
138 | struct ct_timer *timer; | ||
139 | }; | ||
140 | |||
141 | |||
142 | int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, | ||
143 | unsigned int rsr, unsigned int msr, int chip_type, | ||
144 | struct ct_atc **ratc); | ||
145 | int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); | ||
146 | |||
147 | #endif /* CTATC_H */ | ||
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c new file mode 100644 index 000000000000..082e35c08c02 --- /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->chip_type) { | ||
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; | ||
172 | struct daio *daio = &dao->daio; | ||
173 | int i; | ||
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; | ||
200 | struct daio *daio = &dao->daio; | ||
201 | int i; | ||
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; | ||
228 | struct daio *daio = &dao->daio; | ||
229 | int i; | ||
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; | ||
252 | struct daio *daio = &dao->daio; | ||
253 | int i; | ||
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; | ||
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; | ||
344 | unsigned int idx_l, idx_r; | ||
345 | |||
346 | switch (((struct hw *)hw)->chip_type) { | ||
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)->chip_type) { | ||
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; | ||
404 | int err; | ||
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; | ||
479 | struct hw *hw = mgr->mgr.hw; | ||
480 | unsigned int rsr, msr; | ||
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; | ||
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; | ||
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; | ||
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, i; | ||
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/cthardware.c b/sound/pci/ctxfi/cthardware.c new file mode 100644 index 000000000000..8e64f4862e85 --- /dev/null +++ b/sound/pci/ctxfi/cthardware.c | |||
@@ -0,0 +1,91 @@ | |||
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 | int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, | ||
24 | enum CTCARDS model, struct hw **rhw) | ||
25 | { | ||
26 | int err; | ||
27 | |||
28 | switch (chip_type) { | ||
29 | case ATC20K1: | ||
30 | err = create_20k1_hw_obj(rhw); | ||
31 | break; | ||
32 | case ATC20K2: | ||
33 | err = create_20k2_hw_obj(rhw); | ||
34 | break; | ||
35 | default: | ||
36 | err = -ENODEV; | ||
37 | break; | ||
38 | } | ||
39 | if (err) | ||
40 | return err; | ||
41 | |||
42 | (*rhw)->pci = pci; | ||
43 | (*rhw)->chip_type = chip_type; | ||
44 | (*rhw)->model = model; | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | int destroy_hw_obj(struct hw *hw) | ||
50 | { | ||
51 | int err; | ||
52 | |||
53 | switch (hw->pci->device) { | ||
54 | case 0x0005: /* 20k1 device */ | ||
55 | err = destroy_20k1_hw_obj(hw); | ||
56 | break; | ||
57 | case 0x000B: /* 20k2 device */ | ||
58 | err = destroy_20k2_hw_obj(hw); | ||
59 | break; | ||
60 | default: | ||
61 | err = -ENODEV; | ||
62 | break; | ||
63 | } | ||
64 | |||
65 | return err; | ||
66 | } | ||
67 | |||
68 | unsigned int get_field(unsigned int data, unsigned int field) | ||
69 | { | ||
70 | int i; | ||
71 | |||
72 | BUG_ON(!field); | ||
73 | /* @field should always be greater than 0 */ | ||
74 | for (i = 0; !(field & (1 << i)); ) | ||
75 | i++; | ||
76 | |||
77 | return (data & field) >> i; | ||
78 | } | ||
79 | |||
80 | void set_field(unsigned int *data, unsigned int field, unsigned int value) | ||
81 | { | ||
82 | int i; | ||
83 | |||
84 | BUG_ON(!field); | ||
85 | /* @field should always be greater than 0 */ | ||
86 | for (i = 0; !(field & (1 << i)); ) | ||
87 | i++; | ||
88 | |||
89 | *data = (*data & (~field)) | ((value << i) & field); | ||
90 | } | ||
91 | |||
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h new file mode 100644 index 000000000000..4a8e04f090a4 --- /dev/null +++ b/sound/pci/ctxfi/cthardware.h | |||
@@ -0,0 +1,196 @@ | |||
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 | enum CTCARDS { | ||
31 | /* 20k1 models */ | ||
32 | CTSB055X, | ||
33 | CTSB073X, | ||
34 | CTUAA, | ||
35 | CT20K1_UNKNOWN, | ||
36 | /* 20k2 models */ | ||
37 | CTSB0760, | ||
38 | CTHENDRIX, | ||
39 | CTSB0880, | ||
40 | NUM_CTCARDS /* This should always be the last */ | ||
41 | }; | ||
42 | |||
43 | /* Type of input source for ADC */ | ||
44 | enum ADCSRC{ | ||
45 | ADC_MICIN, | ||
46 | ADC_LINEIN, | ||
47 | ADC_VIDEO, | ||
48 | ADC_AUX, | ||
49 | ADC_NONE /* Switch to digital input */ | ||
50 | }; | ||
51 | |||
52 | struct card_conf { | ||
53 | /* device virtual mem page table page physical addr | ||
54 | * (supporting one page table page now) */ | ||
55 | unsigned long vm_pgt_phys; | ||
56 | unsigned int rsr; /* reference sample rate in Hzs*/ | ||
57 | unsigned int msr; /* master sample rate in rsrs */ | ||
58 | }; | ||
59 | |||
60 | struct hw { | ||
61 | int (*card_init)(struct hw *hw, struct card_conf *info); | ||
62 | int (*card_stop)(struct hw *hw); | ||
63 | int (*pll_init)(struct hw *hw, unsigned int rsr); | ||
64 | int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); | ||
65 | int (*select_adc_source)(struct hw *hw, enum ADCSRC source); | ||
66 | int (*have_digit_io_switch)(struct hw *hw); | ||
67 | |||
68 | /* SRC operations */ | ||
69 | int (*src_rsc_get_ctrl_blk)(void **rblk); | ||
70 | int (*src_rsc_put_ctrl_blk)(void *blk); | ||
71 | int (*src_set_state)(void *blk, unsigned int state); | ||
72 | int (*src_set_bm)(void *blk, unsigned int bm); | ||
73 | int (*src_set_rsr)(void *blk, unsigned int rsr); | ||
74 | int (*src_set_sf)(void *blk, unsigned int sf); | ||
75 | int (*src_set_wr)(void *blk, unsigned int wr); | ||
76 | int (*src_set_pm)(void *blk, unsigned int pm); | ||
77 | int (*src_set_rom)(void *blk, unsigned int rom); | ||
78 | int (*src_set_vo)(void *blk, unsigned int vo); | ||
79 | int (*src_set_st)(void *blk, unsigned int st); | ||
80 | int (*src_set_ie)(void *blk, unsigned int ie); | ||
81 | int (*src_set_ilsz)(void *blk, unsigned int ilsz); | ||
82 | int (*src_set_bp)(void *blk, unsigned int bp); | ||
83 | int (*src_set_cisz)(void *blk, unsigned int cisz); | ||
84 | int (*src_set_ca)(void *blk, unsigned int ca); | ||
85 | int (*src_set_sa)(void *blk, unsigned int sa); | ||
86 | int (*src_set_la)(void *blk, unsigned int la); | ||
87 | int (*src_set_pitch)(void *blk, unsigned int pitch); | ||
88 | int (*src_set_clear_zbufs)(void *blk, unsigned int clear); | ||
89 | int (*src_set_dirty)(void *blk, unsigned int flags); | ||
90 | int (*src_set_dirty_all)(void *blk); | ||
91 | int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
92 | int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk); | ||
93 | unsigned int (*src_get_dirty)(void *blk); | ||
94 | unsigned int (*src_dirty_conj_mask)(void); | ||
95 | int (*src_mgr_get_ctrl_blk)(void **rblk); | ||
96 | int (*src_mgr_put_ctrl_blk)(void *blk); | ||
97 | /* syncly enable src @idx */ | ||
98 | int (*src_mgr_enbs_src)(void *blk, unsigned int idx); | ||
99 | /* enable src @idx */ | ||
100 | int (*src_mgr_enb_src)(void *blk, unsigned int idx); | ||
101 | /* disable src @idx */ | ||
102 | int (*src_mgr_dsb_src)(void *blk, unsigned int idx); | ||
103 | int (*src_mgr_commit_write)(struct hw *hw, void *blk); | ||
104 | |||
105 | /* SRC Input Mapper operations */ | ||
106 | int (*srcimp_mgr_get_ctrl_blk)(void **rblk); | ||
107 | int (*srcimp_mgr_put_ctrl_blk)(void *blk); | ||
108 | int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot); | ||
109 | int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user); | ||
110 | int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next); | ||
111 | int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr); | ||
112 | int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk); | ||
113 | |||
114 | /* AMIXER operations */ | ||
115 | int (*amixer_rsc_get_ctrl_blk)(void **rblk); | ||
116 | int (*amixer_rsc_put_ctrl_blk)(void *blk); | ||
117 | int (*amixer_mgr_get_ctrl_blk)(void **rblk); | ||
118 | int (*amixer_mgr_put_ctrl_blk)(void *blk); | ||
119 | int (*amixer_set_mode)(void *blk, unsigned int mode); | ||
120 | int (*amixer_set_iv)(void *blk, unsigned int iv); | ||
121 | int (*amixer_set_x)(void *blk, unsigned int x); | ||
122 | int (*amixer_set_y)(void *blk, unsigned int y); | ||
123 | int (*amixer_set_sadr)(void *blk, unsigned int sadr); | ||
124 | int (*amixer_set_se)(void *blk, unsigned int se); | ||
125 | int (*amixer_set_dirty)(void *blk, unsigned int flags); | ||
126 | int (*amixer_set_dirty_all)(void *blk); | ||
127 | int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
128 | int (*amixer_get_y)(void *blk); | ||
129 | unsigned int (*amixer_get_dirty)(void *blk); | ||
130 | |||
131 | /* DAIO operations */ | ||
132 | int (*dai_get_ctrl_blk)(void **rblk); | ||
133 | int (*dai_put_ctrl_blk)(void *blk); | ||
134 | int (*dai_srt_set_srco)(void *blk, unsigned int src); | ||
135 | int (*dai_srt_set_srcm)(void *blk, unsigned int src); | ||
136 | int (*dai_srt_set_rsr)(void *blk, unsigned int rsr); | ||
137 | int (*dai_srt_set_drat)(void *blk, unsigned int drat); | ||
138 | int (*dai_srt_set_ec)(void *blk, unsigned int ec); | ||
139 | int (*dai_srt_set_et)(void *blk, unsigned int et); | ||
140 | int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
141 | int (*dao_get_ctrl_blk)(void **rblk); | ||
142 | int (*dao_put_ctrl_blk)(void *blk); | ||
143 | int (*dao_set_spos)(void *blk, unsigned int spos); | ||
144 | int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk); | ||
145 | int (*dao_get_spos)(void *blk, unsigned int *spos); | ||
146 | |||
147 | int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk); | ||
148 | int (*daio_mgr_put_ctrl_blk)(void *blk); | ||
149 | int (*daio_mgr_enb_dai)(void *blk, unsigned int idx); | ||
150 | int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx); | ||
151 | int (*daio_mgr_enb_dao)(void *blk, unsigned int idx); | ||
152 | int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx); | ||
153 | int (*daio_mgr_dao_init)(void *blk, unsigned int idx, | ||
154 | unsigned int conf); | ||
155 | int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot); | ||
156 | int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next); | ||
157 | int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); | ||
158 | int (*daio_mgr_commit_write)(struct hw *hw, void *blk); | ||
159 | |||
160 | int (*set_timer_irq)(struct hw *hw, int enable); | ||
161 | int (*set_timer_tick)(struct hw *hw, unsigned int tick); | ||
162 | unsigned int (*get_wc)(struct hw *hw); | ||
163 | |||
164 | void (*irq_callback)(void *data, unsigned int bit); | ||
165 | void *irq_callback_data; | ||
166 | |||
167 | struct pci_dev *pci; /* the pci kernel structure of this card */ | ||
168 | int irq; | ||
169 | unsigned long io_base; | ||
170 | unsigned long mem_base; | ||
171 | |||
172 | enum CHIPTYP chip_type; | ||
173 | enum CTCARDS model; | ||
174 | }; | ||
175 | |||
176 | int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, | ||
177 | enum CTCARDS model, struct hw **rhw); | ||
178 | int destroy_hw_obj(struct hw *hw); | ||
179 | |||
180 | unsigned int get_field(unsigned int data, unsigned int field); | ||
181 | void set_field(unsigned int *data, unsigned int field, unsigned int value); | ||
182 | |||
183 | /* IRQ bits */ | ||
184 | #define PLL_INT (1 << 10) /* PLL input-clock out-of-range */ | ||
185 | #define FI_INT (1 << 9) /* forced interrupt */ | ||
186 | #define IT_INT (1 << 8) /* timer interrupt */ | ||
187 | #define PCI_INT (1 << 7) /* PCI bus error pending */ | ||
188 | #define URT_INT (1 << 6) /* UART Tx/Rx */ | ||
189 | #define GPI_INT (1 << 5) /* GPI pin */ | ||
190 | #define MIX_INT (1 << 4) /* mixer parameter segment FIFO channels */ | ||
191 | #define DAI_INT (1 << 3) /* DAI (SR-tracker or SPDIF-receiver) */ | ||
192 | #define TP_INT (1 << 2) /* transport priority queue */ | ||
193 | #define DSP_INT (1 << 1) /* DSP */ | ||
194 | #define SRC_INT (1 << 0) /* SRC channels */ | ||
195 | |||
196 | #endif /* CTHARDWARE_H */ | ||
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c new file mode 100644 index 000000000000..cb69d9ddfbe3 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.c | |||
@@ -0,0 +1,2248 @@ | |||
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 <linux/types.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include "cthw20k1.h" | ||
28 | #include "ct20k1reg.h" | ||
29 | |||
30 | #if BITS_PER_LONG == 32 | ||
31 | #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ | ||
32 | #else | ||
33 | #define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ | ||
34 | #endif | ||
35 | |||
36 | struct hw20k1 { | ||
37 | struct hw hw; | ||
38 | spinlock_t reg_20k1_lock; | ||
39 | spinlock_t reg_pci_lock; | ||
40 | }; | ||
41 | |||
42 | static u32 hw_read_20kx(struct hw *hw, u32 reg); | ||
43 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); | ||
44 | static u32 hw_read_pci(struct hw *hw, u32 reg); | ||
45 | static void hw_write_pci(struct hw *hw, u32 reg, u32 data); | ||
46 | |||
47 | /* | ||
48 | * Type definition block. | ||
49 | * The layout of control structures can be directly applied on 20k2 chip. | ||
50 | */ | ||
51 | |||
52 | /* | ||
53 | * SRC control block definitions. | ||
54 | */ | ||
55 | |||
56 | /* SRC resource control block */ | ||
57 | #define SRCCTL_STATE 0x00000007 | ||
58 | #define SRCCTL_BM 0x00000008 | ||
59 | #define SRCCTL_RSR 0x00000030 | ||
60 | #define SRCCTL_SF 0x000001C0 | ||
61 | #define SRCCTL_WR 0x00000200 | ||
62 | #define SRCCTL_PM 0x00000400 | ||
63 | #define SRCCTL_ROM 0x00001800 | ||
64 | #define SRCCTL_VO 0x00002000 | ||
65 | #define SRCCTL_ST 0x00004000 | ||
66 | #define SRCCTL_IE 0x00008000 | ||
67 | #define SRCCTL_ILSZ 0x000F0000 | ||
68 | #define SRCCTL_BP 0x00100000 | ||
69 | |||
70 | #define SRCCCR_CISZ 0x000007FF | ||
71 | #define SRCCCR_CWA 0x001FF800 | ||
72 | #define SRCCCR_D 0x00200000 | ||
73 | #define SRCCCR_RS 0x01C00000 | ||
74 | #define SRCCCR_NAL 0x3E000000 | ||
75 | #define SRCCCR_RA 0xC0000000 | ||
76 | |||
77 | #define SRCCA_CA 0x03FFFFFF | ||
78 | #define SRCCA_RS 0x1C000000 | ||
79 | #define SRCCA_NAL 0xE0000000 | ||
80 | |||
81 | #define SRCSA_SA 0x03FFFFFF | ||
82 | |||
83 | #define SRCLA_LA 0x03FFFFFF | ||
84 | |||
85 | /* Mixer Parameter Ring ram Low and Hight register. | ||
86 | * Fixed-point value in 8.24 format for parameter channel */ | ||
87 | #define MPRLH_PITCH 0xFFFFFFFF | ||
88 | |||
89 | /* SRC resource register dirty flags */ | ||
90 | union src_dirty { | ||
91 | struct { | ||
92 | u16 ctl:1; | ||
93 | u16 ccr:1; | ||
94 | u16 sa:1; | ||
95 | u16 la:1; | ||
96 | u16 ca:1; | ||
97 | u16 mpr:1; | ||
98 | u16 czbfs:1; /* Clear Z-Buffers */ | ||
99 | u16 rsv:9; | ||
100 | } bf; | ||
101 | u16 data; | ||
102 | }; | ||
103 | |||
104 | struct src_rsc_ctrl_blk { | ||
105 | unsigned int ctl; | ||
106 | unsigned int ccr; | ||
107 | unsigned int ca; | ||
108 | unsigned int sa; | ||
109 | unsigned int la; | ||
110 | unsigned int mpr; | ||
111 | union src_dirty dirty; | ||
112 | }; | ||
113 | |||
114 | /* SRC manager control block */ | ||
115 | union src_mgr_dirty { | ||
116 | struct { | ||
117 | u16 enb0:1; | ||
118 | u16 enb1:1; | ||
119 | u16 enb2:1; | ||
120 | u16 enb3:1; | ||
121 | u16 enb4:1; | ||
122 | u16 enb5:1; | ||
123 | u16 enb6:1; | ||
124 | u16 enb7:1; | ||
125 | u16 enbsa:1; | ||
126 | u16 rsv:7; | ||
127 | } bf; | ||
128 | u16 data; | ||
129 | }; | ||
130 | |||
131 | struct src_mgr_ctrl_blk { | ||
132 | unsigned int enbsa; | ||
133 | unsigned int enb[8]; | ||
134 | union src_mgr_dirty dirty; | ||
135 | }; | ||
136 | |||
137 | /* SRCIMP manager control block */ | ||
138 | #define SRCAIM_ARC 0x00000FFF | ||
139 | #define SRCAIM_NXT 0x00FF0000 | ||
140 | #define SRCAIM_SRC 0xFF000000 | ||
141 | |||
142 | struct srcimap { | ||
143 | unsigned int srcaim; | ||
144 | unsigned int idx; | ||
145 | }; | ||
146 | |||
147 | /* SRCIMP manager register dirty flags */ | ||
148 | union srcimp_mgr_dirty { | ||
149 | struct { | ||
150 | u16 srcimap:1; | ||
151 | u16 rsv:15; | ||
152 | } bf; | ||
153 | u16 data; | ||
154 | }; | ||
155 | |||
156 | struct srcimp_mgr_ctrl_blk { | ||
157 | struct srcimap srcimap; | ||
158 | union srcimp_mgr_dirty dirty; | ||
159 | }; | ||
160 | |||
161 | /* | ||
162 | * Function implementation block. | ||
163 | */ | ||
164 | |||
165 | static int src_get_rsc_ctrl_blk(void **rblk) | ||
166 | { | ||
167 | struct src_rsc_ctrl_blk *blk; | ||
168 | |||
169 | *rblk = NULL; | ||
170 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
171 | if (NULL == blk) | ||
172 | return -ENOMEM; | ||
173 | |||
174 | *rblk = blk; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int src_put_rsc_ctrl_blk(void *blk) | ||
180 | { | ||
181 | kfree((struct src_rsc_ctrl_blk *)blk); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int src_set_state(void *blk, unsigned int state) | ||
187 | { | ||
188 | struct src_rsc_ctrl_blk *ctl = blk; | ||
189 | |||
190 | set_field(&ctl->ctl, SRCCTL_STATE, state); | ||
191 | ctl->dirty.bf.ctl = 1; | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int src_set_bm(void *blk, unsigned int bm) | ||
196 | { | ||
197 | struct src_rsc_ctrl_blk *ctl = blk; | ||
198 | |||
199 | set_field(&ctl->ctl, SRCCTL_BM, bm); | ||
200 | ctl->dirty.bf.ctl = 1; | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int src_set_rsr(void *blk, unsigned int rsr) | ||
205 | { | ||
206 | struct src_rsc_ctrl_blk *ctl = blk; | ||
207 | |||
208 | set_field(&ctl->ctl, SRCCTL_RSR, rsr); | ||
209 | ctl->dirty.bf.ctl = 1; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int src_set_sf(void *blk, unsigned int sf) | ||
214 | { | ||
215 | struct src_rsc_ctrl_blk *ctl = blk; | ||
216 | |||
217 | set_field(&ctl->ctl, SRCCTL_SF, sf); | ||
218 | ctl->dirty.bf.ctl = 1; | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static int src_set_wr(void *blk, unsigned int wr) | ||
223 | { | ||
224 | struct src_rsc_ctrl_blk *ctl = blk; | ||
225 | |||
226 | set_field(&ctl->ctl, SRCCTL_WR, wr); | ||
227 | ctl->dirty.bf.ctl = 1; | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int src_set_pm(void *blk, unsigned int pm) | ||
232 | { | ||
233 | struct src_rsc_ctrl_blk *ctl = blk; | ||
234 | |||
235 | set_field(&ctl->ctl, SRCCTL_PM, pm); | ||
236 | ctl->dirty.bf.ctl = 1; | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int src_set_rom(void *blk, unsigned int rom) | ||
241 | { | ||
242 | struct src_rsc_ctrl_blk *ctl = blk; | ||
243 | |||
244 | set_field(&ctl->ctl, SRCCTL_ROM, rom); | ||
245 | ctl->dirty.bf.ctl = 1; | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int src_set_vo(void *blk, unsigned int vo) | ||
250 | { | ||
251 | struct src_rsc_ctrl_blk *ctl = blk; | ||
252 | |||
253 | set_field(&ctl->ctl, SRCCTL_VO, vo); | ||
254 | ctl->dirty.bf.ctl = 1; | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int src_set_st(void *blk, unsigned int st) | ||
259 | { | ||
260 | struct src_rsc_ctrl_blk *ctl = blk; | ||
261 | |||
262 | set_field(&ctl->ctl, SRCCTL_ST, st); | ||
263 | ctl->dirty.bf.ctl = 1; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int src_set_ie(void *blk, unsigned int ie) | ||
268 | { | ||
269 | struct src_rsc_ctrl_blk *ctl = blk; | ||
270 | |||
271 | set_field(&ctl->ctl, SRCCTL_IE, ie); | ||
272 | ctl->dirty.bf.ctl = 1; | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static int src_set_ilsz(void *blk, unsigned int ilsz) | ||
277 | { | ||
278 | struct src_rsc_ctrl_blk *ctl = blk; | ||
279 | |||
280 | set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); | ||
281 | ctl->dirty.bf.ctl = 1; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int src_set_bp(void *blk, unsigned int bp) | ||
286 | { | ||
287 | struct src_rsc_ctrl_blk *ctl = blk; | ||
288 | |||
289 | set_field(&ctl->ctl, SRCCTL_BP, bp); | ||
290 | ctl->dirty.bf.ctl = 1; | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int src_set_cisz(void *blk, unsigned int cisz) | ||
295 | { | ||
296 | struct src_rsc_ctrl_blk *ctl = blk; | ||
297 | |||
298 | set_field(&ctl->ccr, SRCCCR_CISZ, cisz); | ||
299 | ctl->dirty.bf.ccr = 1; | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int src_set_ca(void *blk, unsigned int ca) | ||
304 | { | ||
305 | struct src_rsc_ctrl_blk *ctl = blk; | ||
306 | |||
307 | set_field(&ctl->ca, SRCCA_CA, ca); | ||
308 | ctl->dirty.bf.ca = 1; | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int src_set_sa(void *blk, unsigned int sa) | ||
313 | { | ||
314 | struct src_rsc_ctrl_blk *ctl = blk; | ||
315 | |||
316 | set_field(&ctl->sa, SRCSA_SA, sa); | ||
317 | ctl->dirty.bf.sa = 1; | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int src_set_la(void *blk, unsigned int la) | ||
322 | { | ||
323 | struct src_rsc_ctrl_blk *ctl = blk; | ||
324 | |||
325 | set_field(&ctl->la, SRCLA_LA, la); | ||
326 | ctl->dirty.bf.la = 1; | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static int src_set_pitch(void *blk, unsigned int pitch) | ||
331 | { | ||
332 | struct src_rsc_ctrl_blk *ctl = blk; | ||
333 | |||
334 | set_field(&ctl->mpr, MPRLH_PITCH, pitch); | ||
335 | ctl->dirty.bf.mpr = 1; | ||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | static int src_set_clear_zbufs(void *blk, unsigned int clear) | ||
340 | { | ||
341 | ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int src_set_dirty(void *blk, unsigned int flags) | ||
346 | { | ||
347 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int src_set_dirty_all(void *blk) | ||
352 | { | ||
353 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | #define AR_SLOT_SIZE 4096 | ||
358 | #define AR_SLOT_BLOCK_SIZE 16 | ||
359 | #define AR_PTS_PITCH 6 | ||
360 | #define AR_PARAM_SRC_OFFSET 0x60 | ||
361 | |||
362 | static unsigned int src_param_pitch_mixer(unsigned int src_idx) | ||
363 | { | ||
364 | return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE | ||
365 | - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; | ||
366 | |||
367 | } | ||
368 | |||
369 | static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
370 | { | ||
371 | struct src_rsc_ctrl_blk *ctl = blk; | ||
372 | int i; | ||
373 | |||
374 | if (ctl->dirty.bf.czbfs) { | ||
375 | /* Clear Z-Buffer registers */ | ||
376 | for (i = 0; i < 8; i++) | ||
377 | hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0); | ||
378 | |||
379 | for (i = 0; i < 4; i++) | ||
380 | hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0); | ||
381 | |||
382 | for (i = 0; i < 8; i++) | ||
383 | hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0); | ||
384 | |||
385 | ctl->dirty.bf.czbfs = 0; | ||
386 | } | ||
387 | if (ctl->dirty.bf.mpr) { | ||
388 | /* Take the parameter mixer resource in the same group as that | ||
389 | * the idx src is in for simplicity. Unlike src, all conjugate | ||
390 | * parameter mixer resources must be programmed for | ||
391 | * corresponding conjugate src resources. */ | ||
392 | unsigned int pm_idx = src_param_pitch_mixer(idx); | ||
393 | hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr); | ||
394 | hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3); | ||
395 | hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0); | ||
396 | ctl->dirty.bf.mpr = 0; | ||
397 | } | ||
398 | if (ctl->dirty.bf.sa) { | ||
399 | hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa); | ||
400 | ctl->dirty.bf.sa = 0; | ||
401 | } | ||
402 | if (ctl->dirty.bf.la) { | ||
403 | hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la); | ||
404 | ctl->dirty.bf.la = 0; | ||
405 | } | ||
406 | if (ctl->dirty.bf.ca) { | ||
407 | hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca); | ||
408 | ctl->dirty.bf.ca = 0; | ||
409 | } | ||
410 | |||
411 | /* Write srccf register */ | ||
412 | hw_write_20kx(hw, SRCCF+idx*0x100, 0x0); | ||
413 | |||
414 | if (ctl->dirty.bf.ccr) { | ||
415 | hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr); | ||
416 | ctl->dirty.bf.ccr = 0; | ||
417 | } | ||
418 | if (ctl->dirty.bf.ctl) { | ||
419 | hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl); | ||
420 | ctl->dirty.bf.ctl = 0; | ||
421 | } | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) | ||
427 | { | ||
428 | struct src_rsc_ctrl_blk *ctl = blk; | ||
429 | |||
430 | ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100); | ||
431 | ctl->dirty.bf.ca = 0; | ||
432 | |||
433 | return get_field(ctl->ca, SRCCA_CA); | ||
434 | } | ||
435 | |||
436 | static unsigned int src_get_dirty(void *blk) | ||
437 | { | ||
438 | return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; | ||
439 | } | ||
440 | |||
441 | static unsigned int src_dirty_conj_mask(void) | ||
442 | { | ||
443 | return 0x20; | ||
444 | } | ||
445 | |||
446 | static int src_mgr_enbs_src(void *blk, unsigned int idx) | ||
447 | { | ||
448 | ((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0); | ||
449 | ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; | ||
450 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | static int src_mgr_enb_src(void *blk, unsigned int idx) | ||
455 | { | ||
456 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
457 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int src_mgr_dsb_src(void *blk, unsigned int idx) | ||
462 | { | ||
463 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); | ||
464 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | static int src_mgr_commit_write(struct hw *hw, void *blk) | ||
469 | { | ||
470 | struct src_mgr_ctrl_blk *ctl = blk; | ||
471 | int i; | ||
472 | unsigned int ret; | ||
473 | |||
474 | if (ctl->dirty.bf.enbsa) { | ||
475 | do { | ||
476 | ret = hw_read_20kx(hw, SRCENBSTAT); | ||
477 | } while (ret & 0x1); | ||
478 | hw_write_20kx(hw, SRCENBS, ctl->enbsa); | ||
479 | ctl->dirty.bf.enbsa = 0; | ||
480 | } | ||
481 | for (i = 0; i < 8; i++) { | ||
482 | if ((ctl->dirty.data & (0x1 << i))) { | ||
483 | hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]); | ||
484 | ctl->dirty.data &= ~(0x1 << i); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static int src_mgr_get_ctrl_blk(void **rblk) | ||
492 | { | ||
493 | struct src_mgr_ctrl_blk *blk; | ||
494 | |||
495 | *rblk = NULL; | ||
496 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
497 | if (NULL == blk) | ||
498 | return -ENOMEM; | ||
499 | |||
500 | *rblk = blk; | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int src_mgr_put_ctrl_blk(void *blk) | ||
506 | { | ||
507 | kfree((struct src_mgr_ctrl_blk *)blk); | ||
508 | |||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static int srcimp_mgr_get_ctrl_blk(void **rblk) | ||
513 | { | ||
514 | struct srcimp_mgr_ctrl_blk *blk; | ||
515 | |||
516 | *rblk = NULL; | ||
517 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
518 | if (NULL == blk) | ||
519 | return -ENOMEM; | ||
520 | |||
521 | *rblk = blk; | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int srcimp_mgr_put_ctrl_blk(void *blk) | ||
527 | { | ||
528 | kfree((struct srcimp_mgr_ctrl_blk *)blk); | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) | ||
534 | { | ||
535 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
536 | |||
537 | set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); | ||
538 | ctl->dirty.bf.srcimap = 1; | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) | ||
543 | { | ||
544 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
545 | |||
546 | set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); | ||
547 | ctl->dirty.bf.srcimap = 1; | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) | ||
552 | { | ||
553 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
554 | |||
555 | set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); | ||
556 | ctl->dirty.bf.srcimap = 1; | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
561 | { | ||
562 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
563 | |||
564 | ctl->srcimap.idx = addr; | ||
565 | ctl->dirty.bf.srcimap = 1; | ||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static int srcimp_mgr_commit_write(struct hw *hw, void *blk) | ||
570 | { | ||
571 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
572 | |||
573 | if (ctl->dirty.bf.srcimap) { | ||
574 | hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100, | ||
575 | ctl->srcimap.srcaim); | ||
576 | ctl->dirty.bf.srcimap = 0; | ||
577 | } | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | /* | ||
583 | * AMIXER control block definitions. | ||
584 | */ | ||
585 | |||
586 | #define AMOPLO_M 0x00000003 | ||
587 | #define AMOPLO_X 0x0003FFF0 | ||
588 | #define AMOPLO_Y 0xFFFC0000 | ||
589 | |||
590 | #define AMOPHI_SADR 0x000000FF | ||
591 | #define AMOPHI_SE 0x80000000 | ||
592 | |||
593 | /* AMIXER resource register dirty flags */ | ||
594 | union amixer_dirty { | ||
595 | struct { | ||
596 | u16 amoplo:1; | ||
597 | u16 amophi:1; | ||
598 | u16 rsv:14; | ||
599 | } bf; | ||
600 | u16 data; | ||
601 | }; | ||
602 | |||
603 | /* AMIXER resource control block */ | ||
604 | struct amixer_rsc_ctrl_blk { | ||
605 | unsigned int amoplo; | ||
606 | unsigned int amophi; | ||
607 | union amixer_dirty dirty; | ||
608 | }; | ||
609 | |||
610 | static int amixer_set_mode(void *blk, unsigned int mode) | ||
611 | { | ||
612 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
613 | |||
614 | set_field(&ctl->amoplo, AMOPLO_M, mode); | ||
615 | ctl->dirty.bf.amoplo = 1; | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int amixer_set_iv(void *blk, unsigned int iv) | ||
620 | { | ||
621 | /* 20k1 amixer does not have this field */ | ||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static int amixer_set_x(void *blk, unsigned int x) | ||
626 | { | ||
627 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
628 | |||
629 | set_field(&ctl->amoplo, AMOPLO_X, x); | ||
630 | ctl->dirty.bf.amoplo = 1; | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int amixer_set_y(void *blk, unsigned int y) | ||
635 | { | ||
636 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
637 | |||
638 | set_field(&ctl->amoplo, AMOPLO_Y, y); | ||
639 | ctl->dirty.bf.amoplo = 1; | ||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | static int amixer_set_sadr(void *blk, unsigned int sadr) | ||
644 | { | ||
645 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
646 | |||
647 | set_field(&ctl->amophi, AMOPHI_SADR, sadr); | ||
648 | ctl->dirty.bf.amophi = 1; | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int amixer_set_se(void *blk, unsigned int se) | ||
653 | { | ||
654 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
655 | |||
656 | set_field(&ctl->amophi, AMOPHI_SE, se); | ||
657 | ctl->dirty.bf.amophi = 1; | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int amixer_set_dirty(void *blk, unsigned int flags) | ||
662 | { | ||
663 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int amixer_set_dirty_all(void *blk) | ||
668 | { | ||
669 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
674 | { | ||
675 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
676 | |||
677 | if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { | ||
678 | hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo); | ||
679 | ctl->dirty.bf.amoplo = 0; | ||
680 | hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi); | ||
681 | ctl->dirty.bf.amophi = 0; | ||
682 | } | ||
683 | |||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | static int amixer_get_y(void *blk) | ||
688 | { | ||
689 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
690 | |||
691 | return get_field(ctl->amoplo, AMOPLO_Y); | ||
692 | } | ||
693 | |||
694 | static unsigned int amixer_get_dirty(void *blk) | ||
695 | { | ||
696 | return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; | ||
697 | } | ||
698 | |||
699 | static int amixer_rsc_get_ctrl_blk(void **rblk) | ||
700 | { | ||
701 | struct amixer_rsc_ctrl_blk *blk; | ||
702 | |||
703 | *rblk = NULL; | ||
704 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
705 | if (NULL == blk) | ||
706 | return -ENOMEM; | ||
707 | |||
708 | *rblk = blk; | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static int amixer_rsc_put_ctrl_blk(void *blk) | ||
714 | { | ||
715 | kfree((struct amixer_rsc_ctrl_blk *)blk); | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | static int amixer_mgr_get_ctrl_blk(void **rblk) | ||
721 | { | ||
722 | /*amixer_mgr_ctrl_blk_t *blk;*/ | ||
723 | |||
724 | *rblk = NULL; | ||
725 | /*blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
726 | if (NULL == blk) | ||
727 | return -ENOMEM; | ||
728 | |||
729 | *rblk = blk;*/ | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static int amixer_mgr_put_ctrl_blk(void *blk) | ||
735 | { | ||
736 | /*kfree((amixer_mgr_ctrl_blk_t *)blk);*/ | ||
737 | |||
738 | return 0; | ||
739 | } | ||
740 | |||
741 | /* | ||
742 | * DAIO control block definitions. | ||
743 | */ | ||
744 | |||
745 | /* Receiver Sample Rate Tracker Control register */ | ||
746 | #define SRTCTL_SRCR 0x000000FF | ||
747 | #define SRTCTL_SRCL 0x0000FF00 | ||
748 | #define SRTCTL_RSR 0x00030000 | ||
749 | #define SRTCTL_DRAT 0x000C0000 | ||
750 | #define SRTCTL_RLE 0x10000000 | ||
751 | #define SRTCTL_RLP 0x20000000 | ||
752 | #define SRTCTL_EC 0x40000000 | ||
753 | #define SRTCTL_ET 0x80000000 | ||
754 | |||
755 | /* DAIO Receiver register dirty flags */ | ||
756 | union dai_dirty { | ||
757 | struct { | ||
758 | u16 srtctl:1; | ||
759 | u16 rsv:15; | ||
760 | } bf; | ||
761 | u16 data; | ||
762 | }; | ||
763 | |||
764 | /* DAIO Receiver control block */ | ||
765 | struct dai_ctrl_blk { | ||
766 | unsigned int srtctl; | ||
767 | union dai_dirty dirty; | ||
768 | }; | ||
769 | |||
770 | /* S/PDIF Transmitter register dirty flags */ | ||
771 | union dao_dirty { | ||
772 | struct { | ||
773 | u16 spos:1; | ||
774 | u16 rsv:15; | ||
775 | } bf; | ||
776 | u16 data; | ||
777 | }; | ||
778 | |||
779 | /* S/PDIF Transmitter control block */ | ||
780 | struct dao_ctrl_blk { | ||
781 | unsigned int spos; /* S/PDIF Output Channel Status Register */ | ||
782 | union dao_dirty dirty; | ||
783 | }; | ||
784 | |||
785 | /* Audio Input Mapper RAM */ | ||
786 | #define AIM_ARC 0x00000FFF | ||
787 | #define AIM_NXT 0x007F0000 | ||
788 | |||
789 | struct daoimap { | ||
790 | unsigned int aim; | ||
791 | unsigned int idx; | ||
792 | }; | ||
793 | |||
794 | /* I2S Transmitter/Receiver Control register */ | ||
795 | #define I2SCTL_EA 0x00000004 | ||
796 | #define I2SCTL_EI 0x00000010 | ||
797 | |||
798 | /* S/PDIF Transmitter Control register */ | ||
799 | #define SPOCTL_OE 0x00000001 | ||
800 | #define SPOCTL_OS 0x0000000E | ||
801 | #define SPOCTL_RIV 0x00000010 | ||
802 | #define SPOCTL_LIV 0x00000020 | ||
803 | #define SPOCTL_SR 0x000000C0 | ||
804 | |||
805 | /* S/PDIF Receiver Control register */ | ||
806 | #define SPICTL_EN 0x00000001 | ||
807 | #define SPICTL_I24 0x00000002 | ||
808 | #define SPICTL_IB 0x00000004 | ||
809 | #define SPICTL_SM 0x00000008 | ||
810 | #define SPICTL_VM 0x00000010 | ||
811 | |||
812 | /* DAIO manager register dirty flags */ | ||
813 | union daio_mgr_dirty { | ||
814 | struct { | ||
815 | u32 i2soctl:4; | ||
816 | u32 i2sictl:4; | ||
817 | u32 spoctl:4; | ||
818 | u32 spictl:4; | ||
819 | u32 daoimap:1; | ||
820 | u32 rsv:15; | ||
821 | } bf; | ||
822 | u32 data; | ||
823 | }; | ||
824 | |||
825 | /* DAIO manager control block */ | ||
826 | struct daio_mgr_ctrl_blk { | ||
827 | unsigned int i2sctl; | ||
828 | unsigned int spoctl; | ||
829 | unsigned int spictl; | ||
830 | struct daoimap daoimap; | ||
831 | union daio_mgr_dirty dirty; | ||
832 | }; | ||
833 | |||
834 | static int dai_srt_set_srcr(void *blk, unsigned int src) | ||
835 | { | ||
836 | struct dai_ctrl_blk *ctl = blk; | ||
837 | |||
838 | set_field(&ctl->srtctl, SRTCTL_SRCR, src); | ||
839 | ctl->dirty.bf.srtctl = 1; | ||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static int dai_srt_set_srcl(void *blk, unsigned int src) | ||
844 | { | ||
845 | struct dai_ctrl_blk *ctl = blk; | ||
846 | |||
847 | set_field(&ctl->srtctl, SRTCTL_SRCL, src); | ||
848 | ctl->dirty.bf.srtctl = 1; | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static int dai_srt_set_rsr(void *blk, unsigned int rsr) | ||
853 | { | ||
854 | struct dai_ctrl_blk *ctl = blk; | ||
855 | |||
856 | set_field(&ctl->srtctl, SRTCTL_RSR, rsr); | ||
857 | ctl->dirty.bf.srtctl = 1; | ||
858 | return 0; | ||
859 | } | ||
860 | |||
861 | static int dai_srt_set_drat(void *blk, unsigned int drat) | ||
862 | { | ||
863 | struct dai_ctrl_blk *ctl = blk; | ||
864 | |||
865 | set_field(&ctl->srtctl, SRTCTL_DRAT, drat); | ||
866 | ctl->dirty.bf.srtctl = 1; | ||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | static int dai_srt_set_ec(void *blk, unsigned int ec) | ||
871 | { | ||
872 | struct dai_ctrl_blk *ctl = blk; | ||
873 | |||
874 | set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0); | ||
875 | ctl->dirty.bf.srtctl = 1; | ||
876 | return 0; | ||
877 | } | ||
878 | |||
879 | static int dai_srt_set_et(void *blk, unsigned int et) | ||
880 | { | ||
881 | struct dai_ctrl_blk *ctl = blk; | ||
882 | |||
883 | set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0); | ||
884 | ctl->dirty.bf.srtctl = 1; | ||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
889 | { | ||
890 | struct dai_ctrl_blk *ctl = blk; | ||
891 | |||
892 | if (ctl->dirty.bf.srtctl) { | ||
893 | if (idx < 4) { | ||
894 | /* S/PDIF SRTs */ | ||
895 | hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl); | ||
896 | } else { | ||
897 | /* I2S SRT */ | ||
898 | hw_write_20kx(hw, SRTICTL, ctl->srtctl); | ||
899 | } | ||
900 | ctl->dirty.bf.srtctl = 0; | ||
901 | } | ||
902 | |||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | static int dai_get_ctrl_blk(void **rblk) | ||
907 | { | ||
908 | struct dai_ctrl_blk *blk; | ||
909 | |||
910 | *rblk = NULL; | ||
911 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
912 | if (NULL == blk) | ||
913 | return -ENOMEM; | ||
914 | |||
915 | *rblk = blk; | ||
916 | |||
917 | return 0; | ||
918 | } | ||
919 | |||
920 | static int dai_put_ctrl_blk(void *blk) | ||
921 | { | ||
922 | kfree((struct dai_ctrl_blk *)blk); | ||
923 | |||
924 | return 0; | ||
925 | } | ||
926 | |||
927 | static int dao_set_spos(void *blk, unsigned int spos) | ||
928 | { | ||
929 | ((struct dao_ctrl_blk *)blk)->spos = spos; | ||
930 | ((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1; | ||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
935 | { | ||
936 | struct dao_ctrl_blk *ctl = blk; | ||
937 | |||
938 | if (ctl->dirty.bf.spos) { | ||
939 | if (idx < 4) { | ||
940 | /* S/PDIF SPOSx */ | ||
941 | hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos); | ||
942 | } | ||
943 | ctl->dirty.bf.spos = 0; | ||
944 | } | ||
945 | |||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | static int dao_get_spos(void *blk, unsigned int *spos) | ||
950 | { | ||
951 | *spos = ((struct dao_ctrl_blk *)blk)->spos; | ||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | static int dao_get_ctrl_blk(void **rblk) | ||
956 | { | ||
957 | struct dao_ctrl_blk *blk; | ||
958 | |||
959 | *rblk = NULL; | ||
960 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
961 | if (NULL == blk) | ||
962 | return -ENOMEM; | ||
963 | |||
964 | *rblk = blk; | ||
965 | |||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static int dao_put_ctrl_blk(void *blk) | ||
970 | { | ||
971 | kfree((struct dao_ctrl_blk *)blk); | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | static int daio_mgr_enb_dai(void *blk, unsigned int idx) | ||
977 | { | ||
978 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
979 | |||
980 | if (idx < 4) { | ||
981 | /* S/PDIF input */ | ||
982 | set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1); | ||
983 | ctl->dirty.bf.spictl |= (0x1 << idx); | ||
984 | } else { | ||
985 | /* I2S input */ | ||
986 | idx %= 4; | ||
987 | set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1); | ||
988 | ctl->dirty.bf.i2sictl |= (0x1 << idx); | ||
989 | } | ||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | static int daio_mgr_dsb_dai(void *blk, unsigned int idx) | ||
994 | { | ||
995 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
996 | |||
997 | if (idx < 4) { | ||
998 | /* S/PDIF input */ | ||
999 | set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0); | ||
1000 | ctl->dirty.bf.spictl |= (0x1 << idx); | ||
1001 | } else { | ||
1002 | /* I2S input */ | ||
1003 | idx %= 4; | ||
1004 | set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0); | ||
1005 | ctl->dirty.bf.i2sictl |= (0x1 << idx); | ||
1006 | } | ||
1007 | return 0; | ||
1008 | } | ||
1009 | |||
1010 | static int daio_mgr_enb_dao(void *blk, unsigned int idx) | ||
1011 | { | ||
1012 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1013 | |||
1014 | if (idx < 4) { | ||
1015 | /* S/PDIF output */ | ||
1016 | set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1); | ||
1017 | ctl->dirty.bf.spoctl |= (0x1 << idx); | ||
1018 | } else { | ||
1019 | /* I2S output */ | ||
1020 | idx %= 4; | ||
1021 | set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1); | ||
1022 | ctl->dirty.bf.i2soctl |= (0x1 << idx); | ||
1023 | } | ||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | static int daio_mgr_dsb_dao(void *blk, unsigned int idx) | ||
1028 | { | ||
1029 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1030 | |||
1031 | if (idx < 4) { | ||
1032 | /* S/PDIF output */ | ||
1033 | set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0); | ||
1034 | ctl->dirty.bf.spoctl |= (0x1 << idx); | ||
1035 | } else { | ||
1036 | /* I2S output */ | ||
1037 | idx %= 4; | ||
1038 | set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0); | ||
1039 | ctl->dirty.bf.i2soctl |= (0x1 << idx); | ||
1040 | } | ||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) | ||
1045 | { | ||
1046 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1047 | |||
1048 | if (idx < 4) { | ||
1049 | /* S/PDIF output */ | ||
1050 | switch ((conf & 0x7)) { | ||
1051 | case 0: | ||
1052 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3); | ||
1053 | break; /* CDIF */ | ||
1054 | case 1: | ||
1055 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0); | ||
1056 | break; | ||
1057 | case 2: | ||
1058 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1); | ||
1059 | break; | ||
1060 | case 4: | ||
1061 | set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2); | ||
1062 | break; | ||
1063 | default: | ||
1064 | break; | ||
1065 | } | ||
1066 | set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8), | ||
1067 | (conf >> 4) & 0x1); /* Non-audio */ | ||
1068 | set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8), | ||
1069 | (conf >> 4) & 0x1); /* Non-audio */ | ||
1070 | set_field(&ctl->spoctl, SPOCTL_OS << (idx*8), | ||
1071 | ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */ | ||
1072 | |||
1073 | ctl->dirty.bf.spoctl |= (0x1 << idx); | ||
1074 | } else { | ||
1075 | /* I2S output */ | ||
1076 | /*idx %= 4; */ | ||
1077 | } | ||
1078 | return 0; | ||
1079 | } | ||
1080 | |||
1081 | static int daio_mgr_set_imaparc(void *blk, unsigned int slot) | ||
1082 | { | ||
1083 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1084 | |||
1085 | set_field(&ctl->daoimap.aim, AIM_ARC, slot); | ||
1086 | ctl->dirty.bf.daoimap = 1; | ||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1090 | static int daio_mgr_set_imapnxt(void *blk, unsigned int next) | ||
1091 | { | ||
1092 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1093 | |||
1094 | set_field(&ctl->daoimap.aim, AIM_NXT, next); | ||
1095 | ctl->dirty.bf.daoimap = 1; | ||
1096 | return 0; | ||
1097 | } | ||
1098 | |||
1099 | static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
1100 | { | ||
1101 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1102 | |||
1103 | ctl->daoimap.idx = addr; | ||
1104 | ctl->dirty.bf.daoimap = 1; | ||
1105 | return 0; | ||
1106 | } | ||
1107 | |||
1108 | static int daio_mgr_commit_write(struct hw *hw, void *blk) | ||
1109 | { | ||
1110 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1111 | int i; | ||
1112 | |||
1113 | if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) { | ||
1114 | for (i = 0; i < 4; i++) { | ||
1115 | if ((ctl->dirty.bf.i2sictl & (0x1 << i))) | ||
1116 | ctl->dirty.bf.i2sictl &= ~(0x1 << i); | ||
1117 | |||
1118 | if ((ctl->dirty.bf.i2soctl & (0x1 << i))) | ||
1119 | ctl->dirty.bf.i2soctl &= ~(0x1 << i); | ||
1120 | } | ||
1121 | hw_write_20kx(hw, I2SCTL, ctl->i2sctl); | ||
1122 | mdelay(1); | ||
1123 | } | ||
1124 | if (ctl->dirty.bf.spoctl) { | ||
1125 | for (i = 0; i < 4; i++) { | ||
1126 | if ((ctl->dirty.bf.spoctl & (0x1 << i))) | ||
1127 | ctl->dirty.bf.spoctl &= ~(0x1 << i); | ||
1128 | } | ||
1129 | hw_write_20kx(hw, SPOCTL, ctl->spoctl); | ||
1130 | mdelay(1); | ||
1131 | } | ||
1132 | if (ctl->dirty.bf.spictl) { | ||
1133 | for (i = 0; i < 4; i++) { | ||
1134 | if ((ctl->dirty.bf.spictl & (0x1 << i))) | ||
1135 | ctl->dirty.bf.spictl &= ~(0x1 << i); | ||
1136 | } | ||
1137 | hw_write_20kx(hw, SPICTL, ctl->spictl); | ||
1138 | mdelay(1); | ||
1139 | } | ||
1140 | if (ctl->dirty.bf.daoimap) { | ||
1141 | hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4, | ||
1142 | ctl->daoimap.aim); | ||
1143 | ctl->dirty.bf.daoimap = 0; | ||
1144 | } | ||
1145 | |||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) | ||
1150 | { | ||
1151 | struct daio_mgr_ctrl_blk *blk; | ||
1152 | |||
1153 | *rblk = NULL; | ||
1154 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
1155 | if (NULL == blk) | ||
1156 | return -ENOMEM; | ||
1157 | |||
1158 | blk->i2sctl = hw_read_20kx(hw, I2SCTL); | ||
1159 | blk->spoctl = hw_read_20kx(hw, SPOCTL); | ||
1160 | blk->spictl = hw_read_20kx(hw, SPICTL); | ||
1161 | |||
1162 | *rblk = blk; | ||
1163 | |||
1164 | return 0; | ||
1165 | } | ||
1166 | |||
1167 | static int daio_mgr_put_ctrl_blk(void *blk) | ||
1168 | { | ||
1169 | kfree((struct daio_mgr_ctrl_blk *)blk); | ||
1170 | |||
1171 | return 0; | ||
1172 | } | ||
1173 | |||
1174 | /* Timer interrupt */ | ||
1175 | static int set_timer_irq(struct hw *hw, int enable) | ||
1176 | { | ||
1177 | hw_write_20kx(hw, GIE, enable ? IT_INT : 0); | ||
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | static int set_timer_tick(struct hw *hw, unsigned int ticks) | ||
1182 | { | ||
1183 | if (ticks) | ||
1184 | ticks |= TIMR_IE | TIMR_IP; | ||
1185 | hw_write_20kx(hw, TIMR, ticks); | ||
1186 | return 0; | ||
1187 | } | ||
1188 | |||
1189 | static unsigned int get_wc(struct hw *hw) | ||
1190 | { | ||
1191 | return hw_read_20kx(hw, WC); | ||
1192 | } | ||
1193 | |||
1194 | /* Card hardware initialization block */ | ||
1195 | struct dac_conf { | ||
1196 | unsigned int msr; /* master sample rate in rsrs */ | ||
1197 | }; | ||
1198 | |||
1199 | struct adc_conf { | ||
1200 | unsigned int msr; /* master sample rate in rsrs */ | ||
1201 | unsigned char input; /* the input source of ADC */ | ||
1202 | unsigned char mic20db; /* boost mic by 20db if input is microphone */ | ||
1203 | }; | ||
1204 | |||
1205 | struct daio_conf { | ||
1206 | unsigned int msr; /* master sample rate in rsrs */ | ||
1207 | }; | ||
1208 | |||
1209 | struct trn_conf { | ||
1210 | unsigned long vm_pgt_phys; | ||
1211 | }; | ||
1212 | |||
1213 | static int hw_daio_init(struct hw *hw, const struct daio_conf *info) | ||
1214 | { | ||
1215 | u32 i2sorg; | ||
1216 | u32 spdorg; | ||
1217 | |||
1218 | /* Read I2S CTL. Keep original value. */ | ||
1219 | /*i2sorg = hw_read_20kx(hw, I2SCTL);*/ | ||
1220 | i2sorg = 0x94040404; /* enable all audio out and I2S-D input */ | ||
1221 | /* Program I2S with proper master sample rate and enable | ||
1222 | * the correct I2S channel. */ | ||
1223 | i2sorg &= 0xfffffffc; | ||
1224 | |||
1225 | /* Enable S/PDIF-out-A in fixed 24-bit data | ||
1226 | * format and default to 48kHz. */ | ||
1227 | /* Disable all before doing any changes. */ | ||
1228 | hw_write_20kx(hw, SPOCTL, 0x0); | ||
1229 | spdorg = 0x05; | ||
1230 | |||
1231 | switch (info->msr) { | ||
1232 | case 1: | ||
1233 | i2sorg |= 1; | ||
1234 | spdorg |= (0x0 << 6); | ||
1235 | break; | ||
1236 | case 2: | ||
1237 | i2sorg |= 2; | ||
1238 | spdorg |= (0x1 << 6); | ||
1239 | break; | ||
1240 | case 4: | ||
1241 | i2sorg |= 3; | ||
1242 | spdorg |= (0x2 << 6); | ||
1243 | break; | ||
1244 | default: | ||
1245 | i2sorg |= 1; | ||
1246 | break; | ||
1247 | } | ||
1248 | |||
1249 | hw_write_20kx(hw, I2SCTL, i2sorg); | ||
1250 | hw_write_20kx(hw, SPOCTL, spdorg); | ||
1251 | |||
1252 | /* Enable S/PDIF-in-A in fixed 24-bit data format. */ | ||
1253 | /* Disable all before doing any changes. */ | ||
1254 | hw_write_20kx(hw, SPICTL, 0x0); | ||
1255 | mdelay(1); | ||
1256 | spdorg = 0x0a0a0a0a; | ||
1257 | hw_write_20kx(hw, SPICTL, spdorg); | ||
1258 | mdelay(1); | ||
1259 | |||
1260 | return 0; | ||
1261 | } | ||
1262 | |||
1263 | /* TRANSPORT operations */ | ||
1264 | static int hw_trn_init(struct hw *hw, const struct trn_conf *info) | ||
1265 | { | ||
1266 | u32 trnctl; | ||
1267 | u32 ptp_phys_low, ptp_phys_high; | ||
1268 | |||
1269 | /* Set up device page table */ | ||
1270 | if ((~0UL) == info->vm_pgt_phys) { | ||
1271 | printk(KERN_ERR "Wrong device page table page address!\n"); | ||
1272 | return -1; | ||
1273 | } | ||
1274 | |||
1275 | trnctl = 0x13; /* 32-bit, 4k-size page */ | ||
1276 | ptp_phys_low = (u32)info->vm_pgt_phys; | ||
1277 | ptp_phys_high = upper_32_bits(info->vm_pgt_phys); | ||
1278 | if (sizeof(void *) == 8) /* 64bit address */ | ||
1279 | trnctl |= (1 << 2); | ||
1280 | #if 0 /* Only 4k h/w pages for simplicitiy */ | ||
1281 | #if PAGE_SIZE == 8192 | ||
1282 | trnctl |= (1<<5); | ||
1283 | #endif | ||
1284 | #endif | ||
1285 | hw_write_20kx(hw, PTPALX, ptp_phys_low); | ||
1286 | hw_write_20kx(hw, PTPAHX, ptp_phys_high); | ||
1287 | hw_write_20kx(hw, TRNCTL, trnctl); | ||
1288 | hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */ | ||
1289 | |||
1290 | return 0; | ||
1291 | } | ||
1292 | |||
1293 | /* Card initialization */ | ||
1294 | #define GCTL_EAC 0x00000001 | ||
1295 | #define GCTL_EAI 0x00000002 | ||
1296 | #define GCTL_BEP 0x00000004 | ||
1297 | #define GCTL_BES 0x00000008 | ||
1298 | #define GCTL_DSP 0x00000010 | ||
1299 | #define GCTL_DBP 0x00000020 | ||
1300 | #define GCTL_ABP 0x00000040 | ||
1301 | #define GCTL_TBP 0x00000080 | ||
1302 | #define GCTL_SBP 0x00000100 | ||
1303 | #define GCTL_FBP 0x00000200 | ||
1304 | #define GCTL_XA 0x00000400 | ||
1305 | #define GCTL_ET 0x00000800 | ||
1306 | #define GCTL_PR 0x00001000 | ||
1307 | #define GCTL_MRL 0x00002000 | ||
1308 | #define GCTL_SDE 0x00004000 | ||
1309 | #define GCTL_SDI 0x00008000 | ||
1310 | #define GCTL_SM 0x00010000 | ||
1311 | #define GCTL_SR 0x00020000 | ||
1312 | #define GCTL_SD 0x00040000 | ||
1313 | #define GCTL_SE 0x00080000 | ||
1314 | #define GCTL_AID 0x00100000 | ||
1315 | |||
1316 | static int hw_pll_init(struct hw *hw, unsigned int rsr) | ||
1317 | { | ||
1318 | unsigned int pllctl; | ||
1319 | int i; | ||
1320 | |||
1321 | pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731; | ||
1322 | for (i = 0; i < 3; i++) { | ||
1323 | if (hw_read_20kx(hw, PLLCTL) == pllctl) | ||
1324 | break; | ||
1325 | |||
1326 | hw_write_20kx(hw, PLLCTL, pllctl); | ||
1327 | mdelay(40); | ||
1328 | } | ||
1329 | if (i >= 3) { | ||
1330 | printk(KERN_ALERT "PLL initialization failed!!!\n"); | ||
1331 | return -EBUSY; | ||
1332 | } | ||
1333 | |||
1334 | return 0; | ||
1335 | } | ||
1336 | |||
1337 | static int hw_auto_init(struct hw *hw) | ||
1338 | { | ||
1339 | unsigned int gctl; | ||
1340 | int i; | ||
1341 | |||
1342 | gctl = hw_read_20kx(hw, GCTL); | ||
1343 | set_field(&gctl, GCTL_EAI, 0); | ||
1344 | hw_write_20kx(hw, GCTL, gctl); | ||
1345 | set_field(&gctl, GCTL_EAI, 1); | ||
1346 | hw_write_20kx(hw, GCTL, gctl); | ||
1347 | mdelay(10); | ||
1348 | for (i = 0; i < 400000; i++) { | ||
1349 | gctl = hw_read_20kx(hw, GCTL); | ||
1350 | if (get_field(gctl, GCTL_AID)) | ||
1351 | break; | ||
1352 | } | ||
1353 | if (!get_field(gctl, GCTL_AID)) { | ||
1354 | printk(KERN_ALERT "Card Auto-init failed!!!\n"); | ||
1355 | return -EBUSY; | ||
1356 | } | ||
1357 | |||
1358 | return 0; | ||
1359 | } | ||
1360 | |||
1361 | static int i2c_unlock(struct hw *hw) | ||
1362 | { | ||
1363 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1364 | return 0; | ||
1365 | |||
1366 | hw_write_pci(hw, 0xcc, 0x8c); | ||
1367 | hw_write_pci(hw, 0xcc, 0x0e); | ||
1368 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1369 | return 0; | ||
1370 | |||
1371 | hw_write_pci(hw, 0xcc, 0xee); | ||
1372 | hw_write_pci(hw, 0xcc, 0xaa); | ||
1373 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1374 | return 0; | ||
1375 | |||
1376 | return -1; | ||
1377 | } | ||
1378 | |||
1379 | static void i2c_lock(struct hw *hw) | ||
1380 | { | ||
1381 | if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) | ||
1382 | hw_write_pci(hw, 0xcc, 0x00); | ||
1383 | } | ||
1384 | |||
1385 | static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) | ||
1386 | { | ||
1387 | unsigned int ret; | ||
1388 | |||
1389 | do { | ||
1390 | ret = hw_read_pci(hw, 0xEC); | ||
1391 | } while (!(ret & 0x800000)); | ||
1392 | hw_write_pci(hw, 0xE0, device); | ||
1393 | hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff)); | ||
1394 | } | ||
1395 | |||
1396 | /* DAC operations */ | ||
1397 | |||
1398 | static int hw_reset_dac(struct hw *hw) | ||
1399 | { | ||
1400 | u32 i; | ||
1401 | u16 gpioorg; | ||
1402 | unsigned int ret; | ||
1403 | |||
1404 | if (i2c_unlock(hw)) | ||
1405 | return -1; | ||
1406 | |||
1407 | do { | ||
1408 | ret = hw_read_pci(hw, 0xEC); | ||
1409 | } while (!(ret & 0x800000)); | ||
1410 | hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ | ||
1411 | |||
1412 | /* To be effective, need to reset the DAC twice. */ | ||
1413 | for (i = 0; i < 2; i++) { | ||
1414 | /* set gpio */ | ||
1415 | mdelay(100); | ||
1416 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1417 | gpioorg &= 0xfffd; | ||
1418 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1419 | mdelay(1); | ||
1420 | hw_write_20kx(hw, GPIO, gpioorg | 0x2); | ||
1421 | } | ||
1422 | |||
1423 | i2c_write(hw, 0x00180080, 0x01, 0x80); | ||
1424 | i2c_write(hw, 0x00180080, 0x02, 0x10); | ||
1425 | |||
1426 | i2c_lock(hw); | ||
1427 | |||
1428 | return 0; | ||
1429 | } | ||
1430 | |||
1431 | static int hw_dac_init(struct hw *hw, const struct dac_conf *info) | ||
1432 | { | ||
1433 | u32 data; | ||
1434 | u16 gpioorg; | ||
1435 | unsigned int ret; | ||
1436 | |||
1437 | if (hw->model == CTSB055X) { | ||
1438 | /* SB055x, unmute outputs */ | ||
1439 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1440 | gpioorg &= 0xffbf; /* set GPIO6 to low */ | ||
1441 | gpioorg |= 2; /* set GPIO1 to high */ | ||
1442 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1443 | return 0; | ||
1444 | } | ||
1445 | |||
1446 | /* mute outputs */ | ||
1447 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1448 | gpioorg &= 0xffbf; | ||
1449 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1450 | |||
1451 | hw_reset_dac(hw); | ||
1452 | |||
1453 | if (i2c_unlock(hw)) | ||
1454 | return -1; | ||
1455 | |||
1456 | hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ | ||
1457 | do { | ||
1458 | ret = hw_read_pci(hw, 0xEC); | ||
1459 | } while (!(ret & 0x800000)); | ||
1460 | |||
1461 | switch (info->msr) { | ||
1462 | case 1: | ||
1463 | data = 0x24; | ||
1464 | break; | ||
1465 | case 2: | ||
1466 | data = 0x25; | ||
1467 | break; | ||
1468 | case 4: | ||
1469 | data = 0x26; | ||
1470 | break; | ||
1471 | default: | ||
1472 | data = 0x24; | ||
1473 | break; | ||
1474 | } | ||
1475 | |||
1476 | i2c_write(hw, 0x00180080, 0x06, data); | ||
1477 | i2c_write(hw, 0x00180080, 0x09, data); | ||
1478 | i2c_write(hw, 0x00180080, 0x0c, data); | ||
1479 | i2c_write(hw, 0x00180080, 0x0f, data); | ||
1480 | |||
1481 | i2c_lock(hw); | ||
1482 | |||
1483 | /* unmute outputs */ | ||
1484 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1485 | gpioorg = gpioorg | 0x40; | ||
1486 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1487 | |||
1488 | return 0; | ||
1489 | } | ||
1490 | |||
1491 | /* ADC operations */ | ||
1492 | |||
1493 | static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type) | ||
1494 | { | ||
1495 | return 0; | ||
1496 | } | ||
1497 | |||
1498 | static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) | ||
1499 | { | ||
1500 | u32 data; | ||
1501 | |||
1502 | data = hw_read_20kx(hw, GPIO); | ||
1503 | switch (type) { | ||
1504 | case ADC_MICIN: | ||
1505 | data = ((data & (0x1<<7)) && (data & (0x1<<8))); | ||
1506 | break; | ||
1507 | case ADC_LINEIN: | ||
1508 | data = (!(data & (0x1<<7)) && (data & (0x1<<8))); | ||
1509 | break; | ||
1510 | case ADC_NONE: /* Digital I/O */ | ||
1511 | data = (!(data & (0x1<<8))); | ||
1512 | break; | ||
1513 | default: | ||
1514 | data = 0; | ||
1515 | } | ||
1516 | return data; | ||
1517 | } | ||
1518 | |||
1519 | static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) | ||
1520 | { | ||
1521 | u32 data; | ||
1522 | |||
1523 | data = hw_read_20kx(hw, GPIO); | ||
1524 | switch (type) { | ||
1525 | case ADC_MICIN: | ||
1526 | data = (data & (0x1 << 7)) ? 1 : 0; | ||
1527 | break; | ||
1528 | case ADC_LINEIN: | ||
1529 | data = (data & (0x1 << 7)) ? 0 : 1; | ||
1530 | break; | ||
1531 | default: | ||
1532 | data = 0; | ||
1533 | } | ||
1534 | return data; | ||
1535 | } | ||
1536 | |||
1537 | static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) | ||
1538 | { | ||
1539 | switch (hw->model) { | ||
1540 | case CTSB055X: | ||
1541 | return is_adc_input_selected_SB055x(hw, type); | ||
1542 | case CTSB073X: | ||
1543 | return is_adc_input_selected_hendrix(hw, type); | ||
1544 | case CTUAA: | ||
1545 | return is_adc_input_selected_hendrix(hw, type); | ||
1546 | default: | ||
1547 | return is_adc_input_selected_SBx(hw, type); | ||
1548 | } | ||
1549 | } | ||
1550 | |||
1551 | static int | ||
1552 | adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) | ||
1553 | { | ||
1554 | u32 data; | ||
1555 | |||
1556 | /* | ||
1557 | * check and set the following GPIO bits accordingly | ||
1558 | * ADC_Gain = GPIO2 | ||
1559 | * DRM_off = GPIO3 | ||
1560 | * Mic_Pwr_on = GPIO7 | ||
1561 | * Digital_IO_Sel = GPIO8 | ||
1562 | * Mic_Sw = GPIO9 | ||
1563 | * Aux/MicLine_Sw = GPIO12 | ||
1564 | */ | ||
1565 | data = hw_read_20kx(hw, GPIO); | ||
1566 | data &= 0xec73; | ||
1567 | switch (type) { | ||
1568 | case ADC_MICIN: | ||
1569 | data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ; | ||
1570 | data |= boost ? (0x1<<2) : 0; | ||
1571 | break; | ||
1572 | case ADC_LINEIN: | ||
1573 | data |= (0x1<<8); | ||
1574 | break; | ||
1575 | case ADC_AUX: | ||
1576 | data |= (0x1<<8) | (0x1<<12); | ||
1577 | break; | ||
1578 | case ADC_NONE: | ||
1579 | data |= (0x1<<12); /* set to digital */ | ||
1580 | break; | ||
1581 | default: | ||
1582 | return -1; | ||
1583 | } | ||
1584 | |||
1585 | hw_write_20kx(hw, GPIO, data); | ||
1586 | |||
1587 | return 0; | ||
1588 | } | ||
1589 | |||
1590 | |||
1591 | static int | ||
1592 | adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) | ||
1593 | { | ||
1594 | u32 data; | ||
1595 | u32 i2c_data; | ||
1596 | unsigned int ret; | ||
1597 | |||
1598 | if (i2c_unlock(hw)) | ||
1599 | return -1; | ||
1600 | |||
1601 | do { | ||
1602 | ret = hw_read_pci(hw, 0xEC); | ||
1603 | } while (!(ret & 0x800000)); /* i2c ready poll */ | ||
1604 | /* set i2c access mode as Direct Control */ | ||
1605 | hw_write_pci(hw, 0xEC, 0x05); | ||
1606 | |||
1607 | data = hw_read_20kx(hw, GPIO); | ||
1608 | switch (type) { | ||
1609 | case ADC_MICIN: | ||
1610 | data |= ((0x1 << 7) | (0x1 << 8)); | ||
1611 | i2c_data = 0x1; /* Mic-in */ | ||
1612 | break; | ||
1613 | case ADC_LINEIN: | ||
1614 | data &= ~(0x1 << 7); | ||
1615 | data |= (0x1 << 8); | ||
1616 | i2c_data = 0x2; /* Line-in */ | ||
1617 | break; | ||
1618 | case ADC_NONE: | ||
1619 | data &= ~(0x1 << 8); | ||
1620 | i2c_data = 0x0; /* set to Digital */ | ||
1621 | break; | ||
1622 | default: | ||
1623 | i2c_lock(hw); | ||
1624 | return -1; | ||
1625 | } | ||
1626 | hw_write_20kx(hw, GPIO, data); | ||
1627 | i2c_write(hw, 0x001a0080, 0x2a, i2c_data); | ||
1628 | if (boost) { | ||
1629 | i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ | ||
1630 | i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ | ||
1631 | } else { | ||
1632 | i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ | ||
1633 | i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ | ||
1634 | } | ||
1635 | |||
1636 | i2c_lock(hw); | ||
1637 | |||
1638 | return 0; | ||
1639 | } | ||
1640 | |||
1641 | static int | ||
1642 | adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) | ||
1643 | { | ||
1644 | u32 data; | ||
1645 | u32 i2c_data; | ||
1646 | unsigned int ret; | ||
1647 | |||
1648 | if (i2c_unlock(hw)) | ||
1649 | return -1; | ||
1650 | |||
1651 | do { | ||
1652 | ret = hw_read_pci(hw, 0xEC); | ||
1653 | } while (!(ret & 0x800000)); /* i2c ready poll */ | ||
1654 | /* set i2c access mode as Direct Control */ | ||
1655 | hw_write_pci(hw, 0xEC, 0x05); | ||
1656 | |||
1657 | data = hw_read_20kx(hw, GPIO); | ||
1658 | switch (type) { | ||
1659 | case ADC_MICIN: | ||
1660 | data |= (0x1 << 7); | ||
1661 | i2c_data = 0x1; /* Mic-in */ | ||
1662 | break; | ||
1663 | case ADC_LINEIN: | ||
1664 | data &= ~(0x1 << 7); | ||
1665 | i2c_data = 0x2; /* Line-in */ | ||
1666 | break; | ||
1667 | default: | ||
1668 | i2c_lock(hw); | ||
1669 | return -1; | ||
1670 | } | ||
1671 | hw_write_20kx(hw, GPIO, data); | ||
1672 | i2c_write(hw, 0x001a0080, 0x2a, i2c_data); | ||
1673 | if (boost) { | ||
1674 | i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ | ||
1675 | i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ | ||
1676 | } else { | ||
1677 | i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ | ||
1678 | i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ | ||
1679 | } | ||
1680 | |||
1681 | i2c_lock(hw); | ||
1682 | |||
1683 | return 0; | ||
1684 | } | ||
1685 | |||
1686 | static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) | ||
1687 | { | ||
1688 | int state = type == ADC_MICIN; | ||
1689 | |||
1690 | switch (hw->model) { | ||
1691 | case CTSB055X: | ||
1692 | return adc_input_select_SB055x(hw, type, state); | ||
1693 | case CTSB073X: | ||
1694 | return adc_input_select_hendrix(hw, type, state); | ||
1695 | case CTUAA: | ||
1696 | return adc_input_select_hendrix(hw, type, state); | ||
1697 | default: | ||
1698 | return adc_input_select_SBx(hw, type, state); | ||
1699 | } | ||
1700 | } | ||
1701 | |||
1702 | static int adc_init_SB055x(struct hw *hw, int input, int mic20db) | ||
1703 | { | ||
1704 | return adc_input_select_SB055x(hw, input, mic20db); | ||
1705 | } | ||
1706 | |||
1707 | static int adc_init_SBx(struct hw *hw, int input, int mic20db) | ||
1708 | { | ||
1709 | u16 gpioorg; | ||
1710 | u16 input_source; | ||
1711 | u32 adcdata; | ||
1712 | unsigned int ret; | ||
1713 | |||
1714 | input_source = 0x100; /* default to analog */ | ||
1715 | switch (input) { | ||
1716 | case ADC_MICIN: | ||
1717 | adcdata = 0x1; | ||
1718 | input_source = 0x180; /* set GPIO7 to select Mic */ | ||
1719 | break; | ||
1720 | case ADC_LINEIN: | ||
1721 | adcdata = 0x2; | ||
1722 | break; | ||
1723 | case ADC_VIDEO: | ||
1724 | adcdata = 0x4; | ||
1725 | break; | ||
1726 | case ADC_AUX: | ||
1727 | adcdata = 0x8; | ||
1728 | break; | ||
1729 | case ADC_NONE: | ||
1730 | adcdata = 0x0; | ||
1731 | input_source = 0x0; /* set to Digital */ | ||
1732 | break; | ||
1733 | default: | ||
1734 | adcdata = 0x0; | ||
1735 | break; | ||
1736 | } | ||
1737 | |||
1738 | if (i2c_unlock(hw)) | ||
1739 | return -1; | ||
1740 | |||
1741 | do { | ||
1742 | ret = hw_read_pci(hw, 0xEC); | ||
1743 | } while (!(ret & 0x800000)); /* i2c ready poll */ | ||
1744 | hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ | ||
1745 | |||
1746 | i2c_write(hw, 0x001a0080, 0x0e, 0x08); | ||
1747 | i2c_write(hw, 0x001a0080, 0x18, 0x0a); | ||
1748 | i2c_write(hw, 0x001a0080, 0x28, 0x86); | ||
1749 | i2c_write(hw, 0x001a0080, 0x2a, adcdata); | ||
1750 | |||
1751 | if (mic20db) { | ||
1752 | i2c_write(hw, 0x001a0080, 0x1c, 0xf7); | ||
1753 | i2c_write(hw, 0x001a0080, 0x1e, 0xf7); | ||
1754 | } else { | ||
1755 | i2c_write(hw, 0x001a0080, 0x1c, 0xcf); | ||
1756 | i2c_write(hw, 0x001a0080, 0x1e, 0xcf); | ||
1757 | } | ||
1758 | |||
1759 | if (!(hw_read_20kx(hw, ID0) & 0x100)) | ||
1760 | i2c_write(hw, 0x001a0080, 0x16, 0x26); | ||
1761 | |||
1762 | i2c_lock(hw); | ||
1763 | |||
1764 | gpioorg = (u16)hw_read_20kx(hw, GPIO); | ||
1765 | gpioorg &= 0xfe7f; | ||
1766 | gpioorg |= input_source; | ||
1767 | hw_write_20kx(hw, GPIO, gpioorg); | ||
1768 | |||
1769 | return 0; | ||
1770 | } | ||
1771 | |||
1772 | static int hw_adc_init(struct hw *hw, const struct adc_conf *info) | ||
1773 | { | ||
1774 | if (hw->model == CTSB055X) | ||
1775 | return adc_init_SB055x(hw, info->input, info->mic20db); | ||
1776 | else | ||
1777 | return adc_init_SBx(hw, info->input, info->mic20db); | ||
1778 | } | ||
1779 | |||
1780 | static int hw_have_digit_io_switch(struct hw *hw) | ||
1781 | { | ||
1782 | /* SB073x and Vista compatible cards have no digit IO switch */ | ||
1783 | return !(hw->model == CTSB073X || hw->model == CTUAA); | ||
1784 | } | ||
1785 | |||
1786 | #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) | ||
1787 | |||
1788 | #define UAA_CFG_PWRSTATUS 0x44 | ||
1789 | #define UAA_CFG_SPACE_FLAG 0xA0 | ||
1790 | #define UAA_CORE_CHANGE 0x3FFC | ||
1791 | static int uaa_to_xfi(struct pci_dev *pci) | ||
1792 | { | ||
1793 | unsigned int bar0, bar1, bar2, bar3, bar4, bar5; | ||
1794 | unsigned int cmd, irq, cl_size, l_timer, pwr; | ||
1795 | unsigned int is_uaa; | ||
1796 | unsigned int data[4] = {0}; | ||
1797 | unsigned int io_base; | ||
1798 | void *mem_base; | ||
1799 | int i; | ||
1800 | const u32 CTLX = CTLBITS('C', 'T', 'L', 'X'); | ||
1801 | const u32 CTL_ = CTLBITS('C', 'T', 'L', '-'); | ||
1802 | const u32 CTLF = CTLBITS('C', 'T', 'L', 'F'); | ||
1803 | const u32 CTLi = CTLBITS('C', 'T', 'L', 'i'); | ||
1804 | const u32 CTLA = CTLBITS('C', 'T', 'L', 'A'); | ||
1805 | const u32 CTLZ = CTLBITS('C', 'T', 'L', 'Z'); | ||
1806 | const u32 CTLL = CTLBITS('C', 'T', 'L', 'L'); | ||
1807 | |||
1808 | /* By default, Hendrix card UAA Bar0 should be using memory... */ | ||
1809 | io_base = pci_resource_start(pci, 0); | ||
1810 | mem_base = ioremap(io_base, pci_resource_len(pci, 0)); | ||
1811 | if (NULL == mem_base) | ||
1812 | return -ENOENT; | ||
1813 | |||
1814 | /* Read current mode from Mode Change Register */ | ||
1815 | for (i = 0; i < 4; i++) | ||
1816 | data[i] = readl(mem_base + UAA_CORE_CHANGE); | ||
1817 | |||
1818 | /* Determine current mode... */ | ||
1819 | if (data[0] == CTLA) { | ||
1820 | is_uaa = ((data[1] == CTLZ && data[2] == CTLL | ||
1821 | && data[3] == CTLA) || (data[1] == CTLA | ||
1822 | && data[2] == CTLZ && data[3] == CTLL)); | ||
1823 | } else if (data[0] == CTLZ) { | ||
1824 | is_uaa = (data[1] == CTLL | ||
1825 | && data[2] == CTLA && data[3] == CTLA); | ||
1826 | } else if (data[0] == CTLL) { | ||
1827 | is_uaa = (data[1] == CTLA | ||
1828 | && data[2] == CTLA && data[3] == CTLZ); | ||
1829 | } else { | ||
1830 | is_uaa = 0; | ||
1831 | } | ||
1832 | |||
1833 | if (!is_uaa) { | ||
1834 | /* Not in UAA mode currently. Return directly. */ | ||
1835 | iounmap(mem_base); | ||
1836 | return 0; | ||
1837 | } | ||
1838 | |||
1839 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0); | ||
1840 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1); | ||
1841 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2); | ||
1842 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3); | ||
1843 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4); | ||
1844 | pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5); | ||
1845 | pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq); | ||
1846 | pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size); | ||
1847 | pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer); | ||
1848 | pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr); | ||
1849 | pci_read_config_dword(pci, PCI_COMMAND, &cmd); | ||
1850 | |||
1851 | /* Set up X-Fi core PCI configuration space. */ | ||
1852 | /* Switch to X-Fi config space with BAR0 exposed. */ | ||
1853 | pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321); | ||
1854 | /* Copy UAA's BAR5 into X-Fi BAR0 */ | ||
1855 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5); | ||
1856 | /* Switch to X-Fi config space without BAR0 exposed. */ | ||
1857 | pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678); | ||
1858 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1); | ||
1859 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2); | ||
1860 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3); | ||
1861 | pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4); | ||
1862 | pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq); | ||
1863 | pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size); | ||
1864 | pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer); | ||
1865 | pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr); | ||
1866 | pci_write_config_dword(pci, PCI_COMMAND, cmd); | ||
1867 | |||
1868 | /* Switch to X-Fi mode */ | ||
1869 | writel(CTLX, (mem_base + UAA_CORE_CHANGE)); | ||
1870 | writel(CTL_, (mem_base + UAA_CORE_CHANGE)); | ||
1871 | writel(CTLF, (mem_base + UAA_CORE_CHANGE)); | ||
1872 | writel(CTLi, (mem_base + UAA_CORE_CHANGE)); | ||
1873 | |||
1874 | iounmap(mem_base); | ||
1875 | |||
1876 | return 0; | ||
1877 | } | ||
1878 | |||
1879 | static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id) | ||
1880 | { | ||
1881 | struct hw *hw = dev_id; | ||
1882 | unsigned int status; | ||
1883 | |||
1884 | status = hw_read_20kx(hw, GIP); | ||
1885 | if (!status) | ||
1886 | return IRQ_NONE; | ||
1887 | |||
1888 | if (hw->irq_callback) | ||
1889 | hw->irq_callback(hw->irq_callback_data, status); | ||
1890 | |||
1891 | hw_write_20kx(hw, GIP, status); | ||
1892 | return IRQ_HANDLED; | ||
1893 | } | ||
1894 | |||
1895 | static int hw_card_start(struct hw *hw) | ||
1896 | { | ||
1897 | int err; | ||
1898 | struct pci_dev *pci = hw->pci; | ||
1899 | |||
1900 | err = pci_enable_device(pci); | ||
1901 | if (err < 0) | ||
1902 | return err; | ||
1903 | |||
1904 | /* Set DMA transfer mask */ | ||
1905 | if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || | ||
1906 | pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { | ||
1907 | printk(KERN_ERR "architecture does not support PCI " | ||
1908 | "busmaster DMA with mask 0x%llx\n", | ||
1909 | CT_XFI_DMA_MASK); | ||
1910 | err = -ENXIO; | ||
1911 | goto error1; | ||
1912 | } | ||
1913 | |||
1914 | err = pci_request_regions(pci, "XFi"); | ||
1915 | if (err < 0) | ||
1916 | goto error1; | ||
1917 | |||
1918 | /* Switch to X-Fi mode from UAA mode if neeeded */ | ||
1919 | if (hw->model == CTUAA) { | ||
1920 | err = uaa_to_xfi(pci); | ||
1921 | if (err) | ||
1922 | goto error2; | ||
1923 | |||
1924 | hw->io_base = pci_resource_start(pci, 5); | ||
1925 | } else { | ||
1926 | hw->io_base = pci_resource_start(pci, 0); | ||
1927 | } | ||
1928 | |||
1929 | err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED, | ||
1930 | "ctxfi", hw); | ||
1931 | if (err < 0) { | ||
1932 | printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); | ||
1933 | goto error2; | ||
1934 | } | ||
1935 | hw->irq = pci->irq; | ||
1936 | |||
1937 | pci_set_master(pci); | ||
1938 | |||
1939 | return 0; | ||
1940 | |||
1941 | error2: | ||
1942 | pci_release_regions(pci); | ||
1943 | hw->io_base = 0; | ||
1944 | error1: | ||
1945 | pci_disable_device(pci); | ||
1946 | return err; | ||
1947 | } | ||
1948 | |||
1949 | static int hw_card_stop(struct hw *hw) | ||
1950 | { | ||
1951 | /* TODO: Disable interrupt and so on... */ | ||
1952 | if (hw->irq >= 0) | ||
1953 | synchronize_irq(hw->irq); | ||
1954 | return 0; | ||
1955 | } | ||
1956 | |||
1957 | static int hw_card_shutdown(struct hw *hw) | ||
1958 | { | ||
1959 | if (hw->irq >= 0) | ||
1960 | free_irq(hw->irq, hw); | ||
1961 | |||
1962 | hw->irq = -1; | ||
1963 | |||
1964 | if (NULL != ((void *)hw->mem_base)) | ||
1965 | iounmap((void *)hw->mem_base); | ||
1966 | |||
1967 | hw->mem_base = (unsigned long)NULL; | ||
1968 | |||
1969 | if (hw->io_base) | ||
1970 | pci_release_regions(hw->pci); | ||
1971 | |||
1972 | hw->io_base = 0; | ||
1973 | |||
1974 | pci_disable_device(hw->pci); | ||
1975 | |||
1976 | return 0; | ||
1977 | } | ||
1978 | |||
1979 | static int hw_card_init(struct hw *hw, struct card_conf *info) | ||
1980 | { | ||
1981 | int err; | ||
1982 | unsigned int gctl; | ||
1983 | u32 data; | ||
1984 | struct dac_conf dac_info = {0}; | ||
1985 | struct adc_conf adc_info = {0}; | ||
1986 | struct daio_conf daio_info = {0}; | ||
1987 | struct trn_conf trn_info = {0}; | ||
1988 | |||
1989 | /* Get PCI io port base address and do Hendrix switch if needed. */ | ||
1990 | if (!hw->io_base) { | ||
1991 | err = hw_card_start(hw); | ||
1992 | if (err) | ||
1993 | return err; | ||
1994 | } | ||
1995 | |||
1996 | /* PLL init */ | ||
1997 | err = hw_pll_init(hw, info->rsr); | ||
1998 | if (err < 0) | ||
1999 | return err; | ||
2000 | |||
2001 | /* kick off auto-init */ | ||
2002 | err = hw_auto_init(hw); | ||
2003 | if (err < 0) | ||
2004 | return err; | ||
2005 | |||
2006 | /* Enable audio ring */ | ||
2007 | gctl = hw_read_20kx(hw, GCTL); | ||
2008 | set_field(&gctl, GCTL_EAC, 1); | ||
2009 | set_field(&gctl, GCTL_DBP, 1); | ||
2010 | set_field(&gctl, GCTL_TBP, 1); | ||
2011 | set_field(&gctl, GCTL_FBP, 1); | ||
2012 | set_field(&gctl, GCTL_ET, 1); | ||
2013 | hw_write_20kx(hw, GCTL, gctl); | ||
2014 | mdelay(10); | ||
2015 | |||
2016 | /* Reset all global pending interrupts */ | ||
2017 | hw_write_20kx(hw, GIE, 0); | ||
2018 | /* Reset all SRC pending interrupts */ | ||
2019 | hw_write_20kx(hw, SRCIP, 0); | ||
2020 | mdelay(30); | ||
2021 | |||
2022 | /* Detect the card ID and configure GPIO accordingly. */ | ||
2023 | switch (hw->model) { | ||
2024 | case CTSB055X: | ||
2025 | hw_write_20kx(hw, GPIOCTL, 0x13fe); | ||
2026 | break; | ||
2027 | case CTSB073X: | ||
2028 | hw_write_20kx(hw, GPIOCTL, 0x00e6); | ||
2029 | break; | ||
2030 | case CTUAA: | ||
2031 | hw_write_20kx(hw, GPIOCTL, 0x00c2); | ||
2032 | break; | ||
2033 | default: | ||
2034 | hw_write_20kx(hw, GPIOCTL, 0x01e6); | ||
2035 | break; | ||
2036 | } | ||
2037 | |||
2038 | trn_info.vm_pgt_phys = info->vm_pgt_phys; | ||
2039 | err = hw_trn_init(hw, &trn_info); | ||
2040 | if (err < 0) | ||
2041 | return err; | ||
2042 | |||
2043 | daio_info.msr = info->msr; | ||
2044 | err = hw_daio_init(hw, &daio_info); | ||
2045 | if (err < 0) | ||
2046 | return err; | ||
2047 | |||
2048 | dac_info.msr = info->msr; | ||
2049 | err = hw_dac_init(hw, &dac_info); | ||
2050 | if (err < 0) | ||
2051 | return err; | ||
2052 | |||
2053 | adc_info.msr = info->msr; | ||
2054 | adc_info.input = ADC_LINEIN; | ||
2055 | adc_info.mic20db = 0; | ||
2056 | err = hw_adc_init(hw, &adc_info); | ||
2057 | if (err < 0) | ||
2058 | return err; | ||
2059 | |||
2060 | data = hw_read_20kx(hw, SRCMCTL); | ||
2061 | data |= 0x1; /* Enables input from the audio ring */ | ||
2062 | hw_write_20kx(hw, SRCMCTL, data); | ||
2063 | |||
2064 | return 0; | ||
2065 | } | ||
2066 | |||
2067 | static u32 hw_read_20kx(struct hw *hw, u32 reg) | ||
2068 | { | ||
2069 | u32 value; | ||
2070 | unsigned long flags; | ||
2071 | |||
2072 | spin_lock_irqsave( | ||
2073 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2074 | outl(reg, hw->io_base + 0x0); | ||
2075 | value = inl(hw->io_base + 0x4); | ||
2076 | spin_unlock_irqrestore( | ||
2077 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2078 | |||
2079 | return value; | ||
2080 | } | ||
2081 | |||
2082 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) | ||
2083 | { | ||
2084 | unsigned long flags; | ||
2085 | |||
2086 | spin_lock_irqsave( | ||
2087 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2088 | outl(reg, hw->io_base + 0x0); | ||
2089 | outl(data, hw->io_base + 0x4); | ||
2090 | spin_unlock_irqrestore( | ||
2091 | &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); | ||
2092 | |||
2093 | } | ||
2094 | |||
2095 | static u32 hw_read_pci(struct hw *hw, u32 reg) | ||
2096 | { | ||
2097 | u32 value; | ||
2098 | unsigned long flags; | ||
2099 | |||
2100 | spin_lock_irqsave( | ||
2101 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2102 | outl(reg, hw->io_base + 0x10); | ||
2103 | value = inl(hw->io_base + 0x14); | ||
2104 | spin_unlock_irqrestore( | ||
2105 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2106 | |||
2107 | return value; | ||
2108 | } | ||
2109 | |||
2110 | static void hw_write_pci(struct hw *hw, u32 reg, u32 data) | ||
2111 | { | ||
2112 | unsigned long flags; | ||
2113 | |||
2114 | spin_lock_irqsave( | ||
2115 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2116 | outl(reg, hw->io_base + 0x10); | ||
2117 | outl(data, hw->io_base + 0x14); | ||
2118 | spin_unlock_irqrestore( | ||
2119 | &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); | ||
2120 | } | ||
2121 | |||
2122 | static struct hw ct20k1_preset __devinitdata = { | ||
2123 | .irq = -1, | ||
2124 | |||
2125 | .card_init = hw_card_init, | ||
2126 | .card_stop = hw_card_stop, | ||
2127 | .pll_init = hw_pll_init, | ||
2128 | .is_adc_source_selected = hw_is_adc_input_selected, | ||
2129 | .select_adc_source = hw_adc_input_select, | ||
2130 | .have_digit_io_switch = hw_have_digit_io_switch, | ||
2131 | |||
2132 | .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk, | ||
2133 | .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk, | ||
2134 | .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk, | ||
2135 | .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk, | ||
2136 | .src_set_state = src_set_state, | ||
2137 | .src_set_bm = src_set_bm, | ||
2138 | .src_set_rsr = src_set_rsr, | ||
2139 | .src_set_sf = src_set_sf, | ||
2140 | .src_set_wr = src_set_wr, | ||
2141 | .src_set_pm = src_set_pm, | ||
2142 | .src_set_rom = src_set_rom, | ||
2143 | .src_set_vo = src_set_vo, | ||
2144 | .src_set_st = src_set_st, | ||
2145 | .src_set_ie = src_set_ie, | ||
2146 | .src_set_ilsz = src_set_ilsz, | ||
2147 | .src_set_bp = src_set_bp, | ||
2148 | .src_set_cisz = src_set_cisz, | ||
2149 | .src_set_ca = src_set_ca, | ||
2150 | .src_set_sa = src_set_sa, | ||
2151 | .src_set_la = src_set_la, | ||
2152 | .src_set_pitch = src_set_pitch, | ||
2153 | .src_set_dirty = src_set_dirty, | ||
2154 | .src_set_clear_zbufs = src_set_clear_zbufs, | ||
2155 | .src_set_dirty_all = src_set_dirty_all, | ||
2156 | .src_commit_write = src_commit_write, | ||
2157 | .src_get_ca = src_get_ca, | ||
2158 | .src_get_dirty = src_get_dirty, | ||
2159 | .src_dirty_conj_mask = src_dirty_conj_mask, | ||
2160 | .src_mgr_enbs_src = src_mgr_enbs_src, | ||
2161 | .src_mgr_enb_src = src_mgr_enb_src, | ||
2162 | .src_mgr_dsb_src = src_mgr_dsb_src, | ||
2163 | .src_mgr_commit_write = src_mgr_commit_write, | ||
2164 | |||
2165 | .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk, | ||
2166 | .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk, | ||
2167 | .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc, | ||
2168 | .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser, | ||
2169 | .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt, | ||
2170 | .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr, | ||
2171 | .srcimp_mgr_commit_write = srcimp_mgr_commit_write, | ||
2172 | |||
2173 | .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk, | ||
2174 | .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk, | ||
2175 | .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk, | ||
2176 | .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk, | ||
2177 | .amixer_set_mode = amixer_set_mode, | ||
2178 | .amixer_set_iv = amixer_set_iv, | ||
2179 | .amixer_set_x = amixer_set_x, | ||
2180 | .amixer_set_y = amixer_set_y, | ||
2181 | .amixer_set_sadr = amixer_set_sadr, | ||
2182 | .amixer_set_se = amixer_set_se, | ||
2183 | .amixer_set_dirty = amixer_set_dirty, | ||
2184 | .amixer_set_dirty_all = amixer_set_dirty_all, | ||
2185 | .amixer_commit_write = amixer_commit_write, | ||
2186 | .amixer_get_y = amixer_get_y, | ||
2187 | .amixer_get_dirty = amixer_get_dirty, | ||
2188 | |||
2189 | .dai_get_ctrl_blk = dai_get_ctrl_blk, | ||
2190 | .dai_put_ctrl_blk = dai_put_ctrl_blk, | ||
2191 | .dai_srt_set_srco = dai_srt_set_srcr, | ||
2192 | .dai_srt_set_srcm = dai_srt_set_srcl, | ||
2193 | .dai_srt_set_rsr = dai_srt_set_rsr, | ||
2194 | .dai_srt_set_drat = dai_srt_set_drat, | ||
2195 | .dai_srt_set_ec = dai_srt_set_ec, | ||
2196 | .dai_srt_set_et = dai_srt_set_et, | ||
2197 | .dai_commit_write = dai_commit_write, | ||
2198 | |||
2199 | .dao_get_ctrl_blk = dao_get_ctrl_blk, | ||
2200 | .dao_put_ctrl_blk = dao_put_ctrl_blk, | ||
2201 | .dao_set_spos = dao_set_spos, | ||
2202 | .dao_commit_write = dao_commit_write, | ||
2203 | .dao_get_spos = dao_get_spos, | ||
2204 | |||
2205 | .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk, | ||
2206 | .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk, | ||
2207 | .daio_mgr_enb_dai = daio_mgr_enb_dai, | ||
2208 | .daio_mgr_dsb_dai = daio_mgr_dsb_dai, | ||
2209 | .daio_mgr_enb_dao = daio_mgr_enb_dao, | ||
2210 | .daio_mgr_dsb_dao = daio_mgr_dsb_dao, | ||
2211 | .daio_mgr_dao_init = daio_mgr_dao_init, | ||
2212 | .daio_mgr_set_imaparc = daio_mgr_set_imaparc, | ||
2213 | .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, | ||
2214 | .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, | ||
2215 | .daio_mgr_commit_write = daio_mgr_commit_write, | ||
2216 | |||
2217 | .set_timer_irq = set_timer_irq, | ||
2218 | .set_timer_tick = set_timer_tick, | ||
2219 | .get_wc = get_wc, | ||
2220 | }; | ||
2221 | |||
2222 | int __devinit create_20k1_hw_obj(struct hw **rhw) | ||
2223 | { | ||
2224 | struct hw20k1 *hw20k1; | ||
2225 | |||
2226 | *rhw = NULL; | ||
2227 | hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL); | ||
2228 | if (NULL == hw20k1) | ||
2229 | return -ENOMEM; | ||
2230 | |||
2231 | spin_lock_init(&hw20k1->reg_20k1_lock); | ||
2232 | spin_lock_init(&hw20k1->reg_pci_lock); | ||
2233 | |||
2234 | hw20k1->hw = ct20k1_preset; | ||
2235 | |||
2236 | *rhw = &hw20k1->hw; | ||
2237 | |||
2238 | return 0; | ||
2239 | } | ||
2240 | |||
2241 | int destroy_20k1_hw_obj(struct hw *hw) | ||
2242 | { | ||
2243 | if (hw->io_base) | ||
2244 | hw_card_shutdown(hw); | ||
2245 | |||
2246 | kfree(container_of(hw, struct hw20k1, hw)); | ||
2247 | return 0; | ||
2248 | } | ||
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..4493a51c6b01 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.c | |||
@@ -0,0 +1,2137 @@ | |||
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 <linux/types.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include "cthw20k2.h" | ||
27 | #include "ct20k2reg.h" | ||
28 | |||
29 | #if BITS_PER_LONG == 32 | ||
30 | #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ | ||
31 | #else | ||
32 | #define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ | ||
33 | #endif | ||
34 | |||
35 | struct hw20k2 { | ||
36 | struct hw hw; | ||
37 | /* for i2c */ | ||
38 | unsigned char dev_id; | ||
39 | unsigned char addr_size; | ||
40 | unsigned char data_size; | ||
41 | }; | ||
42 | |||
43 | static u32 hw_read_20kx(struct hw *hw, u32 reg); | ||
44 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); | ||
45 | |||
46 | /* | ||
47 | * Type definition block. | ||
48 | * The layout of control structures can be directly applied on 20k2 chip. | ||
49 | */ | ||
50 | |||
51 | /* | ||
52 | * SRC control block definitions. | ||
53 | */ | ||
54 | |||
55 | /* SRC resource control block */ | ||
56 | #define SRCCTL_STATE 0x00000007 | ||
57 | #define SRCCTL_BM 0x00000008 | ||
58 | #define SRCCTL_RSR 0x00000030 | ||
59 | #define SRCCTL_SF 0x000001C0 | ||
60 | #define SRCCTL_WR 0x00000200 | ||
61 | #define SRCCTL_PM 0x00000400 | ||
62 | #define SRCCTL_ROM 0x00001800 | ||
63 | #define SRCCTL_VO 0x00002000 | ||
64 | #define SRCCTL_ST 0x00004000 | ||
65 | #define SRCCTL_IE 0x00008000 | ||
66 | #define SRCCTL_ILSZ 0x000F0000 | ||
67 | #define SRCCTL_BP 0x00100000 | ||
68 | |||
69 | #define SRCCCR_CISZ 0x000007FF | ||
70 | #define SRCCCR_CWA 0x001FF800 | ||
71 | #define SRCCCR_D 0x00200000 | ||
72 | #define SRCCCR_RS 0x01C00000 | ||
73 | #define SRCCCR_NAL 0x3E000000 | ||
74 | #define SRCCCR_RA 0xC0000000 | ||
75 | |||
76 | #define SRCCA_CA 0x0FFFFFFF | ||
77 | #define SRCCA_RS 0xE0000000 | ||
78 | |||
79 | #define SRCSA_SA 0x0FFFFFFF | ||
80 | |||
81 | #define SRCLA_LA 0x0FFFFFFF | ||
82 | |||
83 | /* Mixer Parameter Ring ram Low and Hight register. | ||
84 | * Fixed-point value in 8.24 format for parameter channel */ | ||
85 | #define MPRLH_PITCH 0xFFFFFFFF | ||
86 | |||
87 | /* SRC resource register dirty flags */ | ||
88 | union src_dirty { | ||
89 | struct { | ||
90 | u16 ctl:1; | ||
91 | u16 ccr:1; | ||
92 | u16 sa:1; | ||
93 | u16 la:1; | ||
94 | u16 ca:1; | ||
95 | u16 mpr:1; | ||
96 | u16 czbfs:1; /* Clear Z-Buffers */ | ||
97 | u16 rsv:9; | ||
98 | } bf; | ||
99 | u16 data; | ||
100 | }; | ||
101 | |||
102 | struct src_rsc_ctrl_blk { | ||
103 | unsigned int ctl; | ||
104 | unsigned int ccr; | ||
105 | unsigned int ca; | ||
106 | unsigned int sa; | ||
107 | unsigned int la; | ||
108 | unsigned int mpr; | ||
109 | union src_dirty dirty; | ||
110 | }; | ||
111 | |||
112 | /* SRC manager control block */ | ||
113 | union src_mgr_dirty { | ||
114 | struct { | ||
115 | u16 enb0:1; | ||
116 | u16 enb1:1; | ||
117 | u16 enb2:1; | ||
118 | u16 enb3:1; | ||
119 | u16 enb4:1; | ||
120 | u16 enb5:1; | ||
121 | u16 enb6:1; | ||
122 | u16 enb7:1; | ||
123 | u16 enbsa:1; | ||
124 | u16 rsv:7; | ||
125 | } bf; | ||
126 | u16 data; | ||
127 | }; | ||
128 | |||
129 | struct src_mgr_ctrl_blk { | ||
130 | unsigned int enbsa; | ||
131 | unsigned int enb[8]; | ||
132 | union src_mgr_dirty dirty; | ||
133 | }; | ||
134 | |||
135 | /* SRCIMP manager control block */ | ||
136 | #define SRCAIM_ARC 0x00000FFF | ||
137 | #define SRCAIM_NXT 0x00FF0000 | ||
138 | #define SRCAIM_SRC 0xFF000000 | ||
139 | |||
140 | struct srcimap { | ||
141 | unsigned int srcaim; | ||
142 | unsigned int idx; | ||
143 | }; | ||
144 | |||
145 | /* SRCIMP manager register dirty flags */ | ||
146 | union srcimp_mgr_dirty { | ||
147 | struct { | ||
148 | u16 srcimap:1; | ||
149 | u16 rsv:15; | ||
150 | } bf; | ||
151 | u16 data; | ||
152 | }; | ||
153 | |||
154 | struct srcimp_mgr_ctrl_blk { | ||
155 | struct srcimap srcimap; | ||
156 | union srcimp_mgr_dirty dirty; | ||
157 | }; | ||
158 | |||
159 | /* | ||
160 | * Function implementation block. | ||
161 | */ | ||
162 | |||
163 | static int src_get_rsc_ctrl_blk(void **rblk) | ||
164 | { | ||
165 | struct src_rsc_ctrl_blk *blk; | ||
166 | |||
167 | *rblk = NULL; | ||
168 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
169 | if (NULL == blk) | ||
170 | return -ENOMEM; | ||
171 | |||
172 | *rblk = blk; | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int src_put_rsc_ctrl_blk(void *blk) | ||
178 | { | ||
179 | kfree(blk); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static int src_set_state(void *blk, unsigned int state) | ||
185 | { | ||
186 | struct src_rsc_ctrl_blk *ctl = blk; | ||
187 | |||
188 | set_field(&ctl->ctl, SRCCTL_STATE, state); | ||
189 | ctl->dirty.bf.ctl = 1; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int src_set_bm(void *blk, unsigned int bm) | ||
194 | { | ||
195 | struct src_rsc_ctrl_blk *ctl = blk; | ||
196 | |||
197 | set_field(&ctl->ctl, SRCCTL_BM, bm); | ||
198 | ctl->dirty.bf.ctl = 1; | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int src_set_rsr(void *blk, unsigned int rsr) | ||
203 | { | ||
204 | struct src_rsc_ctrl_blk *ctl = blk; | ||
205 | |||
206 | set_field(&ctl->ctl, SRCCTL_RSR, rsr); | ||
207 | ctl->dirty.bf.ctl = 1; | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int src_set_sf(void *blk, unsigned int sf) | ||
212 | { | ||
213 | struct src_rsc_ctrl_blk *ctl = blk; | ||
214 | |||
215 | set_field(&ctl->ctl, SRCCTL_SF, sf); | ||
216 | ctl->dirty.bf.ctl = 1; | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int src_set_wr(void *blk, unsigned int wr) | ||
221 | { | ||
222 | struct src_rsc_ctrl_blk *ctl = blk; | ||
223 | |||
224 | set_field(&ctl->ctl, SRCCTL_WR, wr); | ||
225 | ctl->dirty.bf.ctl = 1; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int src_set_pm(void *blk, unsigned int pm) | ||
230 | { | ||
231 | struct src_rsc_ctrl_blk *ctl = blk; | ||
232 | |||
233 | set_field(&ctl->ctl, SRCCTL_PM, pm); | ||
234 | ctl->dirty.bf.ctl = 1; | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int src_set_rom(void *blk, unsigned int rom) | ||
239 | { | ||
240 | struct src_rsc_ctrl_blk *ctl = blk; | ||
241 | |||
242 | set_field(&ctl->ctl, SRCCTL_ROM, rom); | ||
243 | ctl->dirty.bf.ctl = 1; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int src_set_vo(void *blk, unsigned int vo) | ||
248 | { | ||
249 | struct src_rsc_ctrl_blk *ctl = blk; | ||
250 | |||
251 | set_field(&ctl->ctl, SRCCTL_VO, vo); | ||
252 | ctl->dirty.bf.ctl = 1; | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int src_set_st(void *blk, unsigned int st) | ||
257 | { | ||
258 | struct src_rsc_ctrl_blk *ctl = blk; | ||
259 | |||
260 | set_field(&ctl->ctl, SRCCTL_ST, st); | ||
261 | ctl->dirty.bf.ctl = 1; | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static int src_set_ie(void *blk, unsigned int ie) | ||
266 | { | ||
267 | struct src_rsc_ctrl_blk *ctl = blk; | ||
268 | |||
269 | set_field(&ctl->ctl, SRCCTL_IE, ie); | ||
270 | ctl->dirty.bf.ctl = 1; | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int src_set_ilsz(void *blk, unsigned int ilsz) | ||
275 | { | ||
276 | struct src_rsc_ctrl_blk *ctl = blk; | ||
277 | |||
278 | set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); | ||
279 | ctl->dirty.bf.ctl = 1; | ||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static int src_set_bp(void *blk, unsigned int bp) | ||
284 | { | ||
285 | struct src_rsc_ctrl_blk *ctl = blk; | ||
286 | |||
287 | set_field(&ctl->ctl, SRCCTL_BP, bp); | ||
288 | ctl->dirty.bf.ctl = 1; | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int src_set_cisz(void *blk, unsigned int cisz) | ||
293 | { | ||
294 | struct src_rsc_ctrl_blk *ctl = blk; | ||
295 | |||
296 | set_field(&ctl->ccr, SRCCCR_CISZ, cisz); | ||
297 | ctl->dirty.bf.ccr = 1; | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int src_set_ca(void *blk, unsigned int ca) | ||
302 | { | ||
303 | struct src_rsc_ctrl_blk *ctl = blk; | ||
304 | |||
305 | set_field(&ctl->ca, SRCCA_CA, ca); | ||
306 | ctl->dirty.bf.ca = 1; | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int src_set_sa(void *blk, unsigned int sa) | ||
311 | { | ||
312 | struct src_rsc_ctrl_blk *ctl = blk; | ||
313 | |||
314 | set_field(&ctl->sa, SRCSA_SA, sa); | ||
315 | ctl->dirty.bf.sa = 1; | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static int src_set_la(void *blk, unsigned int la) | ||
320 | { | ||
321 | struct src_rsc_ctrl_blk *ctl = blk; | ||
322 | |||
323 | set_field(&ctl->la, SRCLA_LA, la); | ||
324 | ctl->dirty.bf.la = 1; | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static int src_set_pitch(void *blk, unsigned int pitch) | ||
329 | { | ||
330 | struct src_rsc_ctrl_blk *ctl = blk; | ||
331 | |||
332 | set_field(&ctl->mpr, MPRLH_PITCH, pitch); | ||
333 | ctl->dirty.bf.mpr = 1; | ||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int src_set_clear_zbufs(void *blk, unsigned int clear) | ||
338 | { | ||
339 | ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static int src_set_dirty(void *blk, unsigned int flags) | ||
344 | { | ||
345 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int src_set_dirty_all(void *blk) | ||
350 | { | ||
351 | ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | #define AR_SLOT_SIZE 4096 | ||
356 | #define AR_SLOT_BLOCK_SIZE 16 | ||
357 | #define AR_PTS_PITCH 6 | ||
358 | #define AR_PARAM_SRC_OFFSET 0x60 | ||
359 | |||
360 | static unsigned int src_param_pitch_mixer(unsigned int src_idx) | ||
361 | { | ||
362 | return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE | ||
363 | - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; | ||
364 | |||
365 | } | ||
366 | |||
367 | static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
368 | { | ||
369 | struct src_rsc_ctrl_blk *ctl = blk; | ||
370 | int i; | ||
371 | |||
372 | if (ctl->dirty.bf.czbfs) { | ||
373 | /* Clear Z-Buffer registers */ | ||
374 | for (i = 0; i < 8; i++) | ||
375 | hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0); | ||
376 | |||
377 | for (i = 0; i < 4; i++) | ||
378 | hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0); | ||
379 | |||
380 | for (i = 0; i < 8; i++) | ||
381 | hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0); | ||
382 | |||
383 | ctl->dirty.bf.czbfs = 0; | ||
384 | } | ||
385 | if (ctl->dirty.bf.mpr) { | ||
386 | /* Take the parameter mixer resource in the same group as that | ||
387 | * the idx src is in for simplicity. Unlike src, all conjugate | ||
388 | * parameter mixer resources must be programmed for | ||
389 | * corresponding conjugate src resources. */ | ||
390 | unsigned int pm_idx = src_param_pitch_mixer(idx); | ||
391 | hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr); | ||
392 | hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3); | ||
393 | hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0); | ||
394 | ctl->dirty.bf.mpr = 0; | ||
395 | } | ||
396 | if (ctl->dirty.bf.sa) { | ||
397 | hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa); | ||
398 | ctl->dirty.bf.sa = 0; | ||
399 | } | ||
400 | if (ctl->dirty.bf.la) { | ||
401 | hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la); | ||
402 | ctl->dirty.bf.la = 0; | ||
403 | } | ||
404 | if (ctl->dirty.bf.ca) { | ||
405 | hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca); | ||
406 | ctl->dirty.bf.ca = 0; | ||
407 | } | ||
408 | |||
409 | /* Write srccf register */ | ||
410 | hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0); | ||
411 | |||
412 | if (ctl->dirty.bf.ccr) { | ||
413 | hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr); | ||
414 | ctl->dirty.bf.ccr = 0; | ||
415 | } | ||
416 | if (ctl->dirty.bf.ctl) { | ||
417 | hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl); | ||
418 | ctl->dirty.bf.ctl = 0; | ||
419 | } | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) | ||
425 | { | ||
426 | struct src_rsc_ctrl_blk *ctl = blk; | ||
427 | |||
428 | ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100); | ||
429 | ctl->dirty.bf.ca = 0; | ||
430 | |||
431 | return get_field(ctl->ca, SRCCA_CA); | ||
432 | } | ||
433 | |||
434 | static unsigned int src_get_dirty(void *blk) | ||
435 | { | ||
436 | return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; | ||
437 | } | ||
438 | |||
439 | static unsigned int src_dirty_conj_mask(void) | ||
440 | { | ||
441 | return 0x20; | ||
442 | } | ||
443 | |||
444 | static int src_mgr_enbs_src(void *blk, unsigned int idx) | ||
445 | { | ||
446 | ((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4)); | ||
447 | ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; | ||
448 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int src_mgr_enb_src(void *blk, unsigned int idx) | ||
453 | { | ||
454 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); | ||
455 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | static int src_mgr_dsb_src(void *blk, unsigned int idx) | ||
460 | { | ||
461 | ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); | ||
462 | ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static int src_mgr_commit_write(struct hw *hw, void *blk) | ||
467 | { | ||
468 | struct src_mgr_ctrl_blk *ctl = blk; | ||
469 | int i; | ||
470 | unsigned int ret; | ||
471 | |||
472 | if (ctl->dirty.bf.enbsa) { | ||
473 | do { | ||
474 | ret = hw_read_20kx(hw, SRC_ENBSTAT); | ||
475 | } while (ret & 0x1); | ||
476 | hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa); | ||
477 | ctl->dirty.bf.enbsa = 0; | ||
478 | } | ||
479 | for (i = 0; i < 8; i++) { | ||
480 | if ((ctl->dirty.data & (0x1 << i))) { | ||
481 | hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]); | ||
482 | ctl->dirty.data &= ~(0x1 << i); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int src_mgr_get_ctrl_blk(void **rblk) | ||
490 | { | ||
491 | struct src_mgr_ctrl_blk *blk; | ||
492 | |||
493 | *rblk = NULL; | ||
494 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
495 | if (NULL == blk) | ||
496 | return -ENOMEM; | ||
497 | |||
498 | *rblk = blk; | ||
499 | |||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | static int src_mgr_put_ctrl_blk(void *blk) | ||
504 | { | ||
505 | kfree(blk); | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static int srcimp_mgr_get_ctrl_blk(void **rblk) | ||
511 | { | ||
512 | struct srcimp_mgr_ctrl_blk *blk; | ||
513 | |||
514 | *rblk = NULL; | ||
515 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
516 | if (NULL == blk) | ||
517 | return -ENOMEM; | ||
518 | |||
519 | *rblk = blk; | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int srcimp_mgr_put_ctrl_blk(void *blk) | ||
525 | { | ||
526 | kfree(blk); | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) | ||
532 | { | ||
533 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
534 | |||
535 | set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); | ||
536 | ctl->dirty.bf.srcimap = 1; | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) | ||
541 | { | ||
542 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
543 | |||
544 | set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); | ||
545 | ctl->dirty.bf.srcimap = 1; | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) | ||
550 | { | ||
551 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
552 | |||
553 | set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); | ||
554 | ctl->dirty.bf.srcimap = 1; | ||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
559 | { | ||
560 | ((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr; | ||
561 | ((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1; | ||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static int srcimp_mgr_commit_write(struct hw *hw, void *blk) | ||
566 | { | ||
567 | struct srcimp_mgr_ctrl_blk *ctl = blk; | ||
568 | |||
569 | if (ctl->dirty.bf.srcimap) { | ||
570 | hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100, | ||
571 | ctl->srcimap.srcaim); | ||
572 | ctl->dirty.bf.srcimap = 0; | ||
573 | } | ||
574 | |||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | /* | ||
579 | * AMIXER control block definitions. | ||
580 | */ | ||
581 | |||
582 | #define AMOPLO_M 0x00000003 | ||
583 | #define AMOPLO_IV 0x00000004 | ||
584 | #define AMOPLO_X 0x0003FFF0 | ||
585 | #define AMOPLO_Y 0xFFFC0000 | ||
586 | |||
587 | #define AMOPHI_SADR 0x000000FF | ||
588 | #define AMOPHI_SE 0x80000000 | ||
589 | |||
590 | /* AMIXER resource register dirty flags */ | ||
591 | union amixer_dirty { | ||
592 | struct { | ||
593 | u16 amoplo:1; | ||
594 | u16 amophi:1; | ||
595 | u16 rsv:14; | ||
596 | } bf; | ||
597 | u16 data; | ||
598 | }; | ||
599 | |||
600 | /* AMIXER resource control block */ | ||
601 | struct amixer_rsc_ctrl_blk { | ||
602 | unsigned int amoplo; | ||
603 | unsigned int amophi; | ||
604 | union amixer_dirty dirty; | ||
605 | }; | ||
606 | |||
607 | static int amixer_set_mode(void *blk, unsigned int mode) | ||
608 | { | ||
609 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
610 | |||
611 | set_field(&ctl->amoplo, AMOPLO_M, mode); | ||
612 | ctl->dirty.bf.amoplo = 1; | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | static int amixer_set_iv(void *blk, unsigned int iv) | ||
617 | { | ||
618 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
619 | |||
620 | set_field(&ctl->amoplo, AMOPLO_IV, iv); | ||
621 | ctl->dirty.bf.amoplo = 1; | ||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static int amixer_set_x(void *blk, unsigned int x) | ||
626 | { | ||
627 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
628 | |||
629 | set_field(&ctl->amoplo, AMOPLO_X, x); | ||
630 | ctl->dirty.bf.amoplo = 1; | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int amixer_set_y(void *blk, unsigned int y) | ||
635 | { | ||
636 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
637 | |||
638 | set_field(&ctl->amoplo, AMOPLO_Y, y); | ||
639 | ctl->dirty.bf.amoplo = 1; | ||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | static int amixer_set_sadr(void *blk, unsigned int sadr) | ||
644 | { | ||
645 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
646 | |||
647 | set_field(&ctl->amophi, AMOPHI_SADR, sadr); | ||
648 | ctl->dirty.bf.amophi = 1; | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int amixer_set_se(void *blk, unsigned int se) | ||
653 | { | ||
654 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
655 | |||
656 | set_field(&ctl->amophi, AMOPHI_SE, se); | ||
657 | ctl->dirty.bf.amophi = 1; | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int amixer_set_dirty(void *blk, unsigned int flags) | ||
662 | { | ||
663 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int amixer_set_dirty_all(void *blk) | ||
668 | { | ||
669 | ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
674 | { | ||
675 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
676 | |||
677 | if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { | ||
678 | hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo); | ||
679 | ctl->dirty.bf.amoplo = 0; | ||
680 | hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi); | ||
681 | ctl->dirty.bf.amophi = 0; | ||
682 | } | ||
683 | |||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | static int amixer_get_y(void *blk) | ||
688 | { | ||
689 | struct amixer_rsc_ctrl_blk *ctl = blk; | ||
690 | |||
691 | return get_field(ctl->amoplo, AMOPLO_Y); | ||
692 | } | ||
693 | |||
694 | static unsigned int amixer_get_dirty(void *blk) | ||
695 | { | ||
696 | return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; | ||
697 | } | ||
698 | |||
699 | static int amixer_rsc_get_ctrl_blk(void **rblk) | ||
700 | { | ||
701 | struct amixer_rsc_ctrl_blk *blk; | ||
702 | |||
703 | *rblk = NULL; | ||
704 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
705 | if (NULL == blk) | ||
706 | return -ENOMEM; | ||
707 | |||
708 | *rblk = blk; | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static int amixer_rsc_put_ctrl_blk(void *blk) | ||
714 | { | ||
715 | kfree(blk); | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | static int amixer_mgr_get_ctrl_blk(void **rblk) | ||
721 | { | ||
722 | *rblk = NULL; | ||
723 | |||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static int amixer_mgr_put_ctrl_blk(void *blk) | ||
728 | { | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | /* | ||
733 | * DAIO control block definitions. | ||
734 | */ | ||
735 | |||
736 | /* Receiver Sample Rate Tracker Control register */ | ||
737 | #define SRTCTL_SRCO 0x000000FF | ||
738 | #define SRTCTL_SRCM 0x0000FF00 | ||
739 | #define SRTCTL_RSR 0x00030000 | ||
740 | #define SRTCTL_DRAT 0x00300000 | ||
741 | #define SRTCTL_EC 0x01000000 | ||
742 | #define SRTCTL_ET 0x10000000 | ||
743 | |||
744 | /* DAIO Receiver register dirty flags */ | ||
745 | union dai_dirty { | ||
746 | struct { | ||
747 | u16 srt:1; | ||
748 | u16 rsv:15; | ||
749 | } bf; | ||
750 | u16 data; | ||
751 | }; | ||
752 | |||
753 | /* DAIO Receiver control block */ | ||
754 | struct dai_ctrl_blk { | ||
755 | unsigned int srt; | ||
756 | union dai_dirty dirty; | ||
757 | }; | ||
758 | |||
759 | /* Audio Input Mapper RAM */ | ||
760 | #define AIM_ARC 0x00000FFF | ||
761 | #define AIM_NXT 0x007F0000 | ||
762 | |||
763 | struct daoimap { | ||
764 | unsigned int aim; | ||
765 | unsigned int idx; | ||
766 | }; | ||
767 | |||
768 | /* Audio Transmitter Control and Status register */ | ||
769 | #define ATXCTL_EN 0x00000001 | ||
770 | #define ATXCTL_MODE 0x00000010 | ||
771 | #define ATXCTL_CD 0x00000020 | ||
772 | #define ATXCTL_RAW 0x00000100 | ||
773 | #define ATXCTL_MT 0x00000200 | ||
774 | #define ATXCTL_NUC 0x00003000 | ||
775 | #define ATXCTL_BEN 0x00010000 | ||
776 | #define ATXCTL_BMUX 0x00700000 | ||
777 | #define ATXCTL_B24 0x01000000 | ||
778 | #define ATXCTL_CPF 0x02000000 | ||
779 | #define ATXCTL_RIV 0x10000000 | ||
780 | #define ATXCTL_LIV 0x20000000 | ||
781 | #define ATXCTL_RSAT 0x40000000 | ||
782 | #define ATXCTL_LSAT 0x80000000 | ||
783 | |||
784 | /* XDIF Transmitter register dirty flags */ | ||
785 | union dao_dirty { | ||
786 | struct { | ||
787 | u16 atxcsl:1; | ||
788 | u16 rsv:15; | ||
789 | } bf; | ||
790 | u16 data; | ||
791 | }; | ||
792 | |||
793 | /* XDIF Transmitter control block */ | ||
794 | struct dao_ctrl_blk { | ||
795 | /* XDIF Transmitter Channel Status Low Register */ | ||
796 | unsigned int atxcsl; | ||
797 | union dao_dirty dirty; | ||
798 | }; | ||
799 | |||
800 | /* Audio Receiver Control register */ | ||
801 | #define ARXCTL_EN 0x00000001 | ||
802 | |||
803 | /* DAIO manager register dirty flags */ | ||
804 | union daio_mgr_dirty { | ||
805 | struct { | ||
806 | u32 atxctl:8; | ||
807 | u32 arxctl:8; | ||
808 | u32 daoimap:1; | ||
809 | u32 rsv:15; | ||
810 | } bf; | ||
811 | u32 data; | ||
812 | }; | ||
813 | |||
814 | /* DAIO manager control block */ | ||
815 | struct daio_mgr_ctrl_blk { | ||
816 | struct daoimap daoimap; | ||
817 | unsigned int txctl[8]; | ||
818 | unsigned int rxctl[8]; | ||
819 | union daio_mgr_dirty dirty; | ||
820 | }; | ||
821 | |||
822 | static int dai_srt_set_srco(void *blk, unsigned int src) | ||
823 | { | ||
824 | struct dai_ctrl_blk *ctl = blk; | ||
825 | |||
826 | set_field(&ctl->srt, SRTCTL_SRCO, src); | ||
827 | ctl->dirty.bf.srt = 1; | ||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | static int dai_srt_set_srcm(void *blk, unsigned int src) | ||
832 | { | ||
833 | struct dai_ctrl_blk *ctl = blk; | ||
834 | |||
835 | set_field(&ctl->srt, SRTCTL_SRCM, src); | ||
836 | ctl->dirty.bf.srt = 1; | ||
837 | return 0; | ||
838 | } | ||
839 | |||
840 | static int dai_srt_set_rsr(void *blk, unsigned int rsr) | ||
841 | { | ||
842 | struct dai_ctrl_blk *ctl = blk; | ||
843 | |||
844 | set_field(&ctl->srt, SRTCTL_RSR, rsr); | ||
845 | ctl->dirty.bf.srt = 1; | ||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static int dai_srt_set_drat(void *blk, unsigned int drat) | ||
850 | { | ||
851 | struct dai_ctrl_blk *ctl = blk; | ||
852 | |||
853 | set_field(&ctl->srt, SRTCTL_DRAT, drat); | ||
854 | ctl->dirty.bf.srt = 1; | ||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | static int dai_srt_set_ec(void *blk, unsigned int ec) | ||
859 | { | ||
860 | struct dai_ctrl_blk *ctl = blk; | ||
861 | |||
862 | set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0); | ||
863 | ctl->dirty.bf.srt = 1; | ||
864 | return 0; | ||
865 | } | ||
866 | |||
867 | static int dai_srt_set_et(void *blk, unsigned int et) | ||
868 | { | ||
869 | struct dai_ctrl_blk *ctl = blk; | ||
870 | |||
871 | set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0); | ||
872 | ctl->dirty.bf.srt = 1; | ||
873 | return 0; | ||
874 | } | ||
875 | |||
876 | static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
877 | { | ||
878 | struct dai_ctrl_blk *ctl = blk; | ||
879 | |||
880 | if (ctl->dirty.bf.srt) { | ||
881 | hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt); | ||
882 | ctl->dirty.bf.srt = 0; | ||
883 | } | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static int dai_get_ctrl_blk(void **rblk) | ||
889 | { | ||
890 | struct dai_ctrl_blk *blk; | ||
891 | |||
892 | *rblk = NULL; | ||
893 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
894 | if (NULL == blk) | ||
895 | return -ENOMEM; | ||
896 | |||
897 | *rblk = blk; | ||
898 | |||
899 | return 0; | ||
900 | } | ||
901 | |||
902 | static int dai_put_ctrl_blk(void *blk) | ||
903 | { | ||
904 | kfree(blk); | ||
905 | |||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | static int dao_set_spos(void *blk, unsigned int spos) | ||
910 | { | ||
911 | ((struct dao_ctrl_blk *)blk)->atxcsl = spos; | ||
912 | ((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1; | ||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) | ||
917 | { | ||
918 | struct dao_ctrl_blk *ctl = blk; | ||
919 | |||
920 | if (ctl->dirty.bf.atxcsl) { | ||
921 | if (idx < 4) { | ||
922 | /* S/PDIF SPOSx */ | ||
923 | hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx, | ||
924 | ctl->atxcsl); | ||
925 | } | ||
926 | ctl->dirty.bf.atxcsl = 0; | ||
927 | } | ||
928 | |||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | static int dao_get_spos(void *blk, unsigned int *spos) | ||
933 | { | ||
934 | *spos = ((struct dao_ctrl_blk *)blk)->atxcsl; | ||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | static int dao_get_ctrl_blk(void **rblk) | ||
939 | { | ||
940 | struct dao_ctrl_blk *blk; | ||
941 | |||
942 | *rblk = NULL; | ||
943 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
944 | if (NULL == blk) | ||
945 | return -ENOMEM; | ||
946 | |||
947 | *rblk = blk; | ||
948 | |||
949 | return 0; | ||
950 | } | ||
951 | |||
952 | static int dao_put_ctrl_blk(void *blk) | ||
953 | { | ||
954 | kfree(blk); | ||
955 | |||
956 | return 0; | ||
957 | } | ||
958 | |||
959 | static int daio_mgr_enb_dai(void *blk, unsigned int idx) | ||
960 | { | ||
961 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
962 | |||
963 | set_field(&ctl->rxctl[idx], ARXCTL_EN, 1); | ||
964 | ctl->dirty.bf.arxctl |= (0x1 << idx); | ||
965 | return 0; | ||
966 | } | ||
967 | |||
968 | static int daio_mgr_dsb_dai(void *blk, unsigned int idx) | ||
969 | { | ||
970 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
971 | |||
972 | set_field(&ctl->rxctl[idx], ARXCTL_EN, 0); | ||
973 | |||
974 | ctl->dirty.bf.arxctl |= (0x1 << idx); | ||
975 | return 0; | ||
976 | } | ||
977 | |||
978 | static int daio_mgr_enb_dao(void *blk, unsigned int idx) | ||
979 | { | ||
980 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
981 | |||
982 | set_field(&ctl->txctl[idx], ATXCTL_EN, 1); | ||
983 | ctl->dirty.bf.atxctl |= (0x1 << idx); | ||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | static int daio_mgr_dsb_dao(void *blk, unsigned int idx) | ||
988 | { | ||
989 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
990 | |||
991 | set_field(&ctl->txctl[idx], ATXCTL_EN, 0); | ||
992 | ctl->dirty.bf.atxctl |= (0x1 << idx); | ||
993 | return 0; | ||
994 | } | ||
995 | |||
996 | static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) | ||
997 | { | ||
998 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
999 | |||
1000 | if (idx < 4) { | ||
1001 | /* S/PDIF output */ | ||
1002 | switch ((conf & 0x7)) { | ||
1003 | case 1: | ||
1004 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 0); | ||
1005 | break; | ||
1006 | case 2: | ||
1007 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 1); | ||
1008 | break; | ||
1009 | case 4: | ||
1010 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 2); | ||
1011 | break; | ||
1012 | case 8: | ||
1013 | set_field(&ctl->txctl[idx], ATXCTL_NUC, 3); | ||
1014 | break; | ||
1015 | default: | ||
1016 | break; | ||
1017 | } | ||
1018 | /* CDIF */ | ||
1019 | set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7))); | ||
1020 | /* Non-audio */ | ||
1021 | set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1); | ||
1022 | /* Non-audio */ | ||
1023 | set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1); | ||
1024 | set_field(&ctl->txctl[idx], ATXCTL_RAW, | ||
1025 | ((conf >> 3) & 0x1) ? 0 : 0); | ||
1026 | ctl->dirty.bf.atxctl |= (0x1 << idx); | ||
1027 | } else { | ||
1028 | /* I2S output */ | ||
1029 | /*idx %= 4; */ | ||
1030 | } | ||
1031 | return 0; | ||
1032 | } | ||
1033 | |||
1034 | static int daio_mgr_set_imaparc(void *blk, unsigned int slot) | ||
1035 | { | ||
1036 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1037 | |||
1038 | set_field(&ctl->daoimap.aim, AIM_ARC, slot); | ||
1039 | ctl->dirty.bf.daoimap = 1; | ||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | static int daio_mgr_set_imapnxt(void *blk, unsigned int next) | ||
1044 | { | ||
1045 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1046 | |||
1047 | set_field(&ctl->daoimap.aim, AIM_NXT, next); | ||
1048 | ctl->dirty.bf.daoimap = 1; | ||
1049 | return 0; | ||
1050 | } | ||
1051 | |||
1052 | static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) | ||
1053 | { | ||
1054 | ((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr; | ||
1055 | ((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1; | ||
1056 | return 0; | ||
1057 | } | ||
1058 | |||
1059 | static int daio_mgr_commit_write(struct hw *hw, void *blk) | ||
1060 | { | ||
1061 | struct daio_mgr_ctrl_blk *ctl = blk; | ||
1062 | unsigned int data; | ||
1063 | int i; | ||
1064 | |||
1065 | for (i = 0; i < 8; i++) { | ||
1066 | if ((ctl->dirty.bf.atxctl & (0x1 << i))) { | ||
1067 | data = ctl->txctl[i]; | ||
1068 | hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); | ||
1069 | ctl->dirty.bf.atxctl &= ~(0x1 << i); | ||
1070 | mdelay(1); | ||
1071 | } | ||
1072 | if ((ctl->dirty.bf.arxctl & (0x1 << i))) { | ||
1073 | data = ctl->rxctl[i]; | ||
1074 | hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); | ||
1075 | ctl->dirty.bf.arxctl &= ~(0x1 << i); | ||
1076 | mdelay(1); | ||
1077 | } | ||
1078 | } | ||
1079 | if (ctl->dirty.bf.daoimap) { | ||
1080 | hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4, | ||
1081 | ctl->daoimap.aim); | ||
1082 | ctl->dirty.bf.daoimap = 0; | ||
1083 | } | ||
1084 | |||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) | ||
1089 | { | ||
1090 | struct daio_mgr_ctrl_blk *blk; | ||
1091 | int i; | ||
1092 | |||
1093 | *rblk = NULL; | ||
1094 | blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||
1095 | if (NULL == blk) | ||
1096 | return -ENOMEM; | ||
1097 | |||
1098 | for (i = 0; i < 8; i++) { | ||
1099 | blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i)); | ||
1100 | blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i)); | ||
1101 | } | ||
1102 | |||
1103 | *rblk = blk; | ||
1104 | |||
1105 | return 0; | ||
1106 | } | ||
1107 | |||
1108 | static int daio_mgr_put_ctrl_blk(void *blk) | ||
1109 | { | ||
1110 | kfree(blk); | ||
1111 | |||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | /* Card hardware initialization block */ | ||
1116 | struct dac_conf { | ||
1117 | unsigned int msr; /* master sample rate in rsrs */ | ||
1118 | }; | ||
1119 | |||
1120 | struct adc_conf { | ||
1121 | unsigned int msr; /* master sample rate in rsrs */ | ||
1122 | unsigned char input; /* the input source of ADC */ | ||
1123 | unsigned char mic20db; /* boost mic by 20db if input is microphone */ | ||
1124 | }; | ||
1125 | |||
1126 | struct daio_conf { | ||
1127 | unsigned int msr; /* master sample rate in rsrs */ | ||
1128 | }; | ||
1129 | |||
1130 | struct trn_conf { | ||
1131 | unsigned long vm_pgt_phys; | ||
1132 | }; | ||
1133 | |||
1134 | static int hw_daio_init(struct hw *hw, const struct daio_conf *info) | ||
1135 | { | ||
1136 | u32 data; | ||
1137 | int i; | ||
1138 | |||
1139 | /* Program I2S with proper sample rate and enable the correct I2S | ||
1140 | * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */ | ||
1141 | if (1 == info->msr) { | ||
1142 | hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101); | ||
1143 | hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101); | ||
1144 | hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); | ||
1145 | } else if (2 == info->msr) { | ||
1146 | hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); | ||
1147 | /* Specify all playing 96khz | ||
1148 | * EA [0] - Enabled | ||
1149 | * RTA [4:5] - 96kHz | ||
1150 | * EB [8] - Enabled | ||
1151 | * RTB [12:13] - 96kHz | ||
1152 | * EC [16] - Enabled | ||
1153 | * RTC [20:21] - 96kHz | ||
1154 | * ED [24] - Enabled | ||
1155 | * RTD [28:29] - 96kHz */ | ||
1156 | hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); | ||
1157 | hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); | ||
1158 | } else { | ||
1159 | printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); | ||
1160 | return -EINVAL; | ||
1161 | } | ||
1162 | |||
1163 | for (i = 0; i < 8; i++) { | ||
1164 | if (i <= 3) { | ||
1165 | /* 1st 3 channels are SPDIFs (SB0960) */ | ||
1166 | if (i == 3) | ||
1167 | data = 0x1001001; | ||
1168 | else | ||
1169 | data = 0x1000001; | ||
1170 | |||
1171 | hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); | ||
1172 | hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); | ||
1173 | |||
1174 | /* Initialize the SPDIF Out Channel status registers. | ||
1175 | * The value specified here is based on the typical | ||
1176 | * values provided in the specification, namely: Clock | ||
1177 | * Accuracy of 1000ppm, Sample Rate of 48KHz, | ||
1178 | * unspecified source number, Generation status = 1, | ||
1179 | * Category code = 0x12 (Digital Signal Mixer), | ||
1180 | * Mode = 0, Emph = 0, Copy Permitted, AN = 0 | ||
1181 | * (indicating that we're transmitting digital audio, | ||
1182 | * and the Professional Use bit is 0. */ | ||
1183 | |||
1184 | hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i), | ||
1185 | 0x02109204); /* Default to 48kHz */ | ||
1186 | |||
1187 | hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); | ||
1188 | } else { | ||
1189 | /* Next 5 channels are I2S (SB0960) */ | ||
1190 | data = 0x11; | ||
1191 | hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data); | ||
1192 | if (2 == info->msr) { | ||
1193 | /* Four channels per sample period */ | ||
1194 | data |= 0x1000; | ||
1195 | } | ||
1196 | hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data); | ||
1197 | } | ||
1198 | } | ||
1199 | |||
1200 | return 0; | ||
1201 | } | ||
1202 | |||
1203 | /* TRANSPORT operations */ | ||
1204 | static int hw_trn_init(struct hw *hw, const struct trn_conf *info) | ||
1205 | { | ||
1206 | u32 vmctl, data; | ||
1207 | u32 ptp_phys_low, ptp_phys_high; | ||
1208 | int i; | ||
1209 | |||
1210 | /* Set up device page table */ | ||
1211 | if ((~0UL) == info->vm_pgt_phys) { | ||
1212 | printk(KERN_ALERT "ctxfi: " | ||
1213 | "Wrong device page table page address!!!\n"); | ||
1214 | return -1; | ||
1215 | } | ||
1216 | |||
1217 | vmctl = 0x80000C0F; /* 32-bit, 4k-size page */ | ||
1218 | ptp_phys_low = (u32)info->vm_pgt_phys; | ||
1219 | ptp_phys_high = upper_32_bits(info->vm_pgt_phys); | ||
1220 | if (sizeof(void *) == 8) /* 64bit address */ | ||
1221 | vmctl |= (3 << 8); | ||
1222 | /* Write page table physical address to all PTPAL registers */ | ||
1223 | for (i = 0; i < 64; i++) { | ||
1224 | hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low); | ||
1225 | hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high); | ||
1226 | } | ||
1227 | /* Enable virtual memory transfer */ | ||
1228 | hw_write_20kx(hw, VMEM_CTL, vmctl); | ||
1229 | /* Enable transport bus master and queueing of request */ | ||
1230 | hw_write_20kx(hw, TRANSPORT_CTL, 0x03); | ||
1231 | hw_write_20kx(hw, TRANSPORT_INT, 0x200c01); | ||
1232 | /* Enable transport ring */ | ||
1233 | data = hw_read_20kx(hw, TRANSPORT_ENB); | ||
1234 | hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03)); | ||
1235 | |||
1236 | return 0; | ||
1237 | } | ||
1238 | |||
1239 | /* Card initialization */ | ||
1240 | #define GCTL_AIE 0x00000001 | ||
1241 | #define GCTL_UAA 0x00000002 | ||
1242 | #define GCTL_DPC 0x00000004 | ||
1243 | #define GCTL_DBP 0x00000008 | ||
1244 | #define GCTL_ABP 0x00000010 | ||
1245 | #define GCTL_TBP 0x00000020 | ||
1246 | #define GCTL_SBP 0x00000040 | ||
1247 | #define GCTL_FBP 0x00000080 | ||
1248 | #define GCTL_ME 0x00000100 | ||
1249 | #define GCTL_AID 0x00001000 | ||
1250 | |||
1251 | #define PLLCTL_SRC 0x00000007 | ||
1252 | #define PLLCTL_SPE 0x00000008 | ||
1253 | #define PLLCTL_RD 0x000000F0 | ||
1254 | #define PLLCTL_FD 0x0001FF00 | ||
1255 | #define PLLCTL_OD 0x00060000 | ||
1256 | #define PLLCTL_B 0x00080000 | ||
1257 | #define PLLCTL_AS 0x00100000 | ||
1258 | #define PLLCTL_LF 0x03E00000 | ||
1259 | #define PLLCTL_SPS 0x1C000000 | ||
1260 | #define PLLCTL_AD 0x60000000 | ||
1261 | |||
1262 | #define PLLSTAT_CCS 0x00000007 | ||
1263 | #define PLLSTAT_SPL 0x00000008 | ||
1264 | #define PLLSTAT_CRD 0x000000F0 | ||
1265 | #define PLLSTAT_CFD 0x0001FF00 | ||
1266 | #define PLLSTAT_SL 0x00020000 | ||
1267 | #define PLLSTAT_FAS 0x00040000 | ||
1268 | #define PLLSTAT_B 0x00080000 | ||
1269 | #define PLLSTAT_PD 0x00100000 | ||
1270 | #define PLLSTAT_OCA 0x00200000 | ||
1271 | #define PLLSTAT_NCA 0x00400000 | ||
1272 | |||
1273 | static int hw_pll_init(struct hw *hw, unsigned int rsr) | ||
1274 | { | ||
1275 | unsigned int pllenb; | ||
1276 | unsigned int pllctl; | ||
1277 | unsigned int pllstat; | ||
1278 | int i; | ||
1279 | |||
1280 | pllenb = 0xB; | ||
1281 | hw_write_20kx(hw, PLL_ENB, pllenb); | ||
1282 | pllctl = 0x20D00000; | ||
1283 | set_field(&pllctl, PLLCTL_FD, 16 - 4); | ||
1284 | hw_write_20kx(hw, PLL_CTL, pllctl); | ||
1285 | mdelay(40); | ||
1286 | pllctl = hw_read_20kx(hw, PLL_CTL); | ||
1287 | set_field(&pllctl, PLLCTL_B, 0); | ||
1288 | if (48000 == rsr) { | ||
1289 | set_field(&pllctl, PLLCTL_FD, 16 - 2); | ||
1290 | set_field(&pllctl, PLLCTL_RD, 1 - 1); | ||
1291 | } else { /* 44100 */ | ||
1292 | set_field(&pllctl, PLLCTL_FD, 147 - 2); | ||
1293 | set_field(&pllctl, PLLCTL_RD, 10 - 1); | ||
1294 | } | ||
1295 | hw_write_20kx(hw, PLL_CTL, pllctl); | ||
1296 | mdelay(40); | ||
1297 | for (i = 0; i < 1000; i++) { | ||
1298 | pllstat = hw_read_20kx(hw, PLL_STAT); | ||
1299 | if (get_field(pllstat, PLLSTAT_PD)) | ||
1300 | continue; | ||
1301 | |||
1302 | if (get_field(pllstat, PLLSTAT_B) != | ||
1303 | get_field(pllctl, PLLCTL_B)) | ||
1304 | continue; | ||
1305 | |||
1306 | if (get_field(pllstat, PLLSTAT_CCS) != | ||
1307 | get_field(pllctl, PLLCTL_SRC)) | ||
1308 | continue; | ||
1309 | |||
1310 | if (get_field(pllstat, PLLSTAT_CRD) != | ||
1311 | get_field(pllctl, PLLCTL_RD)) | ||
1312 | continue; | ||
1313 | |||
1314 | if (get_field(pllstat, PLLSTAT_CFD) != | ||
1315 | get_field(pllctl, PLLCTL_FD)) | ||
1316 | continue; | ||
1317 | |||
1318 | break; | ||
1319 | } | ||
1320 | if (i >= 1000) { | ||
1321 | printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n"); | ||
1322 | return -EBUSY; | ||
1323 | } | ||
1324 | |||
1325 | return 0; | ||
1326 | } | ||
1327 | |||
1328 | static int hw_auto_init(struct hw *hw) | ||
1329 | { | ||
1330 | unsigned int gctl; | ||
1331 | int i; | ||
1332 | |||
1333 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1334 | set_field(&gctl, GCTL_AIE, 0); | ||
1335 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1336 | set_field(&gctl, GCTL_AIE, 1); | ||
1337 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1338 | mdelay(10); | ||
1339 | for (i = 0; i < 400000; i++) { | ||
1340 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1341 | if (get_field(gctl, GCTL_AID)) | ||
1342 | break; | ||
1343 | } | ||
1344 | if (!get_field(gctl, GCTL_AID)) { | ||
1345 | printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n"); | ||
1346 | return -EBUSY; | ||
1347 | } | ||
1348 | |||
1349 | return 0; | ||
1350 | } | ||
1351 | |||
1352 | /* DAC operations */ | ||
1353 | |||
1354 | #define CS4382_MC1 0x1 | ||
1355 | #define CS4382_MC2 0x2 | ||
1356 | #define CS4382_MC3 0x3 | ||
1357 | #define CS4382_FC 0x4 | ||
1358 | #define CS4382_IC 0x5 | ||
1359 | #define CS4382_XC1 0x6 | ||
1360 | #define CS4382_VCA1 0x7 | ||
1361 | #define CS4382_VCB1 0x8 | ||
1362 | #define CS4382_XC2 0x9 | ||
1363 | #define CS4382_VCA2 0xA | ||
1364 | #define CS4382_VCB2 0xB | ||
1365 | #define CS4382_XC3 0xC | ||
1366 | #define CS4382_VCA3 0xD | ||
1367 | #define CS4382_VCB3 0xE | ||
1368 | #define CS4382_XC4 0xF | ||
1369 | #define CS4382_VCA4 0x10 | ||
1370 | #define CS4382_VCB4 0x11 | ||
1371 | #define CS4382_CREV 0x12 | ||
1372 | |||
1373 | /* I2C status */ | ||
1374 | #define STATE_LOCKED 0x00 | ||
1375 | #define STATE_UNLOCKED 0xAA | ||
1376 | #define DATA_READY 0x800000 /* Used with I2C_IF_STATUS */ | ||
1377 | #define DATA_ABORT 0x10000 /* Used with I2C_IF_STATUS */ | ||
1378 | |||
1379 | #define I2C_STATUS_DCM 0x00000001 | ||
1380 | #define I2C_STATUS_BC 0x00000006 | ||
1381 | #define I2C_STATUS_APD 0x00000008 | ||
1382 | #define I2C_STATUS_AB 0x00010000 | ||
1383 | #define I2C_STATUS_DR 0x00800000 | ||
1384 | |||
1385 | #define I2C_ADDRESS_PTAD 0x0000FFFF | ||
1386 | #define I2C_ADDRESS_SLAD 0x007F0000 | ||
1387 | |||
1388 | struct regs_cs4382 { | ||
1389 | u32 mode_control_1; | ||
1390 | u32 mode_control_2; | ||
1391 | u32 mode_control_3; | ||
1392 | |||
1393 | u32 filter_control; | ||
1394 | u32 invert_control; | ||
1395 | |||
1396 | u32 mix_control_P1; | ||
1397 | u32 vol_control_A1; | ||
1398 | u32 vol_control_B1; | ||
1399 | |||
1400 | u32 mix_control_P2; | ||
1401 | u32 vol_control_A2; | ||
1402 | u32 vol_control_B2; | ||
1403 | |||
1404 | u32 mix_control_P3; | ||
1405 | u32 vol_control_A3; | ||
1406 | u32 vol_control_B3; | ||
1407 | |||
1408 | u32 mix_control_P4; | ||
1409 | u32 vol_control_A4; | ||
1410 | u32 vol_control_B4; | ||
1411 | }; | ||
1412 | |||
1413 | static int hw20k2_i2c_unlock_full_access(struct hw *hw) | ||
1414 | { | ||
1415 | u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] = {0xB3, 0xD4}; | ||
1416 | |||
1417 | /* Send keys for forced BIOS mode */ | ||
1418 | hw_write_20kx(hw, I2C_IF_WLOCK, | ||
1419 | UnlockKeySequence_FLASH_FULLACCESS_MODE[0]); | ||
1420 | hw_write_20kx(hw, I2C_IF_WLOCK, | ||
1421 | UnlockKeySequence_FLASH_FULLACCESS_MODE[1]); | ||
1422 | /* Check whether the chip is unlocked */ | ||
1423 | if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED) | ||
1424 | return 0; | ||
1425 | |||
1426 | return -1; | ||
1427 | } | ||
1428 | |||
1429 | static int hw20k2_i2c_lock_chip(struct hw *hw) | ||
1430 | { | ||
1431 | /* Write twice */ | ||
1432 | hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); | ||
1433 | hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); | ||
1434 | if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED) | ||
1435 | return 0; | ||
1436 | |||
1437 | return -1; | ||
1438 | } | ||
1439 | |||
1440 | static int hw20k2_i2c_init(struct hw *hw, u8 dev_id, u8 addr_size, u8 data_size) | ||
1441 | { | ||
1442 | struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; | ||
1443 | int err; | ||
1444 | unsigned int i2c_status; | ||
1445 | unsigned int i2c_addr; | ||
1446 | |||
1447 | err = hw20k2_i2c_unlock_full_access(hw); | ||
1448 | if (err < 0) | ||
1449 | return err; | ||
1450 | |||
1451 | hw20k2->addr_size = addr_size; | ||
1452 | hw20k2->data_size = data_size; | ||
1453 | hw20k2->dev_id = dev_id; | ||
1454 | |||
1455 | i2c_addr = 0; | ||
1456 | set_field(&i2c_addr, I2C_ADDRESS_SLAD, dev_id); | ||
1457 | |||
1458 | hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr); | ||
1459 | |||
1460 | i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1461 | |||
1462 | set_field(&i2c_status, I2C_STATUS_DCM, 1); /* Direct control mode */ | ||
1463 | |||
1464 | hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); | ||
1465 | |||
1466 | return 0; | ||
1467 | } | ||
1468 | |||
1469 | static int hw20k2_i2c_uninit(struct hw *hw) | ||
1470 | { | ||
1471 | unsigned int i2c_status; | ||
1472 | unsigned int i2c_addr; | ||
1473 | |||
1474 | i2c_addr = 0; | ||
1475 | set_field(&i2c_addr, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ | ||
1476 | |||
1477 | hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr); | ||
1478 | |||
1479 | i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1480 | |||
1481 | set_field(&i2c_status, I2C_STATUS_DCM, 0); /* I2C mode */ | ||
1482 | |||
1483 | hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); | ||
1484 | |||
1485 | return hw20k2_i2c_lock_chip(hw); | ||
1486 | } | ||
1487 | |||
1488 | static int hw20k2_i2c_wait_data_ready(struct hw *hw) | ||
1489 | { | ||
1490 | int i = 0x400000; | ||
1491 | unsigned int ret; | ||
1492 | |||
1493 | do { | ||
1494 | ret = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1495 | } while ((!(ret & DATA_READY)) && --i); | ||
1496 | |||
1497 | return i; | ||
1498 | } | ||
1499 | |||
1500 | static int hw20k2_i2c_read(struct hw *hw, u16 addr, u32 *datap) | ||
1501 | { | ||
1502 | struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; | ||
1503 | unsigned int i2c_status; | ||
1504 | |||
1505 | i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1506 | set_field(&i2c_status, I2C_STATUS_BC, | ||
1507 | (4 == hw20k2->addr_size) ? 0 : hw20k2->addr_size); | ||
1508 | hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); | ||
1509 | if (!hw20k2_i2c_wait_data_ready(hw)) | ||
1510 | return -1; | ||
1511 | |||
1512 | hw_write_20kx(hw, I2C_IF_WDATA, addr); | ||
1513 | if (!hw20k2_i2c_wait_data_ready(hw)) | ||
1514 | return -1; | ||
1515 | |||
1516 | /* Force a read operation */ | ||
1517 | hw_write_20kx(hw, I2C_IF_RDATA, 0); | ||
1518 | if (!hw20k2_i2c_wait_data_ready(hw)) | ||
1519 | return -1; | ||
1520 | |||
1521 | *datap = hw_read_20kx(hw, I2C_IF_RDATA); | ||
1522 | |||
1523 | return 0; | ||
1524 | } | ||
1525 | |||
1526 | static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) | ||
1527 | { | ||
1528 | struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; | ||
1529 | unsigned int i2c_data = (data << (hw20k2->addr_size * 8)) | addr; | ||
1530 | unsigned int i2c_status; | ||
1531 | |||
1532 | i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); | ||
1533 | |||
1534 | set_field(&i2c_status, I2C_STATUS_BC, | ||
1535 | (4 == (hw20k2->addr_size + hw20k2->data_size)) ? | ||
1536 | 0 : (hw20k2->addr_size + hw20k2->data_size)); | ||
1537 | |||
1538 | hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); | ||
1539 | hw20k2_i2c_wait_data_ready(hw); | ||
1540 | /* Dummy write to trigger the write oprtation */ | ||
1541 | hw_write_20kx(hw, I2C_IF_WDATA, 0); | ||
1542 | hw20k2_i2c_wait_data_ready(hw); | ||
1543 | |||
1544 | /* This is the real data */ | ||
1545 | hw_write_20kx(hw, I2C_IF_WDATA, i2c_data); | ||
1546 | hw20k2_i2c_wait_data_ready(hw); | ||
1547 | |||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | static int hw_dac_init(struct hw *hw, const struct dac_conf *info) | ||
1552 | { | ||
1553 | int err; | ||
1554 | u32 data; | ||
1555 | int i; | ||
1556 | struct regs_cs4382 cs_read = {0}; | ||
1557 | struct regs_cs4382 cs_def = { | ||
1558 | 0x00000001, /* Mode Control 1 */ | ||
1559 | 0x00000000, /* Mode Control 2 */ | ||
1560 | 0x00000084, /* Mode Control 3 */ | ||
1561 | 0x00000000, /* Filter Control */ | ||
1562 | 0x00000000, /* Invert Control */ | ||
1563 | 0x00000024, /* Mixing Control Pair 1 */ | ||
1564 | 0x00000000, /* Vol Control A1 */ | ||
1565 | 0x00000000, /* Vol Control B1 */ | ||
1566 | 0x00000024, /* Mixing Control Pair 2 */ | ||
1567 | 0x00000000, /* Vol Control A2 */ | ||
1568 | 0x00000000, /* Vol Control B2 */ | ||
1569 | 0x00000024, /* Mixing Control Pair 3 */ | ||
1570 | 0x00000000, /* Vol Control A3 */ | ||
1571 | 0x00000000, /* Vol Control B3 */ | ||
1572 | 0x00000024, /* Mixing Control Pair 4 */ | ||
1573 | 0x00000000, /* Vol Control A4 */ | ||
1574 | 0x00000000 /* Vol Control B4 */ | ||
1575 | }; | ||
1576 | |||
1577 | /* Set DAC reset bit as output */ | ||
1578 | data = hw_read_20kx(hw, GPIO_CTRL); | ||
1579 | data |= 0x02; | ||
1580 | hw_write_20kx(hw, GPIO_CTRL, data); | ||
1581 | |||
1582 | err = hw20k2_i2c_init(hw, 0x18, 1, 1); | ||
1583 | if (err < 0) | ||
1584 | goto End; | ||
1585 | |||
1586 | for (i = 0; i < 2; i++) { | ||
1587 | /* Reset DAC twice just in-case the chip | ||
1588 | * didn't initialized properly */ | ||
1589 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1590 | /* GPIO data bit 1 */ | ||
1591 | data &= 0xFFFFFFFD; | ||
1592 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1593 | mdelay(10); | ||
1594 | data |= 0x2; | ||
1595 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1596 | mdelay(50); | ||
1597 | |||
1598 | /* Reset the 2nd time */ | ||
1599 | data &= 0xFFFFFFFD; | ||
1600 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1601 | mdelay(10); | ||
1602 | data |= 0x2; | ||
1603 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1604 | mdelay(50); | ||
1605 | |||
1606 | if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1)) | ||
1607 | continue; | ||
1608 | |||
1609 | if (hw20k2_i2c_read(hw, CS4382_MC2, &cs_read.mode_control_2)) | ||
1610 | continue; | ||
1611 | |||
1612 | if (hw20k2_i2c_read(hw, CS4382_MC3, &cs_read.mode_control_3)) | ||
1613 | continue; | ||
1614 | |||
1615 | if (hw20k2_i2c_read(hw, CS4382_FC, &cs_read.filter_control)) | ||
1616 | continue; | ||
1617 | |||
1618 | if (hw20k2_i2c_read(hw, CS4382_IC, &cs_read.invert_control)) | ||
1619 | continue; | ||
1620 | |||
1621 | if (hw20k2_i2c_read(hw, CS4382_XC1, &cs_read.mix_control_P1)) | ||
1622 | continue; | ||
1623 | |||
1624 | if (hw20k2_i2c_read(hw, CS4382_VCA1, &cs_read.vol_control_A1)) | ||
1625 | continue; | ||
1626 | |||
1627 | if (hw20k2_i2c_read(hw, CS4382_VCB1, &cs_read.vol_control_B1)) | ||
1628 | continue; | ||
1629 | |||
1630 | if (hw20k2_i2c_read(hw, CS4382_XC2, &cs_read.mix_control_P2)) | ||
1631 | continue; | ||
1632 | |||
1633 | if (hw20k2_i2c_read(hw, CS4382_VCA2, &cs_read.vol_control_A2)) | ||
1634 | continue; | ||
1635 | |||
1636 | if (hw20k2_i2c_read(hw, CS4382_VCB2, &cs_read.vol_control_B2)) | ||
1637 | continue; | ||
1638 | |||
1639 | if (hw20k2_i2c_read(hw, CS4382_XC3, &cs_read.mix_control_P3)) | ||
1640 | continue; | ||
1641 | |||
1642 | if (hw20k2_i2c_read(hw, CS4382_VCA3, &cs_read.vol_control_A3)) | ||
1643 | continue; | ||
1644 | |||
1645 | if (hw20k2_i2c_read(hw, CS4382_VCB3, &cs_read.vol_control_B3)) | ||
1646 | continue; | ||
1647 | |||
1648 | if (hw20k2_i2c_read(hw, CS4382_XC4, &cs_read.mix_control_P4)) | ||
1649 | continue; | ||
1650 | |||
1651 | if (hw20k2_i2c_read(hw, CS4382_VCA4, &cs_read.vol_control_A4)) | ||
1652 | continue; | ||
1653 | |||
1654 | if (hw20k2_i2c_read(hw, CS4382_VCB4, &cs_read.vol_control_B4)) | ||
1655 | continue; | ||
1656 | |||
1657 | if (memcmp(&cs_read, &cs_def, sizeof(cs_read))) | ||
1658 | continue; | ||
1659 | else | ||
1660 | break; | ||
1661 | } | ||
1662 | |||
1663 | if (i >= 2) | ||
1664 | goto End; | ||
1665 | |||
1666 | /* Note: Every I2C write must have some delay. | ||
1667 | * This is not a requirement but the delay works here... */ | ||
1668 | hw20k2_i2c_write(hw, CS4382_MC1, 0x80); | ||
1669 | hw20k2_i2c_write(hw, CS4382_MC2, 0x10); | ||
1670 | if (1 == info->msr) { | ||
1671 | hw20k2_i2c_write(hw, CS4382_XC1, 0x24); | ||
1672 | hw20k2_i2c_write(hw, CS4382_XC2, 0x24); | ||
1673 | hw20k2_i2c_write(hw, CS4382_XC3, 0x24); | ||
1674 | hw20k2_i2c_write(hw, CS4382_XC4, 0x24); | ||
1675 | } else if (2 == info->msr) { | ||
1676 | hw20k2_i2c_write(hw, CS4382_XC1, 0x25); | ||
1677 | hw20k2_i2c_write(hw, CS4382_XC2, 0x25); | ||
1678 | hw20k2_i2c_write(hw, CS4382_XC3, 0x25); | ||
1679 | hw20k2_i2c_write(hw, CS4382_XC4, 0x25); | ||
1680 | } else { | ||
1681 | hw20k2_i2c_write(hw, CS4382_XC1, 0x26); | ||
1682 | hw20k2_i2c_write(hw, CS4382_XC2, 0x26); | ||
1683 | hw20k2_i2c_write(hw, CS4382_XC3, 0x26); | ||
1684 | hw20k2_i2c_write(hw, CS4382_XC4, 0x26); | ||
1685 | } | ||
1686 | |||
1687 | return 0; | ||
1688 | End: | ||
1689 | |||
1690 | hw20k2_i2c_uninit(hw); | ||
1691 | return -1; | ||
1692 | } | ||
1693 | |||
1694 | /* ADC operations */ | ||
1695 | #define MAKE_WM8775_ADDR(addr, data) (u32)(((addr<<1)&0xFE)|((data>>8)&0x1)) | ||
1696 | #define MAKE_WM8775_DATA(data) (u32)(data&0xFF) | ||
1697 | |||
1698 | #define WM8775_IC 0x0B | ||
1699 | #define WM8775_MMC 0x0C | ||
1700 | #define WM8775_AADCL 0x0E | ||
1701 | #define WM8775_AADCR 0x0F | ||
1702 | #define WM8775_ADCMC 0x15 | ||
1703 | #define WM8775_RESET 0x17 | ||
1704 | |||
1705 | static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) | ||
1706 | { | ||
1707 | u32 data; | ||
1708 | |||
1709 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1710 | switch (type) { | ||
1711 | case ADC_MICIN: | ||
1712 | data = (data & (0x1 << 14)) ? 1 : 0; | ||
1713 | break; | ||
1714 | case ADC_LINEIN: | ||
1715 | data = (data & (0x1 << 14)) ? 0 : 1; | ||
1716 | break; | ||
1717 | default: | ||
1718 | data = 0; | ||
1719 | } | ||
1720 | return data; | ||
1721 | } | ||
1722 | |||
1723 | static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) | ||
1724 | { | ||
1725 | u32 data; | ||
1726 | |||
1727 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1728 | switch (type) { | ||
1729 | case ADC_MICIN: | ||
1730 | data |= (0x1 << 14); | ||
1731 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1732 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), | ||
1733 | MAKE_WM8775_DATA(0x101)); /* Mic-in */ | ||
1734 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), | ||
1735 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1736 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), | ||
1737 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1738 | break; | ||
1739 | case ADC_LINEIN: | ||
1740 | data &= ~(0x1 << 14); | ||
1741 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1742 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), | ||
1743 | MAKE_WM8775_DATA(0x102)); /* Line-in */ | ||
1744 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), | ||
1745 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1746 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), | ||
1747 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1748 | break; | ||
1749 | default: | ||
1750 | break; | ||
1751 | } | ||
1752 | |||
1753 | return 0; | ||
1754 | } | ||
1755 | |||
1756 | static int hw_adc_init(struct hw *hw, const struct adc_conf *info) | ||
1757 | { | ||
1758 | int err; | ||
1759 | u32 mux = 2, data, ctl; | ||
1760 | |||
1761 | /* Set ADC reset bit as output */ | ||
1762 | data = hw_read_20kx(hw, GPIO_CTRL); | ||
1763 | data |= (0x1 << 15); | ||
1764 | hw_write_20kx(hw, GPIO_CTRL, data); | ||
1765 | |||
1766 | /* Initialize I2C */ | ||
1767 | err = hw20k2_i2c_init(hw, 0x1A, 1, 1); | ||
1768 | if (err < 0) { | ||
1769 | printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n"); | ||
1770 | goto error; | ||
1771 | } | ||
1772 | |||
1773 | /* Make ADC in normal operation */ | ||
1774 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1775 | data &= ~(0x1 << 15); | ||
1776 | mdelay(10); | ||
1777 | data |= (0x1 << 15); | ||
1778 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1779 | mdelay(50); | ||
1780 | |||
1781 | /* Set the master mode (256fs) */ | ||
1782 | if (1 == info->msr) { | ||
1783 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), | ||
1784 | MAKE_WM8775_DATA(0x02)); | ||
1785 | } else if (2 == info->msr) { | ||
1786 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), | ||
1787 | MAKE_WM8775_DATA(0x0A)); | ||
1788 | } else { | ||
1789 | printk(KERN_ALERT "ctxfi: Invalid master sampling " | ||
1790 | "rate (msr %d)!!!\n", info->msr); | ||
1791 | err = -EINVAL; | ||
1792 | goto error; | ||
1793 | } | ||
1794 | |||
1795 | /* Configure GPIO bit 14 change to line-in/mic-in */ | ||
1796 | ctl = hw_read_20kx(hw, GPIO_CTRL); | ||
1797 | ctl |= 0x1 << 14; | ||
1798 | hw_write_20kx(hw, GPIO_CTRL, ctl); | ||
1799 | |||
1800 | /* Check using Mic-in or Line-in */ | ||
1801 | data = hw_read_20kx(hw, GPIO_DATA); | ||
1802 | |||
1803 | if (mux == 1) { | ||
1804 | /* Configures GPIO data to select Mic-in */ | ||
1805 | data |= 0x1 << 14; | ||
1806 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1807 | |||
1808 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), | ||
1809 | MAKE_WM8775_DATA(0x101)); /* Mic-in */ | ||
1810 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), | ||
1811 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1812 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), | ||
1813 | MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ | ||
1814 | } else if (mux == 2) { | ||
1815 | /* Configures GPIO data to select Line-in */ | ||
1816 | data &= ~(0x1 << 14); | ||
1817 | hw_write_20kx(hw, GPIO_DATA, data); | ||
1818 | |||
1819 | /* Setup ADC */ | ||
1820 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), | ||
1821 | MAKE_WM8775_DATA(0x102)); /* Line-in */ | ||
1822 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), | ||
1823 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1824 | hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), | ||
1825 | MAKE_WM8775_DATA(0xCF)); /* No boost */ | ||
1826 | } else { | ||
1827 | printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); | ||
1828 | err = -EINVAL; | ||
1829 | goto error; | ||
1830 | } | ||
1831 | |||
1832 | return 0; | ||
1833 | |||
1834 | error: | ||
1835 | hw20k2_i2c_uninit(hw); | ||
1836 | return err; | ||
1837 | } | ||
1838 | |||
1839 | static int hw_have_digit_io_switch(struct hw *hw) | ||
1840 | { | ||
1841 | return 0; | ||
1842 | } | ||
1843 | |||
1844 | static int hw_card_start(struct hw *hw) | ||
1845 | { | ||
1846 | int err = 0; | ||
1847 | struct pci_dev *pci = hw->pci; | ||
1848 | unsigned int gctl; | ||
1849 | |||
1850 | err = pci_enable_device(pci); | ||
1851 | if (err < 0) | ||
1852 | return err; | ||
1853 | |||
1854 | /* Set DMA transfer mask */ | ||
1855 | if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || | ||
1856 | pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { | ||
1857 | printk(KERN_ERR "ctxfi: architecture does not support PCI " | ||
1858 | "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK); | ||
1859 | err = -ENXIO; | ||
1860 | goto error1; | ||
1861 | } | ||
1862 | |||
1863 | err = pci_request_regions(pci, "XFi"); | ||
1864 | if (err < 0) | ||
1865 | goto error1; | ||
1866 | |||
1867 | hw->io_base = pci_resource_start(hw->pci, 2); | ||
1868 | hw->mem_base = (unsigned long)ioremap(hw->io_base, | ||
1869 | pci_resource_len(hw->pci, 2)); | ||
1870 | if (NULL == (void *)hw->mem_base) { | ||
1871 | err = -ENOENT; | ||
1872 | goto error2; | ||
1873 | } | ||
1874 | |||
1875 | /* Switch to 20k2 mode from UAA mode. */ | ||
1876 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1877 | set_field(&gctl, GCTL_UAA, 0); | ||
1878 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1879 | |||
1880 | /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, | ||
1881 | atc->chip_details->nm_card, hw))) { | ||
1882 | goto error3; | ||
1883 | } | ||
1884 | hw->irq = pci->irq; | ||
1885 | */ | ||
1886 | |||
1887 | pci_set_master(pci); | ||
1888 | |||
1889 | return 0; | ||
1890 | |||
1891 | /*error3: | ||
1892 | iounmap((void *)hw->mem_base); | ||
1893 | hw->mem_base = (unsigned long)NULL;*/ | ||
1894 | error2: | ||
1895 | pci_release_regions(pci); | ||
1896 | hw->io_base = 0; | ||
1897 | error1: | ||
1898 | pci_disable_device(pci); | ||
1899 | return err; | ||
1900 | } | ||
1901 | |||
1902 | static int hw_card_stop(struct hw *hw) | ||
1903 | { | ||
1904 | /* TODO: Disable interrupt and so on... */ | ||
1905 | return 0; | ||
1906 | } | ||
1907 | |||
1908 | static int hw_card_shutdown(struct hw *hw) | ||
1909 | { | ||
1910 | if (hw->irq >= 0) | ||
1911 | free_irq(hw->irq, hw); | ||
1912 | |||
1913 | hw->irq = -1; | ||
1914 | |||
1915 | if (NULL != ((void *)hw->mem_base)) | ||
1916 | iounmap((void *)hw->mem_base); | ||
1917 | |||
1918 | hw->mem_base = (unsigned long)NULL; | ||
1919 | |||
1920 | if (hw->io_base) | ||
1921 | pci_release_regions(hw->pci); | ||
1922 | |||
1923 | hw->io_base = 0; | ||
1924 | |||
1925 | pci_disable_device(hw->pci); | ||
1926 | |||
1927 | return 0; | ||
1928 | } | ||
1929 | |||
1930 | static int hw_card_init(struct hw *hw, struct card_conf *info) | ||
1931 | { | ||
1932 | int err; | ||
1933 | unsigned int gctl; | ||
1934 | u32 data = 0; | ||
1935 | struct dac_conf dac_info = {0}; | ||
1936 | struct adc_conf adc_info = {0}; | ||
1937 | struct daio_conf daio_info = {0}; | ||
1938 | struct trn_conf trn_info = {0}; | ||
1939 | |||
1940 | /* Get PCI io port/memory base address and | ||
1941 | * do 20kx core switch if needed. */ | ||
1942 | if (!hw->io_base) { | ||
1943 | err = hw_card_start(hw); | ||
1944 | if (err) | ||
1945 | return err; | ||
1946 | } | ||
1947 | |||
1948 | /* PLL init */ | ||
1949 | err = hw_pll_init(hw, info->rsr); | ||
1950 | if (err < 0) | ||
1951 | return err; | ||
1952 | |||
1953 | /* kick off auto-init */ | ||
1954 | err = hw_auto_init(hw); | ||
1955 | if (err < 0) | ||
1956 | return err; | ||
1957 | |||
1958 | gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); | ||
1959 | set_field(&gctl, GCTL_DBP, 1); | ||
1960 | set_field(&gctl, GCTL_TBP, 1); | ||
1961 | set_field(&gctl, GCTL_FBP, 1); | ||
1962 | set_field(&gctl, GCTL_DPC, 0); | ||
1963 | hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||
1964 | |||
1965 | /* Reset all global pending interrupts */ | ||
1966 | hw_write_20kx(hw, INTERRUPT_GIE, 0); | ||
1967 | /* Reset all SRC pending interrupts */ | ||
1968 | hw_write_20kx(hw, SRC_IP, 0); | ||
1969 | |||
1970 | /* TODO: detect the card ID and configure GPIO accordingly. */ | ||
1971 | /* Configures GPIO (0xD802 0x98028) */ | ||
1972 | /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ | ||
1973 | /* Configures GPIO (SB0880) */ | ||
1974 | /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ | ||
1975 | hw_write_20kx(hw, GPIO_CTRL, 0xD802); | ||
1976 | |||
1977 | /* Enable audio ring */ | ||
1978 | hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); | ||
1979 | |||
1980 | trn_info.vm_pgt_phys = info->vm_pgt_phys; | ||
1981 | err = hw_trn_init(hw, &trn_info); | ||
1982 | if (err < 0) | ||
1983 | return err; | ||
1984 | |||
1985 | daio_info.msr = info->msr; | ||
1986 | err = hw_daio_init(hw, &daio_info); | ||
1987 | if (err < 0) | ||
1988 | return err; | ||
1989 | |||
1990 | dac_info.msr = info->msr; | ||
1991 | err = hw_dac_init(hw, &dac_info); | ||
1992 | if (err < 0) | ||
1993 | return err; | ||
1994 | |||
1995 | adc_info.msr = info->msr; | ||
1996 | adc_info.input = ADC_LINEIN; | ||
1997 | adc_info.mic20db = 0; | ||
1998 | err = hw_adc_init(hw, &adc_info); | ||
1999 | if (err < 0) | ||
2000 | return err; | ||
2001 | |||
2002 | data = hw_read_20kx(hw, SRC_MCTL); | ||
2003 | data |= 0x1; /* Enables input from the audio ring */ | ||
2004 | hw_write_20kx(hw, SRC_MCTL, data); | ||
2005 | |||
2006 | return 0; | ||
2007 | } | ||
2008 | |||
2009 | static u32 hw_read_20kx(struct hw *hw, u32 reg) | ||
2010 | { | ||
2011 | return readl((void *)(hw->mem_base + reg)); | ||
2012 | } | ||
2013 | |||
2014 | static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) | ||
2015 | { | ||
2016 | writel(data, (void *)(hw->mem_base + reg)); | ||
2017 | } | ||
2018 | |||
2019 | static struct hw ct20k2_preset __devinitdata = { | ||
2020 | .irq = -1, | ||
2021 | |||
2022 | .card_init = hw_card_init, | ||
2023 | .card_stop = hw_card_stop, | ||
2024 | .pll_init = hw_pll_init, | ||
2025 | .is_adc_source_selected = hw_is_adc_input_selected, | ||
2026 | .select_adc_source = hw_adc_input_select, | ||
2027 | .have_digit_io_switch = hw_have_digit_io_switch, | ||
2028 | |||
2029 | .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk, | ||
2030 | .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk, | ||
2031 | .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk, | ||
2032 | .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk, | ||
2033 | .src_set_state = src_set_state, | ||
2034 | .src_set_bm = src_set_bm, | ||
2035 | .src_set_rsr = src_set_rsr, | ||
2036 | .src_set_sf = src_set_sf, | ||
2037 | .src_set_wr = src_set_wr, | ||
2038 | .src_set_pm = src_set_pm, | ||
2039 | .src_set_rom = src_set_rom, | ||
2040 | .src_set_vo = src_set_vo, | ||
2041 | .src_set_st = src_set_st, | ||
2042 | .src_set_ie = src_set_ie, | ||
2043 | .src_set_ilsz = src_set_ilsz, | ||
2044 | .src_set_bp = src_set_bp, | ||
2045 | .src_set_cisz = src_set_cisz, | ||
2046 | .src_set_ca = src_set_ca, | ||
2047 | .src_set_sa = src_set_sa, | ||
2048 | .src_set_la = src_set_la, | ||
2049 | .src_set_pitch = src_set_pitch, | ||
2050 | .src_set_dirty = src_set_dirty, | ||
2051 | .src_set_clear_zbufs = src_set_clear_zbufs, | ||
2052 | .src_set_dirty_all = src_set_dirty_all, | ||
2053 | .src_commit_write = src_commit_write, | ||
2054 | .src_get_ca = src_get_ca, | ||
2055 | .src_get_dirty = src_get_dirty, | ||
2056 | .src_dirty_conj_mask = src_dirty_conj_mask, | ||
2057 | .src_mgr_enbs_src = src_mgr_enbs_src, | ||
2058 | .src_mgr_enb_src = src_mgr_enb_src, | ||
2059 | .src_mgr_dsb_src = src_mgr_dsb_src, | ||
2060 | .src_mgr_commit_write = src_mgr_commit_write, | ||
2061 | |||
2062 | .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk, | ||
2063 | .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk, | ||
2064 | .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc, | ||
2065 | .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser, | ||
2066 | .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt, | ||
2067 | .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr, | ||
2068 | .srcimp_mgr_commit_write = srcimp_mgr_commit_write, | ||
2069 | |||
2070 | .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk, | ||
2071 | .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk, | ||
2072 | .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk, | ||
2073 | .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk, | ||
2074 | .amixer_set_mode = amixer_set_mode, | ||
2075 | .amixer_set_iv = amixer_set_iv, | ||
2076 | .amixer_set_x = amixer_set_x, | ||
2077 | .amixer_set_y = amixer_set_y, | ||
2078 | .amixer_set_sadr = amixer_set_sadr, | ||
2079 | .amixer_set_se = amixer_set_se, | ||
2080 | .amixer_set_dirty = amixer_set_dirty, | ||
2081 | .amixer_set_dirty_all = amixer_set_dirty_all, | ||
2082 | .amixer_commit_write = amixer_commit_write, | ||
2083 | .amixer_get_y = amixer_get_y, | ||
2084 | .amixer_get_dirty = amixer_get_dirty, | ||
2085 | |||
2086 | .dai_get_ctrl_blk = dai_get_ctrl_blk, | ||
2087 | .dai_put_ctrl_blk = dai_put_ctrl_blk, | ||
2088 | .dai_srt_set_srco = dai_srt_set_srco, | ||
2089 | .dai_srt_set_srcm = dai_srt_set_srcm, | ||
2090 | .dai_srt_set_rsr = dai_srt_set_rsr, | ||
2091 | .dai_srt_set_drat = dai_srt_set_drat, | ||
2092 | .dai_srt_set_ec = dai_srt_set_ec, | ||
2093 | .dai_srt_set_et = dai_srt_set_et, | ||
2094 | .dai_commit_write = dai_commit_write, | ||
2095 | |||
2096 | .dao_get_ctrl_blk = dao_get_ctrl_blk, | ||
2097 | .dao_put_ctrl_blk = dao_put_ctrl_blk, | ||
2098 | .dao_set_spos = dao_set_spos, | ||
2099 | .dao_commit_write = dao_commit_write, | ||
2100 | .dao_get_spos = dao_get_spos, | ||
2101 | |||
2102 | .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk, | ||
2103 | .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk, | ||
2104 | .daio_mgr_enb_dai = daio_mgr_enb_dai, | ||
2105 | .daio_mgr_dsb_dai = daio_mgr_dsb_dai, | ||
2106 | .daio_mgr_enb_dao = daio_mgr_enb_dao, | ||
2107 | .daio_mgr_dsb_dao = daio_mgr_dsb_dao, | ||
2108 | .daio_mgr_dao_init = daio_mgr_dao_init, | ||
2109 | .daio_mgr_set_imaparc = daio_mgr_set_imaparc, | ||
2110 | .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, | ||
2111 | .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, | ||
2112 | .daio_mgr_commit_write = daio_mgr_commit_write, | ||
2113 | }; | ||
2114 | |||
2115 | int __devinit create_20k2_hw_obj(struct hw **rhw) | ||
2116 | { | ||
2117 | struct hw20k2 *hw20k2; | ||
2118 | |||
2119 | *rhw = NULL; | ||
2120 | hw20k2 = kzalloc(sizeof(*hw20k2), GFP_KERNEL); | ||
2121 | if (!hw20k2) | ||
2122 | return -ENOMEM; | ||
2123 | |||
2124 | hw20k2->hw = ct20k2_preset; | ||
2125 | *rhw = &hw20k2->hw; | ||
2126 | |||
2127 | return 0; | ||
2128 | } | ||
2129 | |||
2130 | int destroy_20k2_hw_obj(struct hw *hw) | ||
2131 | { | ||
2132 | if (hw->io_base) | ||
2133 | hw_card_shutdown(hw); | ||
2134 | |||
2135 | kfree(hw); | ||
2136 | return 0; | ||
2137 | } | ||
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..0b73368a4df6 --- /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; | ||
103 | struct list_head *pos; | ||
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..666722d9de41 --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.c | |||
@@ -0,0 +1,1123 @@ | |||
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 <linux/slab.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/control.h> | ||
24 | #include <sound/asoundef.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/tlv.h> | ||
27 | |||
28 | enum CT_SUM_CTL { | ||
29 | SUM_IN_F, | ||
30 | SUM_IN_R, | ||
31 | SUM_IN_C, | ||
32 | SUM_IN_S, | ||
33 | SUM_IN_F_C, | ||
34 | |||
35 | NUM_CT_SUMS | ||
36 | }; | ||
37 | |||
38 | enum CT_AMIXER_CTL { | ||
39 | /* volume control mixers */ | ||
40 | AMIXER_MASTER_F, | ||
41 | AMIXER_MASTER_R, | ||
42 | AMIXER_MASTER_C, | ||
43 | AMIXER_MASTER_S, | ||
44 | AMIXER_PCM_F, | ||
45 | AMIXER_PCM_R, | ||
46 | AMIXER_PCM_C, | ||
47 | AMIXER_PCM_S, | ||
48 | AMIXER_SPDIFI, | ||
49 | AMIXER_LINEIN, | ||
50 | AMIXER_MIC, | ||
51 | AMIXER_SPDIFO, | ||
52 | AMIXER_WAVE_F, | ||
53 | AMIXER_WAVE_R, | ||
54 | AMIXER_WAVE_C, | ||
55 | AMIXER_WAVE_S, | ||
56 | AMIXER_MASTER_F_C, | ||
57 | AMIXER_PCM_F_C, | ||
58 | AMIXER_SPDIFI_C, | ||
59 | AMIXER_LINEIN_C, | ||
60 | AMIXER_MIC_C, | ||
61 | |||
62 | /* this should always be the last one */ | ||
63 | NUM_CT_AMIXERS | ||
64 | }; | ||
65 | |||
66 | enum CTALSA_MIXER_CTL { | ||
67 | /* volume control mixers */ | ||
68 | MIXER_MASTER_P, | ||
69 | MIXER_PCM_P, | ||
70 | MIXER_LINEIN_P, | ||
71 | MIXER_MIC_P, | ||
72 | MIXER_SPDIFI_P, | ||
73 | MIXER_SPDIFO_P, | ||
74 | MIXER_WAVEF_P, | ||
75 | MIXER_WAVER_P, | ||
76 | MIXER_WAVEC_P, | ||
77 | MIXER_WAVES_P, | ||
78 | MIXER_MASTER_C, | ||
79 | MIXER_PCM_C, | ||
80 | MIXER_LINEIN_C, | ||
81 | MIXER_MIC_C, | ||
82 | MIXER_SPDIFI_C, | ||
83 | |||
84 | /* switch control mixers */ | ||
85 | MIXER_PCM_C_S, | ||
86 | MIXER_LINEIN_C_S, | ||
87 | MIXER_MIC_C_S, | ||
88 | MIXER_SPDIFI_C_S, | ||
89 | MIXER_LINEIN_P_S, | ||
90 | MIXER_SPDIFO_P_S, | ||
91 | MIXER_SPDIFI_P_S, | ||
92 | MIXER_WAVEF_P_S, | ||
93 | MIXER_WAVER_P_S, | ||
94 | MIXER_WAVEC_P_S, | ||
95 | MIXER_WAVES_P_S, | ||
96 | MIXER_DIGITAL_IO_S, | ||
97 | MIXER_IEC958_MASK, | ||
98 | MIXER_IEC958_DEFAULT, | ||
99 | MIXER_IEC958_STREAM, | ||
100 | |||
101 | /* this should always be the last one */ | ||
102 | NUM_CTALSA_MIXERS | ||
103 | }; | ||
104 | |||
105 | #define VOL_MIXER_START MIXER_MASTER_P | ||
106 | #define VOL_MIXER_END MIXER_SPDIFI_C | ||
107 | #define VOL_MIXER_NUM (VOL_MIXER_END - VOL_MIXER_START + 1) | ||
108 | #define SWH_MIXER_START MIXER_PCM_C_S | ||
109 | #define SWH_MIXER_END MIXER_DIGITAL_IO_S | ||
110 | #define SWH_CAPTURE_START MIXER_PCM_C_S | ||
111 | #define SWH_CAPTURE_END MIXER_SPDIFI_C_S | ||
112 | |||
113 | #define CHN_NUM 2 | ||
114 | |||
115 | struct ct_kcontrol_init { | ||
116 | unsigned char ctl; | ||
117 | char *name; | ||
118 | }; | ||
119 | |||
120 | static struct ct_kcontrol_init | ||
121 | ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { | ||
122 | [MIXER_MASTER_P] = { | ||
123 | .ctl = 1, | ||
124 | .name = "Master Playback Volume", | ||
125 | }, | ||
126 | [MIXER_MASTER_C] = { | ||
127 | .ctl = 1, | ||
128 | .name = "Master Capture Volume", | ||
129 | }, | ||
130 | [MIXER_PCM_P] = { | ||
131 | .ctl = 1, | ||
132 | .name = "PCM Playback Volume", | ||
133 | }, | ||
134 | [MIXER_PCM_C] = { | ||
135 | .ctl = 1, | ||
136 | .name = "PCM Capture Volume", | ||
137 | }, | ||
138 | [MIXER_LINEIN_P] = { | ||
139 | .ctl = 1, | ||
140 | .name = "Line-in Playback Volume", | ||
141 | }, | ||
142 | [MIXER_LINEIN_C] = { | ||
143 | .ctl = 1, | ||
144 | .name = "Line-in Capture Volume", | ||
145 | }, | ||
146 | [MIXER_MIC_P] = { | ||
147 | .ctl = 1, | ||
148 | .name = "Mic Playback Volume", | ||
149 | }, | ||
150 | [MIXER_MIC_C] = { | ||
151 | .ctl = 1, | ||
152 | .name = "Mic Capture Volume", | ||
153 | }, | ||
154 | [MIXER_SPDIFI_P] = { | ||
155 | .ctl = 1, | ||
156 | .name = "S/PDIF-in Playback Volume", | ||
157 | }, | ||
158 | [MIXER_SPDIFI_C] = { | ||
159 | .ctl = 1, | ||
160 | .name = "S/PDIF-in Capture Volume", | ||
161 | }, | ||
162 | [MIXER_SPDIFO_P] = { | ||
163 | .ctl = 1, | ||
164 | .name = "S/PDIF-out Playback Volume", | ||
165 | }, | ||
166 | [MIXER_WAVEF_P] = { | ||
167 | .ctl = 1, | ||
168 | .name = "Front Playback Volume", | ||
169 | }, | ||
170 | [MIXER_WAVES_P] = { | ||
171 | .ctl = 1, | ||
172 | .name = "Side Playback Volume", | ||
173 | }, | ||
174 | [MIXER_WAVEC_P] = { | ||
175 | .ctl = 1, | ||
176 | .name = "Center/LFE Playback Volume", | ||
177 | }, | ||
178 | [MIXER_WAVER_P] = { | ||
179 | .ctl = 1, | ||
180 | .name = "Surround Playback Volume", | ||
181 | }, | ||
182 | |||
183 | [MIXER_PCM_C_S] = { | ||
184 | .ctl = 1, | ||
185 | .name = "PCM Capture Switch", | ||
186 | }, | ||
187 | [MIXER_LINEIN_C_S] = { | ||
188 | .ctl = 1, | ||
189 | .name = "Line-in Capture Switch", | ||
190 | }, | ||
191 | [MIXER_MIC_C_S] = { | ||
192 | .ctl = 1, | ||
193 | .name = "Mic Capture Switch", | ||
194 | }, | ||
195 | [MIXER_SPDIFI_C_S] = { | ||
196 | .ctl = 1, | ||
197 | .name = "S/PDIF-in Capture Switch", | ||
198 | }, | ||
199 | [MIXER_LINEIN_P_S] = { | ||
200 | .ctl = 1, | ||
201 | .name = "Line-in Playback Switch", | ||
202 | }, | ||
203 | [MIXER_SPDIFO_P_S] = { | ||
204 | .ctl = 1, | ||
205 | .name = "S/PDIF-out Playback Switch", | ||
206 | }, | ||
207 | [MIXER_SPDIFI_P_S] = { | ||
208 | .ctl = 1, | ||
209 | .name = "S/PDIF-in Playback Switch", | ||
210 | }, | ||
211 | [MIXER_WAVEF_P_S] = { | ||
212 | .ctl = 1, | ||
213 | .name = "Front Playback Switch", | ||
214 | }, | ||
215 | [MIXER_WAVES_P_S] = { | ||
216 | .ctl = 1, | ||
217 | .name = "Side Playback Switch", | ||
218 | }, | ||
219 | [MIXER_WAVEC_P_S] = { | ||
220 | .ctl = 1, | ||
221 | .name = "Center/LFE Playback Switch", | ||
222 | }, | ||
223 | [MIXER_WAVER_P_S] = { | ||
224 | .ctl = 1, | ||
225 | .name = "Surround Playback Switch", | ||
226 | }, | ||
227 | [MIXER_DIGITAL_IO_S] = { | ||
228 | .ctl = 0, | ||
229 | .name = "Digit-IO Playback Switch", | ||
230 | }, | ||
231 | }; | ||
232 | |||
233 | static void | ||
234 | ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); | ||
235 | |||
236 | static void | ||
237 | ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); | ||
238 | |||
239 | static struct snd_kcontrol *kctls[2] = {NULL}; | ||
240 | |||
241 | static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) | ||
242 | { | ||
243 | switch (alsa_index) { | ||
244 | case MIXER_MASTER_P: return AMIXER_MASTER_F; | ||
245 | case MIXER_MASTER_C: return AMIXER_MASTER_F_C; | ||
246 | case MIXER_PCM_P: return AMIXER_PCM_F; | ||
247 | case MIXER_PCM_C: | ||
248 | case MIXER_PCM_C_S: return AMIXER_PCM_F_C; | ||
249 | case MIXER_LINEIN_P: return AMIXER_LINEIN; | ||
250 | case MIXER_LINEIN_C: | ||
251 | case MIXER_LINEIN_C_S: return AMIXER_LINEIN_C; | ||
252 | case MIXER_MIC_P: return AMIXER_MIC; | ||
253 | case MIXER_MIC_C: | ||
254 | case MIXER_MIC_C_S: return AMIXER_MIC_C; | ||
255 | case MIXER_SPDIFI_P: return AMIXER_SPDIFI; | ||
256 | case MIXER_SPDIFI_C: | ||
257 | case MIXER_SPDIFI_C_S: return AMIXER_SPDIFI_C; | ||
258 | case MIXER_SPDIFO_P: return AMIXER_SPDIFO; | ||
259 | case MIXER_WAVEF_P: return AMIXER_WAVE_F; | ||
260 | case MIXER_WAVES_P: return AMIXER_WAVE_S; | ||
261 | case MIXER_WAVEC_P: return AMIXER_WAVE_C; | ||
262 | case MIXER_WAVER_P: return AMIXER_WAVE_R; | ||
263 | default: return NUM_CT_AMIXERS; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index) | ||
268 | { | ||
269 | switch (index) { | ||
270 | case AMIXER_MASTER_F: return AMIXER_MASTER_F_C; | ||
271 | case AMIXER_PCM_F: return AMIXER_PCM_F_C; | ||
272 | case AMIXER_SPDIFI: return AMIXER_SPDIFI_C; | ||
273 | case AMIXER_LINEIN: return AMIXER_LINEIN_C; | ||
274 | case AMIXER_MIC: return AMIXER_MIC_C; | ||
275 | default: return NUM_CT_AMIXERS; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | static unsigned char | ||
280 | get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type) | ||
281 | { | ||
282 | return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START))) | ||
283 | ? 1 : 0; | ||
284 | } | ||
285 | |||
286 | static void | ||
287 | set_switch_state(struct ct_mixer *mixer, | ||
288 | enum CTALSA_MIXER_CTL type, unsigned char state) | ||
289 | { | ||
290 | if (state) | ||
291 | mixer->switch_state |= (0x1 << (type - SWH_MIXER_START)); | ||
292 | else | ||
293 | mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START)); | ||
294 | } | ||
295 | |||
296 | #if 0 /* not used */ | ||
297 | /* Map integer value ranging from 0 to 65535 to 14-bit float value ranging | ||
298 | * from 2^-6 to (1+1023/1024) */ | ||
299 | static unsigned int uint16_to_float14(unsigned int x) | ||
300 | { | ||
301 | unsigned int i; | ||
302 | |||
303 | if (x < 17) | ||
304 | return 0; | ||
305 | |||
306 | x *= 2031; | ||
307 | x /= 65535; | ||
308 | x += 16; | ||
309 | |||
310 | /* i <= 6 */ | ||
311 | for (i = 0; !(x & 0x400); i++) | ||
312 | x <<= 1; | ||
313 | |||
314 | x = (((7 - i) & 0x7) << 10) | (x & 0x3ff); | ||
315 | |||
316 | return x; | ||
317 | } | ||
318 | |||
319 | static unsigned int float14_to_uint16(unsigned int x) | ||
320 | { | ||
321 | unsigned int e; | ||
322 | |||
323 | if (!x) | ||
324 | return x; | ||
325 | |||
326 | e = (x >> 10) & 0x7; | ||
327 | x &= 0x3ff; | ||
328 | x += 1024; | ||
329 | x >>= (7 - e); | ||
330 | x -= 16; | ||
331 | x *= 65535; | ||
332 | x /= 2031; | ||
333 | |||
334 | return x; | ||
335 | } | ||
336 | #endif /* not used */ | ||
337 | |||
338 | #define VOL_SCALE 0x1c | ||
339 | #define VOL_MAX 0x100 | ||
340 | |||
341 | static const DECLARE_TLV_DB_SCALE(ct_vol_db_scale, -6400, 25, 1); | ||
342 | |||
343 | static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, | ||
344 | struct snd_ctl_elem_info *uinfo) | ||
345 | { | ||
346 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
347 | uinfo->count = 2; | ||
348 | uinfo->value.integer.min = 0; | ||
349 | uinfo->value.integer.max = VOL_MAX; | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol, | ||
355 | struct snd_ctl_elem_value *ucontrol) | ||
356 | { | ||
357 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
358 | enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); | ||
359 | struct amixer *amixer; | ||
360 | int i, val; | ||
361 | |||
362 | for (i = 0; i < 2; i++) { | ||
363 | amixer = ((struct ct_mixer *)atc->mixer)-> | ||
364 | amixers[type*CHN_NUM+i]; | ||
365 | val = amixer->ops->get_scale(amixer) / VOL_SCALE; | ||
366 | if (val < 0) | ||
367 | val = 0; | ||
368 | else if (val > VOL_MAX) | ||
369 | val = VOL_MAX; | ||
370 | ucontrol->value.integer.value[i] = val; | ||
371 | } | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, | ||
377 | struct snd_ctl_elem_value *ucontrol) | ||
378 | { | ||
379 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
380 | struct ct_mixer *mixer = atc->mixer; | ||
381 | enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); | ||
382 | struct amixer *amixer; | ||
383 | int i, j, val, oval, change = 0; | ||
384 | |||
385 | for (i = 0; i < 2; i++) { | ||
386 | val = ucontrol->value.integer.value[i]; | ||
387 | if (val < 0) | ||
388 | val = 0; | ||
389 | else if (val > VOL_MAX) | ||
390 | val = VOL_MAX; | ||
391 | val *= VOL_SCALE; | ||
392 | amixer = mixer->amixers[type*CHN_NUM+i]; | ||
393 | oval = amixer->ops->get_scale(amixer); | ||
394 | if (val != oval) { | ||
395 | amixer->ops->set_scale(amixer, val); | ||
396 | amixer->ops->commit_write(amixer); | ||
397 | change = 1; | ||
398 | /* Synchronize Master/PCM playback AMIXERs. */ | ||
399 | if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) { | ||
400 | for (j = 1; j < 4; j++) { | ||
401 | amixer = mixer-> | ||
402 | amixers[(type+j)*CHN_NUM+i]; | ||
403 | amixer->ops->set_scale(amixer, val); | ||
404 | amixer->ops->commit_write(amixer); | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | |||
410 | return change; | ||
411 | } | ||
412 | |||
413 | static struct snd_kcontrol_new vol_ctl = { | ||
414 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
415 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
416 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
417 | .info = ct_alsa_mix_volume_info, | ||
418 | .get = ct_alsa_mix_volume_get, | ||
419 | .put = ct_alsa_mix_volume_put, | ||
420 | .tlv = { .p = ct_vol_db_scale }, | ||
421 | }; | ||
422 | |||
423 | static void | ||
424 | do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) | ||
425 | { | ||
426 | |||
427 | if (MIXER_LINEIN_C_S == type) { | ||
428 | atc->select_line_in(atc); | ||
429 | set_switch_state(atc->mixer, MIXER_MIC_C_S, 0); | ||
430 | snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
431 | &kctls[1]->id); | ||
432 | } else if (MIXER_MIC_C_S == type) { | ||
433 | atc->select_mic_in(atc); | ||
434 | set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0); | ||
435 | snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
436 | &kctls[0]->id); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | static void | ||
441 | do_digit_io_switch(struct ct_atc *atc, int state) | ||
442 | { | ||
443 | struct ct_mixer *mixer = atc->mixer; | ||
444 | |||
445 | if (state) { | ||
446 | atc->select_digit_io(atc); | ||
447 | atc->spdif_out_unmute(atc, | ||
448 | get_switch_state(mixer, MIXER_SPDIFO_P_S)); | ||
449 | atc->spdif_in_unmute(atc, 1); | ||
450 | atc->line_in_unmute(atc, 0); | ||
451 | return; | ||
452 | } | ||
453 | |||
454 | if (get_switch_state(mixer, MIXER_LINEIN_C_S)) | ||
455 | atc->select_line_in(atc); | ||
456 | else if (get_switch_state(mixer, MIXER_MIC_C_S)) | ||
457 | atc->select_mic_in(atc); | ||
458 | |||
459 | atc->spdif_out_unmute(atc, 0); | ||
460 | atc->spdif_in_unmute(atc, 0); | ||
461 | atc->line_in_unmute(atc, 1); | ||
462 | return; | ||
463 | } | ||
464 | |||
465 | static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol, | ||
466 | struct snd_ctl_elem_info *uinfo) | ||
467 | { | ||
468 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
469 | uinfo->count = 1; | ||
470 | uinfo->value.integer.min = 0; | ||
471 | uinfo->value.integer.max = 1; | ||
472 | uinfo->value.integer.step = 1; | ||
473 | |||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol, | ||
478 | struct snd_ctl_elem_value *ucontrol) | ||
479 | { | ||
480 | struct ct_mixer *mixer = | ||
481 | ((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer; | ||
482 | enum CTALSA_MIXER_CTL type = kcontrol->private_value; | ||
483 | |||
484 | ucontrol->value.integer.value[0] = get_switch_state(mixer, type); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol, | ||
489 | struct snd_ctl_elem_value *ucontrol) | ||
490 | { | ||
491 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
492 | struct ct_mixer *mixer = atc->mixer; | ||
493 | enum CTALSA_MIXER_CTL type = kcontrol->private_value; | ||
494 | int state; | ||
495 | |||
496 | state = ucontrol->value.integer.value[0]; | ||
497 | if (get_switch_state(mixer, type) == state) | ||
498 | return 0; | ||
499 | |||
500 | set_switch_state(mixer, type, state); | ||
501 | /* Do changes in mixer. */ | ||
502 | if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { | ||
503 | if (state) { | ||
504 | ct_mixer_recording_select(mixer, | ||
505 | get_amixer_index(type)); | ||
506 | } else { | ||
507 | ct_mixer_recording_unselect(mixer, | ||
508 | get_amixer_index(type)); | ||
509 | } | ||
510 | } | ||
511 | /* Do changes out of mixer. */ | ||
512 | if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) | ||
513 | do_line_mic_switch(atc, type); | ||
514 | else if (MIXER_WAVEF_P_S == type) | ||
515 | atc->line_front_unmute(atc, state); | ||
516 | else if (MIXER_WAVES_P_S == type) | ||
517 | atc->line_surround_unmute(atc, state); | ||
518 | else if (MIXER_WAVEC_P_S == type) | ||
519 | atc->line_clfe_unmute(atc, state); | ||
520 | else if (MIXER_WAVER_P_S == type) | ||
521 | atc->line_rear_unmute(atc, state); | ||
522 | else if (MIXER_LINEIN_P_S == type) | ||
523 | atc->line_in_unmute(atc, state); | ||
524 | else if (MIXER_SPDIFO_P_S == type) | ||
525 | atc->spdif_out_unmute(atc, state); | ||
526 | else if (MIXER_SPDIFI_P_S == type) | ||
527 | atc->spdif_in_unmute(atc, state); | ||
528 | else if (MIXER_DIGITAL_IO_S == type) | ||
529 | do_digit_io_switch(atc, state); | ||
530 | |||
531 | return 1; | ||
532 | } | ||
533 | |||
534 | static struct snd_kcontrol_new swh_ctl = { | ||
535 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
536 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
537 | .info = ct_alsa_mix_switch_info, | ||
538 | .get = ct_alsa_mix_switch_get, | ||
539 | .put = ct_alsa_mix_switch_put | ||
540 | }; | ||
541 | |||
542 | static int ct_spdif_info(struct snd_kcontrol *kcontrol, | ||
543 | struct snd_ctl_elem_info *uinfo) | ||
544 | { | ||
545 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
546 | uinfo->count = 1; | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, | ||
551 | struct snd_ctl_elem_value *ucontrol) | ||
552 | { | ||
553 | ucontrol->value.iec958.status[0] = 0xff; | ||
554 | ucontrol->value.iec958.status[1] = 0xff; | ||
555 | ucontrol->value.iec958.status[2] = 0xff; | ||
556 | ucontrol->value.iec958.status[3] = 0xff; | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, | ||
561 | struct snd_ctl_elem_value *ucontrol) | ||
562 | { | ||
563 | unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; | ||
564 | |||
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_get(struct snd_kcontrol *kcontrol, | ||
574 | struct snd_ctl_elem_value *ucontrol) | ||
575 | { | ||
576 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
577 | unsigned int status; | ||
578 | |||
579 | atc->spdif_out_get_status(atc, &status); | ||
580 | ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; | ||
581 | ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; | ||
582 | ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; | ||
583 | ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; | ||
584 | |||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | static int ct_spdif_put(struct snd_kcontrol *kcontrol, | ||
589 | struct snd_ctl_elem_value *ucontrol) | ||
590 | { | ||
591 | struct ct_atc *atc = snd_kcontrol_chip(kcontrol); | ||
592 | int change; | ||
593 | unsigned int status, old_status; | ||
594 | |||
595 | status = (ucontrol->value.iec958.status[0] << 0) | | ||
596 | (ucontrol->value.iec958.status[1] << 8) | | ||
597 | (ucontrol->value.iec958.status[2] << 16) | | ||
598 | (ucontrol->value.iec958.status[3] << 24); | ||
599 | |||
600 | atc->spdif_out_get_status(atc, &old_status); | ||
601 | change = (old_status != status); | ||
602 | if (change) | ||
603 | atc->spdif_out_set_status(atc, status); | ||
604 | |||
605 | return change; | ||
606 | } | ||
607 | |||
608 | static struct snd_kcontrol_new iec958_mask_ctl = { | ||
609 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
610 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
611 | .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), | ||
612 | .count = 1, | ||
613 | .info = ct_spdif_info, | ||
614 | .get = ct_spdif_get_mask, | ||
615 | .private_value = MIXER_IEC958_MASK | ||
616 | }; | ||
617 | |||
618 | static struct snd_kcontrol_new iec958_default_ctl = { | ||
619 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
620 | .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), | ||
621 | .count = 1, | ||
622 | .info = ct_spdif_info, | ||
623 | .get = ct_spdif_default_get, | ||
624 | .put = ct_spdif_put, | ||
625 | .private_value = MIXER_IEC958_DEFAULT | ||
626 | }; | ||
627 | |||
628 | static struct snd_kcontrol_new iec958_ctl = { | ||
629 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
630 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
631 | .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), | ||
632 | .count = 1, | ||
633 | .info = ct_spdif_info, | ||
634 | .get = ct_spdif_get, | ||
635 | .put = ct_spdif_put, | ||
636 | .private_value = MIXER_IEC958_STREAM | ||
637 | }; | ||
638 | |||
639 | #define NUM_IEC958_CTL 3 | ||
640 | |||
641 | static int | ||
642 | ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) | ||
643 | { | ||
644 | struct snd_kcontrol *kctl; | ||
645 | int err; | ||
646 | |||
647 | kctl = snd_ctl_new1(new, mixer->atc); | ||
648 | if (NULL == kctl) | ||
649 | return -ENOMEM; | ||
650 | |||
651 | if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface) | ||
652 | kctl->id.device = IEC958; | ||
653 | |||
654 | err = snd_ctl_add(mixer->atc->card, kctl); | ||
655 | if (err) | ||
656 | return err; | ||
657 | |||
658 | switch (new->private_value) { | ||
659 | case MIXER_LINEIN_C_S: | ||
660 | kctls[0] = kctl; break; | ||
661 | case MIXER_MIC_C_S: | ||
662 | kctls[1] = kctl; break; | ||
663 | default: | ||
664 | break; | ||
665 | } | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) | ||
671 | { | ||
672 | enum CTALSA_MIXER_CTL type; | ||
673 | struct ct_atc *atc = mixer->atc; | ||
674 | int err; | ||
675 | |||
676 | /* Create snd kcontrol instances on demand */ | ||
677 | for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) { | ||
678 | if (ct_kcontrol_init_table[type].ctl) { | ||
679 | vol_ctl.name = ct_kcontrol_init_table[type].name; | ||
680 | vol_ctl.private_value = (unsigned long)type; | ||
681 | err = ct_mixer_kcontrol_new(mixer, &vol_ctl); | ||
682 | if (err) | ||
683 | return err; | ||
684 | } | ||
685 | } | ||
686 | |||
687 | ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = | ||
688 | atc->have_digit_io_switch(atc); | ||
689 | for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { | ||
690 | if (ct_kcontrol_init_table[type].ctl) { | ||
691 | swh_ctl.name = ct_kcontrol_init_table[type].name; | ||
692 | swh_ctl.private_value = (unsigned long)type; | ||
693 | err = ct_mixer_kcontrol_new(mixer, &swh_ctl); | ||
694 | if (err) | ||
695 | return err; | ||
696 | } | ||
697 | } | ||
698 | |||
699 | err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl); | ||
700 | if (err) | ||
701 | return err; | ||
702 | |||
703 | err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl); | ||
704 | if (err) | ||
705 | return err; | ||
706 | |||
707 | err = ct_mixer_kcontrol_new(mixer, &iec958_ctl); | ||
708 | if (err) | ||
709 | return err; | ||
710 | |||
711 | atc->line_front_unmute(atc, 1); | ||
712 | set_switch_state(mixer, MIXER_WAVEF_P_S, 1); | ||
713 | atc->line_surround_unmute(atc, 0); | ||
714 | set_switch_state(mixer, MIXER_WAVES_P_S, 0); | ||
715 | atc->line_clfe_unmute(atc, 0); | ||
716 | set_switch_state(mixer, MIXER_WAVEC_P_S, 0); | ||
717 | atc->line_rear_unmute(atc, 0); | ||
718 | set_switch_state(mixer, MIXER_WAVER_P_S, 0); | ||
719 | atc->spdif_out_unmute(atc, 0); | ||
720 | set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); | ||
721 | atc->line_in_unmute(atc, 0); | ||
722 | set_switch_state(mixer, MIXER_LINEIN_P_S, 0); | ||
723 | atc->spdif_in_unmute(atc, 0); | ||
724 | set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); | ||
725 | |||
726 | set_switch_state(mixer, MIXER_PCM_C_S, 1); | ||
727 | set_switch_state(mixer, MIXER_LINEIN_C_S, 1); | ||
728 | set_switch_state(mixer, MIXER_SPDIFI_C_S, 1); | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static void | ||
734 | ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) | ||
735 | { | ||
736 | struct amixer *amix_d; | ||
737 | struct sum *sum_c; | ||
738 | int i; | ||
739 | |||
740 | for (i = 0; i < 2; i++) { | ||
741 | amix_d = mixer->amixers[type*CHN_NUM+i]; | ||
742 | sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i]; | ||
743 | amix_d->ops->set_sum(amix_d, sum_c); | ||
744 | amix_d->ops->commit_write(amix_d); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | static void | ||
749 | ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) | ||
750 | { | ||
751 | struct amixer *amix_d; | ||
752 | int i; | ||
753 | |||
754 | for (i = 0; i < 2; i++) { | ||
755 | amix_d = mixer->amixers[type*CHN_NUM+i]; | ||
756 | amix_d->ops->set_sum(amix_d, NULL); | ||
757 | amix_d->ops->commit_write(amix_d); | ||
758 | } | ||
759 | } | ||
760 | |||
761 | static int ct_mixer_get_resources(struct ct_mixer *mixer) | ||
762 | { | ||
763 | struct sum_mgr *sum_mgr; | ||
764 | struct sum *sum; | ||
765 | struct sum_desc sum_desc = {0}; | ||
766 | struct amixer_mgr *amixer_mgr; | ||
767 | struct amixer *amixer; | ||
768 | struct amixer_desc am_desc = {0}; | ||
769 | int err; | ||
770 | int i; | ||
771 | |||
772 | /* Allocate sum resources for mixer obj */ | ||
773 | sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; | ||
774 | sum_desc.msr = mixer->atc->msr; | ||
775 | for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { | ||
776 | err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); | ||
777 | if (err) { | ||
778 | printk(KERN_ERR "ctxfi:Failed to get sum resources for " | ||
779 | "front output!\n"); | ||
780 | break; | ||
781 | } | ||
782 | mixer->sums[i] = sum; | ||
783 | } | ||
784 | if (err) | ||
785 | goto error1; | ||
786 | |||
787 | /* Allocate amixer resources for mixer obj */ | ||
788 | amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; | ||
789 | am_desc.msr = mixer->atc->msr; | ||
790 | for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { | ||
791 | err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); | ||
792 | if (err) { | ||
793 | printk(KERN_ERR "ctxfi:Failed to get amixer resources " | ||
794 | "for mixer obj!\n"); | ||
795 | break; | ||
796 | } | ||
797 | mixer->amixers[i] = amixer; | ||
798 | } | ||
799 | if (err) | ||
800 | goto error2; | ||
801 | |||
802 | return 0; | ||
803 | |||
804 | error2: | ||
805 | for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { | ||
806 | if (NULL != mixer->amixers[i]) { | ||
807 | amixer = mixer->amixers[i]; | ||
808 | amixer_mgr->put_amixer(amixer_mgr, amixer); | ||
809 | mixer->amixers[i] = NULL; | ||
810 | } | ||
811 | } | ||
812 | error1: | ||
813 | for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { | ||
814 | if (NULL != mixer->sums[i]) { | ||
815 | sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); | ||
816 | mixer->sums[i] = NULL; | ||
817 | } | ||
818 | } | ||
819 | |||
820 | return err; | ||
821 | } | ||
822 | |||
823 | static int ct_mixer_get_mem(struct ct_mixer **rmixer) | ||
824 | { | ||
825 | struct ct_mixer *mixer; | ||
826 | int err; | ||
827 | |||
828 | *rmixer = NULL; | ||
829 | /* Allocate mem for mixer obj */ | ||
830 | mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); | ||
831 | if (NULL == mixer) | ||
832 | return -ENOMEM; | ||
833 | |||
834 | mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), | ||
835 | GFP_KERNEL); | ||
836 | if (NULL == mixer->amixers) { | ||
837 | err = -ENOMEM; | ||
838 | goto error1; | ||
839 | } | ||
840 | mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); | ||
841 | if (NULL == mixer->sums) { | ||
842 | err = -ENOMEM; | ||
843 | goto error2; | ||
844 | } | ||
845 | |||
846 | *rmixer = mixer; | ||
847 | return 0; | ||
848 | |||
849 | error2: | ||
850 | kfree(mixer->amixers); | ||
851 | error1: | ||
852 | kfree(mixer); | ||
853 | return err; | ||
854 | } | ||
855 | |||
856 | static int ct_mixer_topology_build(struct ct_mixer *mixer) | ||
857 | { | ||
858 | struct sum *sum; | ||
859 | struct amixer *amix_d, *amix_s; | ||
860 | enum CT_AMIXER_CTL i, j; | ||
861 | |||
862 | /* Build topology from destination to source */ | ||
863 | |||
864 | /* Set up Master mixer */ | ||
865 | for (i = AMIXER_MASTER_F, j = SUM_IN_F; | ||
866 | i <= AMIXER_MASTER_S; i++, j++) { | ||
867 | amix_d = mixer->amixers[i*CHN_NUM]; | ||
868 | sum = mixer->sums[j*CHN_NUM]; | ||
869 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
870 | amix_d = mixer->amixers[i*CHN_NUM+1]; | ||
871 | sum = mixer->sums[j*CHN_NUM+1]; | ||
872 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
873 | } | ||
874 | |||
875 | /* Set up Wave-out mixer */ | ||
876 | for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F; | ||
877 | i <= AMIXER_WAVE_S; i++, j++) { | ||
878 | amix_d = mixer->amixers[i*CHN_NUM]; | ||
879 | amix_s = mixer->amixers[j*CHN_NUM]; | ||
880 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
881 | amix_d = mixer->amixers[i*CHN_NUM+1]; | ||
882 | amix_s = mixer->amixers[j*CHN_NUM+1]; | ||
883 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
884 | } | ||
885 | |||
886 | /* Set up S/PDIF-out mixer */ | ||
887 | amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM]; | ||
888 | amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM]; | ||
889 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
890 | amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1]; | ||
891 | amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1]; | ||
892 | amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); | ||
893 | |||
894 | /* Set up PCM-in mixer */ | ||
895 | for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) { | ||
896 | amix_d = mixer->amixers[i*CHN_NUM]; | ||
897 | sum = mixer->sums[j*CHN_NUM]; | ||
898 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
899 | amix_d = mixer->amixers[i*CHN_NUM+1]; | ||
900 | sum = mixer->sums[j*CHN_NUM+1]; | ||
901 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
902 | } | ||
903 | |||
904 | /* Set up Line-in mixer */ | ||
905 | amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM]; | ||
906 | sum = mixer->sums[SUM_IN_F*CHN_NUM]; | ||
907 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
908 | amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1]; | ||
909 | sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; | ||
910 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
911 | |||
912 | /* Set up Mic-in mixer */ | ||
913 | amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM]; | ||
914 | sum = mixer->sums[SUM_IN_F*CHN_NUM]; | ||
915 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
916 | amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1]; | ||
917 | sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; | ||
918 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
919 | |||
920 | /* Set up S/PDIF-in mixer */ | ||
921 | amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM]; | ||
922 | sum = mixer->sums[SUM_IN_F*CHN_NUM]; | ||
923 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
924 | amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1]; | ||
925 | sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; | ||
926 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
927 | |||
928 | /* Set up Master recording mixer */ | ||
929 | amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM]; | ||
930 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
931 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
932 | amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1]; | ||
933 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
934 | amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); | ||
935 | |||
936 | /* Set up PCM-in recording mixer */ | ||
937 | amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM]; | ||
938 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
939 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
940 | amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1]; | ||
941 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
942 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
943 | |||
944 | /* Set up Line-in recording mixer */ | ||
945 | amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM]; | ||
946 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
947 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
948 | amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1]; | ||
949 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
950 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
951 | |||
952 | /* Set up Mic-in recording mixer */ | ||
953 | amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM]; | ||
954 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
955 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
956 | amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1]; | ||
957 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
958 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
959 | |||
960 | /* Set up S/PDIF-in recording mixer */ | ||
961 | amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM]; | ||
962 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; | ||
963 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
964 | amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1]; | ||
965 | sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; | ||
966 | amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc) | ||
972 | { | ||
973 | amixer->ops->set_input(amixer, rsc); | ||
974 | amixer->ops->commit_write(amixer); | ||
975 | |||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type) | ||
980 | { | ||
981 | switch (type) { | ||
982 | case MIX_WAVE_FRONT: return AMIXER_WAVE_F; | ||
983 | case MIX_WAVE_SURROUND: return AMIXER_WAVE_S; | ||
984 | case MIX_WAVE_CENTLFE: return AMIXER_WAVE_C; | ||
985 | case MIX_WAVE_REAR: return AMIXER_WAVE_R; | ||
986 | case MIX_PCMO_FRONT: return AMIXER_MASTER_F_C; | ||
987 | case MIX_SPDIF_OUT: return AMIXER_SPDIFO; | ||
988 | case MIX_LINE_IN: return AMIXER_LINEIN; | ||
989 | case MIX_MIC_IN: return AMIXER_MIC; | ||
990 | case MIX_SPDIF_IN: return AMIXER_SPDIFI; | ||
991 | case MIX_PCMI_FRONT: return AMIXER_PCM_F; | ||
992 | case MIX_PCMI_SURROUND: return AMIXER_PCM_S; | ||
993 | case MIX_PCMI_CENTLFE: return AMIXER_PCM_C; | ||
994 | case MIX_PCMI_REAR: return AMIXER_PCM_R; | ||
995 | default: return 0; | ||
996 | } | ||
997 | } | ||
998 | |||
999 | static int mixer_get_output_ports(struct ct_mixer *mixer, | ||
1000 | enum MIXER_PORT_T type, | ||
1001 | struct rsc **rleft, struct rsc **rright) | ||
1002 | { | ||
1003 | enum CT_AMIXER_CTL amix = port_to_amixer(type); | ||
1004 | |||
1005 | if (NULL != rleft) | ||
1006 | *rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc; | ||
1007 | |||
1008 | if (NULL != rright) | ||
1009 | *rright = | ||
1010 | &((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc; | ||
1011 | |||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | static int mixer_set_input_left(struct ct_mixer *mixer, | ||
1016 | enum MIXER_PORT_T type, struct rsc *rsc) | ||
1017 | { | ||
1018 | enum CT_AMIXER_CTL amix = port_to_amixer(type); | ||
1019 | |||
1020 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); | ||
1021 | amix = get_recording_amixer(amix); | ||
1022 | if (amix < NUM_CT_AMIXERS) | ||
1023 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); | ||
1024 | |||
1025 | return 0; | ||
1026 | } | ||
1027 | |||
1028 | static int | ||
1029 | mixer_set_input_right(struct ct_mixer *mixer, | ||
1030 | enum MIXER_PORT_T type, struct rsc *rsc) | ||
1031 | { | ||
1032 | enum CT_AMIXER_CTL amix = port_to_amixer(type); | ||
1033 | |||
1034 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); | ||
1035 | amix = get_recording_amixer(amix); | ||
1036 | if (amix < NUM_CT_AMIXERS) | ||
1037 | mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); | ||
1038 | |||
1039 | return 0; | ||
1040 | } | ||
1041 | |||
1042 | int ct_mixer_destroy(struct ct_mixer *mixer) | ||
1043 | { | ||
1044 | struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; | ||
1045 | struct amixer_mgr *amixer_mgr = | ||
1046 | (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; | ||
1047 | struct amixer *amixer; | ||
1048 | int i = 0; | ||
1049 | |||
1050 | /* Release amixer resources */ | ||
1051 | for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { | ||
1052 | if (NULL != mixer->amixers[i]) { | ||
1053 | amixer = mixer->amixers[i]; | ||
1054 | amixer_mgr->put_amixer(amixer_mgr, amixer); | ||
1055 | } | ||
1056 | } | ||
1057 | |||
1058 | /* Release sum resources */ | ||
1059 | for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { | ||
1060 | if (NULL != mixer->sums[i]) | ||
1061 | sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); | ||
1062 | } | ||
1063 | |||
1064 | /* Release mem assigned to mixer object */ | ||
1065 | kfree(mixer->sums); | ||
1066 | kfree(mixer->amixers); | ||
1067 | kfree(mixer); | ||
1068 | |||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer) | ||
1073 | { | ||
1074 | struct ct_mixer *mixer; | ||
1075 | int err; | ||
1076 | |||
1077 | *rmixer = NULL; | ||
1078 | |||
1079 | /* Allocate mem for mixer obj */ | ||
1080 | err = ct_mixer_get_mem(&mixer); | ||
1081 | if (err) | ||
1082 | return err; | ||
1083 | |||
1084 | mixer->switch_state = 0; | ||
1085 | mixer->atc = atc; | ||
1086 | /* Set operations */ | ||
1087 | mixer->get_output_ports = mixer_get_output_ports; | ||
1088 | mixer->set_input_left = mixer_set_input_left; | ||
1089 | mixer->set_input_right = mixer_set_input_right; | ||
1090 | |||
1091 | /* Allocate chip resources for mixer obj */ | ||
1092 | err = ct_mixer_get_resources(mixer); | ||
1093 | if (err) | ||
1094 | goto error; | ||
1095 | |||
1096 | /* Build internal mixer topology */ | ||
1097 | ct_mixer_topology_build(mixer); | ||
1098 | |||
1099 | *rmixer = mixer; | ||
1100 | |||
1101 | return 0; | ||
1102 | |||
1103 | error: | ||
1104 | ct_mixer_destroy(mixer); | ||
1105 | return err; | ||
1106 | } | ||
1107 | |||
1108 | int ct_alsa_mix_create(struct ct_atc *atc, | ||
1109 | enum CTALSADEVS device, | ||
1110 | const char *device_name) | ||
1111 | { | ||
1112 | int err; | ||
1113 | |||
1114 | /* Create snd kcontrol instances on demand */ | ||
1115 | /* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */ | ||
1116 | err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer); | ||
1117 | if (err) | ||
1118 | return err; | ||
1119 | |||
1120 | strcpy(atc->card->mixername, device_name); | ||
1121 | |||
1122 | return 0; | ||
1123 | } | ||
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..9e5c0c4da726 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.c | |||
@@ -0,0 +1,426 @@ | |||
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 "cttimer.h" | ||
20 | #include <sound/pcm.h> | ||
21 | |||
22 | /* Hardware descriptions for playback */ | ||
23 | static struct snd_pcm_hardware ct_pcm_playback_hw = { | ||
24 | .info = (SNDRV_PCM_INFO_MMAP | | ||
25 | SNDRV_PCM_INFO_INTERLEAVED | | ||
26 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
27 | SNDRV_PCM_INFO_MMAP_VALID | | ||
28 | SNDRV_PCM_INFO_PAUSE), | ||
29 | .formats = (SNDRV_PCM_FMTBIT_U8 | | ||
30 | SNDRV_PCM_FMTBIT_S16_LE | | ||
31 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
32 | SNDRV_PCM_FMTBIT_S32_LE | | ||
33 | SNDRV_PCM_FMTBIT_FLOAT_LE), | ||
34 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | | ||
35 | SNDRV_PCM_RATE_8000_192000), | ||
36 | .rate_min = 8000, | ||
37 | .rate_max = 192000, | ||
38 | .channels_min = 1, | ||
39 | .channels_max = 2, | ||
40 | .buffer_bytes_max = (128*1024), | ||
41 | .period_bytes_min = (64), | ||
42 | .period_bytes_max = (128*1024), | ||
43 | .periods_min = 2, | ||
44 | .periods_max = 1024, | ||
45 | .fifo_size = 0, | ||
46 | }; | ||
47 | |||
48 | static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { | ||
49 | .info = (SNDRV_PCM_INFO_MMAP | | ||
50 | SNDRV_PCM_INFO_INTERLEAVED | | ||
51 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
52 | SNDRV_PCM_INFO_MMAP_VALID | | ||
53 | SNDRV_PCM_INFO_PAUSE), | ||
54 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
55 | .rates = (SNDRV_PCM_RATE_48000 | | ||
56 | SNDRV_PCM_RATE_44100 | | ||
57 | SNDRV_PCM_RATE_32000), | ||
58 | .rate_min = 32000, | ||
59 | .rate_max = 48000, | ||
60 | .channels_min = 2, | ||
61 | .channels_max = 2, | ||
62 | .buffer_bytes_max = (128*1024), | ||
63 | .period_bytes_min = (64), | ||
64 | .period_bytes_max = (128*1024), | ||
65 | .periods_min = 2, | ||
66 | .periods_max = 1024, | ||
67 | .fifo_size = 0, | ||
68 | }; | ||
69 | |||
70 | /* Hardware descriptions for capture */ | ||
71 | static struct snd_pcm_hardware ct_pcm_capture_hw = { | ||
72 | .info = (SNDRV_PCM_INFO_MMAP | | ||
73 | SNDRV_PCM_INFO_INTERLEAVED | | ||
74 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
75 | SNDRV_PCM_INFO_PAUSE | | ||
76 | SNDRV_PCM_INFO_MMAP_VALID), | ||
77 | .formats = (SNDRV_PCM_FMTBIT_U8 | | ||
78 | SNDRV_PCM_FMTBIT_S16_LE | | ||
79 | SNDRV_PCM_FMTBIT_S24_3LE | | ||
80 | SNDRV_PCM_FMTBIT_S32_LE | | ||
81 | SNDRV_PCM_FMTBIT_FLOAT_LE), | ||
82 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | | ||
83 | SNDRV_PCM_RATE_8000_96000), | ||
84 | .rate_min = 8000, | ||
85 | .rate_max = 96000, | ||
86 | .channels_min = 1, | ||
87 | .channels_max = 2, | ||
88 | .buffer_bytes_max = (128*1024), | ||
89 | .period_bytes_min = (384), | ||
90 | .period_bytes_max = (64*1024), | ||
91 | .periods_min = 2, | ||
92 | .periods_max = 1024, | ||
93 | .fifo_size = 0, | ||
94 | }; | ||
95 | |||
96 | static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm) | ||
97 | { | ||
98 | struct ct_atc_pcm *apcm = atc_pcm; | ||
99 | |||
100 | if (NULL == apcm->substream) | ||
101 | return; | ||
102 | |||
103 | snd_pcm_period_elapsed(apcm->substream); | ||
104 | } | ||
105 | |||
106 | static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) | ||
107 | { | ||
108 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
109 | struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); | ||
110 | |||
111 | atc->pcm_release_resources(atc, apcm); | ||
112 | ct_timer_instance_free(apcm->timer); | ||
113 | kfree(apcm); | ||
114 | runtime->private_data = NULL; | ||
115 | } | ||
116 | |||
117 | /* pcm playback operations */ | ||
118 | static int ct_pcm_playback_open(struct snd_pcm_substream *substream) | ||
119 | { | ||
120 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
121 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
122 | struct ct_atc_pcm *apcm; | ||
123 | int err; | ||
124 | |||
125 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||
126 | if (NULL == apcm) | ||
127 | return -ENOMEM; | ||
128 | |||
129 | apcm->substream = substream; | ||
130 | apcm->interrupt = ct_atc_pcm_interrupt; | ||
131 | runtime->private_data = apcm; | ||
132 | runtime->private_free = ct_atc_pcm_free_substream; | ||
133 | if (IEC958 == substream->pcm->device) { | ||
134 | runtime->hw = ct_spdif_passthru_playback_hw; | ||
135 | atc->spdif_out_passthru(atc, 1); | ||
136 | } else { | ||
137 | runtime->hw = ct_pcm_playback_hw; | ||
138 | if (FRONT == substream->pcm->device) | ||
139 | runtime->hw.channels_max = 8; | ||
140 | } | ||
141 | |||
142 | err = snd_pcm_hw_constraint_integer(runtime, | ||
143 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
144 | if (err < 0) { | ||
145 | kfree(apcm); | ||
146 | return err; | ||
147 | } | ||
148 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
149 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
150 | 1024, UINT_MAX); | ||
151 | if (err < 0) { | ||
152 | kfree(apcm); | ||
153 | return err; | ||
154 | } | ||
155 | |||
156 | apcm->timer = ct_timer_instance_new(atc->timer, apcm); | ||
157 | if (!apcm->timer) | ||
158 | return -ENOMEM; | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int ct_pcm_playback_close(struct snd_pcm_substream *substream) | ||
164 | { | ||
165 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
166 | |||
167 | /* TODO: Notify mixer inactive. */ | ||
168 | if (IEC958 == substream->pcm->device) | ||
169 | atc->spdif_out_passthru(atc, 0); | ||
170 | |||
171 | /* The ct_atc_pcm object will be freed by runtime->private_free */ | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int ct_pcm_hw_params(struct snd_pcm_substream *substream, | ||
177 | struct snd_pcm_hw_params *hw_params) | ||
178 | { | ||
179 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
180 | struct ct_atc_pcm *apcm = substream->runtime->private_data; | ||
181 | int err; | ||
182 | |||
183 | err = snd_pcm_lib_malloc_pages(substream, | ||
184 | params_buffer_bytes(hw_params)); | ||
185 | if (err < 0) | ||
186 | return err; | ||
187 | /* clear previous resources */ | ||
188 | atc->pcm_release_resources(atc, apcm); | ||
189 | return err; | ||
190 | } | ||
191 | |||
192 | static int ct_pcm_hw_free(struct snd_pcm_substream *substream) | ||
193 | { | ||
194 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
195 | struct ct_atc_pcm *apcm = substream->runtime->private_data; | ||
196 | |||
197 | /* clear previous resources */ | ||
198 | atc->pcm_release_resources(atc, apcm); | ||
199 | /* Free snd-allocated pages */ | ||
200 | return snd_pcm_lib_free_pages(substream); | ||
201 | } | ||
202 | |||
203 | |||
204 | static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) | ||
205 | { | ||
206 | int err; | ||
207 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
208 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
209 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
210 | |||
211 | if (IEC958 == substream->pcm->device) | ||
212 | err = atc->spdif_passthru_playback_prepare(atc, apcm); | ||
213 | else | ||
214 | err = atc->pcm_playback_prepare(atc, apcm); | ||
215 | |||
216 | if (err < 0) { | ||
217 | printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n"); | ||
218 | return err; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int | ||
225 | ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
226 | { | ||
227 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
228 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
229 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
230 | |||
231 | switch (cmd) { | ||
232 | case SNDRV_PCM_TRIGGER_START: | ||
233 | case SNDRV_PCM_TRIGGER_RESUME: | ||
234 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
235 | atc->pcm_playback_start(atc, apcm); | ||
236 | break; | ||
237 | case SNDRV_PCM_TRIGGER_STOP: | ||
238 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
239 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
240 | atc->pcm_playback_stop(atc, apcm); | ||
241 | break; | ||
242 | default: | ||
243 | break; | ||
244 | } | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static snd_pcm_uframes_t | ||
250 | ct_pcm_playback_pointer(struct snd_pcm_substream *substream) | ||
251 | { | ||
252 | unsigned long position; | ||
253 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
254 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
255 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
256 | |||
257 | /* Read out playback position */ | ||
258 | position = atc->pcm_playback_position(atc, apcm); | ||
259 | position = bytes_to_frames(runtime, position); | ||
260 | if (position >= runtime->buffer_size) | ||
261 | position = 0; | ||
262 | return position; | ||
263 | } | ||
264 | |||
265 | /* pcm capture operations */ | ||
266 | static int ct_pcm_capture_open(struct snd_pcm_substream *substream) | ||
267 | { | ||
268 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
269 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
270 | struct ct_atc_pcm *apcm; | ||
271 | int err; | ||
272 | |||
273 | apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||
274 | if (NULL == apcm) | ||
275 | return -ENOMEM; | ||
276 | |||
277 | apcm->started = 0; | ||
278 | apcm->substream = substream; | ||
279 | apcm->interrupt = ct_atc_pcm_interrupt; | ||
280 | runtime->private_data = apcm; | ||
281 | runtime->private_free = ct_atc_pcm_free_substream; | ||
282 | runtime->hw = ct_pcm_capture_hw; | ||
283 | runtime->hw.rate_max = atc->rsr * atc->msr; | ||
284 | |||
285 | err = snd_pcm_hw_constraint_integer(runtime, | ||
286 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
287 | if (err < 0) { | ||
288 | kfree(apcm); | ||
289 | return err; | ||
290 | } | ||
291 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
292 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
293 | 1024, UINT_MAX); | ||
294 | if (err < 0) { | ||
295 | kfree(apcm); | ||
296 | return err; | ||
297 | } | ||
298 | |||
299 | apcm->timer = ct_timer_instance_new(atc->timer, apcm); | ||
300 | if (!apcm->timer) | ||
301 | return -ENOMEM; | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static int ct_pcm_capture_close(struct snd_pcm_substream *substream) | ||
307 | { | ||
308 | /* The ct_atc_pcm object will be freed by runtime->private_free */ | ||
309 | /* TODO: Notify mixer inactive. */ | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) | ||
314 | { | ||
315 | int err; | ||
316 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
317 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
318 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
319 | |||
320 | err = atc->pcm_capture_prepare(atc, apcm); | ||
321 | if (err < 0) { | ||
322 | printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n"); | ||
323 | return err; | ||
324 | } | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int | ||
330 | ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) | ||
331 | { | ||
332 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
333 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
334 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
335 | |||
336 | switch (cmd) { | ||
337 | case SNDRV_PCM_TRIGGER_START: | ||
338 | atc->pcm_capture_start(atc, apcm); | ||
339 | break; | ||
340 | case SNDRV_PCM_TRIGGER_STOP: | ||
341 | atc->pcm_capture_stop(atc, apcm); | ||
342 | break; | ||
343 | default: | ||
344 | atc->pcm_capture_stop(atc, apcm); | ||
345 | break; | ||
346 | } | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static snd_pcm_uframes_t | ||
352 | ct_pcm_capture_pointer(struct snd_pcm_substream *substream) | ||
353 | { | ||
354 | unsigned long position; | ||
355 | struct ct_atc *atc = snd_pcm_substream_chip(substream); | ||
356 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
357 | struct ct_atc_pcm *apcm = runtime->private_data; | ||
358 | |||
359 | /* Read out playback position */ | ||
360 | position = atc->pcm_capture_position(atc, apcm); | ||
361 | position = bytes_to_frames(runtime, position); | ||
362 | if (position >= runtime->buffer_size) | ||
363 | position = 0; | ||
364 | return position; | ||
365 | } | ||
366 | |||
367 | /* PCM operators for playback */ | ||
368 | static struct snd_pcm_ops ct_pcm_playback_ops = { | ||
369 | .open = ct_pcm_playback_open, | ||
370 | .close = ct_pcm_playback_close, | ||
371 | .ioctl = snd_pcm_lib_ioctl, | ||
372 | .hw_params = ct_pcm_hw_params, | ||
373 | .hw_free = ct_pcm_hw_free, | ||
374 | .prepare = ct_pcm_playback_prepare, | ||
375 | .trigger = ct_pcm_playback_trigger, | ||
376 | .pointer = ct_pcm_playback_pointer, | ||
377 | .page = snd_pcm_sgbuf_ops_page, | ||
378 | }; | ||
379 | |||
380 | /* PCM operators for capture */ | ||
381 | static struct snd_pcm_ops ct_pcm_capture_ops = { | ||
382 | .open = ct_pcm_capture_open, | ||
383 | .close = ct_pcm_capture_close, | ||
384 | .ioctl = snd_pcm_lib_ioctl, | ||
385 | .hw_params = ct_pcm_hw_params, | ||
386 | .hw_free = ct_pcm_hw_free, | ||
387 | .prepare = ct_pcm_capture_prepare, | ||
388 | .trigger = ct_pcm_capture_trigger, | ||
389 | .pointer = ct_pcm_capture_pointer, | ||
390 | .page = snd_pcm_sgbuf_ops_page, | ||
391 | }; | ||
392 | |||
393 | /* Create ALSA pcm device */ | ||
394 | int ct_alsa_pcm_create(struct ct_atc *atc, | ||
395 | enum CTALSADEVS device, | ||
396 | const char *device_name) | ||
397 | { | ||
398 | struct snd_pcm *pcm; | ||
399 | int err; | ||
400 | int playback_count, capture_count; | ||
401 | |||
402 | playback_count = (IEC958 == device) ? 1 : 8; | ||
403 | capture_count = (FRONT == device) ? 1 : 0; | ||
404 | err = snd_pcm_new(atc->card, "ctxfi", device, | ||
405 | playback_count, capture_count, &pcm); | ||
406 | if (err < 0) { | ||
407 | printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err); | ||
408 | return err; | ||
409 | } | ||
410 | |||
411 | pcm->private_data = atc; | ||
412 | pcm->info_flags = 0; | ||
413 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | ||
414 | strlcpy(pcm->name, device_name, sizeof(pcm->name)); | ||
415 | |||
416 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); | ||
417 | |||
418 | if (FRONT == device) | ||
419 | snd_pcm_set_ops(pcm, | ||
420 | SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops); | ||
421 | |||
422 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, | ||
423 | snd_dma_pci_data(atc->pci), 128*1024, 128*1024); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
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..889c495bb7d1 --- /dev/null +++ b/sound/pci/ctxfi/ctresource.c | |||
@@ -0,0 +1,301 @@ | |||
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, j, k, n; | ||
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, j, k, n; | ||
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; | ||
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 | ||
166 | "ctxfi: Invalid resource type value %d!\n", type); | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | |||
170 | if (err) { | ||
171 | printk(KERN_ERR | ||
172 | "ctxfi: Failed to get resource control block!\n"); | ||
173 | return err; | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | int rsc_uninit(struct rsc *rsc) | ||
180 | { | ||
181 | if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) { | ||
182 | switch (rsc->type) { | ||
183 | case SRC: | ||
184 | ((struct hw *)rsc->hw)-> | ||
185 | src_rsc_put_ctrl_blk(rsc->ctrl_blk); | ||
186 | break; | ||
187 | case AMIXER: | ||
188 | ((struct hw *)rsc->hw)-> | ||
189 | amixer_rsc_put_ctrl_blk(rsc->ctrl_blk); | ||
190 | break; | ||
191 | case SUM: | ||
192 | case DAIO: | ||
193 | break; | ||
194 | default: | ||
195 | printk(KERN_ERR "ctxfi: " | ||
196 | "Invalid resource type value %d!\n", rsc->type); | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | rsc->hw = rsc->ctrl_blk = NULL; | ||
201 | } | ||
202 | |||
203 | rsc->idx = rsc->conj = 0; | ||
204 | rsc->type = NUM_RSCTYP; | ||
205 | rsc->msr = 0; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, | ||
211 | unsigned int amount, void *hw_obj) | ||
212 | { | ||
213 | int err = 0; | ||
214 | struct hw *hw = hw_obj; | ||
215 | |||
216 | mgr->type = NUM_RSCTYP; | ||
217 | |||
218 | mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL); | ||
219 | if (NULL == mgr->rscs) | ||
220 | return -ENOMEM; | ||
221 | |||
222 | switch (type) { | ||
223 | case SRC: | ||
224 | err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk); | ||
225 | break; | ||
226 | case SRCIMP: | ||
227 | err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk); | ||
228 | break; | ||
229 | case AMIXER: | ||
230 | err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk); | ||
231 | break; | ||
232 | case DAIO: | ||
233 | err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk); | ||
234 | break; | ||
235 | case SUM: | ||
236 | break; | ||
237 | default: | ||
238 | printk(KERN_ERR | ||
239 | "ctxfi: Invalid resource type value %d!\n", type); | ||
240 | err = -EINVAL; | ||
241 | goto error; | ||
242 | } | ||
243 | |||
244 | if (err) { | ||
245 | printk(KERN_ERR | ||
246 | "ctxfi: Failed to get manager control block!\n"); | ||
247 | goto error; | ||
248 | } | ||
249 | |||
250 | mgr->type = type; | ||
251 | mgr->avail = mgr->amount = amount; | ||
252 | mgr->hw = hw; | ||
253 | |||
254 | return 0; | ||
255 | |||
256 | error: | ||
257 | kfree(mgr->rscs); | ||
258 | return err; | ||
259 | } | ||
260 | |||
261 | int rsc_mgr_uninit(struct rsc_mgr *mgr) | ||
262 | { | ||
263 | if (NULL != mgr->rscs) { | ||
264 | kfree(mgr->rscs); | ||
265 | mgr->rscs = NULL; | ||
266 | } | ||
267 | |||
268 | if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) { | ||
269 | switch (mgr->type) { | ||
270 | case SRC: | ||
271 | ((struct hw *)mgr->hw)-> | ||
272 | src_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
273 | break; | ||
274 | case SRCIMP: | ||
275 | ((struct hw *)mgr->hw)-> | ||
276 | srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
277 | break; | ||
278 | case AMIXER: | ||
279 | ((struct hw *)mgr->hw)-> | ||
280 | amixer_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
281 | break; | ||
282 | case DAIO: | ||
283 | ((struct hw *)mgr->hw)-> | ||
284 | daio_mgr_put_ctrl_blk(mgr->ctrl_blk); | ||
285 | break; | ||
286 | case SUM: | ||
287 | break; | ||
288 | default: | ||
289 | printk(KERN_ERR "ctxfi: " | ||
290 | "Invalid resource type value %d!\n", mgr->type); | ||
291 | break; | ||
292 | } | ||
293 | |||
294 | mgr->hw = mgr->ctrl_blk = NULL; | ||
295 | } | ||
296 | |||
297 | mgr->type = NUM_RSCTYP; | ||
298 | mgr->avail = mgr->amount = 0; | ||
299 | |||
300 | return 0; | ||
301 | } | ||
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..e1c145d8b702 --- /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; | ||
41 | |||
42 | 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; | ||
51 | |||
52 | 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; | ||
61 | |||
62 | 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; | ||
71 | |||
72 | 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; | ||
81 | |||
82 | 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; | ||
91 | |||
92 | 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; | ||
101 | |||
102 | 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; | ||
111 | |||
112 | 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; | ||
121 | |||
122 | 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; | ||
131 | |||
132 | 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; | ||
141 | |||
142 | 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; | ||
151 | |||
152 | 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; | ||
161 | |||
162 | 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; | ||
171 | |||
172 | 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; | ||
181 | int i; | ||
182 | unsigned int dirty = 0; | ||
183 | |||
184 | 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; | ||
211 | |||
212 | 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, msr; | ||
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, msr; | ||
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; | ||
364 | int i, n; | ||
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, n; | ||
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; | ||
420 | struct src *src; | ||
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 "ctxfi: 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; | ||
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; | ||
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; | ||
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, i; | ||
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; | ||
622 | int i; | ||
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; | ||
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; | ||
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, i; | ||
719 | unsigned int idx; | ||
720 | struct srcimp *srcimp; | ||
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 "ctxfi: 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; | ||
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; | ||
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; | ||
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; | ||
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/cttimer.c b/sound/pci/ctxfi/cttimer.c new file mode 100644 index 000000000000..779c6c3591a5 --- /dev/null +++ b/sound/pci/ctxfi/cttimer.c | |||
@@ -0,0 +1,441 @@ | |||
1 | /* | ||
2 | * PCM timer handling on ctxfi | ||
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 | #include <linux/slab.h> | ||
10 | #include <linux/math64.h> | ||
11 | #include <linux/moduleparam.h> | ||
12 | #include <sound/core.h> | ||
13 | #include <sound/pcm.h> | ||
14 | #include "ctatc.h" | ||
15 | #include "cthardware.h" | ||
16 | #include "cttimer.h" | ||
17 | |||
18 | static int use_system_timer; | ||
19 | MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer"); | ||
20 | module_param(use_system_timer, bool, S_IRUGO); | ||
21 | |||
22 | struct ct_timer_ops { | ||
23 | void (*init)(struct ct_timer_instance *); | ||
24 | void (*prepare)(struct ct_timer_instance *); | ||
25 | void (*start)(struct ct_timer_instance *); | ||
26 | void (*stop)(struct ct_timer_instance *); | ||
27 | void (*free_instance)(struct ct_timer_instance *); | ||
28 | void (*interrupt)(struct ct_timer *); | ||
29 | void (*free_global)(struct ct_timer *); | ||
30 | }; | ||
31 | |||
32 | /* timer instance -- assigned to each PCM stream */ | ||
33 | struct ct_timer_instance { | ||
34 | spinlock_t lock; | ||
35 | struct ct_timer *timer_base; | ||
36 | struct ct_atc_pcm *apcm; | ||
37 | struct snd_pcm_substream *substream; | ||
38 | struct timer_list timer; | ||
39 | struct list_head instance_list; | ||
40 | struct list_head running_list; | ||
41 | unsigned int position; | ||
42 | unsigned int frag_count; | ||
43 | unsigned int running:1; | ||
44 | unsigned int need_update:1; | ||
45 | }; | ||
46 | |||
47 | /* timer instance manager */ | ||
48 | struct ct_timer { | ||
49 | spinlock_t lock; /* global timer lock (for xfitimer) */ | ||
50 | spinlock_t list_lock; /* lock for instance list */ | ||
51 | struct ct_atc *atc; | ||
52 | struct ct_timer_ops *ops; | ||
53 | struct list_head instance_head; | ||
54 | struct list_head running_head; | ||
55 | unsigned int wc; /* current wallclock */ | ||
56 | unsigned int irq_handling:1; /* in IRQ handling */ | ||
57 | unsigned int reprogram:1; /* need to reprogram the internval */ | ||
58 | unsigned int running:1; /* global timer running */ | ||
59 | }; | ||
60 | |||
61 | |||
62 | /* | ||
63 | * system-timer-based updates | ||
64 | */ | ||
65 | |||
66 | static void ct_systimer_callback(unsigned long data) | ||
67 | { | ||
68 | struct ct_timer_instance *ti = (struct ct_timer_instance *)data; | ||
69 | struct snd_pcm_substream *substream = ti->substream; | ||
70 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
71 | struct ct_atc_pcm *apcm = ti->apcm; | ||
72 | unsigned int period_size = runtime->period_size; | ||
73 | unsigned int buffer_size = runtime->buffer_size; | ||
74 | unsigned long flags; | ||
75 | unsigned int position, dist, interval; | ||
76 | |||
77 | position = substream->ops->pointer(substream); | ||
78 | dist = (position + buffer_size - ti->position) % buffer_size; | ||
79 | if (dist >= period_size || | ||
80 | position / period_size != ti->position / period_size) { | ||
81 | apcm->interrupt(apcm); | ||
82 | ti->position = position; | ||
83 | } | ||
84 | /* Add extra HZ*5/1000 to avoid overrun issue when recording | ||
85 | * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ | ||
86 | interval = ((period_size - (position % period_size)) | ||
87 | * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; | ||
88 | spin_lock_irqsave(&ti->lock, flags); | ||
89 | if (ti->running) | ||
90 | mod_timer(&ti->timer, jiffies + interval); | ||
91 | spin_unlock_irqrestore(&ti->lock, flags); | ||
92 | } | ||
93 | |||
94 | static void ct_systimer_init(struct ct_timer_instance *ti) | ||
95 | { | ||
96 | setup_timer(&ti->timer, ct_systimer_callback, | ||
97 | (unsigned long)ti); | ||
98 | } | ||
99 | |||
100 | static void ct_systimer_start(struct ct_timer_instance *ti) | ||
101 | { | ||
102 | struct snd_pcm_runtime *runtime = ti->substream->runtime; | ||
103 | unsigned long flags; | ||
104 | |||
105 | spin_lock_irqsave(&ti->lock, flags); | ||
106 | ti->running = 1; | ||
107 | mod_timer(&ti->timer, | ||
108 | jiffies + (runtime->period_size * HZ + | ||
109 | (runtime->rate - 1)) / runtime->rate); | ||
110 | spin_unlock_irqrestore(&ti->lock, flags); | ||
111 | } | ||
112 | |||
113 | static void ct_systimer_stop(struct ct_timer_instance *ti) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | |||
117 | spin_lock_irqsave(&ti->lock, flags); | ||
118 | ti->running = 0; | ||
119 | del_timer(&ti->timer); | ||
120 | spin_unlock_irqrestore(&ti->lock, flags); | ||
121 | } | ||
122 | |||
123 | static void ct_systimer_prepare(struct ct_timer_instance *ti) | ||
124 | { | ||
125 | ct_systimer_stop(ti); | ||
126 | try_to_del_timer_sync(&ti->timer); | ||
127 | } | ||
128 | |||
129 | #define ct_systimer_free ct_systimer_prepare | ||
130 | |||
131 | static struct ct_timer_ops ct_systimer_ops = { | ||
132 | .init = ct_systimer_init, | ||
133 | .free_instance = ct_systimer_free, | ||
134 | .prepare = ct_systimer_prepare, | ||
135 | .start = ct_systimer_start, | ||
136 | .stop = ct_systimer_stop, | ||
137 | }; | ||
138 | |||
139 | |||
140 | /* | ||
141 | * Handling multiple streams using a global emu20k1 timer irq | ||
142 | */ | ||
143 | |||
144 | #define CT_TIMER_FREQ 48000 | ||
145 | #define MIN_TICKS 1 | ||
146 | #define MAX_TICKS ((1 << 13) - 1) | ||
147 | |||
148 | static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks) | ||
149 | { | ||
150 | struct hw *hw = atimer->atc->hw; | ||
151 | if (ticks > MAX_TICKS) | ||
152 | ticks = MAX_TICKS; | ||
153 | hw->set_timer_tick(hw, ticks); | ||
154 | if (!atimer->running) | ||
155 | hw->set_timer_irq(hw, 1); | ||
156 | atimer->running = 1; | ||
157 | } | ||
158 | |||
159 | static void ct_xfitimer_irq_stop(struct ct_timer *atimer) | ||
160 | { | ||
161 | if (atimer->running) { | ||
162 | struct hw *hw = atimer->atc->hw; | ||
163 | hw->set_timer_irq(hw, 0); | ||
164 | hw->set_timer_tick(hw, 0); | ||
165 | atimer->running = 0; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer) | ||
170 | { | ||
171 | struct hw *hw = atimer->atc->hw; | ||
172 | return hw->get_wc(hw); | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * reprogram the timer interval; | ||
177 | * checks the running instance list and determines the next timer interval. | ||
178 | * also updates the each stream position, returns the number of streams | ||
179 | * to call snd_pcm_period_elapsed() appropriately | ||
180 | * | ||
181 | * call this inside the lock and irq disabled | ||
182 | */ | ||
183 | static int ct_xfitimer_reprogram(struct ct_timer *atimer) | ||
184 | { | ||
185 | struct ct_timer_instance *ti; | ||
186 | unsigned int min_intr = (unsigned int)-1; | ||
187 | int updates = 0; | ||
188 | unsigned int wc, diff; | ||
189 | |||
190 | if (list_empty(&atimer->running_head)) { | ||
191 | ct_xfitimer_irq_stop(atimer); | ||
192 | atimer->reprogram = 0; /* clear flag */ | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | wc = ct_xfitimer_get_wc(atimer); | ||
197 | diff = wc - atimer->wc; | ||
198 | atimer->wc = wc; | ||
199 | list_for_each_entry(ti, &atimer->running_head, running_list) { | ||
200 | if (ti->frag_count > diff) | ||
201 | ti->frag_count -= diff; | ||
202 | else { | ||
203 | unsigned int pos; | ||
204 | unsigned int period_size, rate; | ||
205 | |||
206 | period_size = ti->substream->runtime->period_size; | ||
207 | rate = ti->substream->runtime->rate; | ||
208 | pos = ti->substream->ops->pointer(ti->substream); | ||
209 | if (pos / period_size != ti->position / period_size) { | ||
210 | ti->need_update = 1; | ||
211 | ti->position = pos; | ||
212 | updates++; | ||
213 | } | ||
214 | pos %= period_size; | ||
215 | pos = period_size - pos; | ||
216 | ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ + | ||
217 | rate - 1, rate); | ||
218 | } | ||
219 | if (ti->frag_count < min_intr) | ||
220 | min_intr = ti->frag_count; | ||
221 | } | ||
222 | |||
223 | if (min_intr < MIN_TICKS) | ||
224 | min_intr = MIN_TICKS; | ||
225 | ct_xfitimer_irq_rearm(atimer, min_intr); | ||
226 | atimer->reprogram = 0; /* clear flag */ | ||
227 | return updates; | ||
228 | } | ||
229 | |||
230 | /* look through the instance list and call period_elapsed if needed */ | ||
231 | static void ct_xfitimer_check_period(struct ct_timer *atimer) | ||
232 | { | ||
233 | struct ct_timer_instance *ti; | ||
234 | unsigned long flags; | ||
235 | |||
236 | spin_lock_irqsave(&atimer->list_lock, flags); | ||
237 | list_for_each_entry(ti, &atimer->instance_head, instance_list) { | ||
238 | if (ti->need_update) { | ||
239 | ti->need_update = 0; | ||
240 | ti->apcm->interrupt(ti->apcm); | ||
241 | } | ||
242 | } | ||
243 | spin_unlock_irqrestore(&atimer->list_lock, flags); | ||
244 | } | ||
245 | |||
246 | /* Handle timer-interrupt */ | ||
247 | static void ct_xfitimer_callback(struct ct_timer *atimer) | ||
248 | { | ||
249 | int update; | ||
250 | unsigned long flags; | ||
251 | |||
252 | spin_lock_irqsave(&atimer->lock, flags); | ||
253 | atimer->irq_handling = 1; | ||
254 | do { | ||
255 | update = ct_xfitimer_reprogram(atimer); | ||
256 | spin_unlock(&atimer->lock); | ||
257 | if (update) | ||
258 | ct_xfitimer_check_period(atimer); | ||
259 | spin_lock(&atimer->lock); | ||
260 | } while (atimer->reprogram); | ||
261 | atimer->irq_handling = 0; | ||
262 | spin_unlock_irqrestore(&atimer->lock, flags); | ||
263 | } | ||
264 | |||
265 | static void ct_xfitimer_prepare(struct ct_timer_instance *ti) | ||
266 | { | ||
267 | ti->frag_count = ti->substream->runtime->period_size; | ||
268 | ti->need_update = 0; | ||
269 | } | ||
270 | |||
271 | |||
272 | /* start/stop the timer */ | ||
273 | static void ct_xfitimer_update(struct ct_timer *atimer) | ||
274 | { | ||
275 | unsigned long flags; | ||
276 | int update; | ||
277 | |||
278 | spin_lock_irqsave(&atimer->lock, flags); | ||
279 | if (atimer->irq_handling) { | ||
280 | /* reached from IRQ handler; let it handle later */ | ||
281 | atimer->reprogram = 1; | ||
282 | spin_unlock_irqrestore(&atimer->lock, flags); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | ct_xfitimer_irq_stop(atimer); | ||
287 | update = ct_xfitimer_reprogram(atimer); | ||
288 | spin_unlock_irqrestore(&atimer->lock, flags); | ||
289 | if (update) | ||
290 | ct_xfitimer_check_period(atimer); | ||
291 | } | ||
292 | |||
293 | static void ct_xfitimer_start(struct ct_timer_instance *ti) | ||
294 | { | ||
295 | struct ct_timer *atimer = ti->timer_base; | ||
296 | unsigned long flags; | ||
297 | |||
298 | spin_lock_irqsave(&atimer->lock, flags); | ||
299 | if (list_empty(&ti->running_list)) | ||
300 | atimer->wc = ct_xfitimer_get_wc(atimer); | ||
301 | list_add(&ti->running_list, &atimer->running_head); | ||
302 | spin_unlock_irqrestore(&atimer->lock, flags); | ||
303 | ct_xfitimer_update(atimer); | ||
304 | } | ||
305 | |||
306 | static void ct_xfitimer_stop(struct ct_timer_instance *ti) | ||
307 | { | ||
308 | struct ct_timer *atimer = ti->timer_base; | ||
309 | unsigned long flags; | ||
310 | |||
311 | spin_lock_irqsave(&atimer->lock, flags); | ||
312 | list_del_init(&ti->running_list); | ||
313 | ti->need_update = 0; | ||
314 | spin_unlock_irqrestore(&atimer->lock, flags); | ||
315 | ct_xfitimer_update(atimer); | ||
316 | } | ||
317 | |||
318 | static void ct_xfitimer_free_global(struct ct_timer *atimer) | ||
319 | { | ||
320 | ct_xfitimer_irq_stop(atimer); | ||
321 | } | ||
322 | |||
323 | static struct ct_timer_ops ct_xfitimer_ops = { | ||
324 | .prepare = ct_xfitimer_prepare, | ||
325 | .start = ct_xfitimer_start, | ||
326 | .stop = ct_xfitimer_stop, | ||
327 | .interrupt = ct_xfitimer_callback, | ||
328 | .free_global = ct_xfitimer_free_global, | ||
329 | }; | ||
330 | |||
331 | /* | ||
332 | * timer instance | ||
333 | */ | ||
334 | |||
335 | struct ct_timer_instance * | ||
336 | ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm) | ||
337 | { | ||
338 | struct ct_timer_instance *ti; | ||
339 | |||
340 | ti = kzalloc(sizeof(*ti), GFP_KERNEL); | ||
341 | if (!ti) | ||
342 | return NULL; | ||
343 | spin_lock_init(&ti->lock); | ||
344 | INIT_LIST_HEAD(&ti->instance_list); | ||
345 | INIT_LIST_HEAD(&ti->running_list); | ||
346 | ti->timer_base = atimer; | ||
347 | ti->apcm = apcm; | ||
348 | ti->substream = apcm->substream; | ||
349 | if (atimer->ops->init) | ||
350 | atimer->ops->init(ti); | ||
351 | |||
352 | spin_lock_irq(&atimer->list_lock); | ||
353 | list_add(&ti->instance_list, &atimer->instance_head); | ||
354 | spin_unlock_irq(&atimer->list_lock); | ||
355 | |||
356 | return ti; | ||
357 | } | ||
358 | |||
359 | void ct_timer_prepare(struct ct_timer_instance *ti) | ||
360 | { | ||
361 | if (ti->timer_base->ops->prepare) | ||
362 | ti->timer_base->ops->prepare(ti); | ||
363 | ti->position = 0; | ||
364 | ti->running = 0; | ||
365 | } | ||
366 | |||
367 | void ct_timer_start(struct ct_timer_instance *ti) | ||
368 | { | ||
369 | struct ct_timer *atimer = ti->timer_base; | ||
370 | atimer->ops->start(ti); | ||
371 | } | ||
372 | |||
373 | void ct_timer_stop(struct ct_timer_instance *ti) | ||
374 | { | ||
375 | struct ct_timer *atimer = ti->timer_base; | ||
376 | atimer->ops->stop(ti); | ||
377 | } | ||
378 | |||
379 | void ct_timer_instance_free(struct ct_timer_instance *ti) | ||
380 | { | ||
381 | struct ct_timer *atimer = ti->timer_base; | ||
382 | |||
383 | atimer->ops->stop(ti); /* to be sure */ | ||
384 | if (atimer->ops->free_instance) | ||
385 | atimer->ops->free_instance(ti); | ||
386 | |||
387 | spin_lock_irq(&atimer->list_lock); | ||
388 | list_del(&ti->instance_list); | ||
389 | spin_unlock_irq(&atimer->list_lock); | ||
390 | |||
391 | kfree(ti); | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * timer manager | ||
396 | */ | ||
397 | |||
398 | static void ct_timer_interrupt(void *data, unsigned int status) | ||
399 | { | ||
400 | struct ct_timer *timer = data; | ||
401 | |||
402 | /* Interval timer interrupt */ | ||
403 | if ((status & IT_INT) && timer->ops->interrupt) | ||
404 | timer->ops->interrupt(timer); | ||
405 | } | ||
406 | |||
407 | struct ct_timer *ct_timer_new(struct ct_atc *atc) | ||
408 | { | ||
409 | struct ct_timer *atimer; | ||
410 | struct hw *hw; | ||
411 | |||
412 | atimer = kzalloc(sizeof(*atimer), GFP_KERNEL); | ||
413 | if (!atimer) | ||
414 | return NULL; | ||
415 | spin_lock_init(&atimer->lock); | ||
416 | spin_lock_init(&atimer->list_lock); | ||
417 | INIT_LIST_HEAD(&atimer->instance_head); | ||
418 | INIT_LIST_HEAD(&atimer->running_head); | ||
419 | atimer->atc = atc; | ||
420 | hw = atc->hw; | ||
421 | if (!use_system_timer && hw->set_timer_irq) { | ||
422 | snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n"); | ||
423 | atimer->ops = &ct_xfitimer_ops; | ||
424 | hw->irq_callback_data = atimer; | ||
425 | hw->irq_callback = ct_timer_interrupt; | ||
426 | } else { | ||
427 | snd_printd(KERN_INFO "ctxfi: Use system timer\n"); | ||
428 | atimer->ops = &ct_systimer_ops; | ||
429 | } | ||
430 | return atimer; | ||
431 | } | ||
432 | |||
433 | void ct_timer_free(struct ct_timer *atimer) | ||
434 | { | ||
435 | struct hw *hw = atimer->atc->hw; | ||
436 | hw->irq_callback = NULL; | ||
437 | if (atimer->ops->free_global) | ||
438 | atimer->ops->free_global(atimer); | ||
439 | kfree(atimer); | ||
440 | } | ||
441 | |||
diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h new file mode 100644 index 000000000000..979348229291 --- /dev/null +++ b/sound/pci/ctxfi/cttimer.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Timer handling | ||
3 | */ | ||
4 | |||
5 | #ifndef __CTTIMER_H | ||
6 | #define __CTTIMER_H | ||
7 | |||
8 | #include <linux/spinlock.h> | ||
9 | #include <linux/timer.h> | ||
10 | #include <linux/list.h> | ||
11 | |||
12 | struct snd_pcm_substream; | ||
13 | struct ct_atc; | ||
14 | struct ct_atc_pcm; | ||
15 | |||
16 | struct ct_timer; | ||
17 | struct ct_timer_instance; | ||
18 | |||
19 | struct ct_timer *ct_timer_new(struct ct_atc *atc); | ||
20 | void ct_timer_free(struct ct_timer *atimer); | ||
21 | |||
22 | struct ct_timer_instance * | ||
23 | ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm); | ||
24 | void ct_timer_instance_free(struct ct_timer_instance *ti); | ||
25 | void ct_timer_start(struct ct_timer_instance *ti); | ||
26 | void ct_timer_stop(struct ct_timer_instance *ti); | ||
27 | void ct_timer_prepare(struct ct_timer_instance *ti); | ||
28 | |||
29 | #endif /* __CTTIMER_H */ | ||
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c new file mode 100644 index 000000000000..67665a7e43c6 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.c | |||
@@ -0,0 +1,250 @@ | |||
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 <linux/io.h> | ||
22 | #include <sound/pcm.h> | ||
23 | |||
24 | #define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *)) | ||
25 | #define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE) | ||
26 | |||
27 | /* * | ||
28 | * Find or create vm block based on requested @size. | ||
29 | * @size must be page aligned. | ||
30 | * */ | ||
31 | static struct ct_vm_block * | ||
32 | get_vm_block(struct ct_vm *vm, unsigned int size) | ||
33 | { | ||
34 | struct ct_vm_block *block = NULL, *entry; | ||
35 | struct list_head *pos; | ||
36 | |||
37 | size = CT_PAGE_ALIGN(size); | ||
38 | if (size > vm->size) { | ||
39 | printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " | ||
40 | "memory space available!\n"); | ||
41 | return NULL; | ||
42 | } | ||
43 | |||
44 | mutex_lock(&vm->lock); | ||
45 | list_for_each(pos, &vm->unused) { | ||
46 | entry = list_entry(pos, struct ct_vm_block, list); | ||
47 | if (entry->size >= size) | ||
48 | break; /* found a block that is big enough */ | ||
49 | } | ||
50 | if (pos == &vm->unused) | ||
51 | goto out; | ||
52 | |||
53 | if (entry->size == size) { | ||
54 | /* Move the vm node from unused list to used list directly */ | ||
55 | list_del(&entry->list); | ||
56 | list_add(&entry->list, &vm->used); | ||
57 | vm->size -= size; | ||
58 | block = entry; | ||
59 | goto out; | ||
60 | } | ||
61 | |||
62 | block = kzalloc(sizeof(*block), GFP_KERNEL); | ||
63 | if (NULL == block) | ||
64 | goto out; | ||
65 | |||
66 | block->addr = entry->addr; | ||
67 | block->size = size; | ||
68 | list_add(&block->list, &vm->used); | ||
69 | entry->addr += size; | ||
70 | entry->size -= size; | ||
71 | vm->size -= size; | ||
72 | |||
73 | out: | ||
74 | mutex_unlock(&vm->lock); | ||
75 | return block; | ||
76 | } | ||
77 | |||
78 | static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) | ||
79 | { | ||
80 | struct ct_vm_block *entry, *pre_ent; | ||
81 | struct list_head *pos, *pre; | ||
82 | |||
83 | block->size = CT_PAGE_ALIGN(block->size); | ||
84 | |||
85 | mutex_lock(&vm->lock); | ||
86 | list_del(&block->list); | ||
87 | vm->size += block->size; | ||
88 | |||
89 | list_for_each(pos, &vm->unused) { | ||
90 | entry = list_entry(pos, struct ct_vm_block, list); | ||
91 | if (entry->addr >= (block->addr + block->size)) | ||
92 | break; /* found a position */ | ||
93 | } | ||
94 | if (pos == &vm->unused) { | ||
95 | list_add_tail(&block->list, &vm->unused); | ||
96 | entry = block; | ||
97 | } else { | ||
98 | if ((block->addr + block->size) == entry->addr) { | ||
99 | entry->addr = block->addr; | ||
100 | entry->size += block->size; | ||
101 | kfree(block); | ||
102 | } else { | ||
103 | __list_add(&block->list, pos->prev, pos); | ||
104 | entry = block; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | pos = &entry->list; | ||
109 | pre = pos->prev; | ||
110 | while (pre != &vm->unused) { | ||
111 | entry = list_entry(pos, struct ct_vm_block, list); | ||
112 | pre_ent = list_entry(pre, struct ct_vm_block, list); | ||
113 | if ((pre_ent->addr + pre_ent->size) > entry->addr) | ||
114 | break; | ||
115 | |||
116 | pre_ent->size += entry->size; | ||
117 | list_del(pos); | ||
118 | kfree(entry); | ||
119 | pos = pre; | ||
120 | pre = pos->prev; | ||
121 | } | ||
122 | mutex_unlock(&vm->lock); | ||
123 | } | ||
124 | |||
125 | /* Map host addr (kmalloced/vmalloced) to device logical addr. */ | ||
126 | static struct ct_vm_block * | ||
127 | ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size) | ||
128 | { | ||
129 | struct ct_vm_block *block; | ||
130 | unsigned int pte_start; | ||
131 | unsigned i, pages; | ||
132 | unsigned long *ptp; | ||
133 | |||
134 | block = get_vm_block(vm, size); | ||
135 | if (block == NULL) { | ||
136 | printk(KERN_ERR "ctxfi: No virtual memory block that is big " | ||
137 | "enough to allocate!\n"); | ||
138 | return NULL; | ||
139 | } | ||
140 | |||
141 | ptp = vm->ptp[0]; | ||
142 | pte_start = (block->addr >> CT_PAGE_SHIFT); | ||
143 | pages = block->size >> CT_PAGE_SHIFT; | ||
144 | for (i = 0; i < pages; i++) { | ||
145 | unsigned long addr; | ||
146 | addr = snd_pcm_sgbuf_get_addr(substream, i << CT_PAGE_SHIFT); | ||
147 | ptp[pte_start + i] = addr; | ||
148 | } | ||
149 | |||
150 | block->size = size; | ||
151 | return block; | ||
152 | } | ||
153 | |||
154 | static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) | ||
155 | { | ||
156 | /* do unmapping */ | ||
157 | put_vm_block(vm, block); | ||
158 | } | ||
159 | |||
160 | /* * | ||
161 | * return the host (kmalloced) addr of the @index-th device | ||
162 | * page talbe page on success, or NULL on failure. | ||
163 | * The first returned NULL indicates the termination. | ||
164 | * */ | ||
165 | static void * | ||
166 | ct_get_ptp_virt(struct ct_vm *vm, int index) | ||
167 | { | ||
168 | void *addr; | ||
169 | |||
170 | addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index]; | ||
171 | |||
172 | return addr; | ||
173 | } | ||
174 | |||
175 | int ct_vm_create(struct ct_vm **rvm) | ||
176 | { | ||
177 | struct ct_vm *vm; | ||
178 | struct ct_vm_block *block; | ||
179 | int i; | ||
180 | |||
181 | *rvm = NULL; | ||
182 | |||
183 | vm = kzalloc(sizeof(*vm), GFP_KERNEL); | ||
184 | if (NULL == vm) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | mutex_init(&vm->lock); | ||
188 | |||
189 | /* Allocate page table pages */ | ||
190 | for (i = 0; i < CT_PTP_NUM; i++) { | ||
191 | vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
192 | if (NULL == vm->ptp[i]) | ||
193 | break; | ||
194 | } | ||
195 | if (!i) { | ||
196 | /* no page table pages are allocated */ | ||
197 | kfree(vm); | ||
198 | return -ENOMEM; | ||
199 | } | ||
200 | vm->size = CT_ADDRS_PER_PAGE * i; | ||
201 | /* Initialise remaining ptps */ | ||
202 | for (; i < CT_PTP_NUM; i++) | ||
203 | vm->ptp[i] = NULL; | ||
204 | |||
205 | vm->map = ct_vm_map; | ||
206 | vm->unmap = ct_vm_unmap; | ||
207 | vm->get_ptp_virt = ct_get_ptp_virt; | ||
208 | INIT_LIST_HEAD(&vm->unused); | ||
209 | INIT_LIST_HEAD(&vm->used); | ||
210 | block = kzalloc(sizeof(*block), GFP_KERNEL); | ||
211 | if (NULL != block) { | ||
212 | block->addr = 0; | ||
213 | block->size = vm->size; | ||
214 | list_add(&block->list, &vm->unused); | ||
215 | } | ||
216 | |||
217 | *rvm = vm; | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | /* The caller must ensure no mapping pages are being used | ||
222 | * by hardware before calling this function */ | ||
223 | void ct_vm_destroy(struct ct_vm *vm) | ||
224 | { | ||
225 | int i; | ||
226 | struct list_head *pos; | ||
227 | struct ct_vm_block *entry; | ||
228 | |||
229 | /* free used and unused list nodes */ | ||
230 | while (!list_empty(&vm->used)) { | ||
231 | pos = vm->used.next; | ||
232 | list_del(pos); | ||
233 | entry = list_entry(pos, struct ct_vm_block, list); | ||
234 | kfree(entry); | ||
235 | } | ||
236 | while (!list_empty(&vm->unused)) { | ||
237 | pos = vm->unused.next; | ||
238 | list_del(pos); | ||
239 | entry = list_entry(pos, struct ct_vm_block, list); | ||
240 | kfree(entry); | ||
241 | } | ||
242 | |||
243 | /* free allocated page table pages */ | ||
244 | for (i = 0; i < CT_PTP_NUM; i++) | ||
245 | kfree(vm->ptp[i]); | ||
246 | |||
247 | vm->size = 0; | ||
248 | |||
249 | kfree(vm); | ||
250 | } | ||
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h new file mode 100644 index 000000000000..01e4fd0386a3 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.h | |||
@@ -0,0 +1,61 @@ | |||
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/mutex.h> | ||
24 | #include <linux/list.h> | ||
25 | |||
26 | /* The chip can handle the page table of 4k pages | ||
27 | * (emu20k1 can handle even 8k pages, but we don't use it right now) | ||
28 | */ | ||
29 | #define CT_PAGE_SIZE 4096 | ||
30 | #define CT_PAGE_SHIFT 12 | ||
31 | #define CT_PAGE_MASK (~(PAGE_SIZE - 1)) | ||
32 | #define CT_PAGE_ALIGN(addr) ALIGN(addr, CT_PAGE_SIZE) | ||
33 | |||
34 | struct ct_vm_block { | ||
35 | unsigned int addr; /* starting logical addr of this block */ | ||
36 | unsigned int size; /* size of this device virtual mem block */ | ||
37 | struct list_head list; | ||
38 | }; | ||
39 | |||
40 | struct snd_pcm_substream; | ||
41 | |||
42 | /* Virtual memory management object for card device */ | ||
43 | struct ct_vm { | ||
44 | void *ptp[CT_PTP_NUM]; /* Device page table pages */ | ||
45 | unsigned int size; /* Available addr space in bytes */ | ||
46 | struct list_head unused; /* List of unused blocks */ | ||
47 | struct list_head used; /* List of used blocks */ | ||
48 | struct mutex lock; | ||
49 | |||
50 | /* Map host addr (kmalloced/vmalloced) to device logical addr. */ | ||
51 | struct ct_vm_block *(*map)(struct ct_vm *, struct snd_pcm_substream *, | ||
52 | int size); | ||
53 | /* Unmap device logical addr area. */ | ||
54 | void (*unmap)(struct ct_vm *, struct ct_vm_block *block); | ||
55 | void *(*get_ptp_virt)(struct ct_vm *vm, int index); | ||
56 | }; | ||
57 | |||
58 | int ct_vm_create(struct ct_vm **rvm); | ||
59 | void ct_vm_destroy(struct ct_vm *vm); | ||
60 | |||
61 | #endif /* CTVMEM_H */ | ||
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c new file mode 100644 index 000000000000..2d3dd89af151 --- /dev/null +++ b/sound/pci/ctxfi/xfi.c | |||
@@ -0,0 +1,142 @@ | |||
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 <linux/pci_ids.h> | ||
15 | #include <sound/core.h> | ||
16 | #include <sound/initval.h> | ||
17 | #include "ctatc.h" | ||
18 | #include "cthardware.h" | ||
19 | |||
20 | MODULE_AUTHOR("Creative Technology Ltd"); | ||
21 | MODULE_DESCRIPTION("X-Fi driver version 1.03"); | ||
22 | MODULE_LICENSE("GPL v2"); | ||
23 | MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); | ||
24 | |||
25 | static unsigned int reference_rate = 48000; | ||
26 | static unsigned int multiple = 2; | ||
27 | MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)"); | ||
28 | module_param(reference_rate, uint, S_IRUGO); | ||
29 | MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)"); | ||
30 | module_param(multiple, uint, S_IRUGO); | ||
31 | |||
32 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
33 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
34 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | ||
35 | |||
36 | module_param_array(index, int, NULL, 0444); | ||
37 | MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver"); | ||
38 | module_param_array(id, charp, NULL, 0444); | ||
39 | MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver"); | ||
40 | module_param_array(enable, bool, NULL, 0444); | ||
41 | MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver"); | ||
42 | |||
43 | static struct pci_device_id ct_pci_dev_ids[] = { | ||
44 | /* only X-Fi is supported, so... */ | ||
45 | { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1), | ||
46 | .driver_data = ATC20K1, | ||
47 | }, | ||
48 | { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2), | ||
49 | .driver_data = ATC20K2, | ||
50 | }, | ||
51 | { 0, } | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); | ||
54 | |||
55 | static int __devinit | ||
56 | ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) | ||
57 | { | ||
58 | static int dev; | ||
59 | struct snd_card *card; | ||
60 | struct ct_atc *atc; | ||
61 | int err; | ||
62 | |||
63 | if (dev >= SNDRV_CARDS) | ||
64 | return -ENODEV; | ||
65 | |||
66 | if (!enable[dev]) { | ||
67 | dev++; | ||
68 | return -ENOENT; | ||
69 | } | ||
70 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); | ||
71 | if (err) | ||
72 | return err; | ||
73 | if ((reference_rate != 48000) && (reference_rate != 44100)) { | ||
74 | printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n", | ||
75 | reference_rate); | ||
76 | printk(KERN_ERR "ctxfi: The valid values for reference_rate " | ||
77 | "are 48000 and 44100, Value 48000 is assumed.\n"); | ||
78 | reference_rate = 48000; | ||
79 | } | ||
80 | if ((multiple != 1) && (multiple != 2)) { | ||
81 | printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n", | ||
82 | multiple); | ||
83 | printk(KERN_ERR "ctxfi: The valid values for multiple are " | ||
84 | "1 and 2, Value 2 is assumed.\n"); | ||
85 | multiple = 2; | ||
86 | } | ||
87 | err = ct_atc_create(card, pci, reference_rate, multiple, | ||
88 | pci_id->driver_data, &atc); | ||
89 | if (err < 0) | ||
90 | goto error; | ||
91 | |||
92 | card->private_data = atc; | ||
93 | |||
94 | /* Create alsa devices supported by this card */ | ||
95 | err = ct_atc_create_alsa_devs(atc); | ||
96 | if (err < 0) | ||
97 | goto error; | ||
98 | |||
99 | strcpy(card->driver, "SB-XFi"); | ||
100 | strcpy(card->shortname, "Creative X-Fi"); | ||
101 | snprintf(card->longname, sizeof(card->longname), "%s %s %s", | ||
102 | card->shortname, atc->chip_name, atc->model_name); | ||
103 | |||
104 | err = snd_card_register(card); | ||
105 | if (err < 0) | ||
106 | goto error; | ||
107 | |||
108 | pci_set_drvdata(pci, card); | ||
109 | dev++; | ||
110 | |||
111 | return 0; | ||
112 | |||
113 | error: | ||
114 | snd_card_free(card); | ||
115 | return err; | ||
116 | } | ||
117 | |||
118 | static void __devexit ct_card_remove(struct pci_dev *pci) | ||
119 | { | ||
120 | snd_card_free(pci_get_drvdata(pci)); | ||
121 | pci_set_drvdata(pci, NULL); | ||
122 | } | ||
123 | |||
124 | static struct pci_driver ct_driver = { | ||
125 | .name = "SB-XFi", | ||
126 | .id_table = ct_pci_dev_ids, | ||
127 | .probe = ct_card_probe, | ||
128 | .remove = __devexit_p(ct_card_remove), | ||
129 | }; | ||
130 | |||
131 | static int __init ct_card_init(void) | ||
132 | { | ||
133 | return pci_register_driver(&ct_driver); | ||
134 | } | ||
135 | |||
136 | static void __exit ct_card_exit(void) | ||
137 | { | ||
138 | pci_unregister_driver(&ct_driver); | ||
139 | } | ||
140 | |||
141 | module_init(ct_card_init) | ||
142 | module_exit(ct_card_exit) | ||