diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/drivers/vx |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/drivers/vx')
-rw-r--r-- | sound/drivers/vx/Makefile | 8 | ||||
-rw-r--r-- | sound/drivers/vx/vx_cmd.c | 109 | ||||
-rw-r--r-- | sound/drivers/vx/vx_cmd.h | 246 | ||||
-rw-r--r-- | sound/drivers/vx/vx_core.c | 837 | ||||
-rw-r--r-- | sound/drivers/vx/vx_hwdep.c | 249 | ||||
-rw-r--r-- | sound/drivers/vx/vx_mixer.c | 1000 | ||||
-rw-r--r-- | sound/drivers/vx/vx_pcm.c | 1312 | ||||
-rw-r--r-- | sound/drivers/vx/vx_uer.c | 321 |
8 files changed, 4082 insertions, 0 deletions
diff --git a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile new file mode 100644 index 000000000000..269bd8544a5d --- /dev/null +++ b/sound/drivers/vx/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o | ||
7 | |||
8 | obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o | ||
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c new file mode 100644 index 000000000000..7a221349f285 --- /dev/null +++ b/sound/drivers/vx/vx_cmd.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * DSP commands | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | /* | ||
30 | * Array of DSP commands | ||
31 | */ | ||
32 | static struct vx_cmd_info vx_dsp_cmds[] = { | ||
33 | [CMD_VERSION] = { 0x010000, 2, RMH_SSIZE_FIXED, 1 }, | ||
34 | [CMD_SUPPORTED] = { 0x020000, 1, RMH_SSIZE_FIXED, 2 }, | ||
35 | [CMD_TEST_IT] = { 0x040000, 1, RMH_SSIZE_FIXED, 1 }, | ||
36 | [CMD_SEND_IRQA] = { 0x070001, 1, RMH_SSIZE_FIXED, 0 }, | ||
37 | [CMD_IBL] = { 0x080000, 1, RMH_SSIZE_FIXED, 4 }, | ||
38 | [CMD_ASYNC] = { 0x0A0000, 1, RMH_SSIZE_ARG, 0 }, | ||
39 | [CMD_RES_PIPE] = { 0x400000, 1, RMH_SSIZE_FIXED, 0 }, | ||
40 | [CMD_FREE_PIPE] = { 0x410000, 1, RMH_SSIZE_FIXED, 0 }, | ||
41 | [CMD_CONF_PIPE] = { 0x42A101, 2, RMH_SSIZE_FIXED, 0 }, | ||
42 | [CMD_ABORT_CONF_PIPE] = { 0x42A100, 2, RMH_SSIZE_FIXED, 0 }, | ||
43 | [CMD_PARAM_OUTPUT_PIPE] = { 0x43A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
44 | [CMD_STOP_PIPE] = { 0x470004, 1, RMH_SSIZE_FIXED, 0 }, | ||
45 | [CMD_PIPE_STATE] = { 0x480000, 1, RMH_SSIZE_FIXED, 1 }, | ||
46 | [CMD_PIPE_SPL_COUNT] = { 0x49A000, 2, RMH_SSIZE_FIXED, 2 }, | ||
47 | [CMD_CAN_START_PIPE] = { 0x4b0000, 1, RMH_SSIZE_FIXED, 1 }, | ||
48 | [CMD_SIZE_HBUFFER] = { 0x4C0000, 1, RMH_SSIZE_FIXED, 1 }, | ||
49 | [CMD_START_STREAM] = { 0x80A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
50 | [CMD_START_ONE_STREAM] = { 0x800000, 1, RMH_SSIZE_FIXED, 0 }, | ||
51 | [CMD_PAUSE_STREAM] = { 0x81A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
52 | [CMD_PAUSE_ONE_STREAM] = { 0x810000, 1, RMH_SSIZE_FIXED, 0 }, | ||
53 | [CMD_STREAM_OUT_LEVEL_ADJUST] = { 0x828000, 2, RMH_SSIZE_FIXED, 0 }, | ||
54 | [CMD_STOP_STREAM] = { 0x830000, 1, RMH_SSIZE_FIXED, 0 }, | ||
55 | [CMD_FORMAT_STREAM_OUT] = { 0x868000, 1, RMH_SSIZE_FIXED, 0 }, | ||
56 | [CMD_FORMAT_STREAM_IN] = { 0x878800, 1, RMH_SSIZE_FIXED, 0 }, | ||
57 | [CMD_GET_STREAM_STATE] = { 0x890001, 2, RMH_SSIZE_FIXED, 1 }, | ||
58 | [CMD_DROP_BYTES_AWAY] = { 0x8A8000, 2, RMH_SSIZE_FIXED, 0 }, | ||
59 | [CMD_GET_REMAINING_BYTES] = { 0x8D0800, 1, RMH_SSIZE_FIXED, 2 }, | ||
60 | [CMD_CONNECT_AUDIO] = { 0xC10000, 1, RMH_SSIZE_FIXED, 0 }, | ||
61 | [CMD_AUDIO_LEVEL_ADJUST] = { 0xC2A000, 3, RMH_SSIZE_FIXED, 0 }, | ||
62 | [CMD_AUDIO_VU_PIC_METER] = { 0xC3A003, 2, RMH_SSIZE_FIXED, 1 }, | ||
63 | [CMD_GET_AUDIO_LEVELS] = { 0xC4A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
64 | [CMD_GET_NOTIFY_EVENT] = { 0x4D0000, 1, RMH_SSIZE_ARG, 0 }, | ||
65 | [CMD_INFO_NOTIFIED] = { 0x0B0000, 1, RMH_SSIZE_FIXED, 2 }, | ||
66 | [CMD_ACCESS_IO_FCT] = { 0x098000, 1, RMH_SSIZE_ARG, 0 }, | ||
67 | [CMD_STATUS_R_BUFFERS] = { 0x440000, 1, RMH_SSIZE_ARG, 0 }, | ||
68 | [CMD_UPDATE_R_BUFFERS] = { 0x848000, 4, RMH_SSIZE_FIXED, 0 }, | ||
69 | [CMD_LOAD_EFFECT_CONTEXT] = { 0x0c8000, 3, RMH_SSIZE_FIXED, 1 }, | ||
70 | [CMD_EFFECT_ONE_PIPE] = { 0x458000, 0, RMH_SSIZE_FIXED, 0 }, | ||
71 | [CMD_MODIFY_CLOCK] = { 0x0d0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
72 | [CMD_STREAM1_OUT_SET_N_LEVELS] ={ 0x858000, 3, RMH_SSIZE_FIXED, 0 }, | ||
73 | [CMD_PURGE_STREAM_DCMDS] = { 0x8b8000, 3, RMH_SSIZE_FIXED, 0 }, | ||
74 | [CMD_NOTIFY_PIPE_TIME] = { 0x4e0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
75 | [CMD_LOAD_EFFECT_CONTEXT_PACKET] = { 0x0c8000, 1, RMH_SSIZE_FIXED, 0 }, | ||
76 | [CMD_RELIC_R_BUFFER] = { 0x8e0800, 1, RMH_SSIZE_FIXED, 1 }, | ||
77 | [CMD_RESYNC_AUDIO_INPUTS] = { 0x0e0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
78 | [CMD_NOTIFY_STREAM_TIME] = { 0x8f0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
79 | [CMD_STREAM_SAMPLE_COUNT] = { 0x900000, 1, RMH_SSIZE_FIXED, 2 }, | ||
80 | [CMD_CONFIG_TIME_CODE] = { 0x050000, 2, RMH_SSIZE_FIXED, 0 }, | ||
81 | [CMD_GET_TIME_CODE] = { 0x060000, 1, RMH_SSIZE_FIXED, 5 }, | ||
82 | [CMD_MANAGE_SIGNAL] = { 0x0f0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
83 | [CMD_PARAMETER_STREAM_OUT] = { 0x91A000, 3, RMH_SSIZE_FIXED, 0 }, | ||
84 | [CMD_READ_BOARD_FREQ] = { 0x030000, 1, RMH_SSIZE_FIXED, 2 }, | ||
85 | [CMD_GET_STREAM_LEVELS] = { 0x8c0000, 1, RMH_SSIZE_FIXED, 3 }, | ||
86 | [CMD_PURGE_PIPE_DCMDS] = { 0x4f8000, 3, RMH_SSIZE_FIXED, 0 }, | ||
87 | // [CMD_SET_STREAM_OUT_EFFECTS] = { 0x888000, 34, RMH_SSIZE_FIXED, 0 }, | ||
88 | // [CMD_GET_STREAM_OUT_EFFECTS] = { 0x928000, 2, RMH_SSIZE_FIXED, 32 }, | ||
89 | [CMD_CONNECT_MONITORING] = { 0xC00000, 1, RMH_SSIZE_FIXED, 0 }, | ||
90 | [CMD_STREAM2_OUT_SET_N_LEVELS] = { 0x938000, 3, RMH_SSIZE_FIXED, 0 }, | ||
91 | [CMD_CANCEL_R_BUFFERS] = { 0x948000, 4, RMH_SSIZE_FIXED, 0 }, | ||
92 | [CMD_NOTIFY_END_OF_BUFFER] = { 0x950000, 1, RMH_SSIZE_FIXED, 0 }, | ||
93 | [CMD_GET_STREAM_VU_METER] = { 0x95A000, 2, RMH_SSIZE_ARG, 0 }, | ||
94 | }; | ||
95 | |||
96 | /** | ||
97 | * vx_init_rmh - initialize the RMH instance | ||
98 | * @rmh: the rmh pointer to be initialized | ||
99 | * @cmd: the rmh command to be set | ||
100 | */ | ||
101 | void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd) | ||
102 | { | ||
103 | snd_assert(cmd < CMD_LAST_INDEX, return); | ||
104 | rmh->LgCmd = vx_dsp_cmds[cmd].length; | ||
105 | rmh->LgStat = vx_dsp_cmds[cmd].st_length; | ||
106 | rmh->DspStat = vx_dsp_cmds[cmd].st_type; | ||
107 | rmh->Cmd[0] = vx_dsp_cmds[cmd].opcode; | ||
108 | } | ||
109 | |||
diff --git a/sound/drivers/vx/vx_cmd.h b/sound/drivers/vx/vx_cmd.h new file mode 100644 index 000000000000..a85248ba3cc5 --- /dev/null +++ b/sound/drivers/vx/vx_cmd.h | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Definitions of DSP commands | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #ifndef __VX_CMD_H | ||
24 | #define __VX_CMD_H | ||
25 | |||
26 | enum { | ||
27 | CMD_VERSION, | ||
28 | CMD_SUPPORTED, | ||
29 | CMD_TEST_IT, | ||
30 | CMD_SEND_IRQA, | ||
31 | CMD_IBL, | ||
32 | CMD_ASYNC, | ||
33 | CMD_RES_PIPE, | ||
34 | CMD_FREE_PIPE, | ||
35 | CMD_CONF_PIPE, | ||
36 | CMD_ABORT_CONF_PIPE, | ||
37 | CMD_PARAM_OUTPUT_PIPE, | ||
38 | CMD_STOP_PIPE, | ||
39 | CMD_PIPE_STATE, | ||
40 | CMD_PIPE_SPL_COUNT, | ||
41 | CMD_CAN_START_PIPE, | ||
42 | CMD_SIZE_HBUFFER, | ||
43 | CMD_START_STREAM, | ||
44 | CMD_START_ONE_STREAM, | ||
45 | CMD_PAUSE_STREAM, | ||
46 | CMD_PAUSE_ONE_STREAM, | ||
47 | CMD_STREAM_OUT_LEVEL_ADJUST, | ||
48 | CMD_STOP_STREAM, | ||
49 | CMD_FORMAT_STREAM_OUT, | ||
50 | CMD_FORMAT_STREAM_IN, | ||
51 | CMD_GET_STREAM_STATE, | ||
52 | CMD_DROP_BYTES_AWAY, | ||
53 | CMD_GET_REMAINING_BYTES, | ||
54 | CMD_CONNECT_AUDIO, | ||
55 | CMD_AUDIO_LEVEL_ADJUST, | ||
56 | CMD_AUDIO_VU_PIC_METER, | ||
57 | CMD_GET_AUDIO_LEVELS, | ||
58 | CMD_GET_NOTIFY_EVENT, | ||
59 | CMD_INFO_NOTIFIED, | ||
60 | CMD_ACCESS_IO_FCT, | ||
61 | CMD_STATUS_R_BUFFERS, | ||
62 | CMD_UPDATE_R_BUFFERS, | ||
63 | CMD_LOAD_EFFECT_CONTEXT, | ||
64 | CMD_EFFECT_ONE_PIPE, | ||
65 | CMD_MODIFY_CLOCK, | ||
66 | CMD_STREAM1_OUT_SET_N_LEVELS, | ||
67 | CMD_PURGE_STREAM_DCMDS, | ||
68 | CMD_NOTIFY_PIPE_TIME, | ||
69 | CMD_LOAD_EFFECT_CONTEXT_PACKET, | ||
70 | CMD_RELIC_R_BUFFER, | ||
71 | CMD_RESYNC_AUDIO_INPUTS, | ||
72 | CMD_NOTIFY_STREAM_TIME, | ||
73 | CMD_STREAM_SAMPLE_COUNT, | ||
74 | CMD_CONFIG_TIME_CODE, | ||
75 | CMD_GET_TIME_CODE, | ||
76 | CMD_MANAGE_SIGNAL, | ||
77 | CMD_PARAMETER_STREAM_OUT, | ||
78 | CMD_READ_BOARD_FREQ, | ||
79 | CMD_GET_STREAM_LEVELS, | ||
80 | CMD_PURGE_PIPE_DCMDS, | ||
81 | // CMD_SET_STREAM_OUT_EFFECTS, | ||
82 | // CMD_GET_STREAM_OUT_EFFECTS, | ||
83 | CMD_CONNECT_MONITORING, | ||
84 | CMD_STREAM2_OUT_SET_N_LEVELS, | ||
85 | CMD_CANCEL_R_BUFFERS, | ||
86 | CMD_NOTIFY_END_OF_BUFFER, | ||
87 | CMD_GET_STREAM_VU_METER, | ||
88 | CMD_LAST_INDEX | ||
89 | }; | ||
90 | |||
91 | struct vx_cmd_info { | ||
92 | unsigned int opcode; /* command word */ | ||
93 | int length; /* command length (in words) */ | ||
94 | int st_type; /* status type (RMH_SSIZE_XXX) */ | ||
95 | int st_length; /* fixed length */ | ||
96 | }; | ||
97 | |||
98 | /* Family and code op of some DSP requests. */ | ||
99 | #define CODE_OP_PIPE_TIME 0x004e0000 | ||
100 | #define CODE_OP_START_STREAM 0x00800000 | ||
101 | #define CODE_OP_PAUSE_STREAM 0x00810000 | ||
102 | #define CODE_OP_OUT_STREAM_LEVEL 0x00820000 | ||
103 | #define CODE_OP_UPDATE_R_BUFFERS 0x00840000 | ||
104 | #define CODE_OP_OUT_STREAM1_LEVEL_CURVE 0x00850000 | ||
105 | #define CODE_OP_OUT_STREAM2_LEVEL_CURVE 0x00930000 | ||
106 | #define CODE_OP_OUT_STREAM_FORMAT 0x00860000 | ||
107 | #define CODE_OP_STREAM_TIME 0x008f0000 | ||
108 | #define CODE_OP_OUT_STREAM_EXTRAPARAMETER 0x00910000 | ||
109 | #define CODE_OP_OUT_AUDIO_LEVEL 0x00c20000 | ||
110 | |||
111 | #define NOTIFY_LAST_COMMAND 0x00400000 | ||
112 | |||
113 | /* Values for a user delay */ | ||
114 | #define DC_DIFFERED_DELAY (1<<BIT_DIFFERED_COMMAND) | ||
115 | #define DC_NOTIFY_DELAY (1<<BIT_NOTIFIED_COMMAND) | ||
116 | #define DC_HBUFFER_DELAY (1<<BIT_TIME_RELATIVE_TO_BUFFER) | ||
117 | #define DC_MULTIPLE_DELAY (1<<BIT_RESERVED) | ||
118 | #define DC_STREAM_TIME_DELAY (1<<BIT_STREAM_TIME) | ||
119 | #define DC_CANCELLED_DELAY (1<<BIT_CANCELLED_COMMAND) | ||
120 | |||
121 | /* Values for tiDelayed field in TIME_INFO structure, | ||
122 | * and for pbPause field in PLAY_BUFFER_INFO structure | ||
123 | */ | ||
124 | #define BIT_DIFFERED_COMMAND 0 | ||
125 | #define BIT_NOTIFIED_COMMAND 1 | ||
126 | #define BIT_TIME_RELATIVE_TO_BUFFER 2 | ||
127 | #define BIT_RESERVED 3 | ||
128 | #define BIT_STREAM_TIME 4 | ||
129 | #define BIT_CANCELLED_COMMAND 5 | ||
130 | |||
131 | /* Access to the "Size" field of the response of the CMD_GET_NOTIFY_EVENT request. */ | ||
132 | #define GET_NOTIFY_EVENT_SIZE_FIELD_MASK 0x000000ff | ||
133 | |||
134 | /* DSP commands general masks */ | ||
135 | #define OPCODE_MASK 0x00ff0000 | ||
136 | #define DSP_DIFFERED_COMMAND_MASK 0x0000C000 | ||
137 | |||
138 | /* Notifications (NOTIFY_INFO) */ | ||
139 | #define ALL_CMDS_NOTIFIED 0x0000 // reserved | ||
140 | #define START_STREAM_NOTIFIED 0x0001 | ||
141 | #define PAUSE_STREAM_NOTIFIED 0x0002 | ||
142 | #define OUT_STREAM_LEVEL_NOTIFIED 0x0003 | ||
143 | #define OUT_STREAM_PARAMETER_NOTIFIED 0x0004 // left for backward compatibility | ||
144 | #define OUT_STREAM_FORMAT_NOTIFIED 0x0004 | ||
145 | #define PIPE_TIME_NOTIFIED 0x0005 | ||
146 | #define OUT_AUDIO_LEVEL_NOTIFIED 0x0006 | ||
147 | #define OUT_STREAM_LEVEL_CURVE_NOTIFIED 0x0007 | ||
148 | #define STREAM_TIME_NOTIFIED 0x0008 | ||
149 | #define OUT_STREAM_EXTRAPARAMETER_NOTIFIED 0x0009 | ||
150 | #define UNKNOWN_COMMAND_NOTIFIED 0xffff | ||
151 | |||
152 | /* Output pipe parameters setting */ | ||
153 | #define MASK_VALID_PIPE_MPEG_PARAM 0x000040 | ||
154 | #define MASK_VALID_PIPE_BACKWARD_PARAM 0x000020 | ||
155 | #define MASK_SET_PIPE_MPEG_PARAM 0x000002 | ||
156 | #define MASK_SET_PIPE_BACKWARD_PARAM 0x000001 | ||
157 | |||
158 | #define MASK_DSP_WORD 0x00FFFFFF | ||
159 | #define MASK_ALL_STREAM 0x00FFFFFF | ||
160 | #define MASK_DSP_WORD_LEVEL 0x000001FF | ||
161 | #define MASK_FIRST_FIELD 0x0000001F | ||
162 | #define FIELD_SIZE 5 | ||
163 | |||
164 | #define COMMAND_RECORD_MASK 0x000800 | ||
165 | |||
166 | /* PipeManagement definition bits (PIPE_DECL_INFO) */ | ||
167 | #define P_UNDERRUN_SKIP_SOUND_MASK 0x01 | ||
168 | #define P_PREPARE_FOR_MPEG3_MASK 0x02 | ||
169 | #define P_DO_NOT_RESET_ANALOG_LEVELS 0x04 | ||
170 | #define P_ALLOW_UNDER_ALLOCATION_MASK 0x08 | ||
171 | #define P_DATA_MODE_MASK 0x10 | ||
172 | #define P_ASIO_BUFFER_MANAGEMENT_MASK 0x20 | ||
173 | |||
174 | #define BIT_SKIP_SOUND 0x08 // bit 3 | ||
175 | #define BIT_DATA_MODE 0x10 // bit 4 | ||
176 | |||
177 | /* Bits in the CMD_MODIFY_CLOCK request. */ | ||
178 | #define CMD_MODIFY_CLOCK_FD_BIT 0x00000001 | ||
179 | #define CMD_MODIFY_CLOCK_T_BIT 0x00000002 | ||
180 | #define CMD_MODIFY_CLOCK_S_BIT 0x00000004 | ||
181 | |||
182 | /* Access to the results of the CMD_GET_TIME_CODE RMH. */ | ||
183 | #define TIME_CODE_V_MASK 0x00800000 | ||
184 | #define TIME_CODE_N_MASK 0x00400000 | ||
185 | #define TIME_CODE_B_MASK 0x00200000 | ||
186 | #define TIME_CODE_W_MASK 0x00100000 | ||
187 | |||
188 | /* Values for the CMD_MANAGE_SIGNAL RMH. */ | ||
189 | #define MANAGE_SIGNAL_TIME_CODE 0x01 | ||
190 | #define MANAGE_SIGNAL_MIDI 0x02 | ||
191 | |||
192 | /* Values for the CMD_CONFIG_TIME_CODE RMH. */ | ||
193 | #define CONFIG_TIME_CODE_CANCEL 0x00001000 | ||
194 | |||
195 | /* Mask to get only the effective time from the | ||
196 | * high word out of the 2 returned by the DSP | ||
197 | */ | ||
198 | #define PCX_TIME_HI_MASK 0x000fffff | ||
199 | |||
200 | /* Values for setting a H-Buffer time */ | ||
201 | #define HBUFFER_TIME_HIGH 0x00200000 | ||
202 | #define HBUFFER_TIME_LOW 0x00000000 | ||
203 | |||
204 | #define NOTIFY_MASK_TIME_HIGH 0x00400000 | ||
205 | #define MULTIPLE_MASK_TIME_HIGH 0x00100000 | ||
206 | #define STREAM_MASK_TIME_HIGH 0x00800000 | ||
207 | |||
208 | |||
209 | /* | ||
210 | * | ||
211 | */ | ||
212 | void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd); | ||
213 | |||
214 | /** | ||
215 | * vx_send_pipe_cmd_params - fill first command word for pipe commands | ||
216 | * @rmh: the rmh to be modified | ||
217 | * @is_capture: 0 = playback, 1 = capture operation | ||
218 | * @param1: first pipe-parameter | ||
219 | * @param2: second pipe-parameter | ||
220 | */ | ||
221 | static inline void vx_set_pipe_cmd_params(struct vx_rmh *rmh, int is_capture, | ||
222 | int param1, int param2) | ||
223 | { | ||
224 | if (is_capture) | ||
225 | rmh->Cmd[0] |= COMMAND_RECORD_MASK; | ||
226 | rmh->Cmd[0] |= (((u32)param1 & MASK_FIRST_FIELD) << FIELD_SIZE) & MASK_DSP_WORD; | ||
227 | |||
228 | if (param2) | ||
229 | rmh->Cmd[0] |= ((u32)param2 & MASK_FIRST_FIELD) & MASK_DSP_WORD; | ||
230 | |||
231 | } | ||
232 | |||
233 | /** | ||
234 | * vx_set_stream_cmd_params - fill first command word for stream commands | ||
235 | * @rmh: the rmh to be modified | ||
236 | * @is_capture: 0 = playback, 1 = capture operation | ||
237 | * @pipe: the pipe index (zero-based) | ||
238 | */ | ||
239 | static inline void vx_set_stream_cmd_params(struct vx_rmh *rmh, int is_capture, int pipe) | ||
240 | { | ||
241 | if (is_capture) | ||
242 | rmh->Cmd[0] |= COMMAND_RECORD_MASK; | ||
243 | rmh->Cmd[0] |= (((u32)pipe & MASK_FIRST_FIELD) << FIELD_SIZE) & MASK_DSP_WORD; | ||
244 | } | ||
245 | |||
246 | #endif /* __VX_CMD_H */ | ||
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c new file mode 100644 index 000000000000..c6fa5afa3e9a --- /dev/null +++ b/sound/drivers/vx/vx_core.c | |||
@@ -0,0 +1,837 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Hardware core part | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/firmware.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/pcm.h> | ||
32 | #include <sound/asoundef.h> | ||
33 | #include <sound/info.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <sound/vx_core.h> | ||
36 | #include "vx_cmd.h" | ||
37 | |||
38 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
39 | MODULE_DESCRIPTION("Common routines for Digigram VX drivers"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | |||
43 | /* | ||
44 | * snd_vx_delay - delay for the specified time | ||
45 | * @xmsec: the time to delay in msec | ||
46 | */ | ||
47 | void snd_vx_delay(vx_core_t *chip, int xmsec) | ||
48 | { | ||
49 | if (! in_interrupt() && xmsec >= 1000 / HZ) | ||
50 | msleep(xmsec); | ||
51 | else | ||
52 | mdelay(xmsec); | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * vx_check_reg_bit - wait for the specified bit is set/reset on a register | ||
57 | * @reg: register to check | ||
58 | * @mask: bit mask | ||
59 | * @bit: resultant bit to be checked | ||
60 | * @time: time-out of loop in msec | ||
61 | * | ||
62 | * returns zero if a bit matches, or a negative error code. | ||
63 | */ | ||
64 | int snd_vx_check_reg_bit(vx_core_t *chip, int reg, int mask, int bit, int time) | ||
65 | { | ||
66 | unsigned long end_time = jiffies + (time * HZ + 999) / 1000; | ||
67 | #ifdef CONFIG_SND_DEBUG | ||
68 | static char *reg_names[VX_REG_MAX] = { | ||
69 | "ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL", | ||
70 | "DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ", | ||
71 | "ACQ", "BIT0", "BIT1", "MIC0", "MIC1", "MIC2", | ||
72 | "MIC3", "INTCSR", "CNTRL", "GPIOC", | ||
73 | "LOFREQ", "HIFREQ", "CSUER", "RUER" | ||
74 | }; | ||
75 | #endif | ||
76 | do { | ||
77 | if ((snd_vx_inb(chip, reg) & mask) == bit) | ||
78 | return 0; | ||
79 | //snd_vx_delay(chip, 10); | ||
80 | } while (time_after_eq(end_time, jiffies)); | ||
81 | snd_printd(KERN_DEBUG "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n", reg_names[reg], mask, snd_vx_inb(chip, reg)); | ||
82 | return -EIO; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * vx_send_irq_dsp - set command irq bit | ||
87 | * @num: the requested IRQ type, IRQ_XXX | ||
88 | * | ||
89 | * this triggers the specified IRQ request | ||
90 | * returns 0 if successful, or a negative error code. | ||
91 | * | ||
92 | */ | ||
93 | static int vx_send_irq_dsp(vx_core_t *chip, int num) | ||
94 | { | ||
95 | int nirq; | ||
96 | |||
97 | /* wait for Hc = 0 */ | ||
98 | if (snd_vx_check_reg_bit(chip, VX_CVR, CVR_HC, 0, 200) < 0) | ||
99 | return -EIO; | ||
100 | |||
101 | nirq = num; | ||
102 | if (vx_has_new_dsp(chip)) | ||
103 | nirq += VXP_IRQ_OFFSET; | ||
104 | vx_outb(chip, CVR, (nirq >> 1) | CVR_HC); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | |||
109 | /* | ||
110 | * vx_reset_chk - reset CHK bit on ISR | ||
111 | * | ||
112 | * returns 0 if successful, or a negative error code. | ||
113 | */ | ||
114 | static int vx_reset_chk(vx_core_t *chip) | ||
115 | { | ||
116 | /* Reset irq CHK */ | ||
117 | if (vx_send_irq_dsp(chip, IRQ_RESET_CHK) < 0) | ||
118 | return -EIO; | ||
119 | /* Wait until CHK = 0 */ | ||
120 | if (vx_check_isr(chip, ISR_CHK, 0, 200) < 0) | ||
121 | return -EIO; | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * vx_transfer_end - terminate message transfer | ||
127 | * @cmd: IRQ message to send (IRQ_MESS_XXX_END) | ||
128 | * | ||
129 | * returns 0 if successful, or a negative error code. | ||
130 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
131 | * NB: call with spinlock held! | ||
132 | */ | ||
133 | static int vx_transfer_end(vx_core_t *chip, int cmd) | ||
134 | { | ||
135 | int err; | ||
136 | |||
137 | if ((err = vx_reset_chk(chip)) < 0) | ||
138 | return err; | ||
139 | |||
140 | /* irq MESS_READ/WRITE_END */ | ||
141 | if ((err = vx_send_irq_dsp(chip, cmd)) < 0) | ||
142 | return err; | ||
143 | |||
144 | /* Wait CHK = 1 */ | ||
145 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
146 | return err; | ||
147 | |||
148 | /* If error, Read RX */ | ||
149 | if ((err = vx_inb(chip, ISR)) & ISR_ERR) { | ||
150 | if ((err = vx_wait_for_rx_full(chip)) < 0) { | ||
151 | snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n"); | ||
152 | return err; | ||
153 | } | ||
154 | err = vx_inb(chip, RXH) << 16; | ||
155 | err |= vx_inb(chip, RXM) << 8; | ||
156 | err |= vx_inb(chip, RXL); | ||
157 | snd_printd(KERN_DEBUG "transfer_end: error = 0x%x\n", err); | ||
158 | return -(VX_ERR_MASK | err); | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * vx_read_status - return the status rmh | ||
165 | * @rmh: rmh record to store the status | ||
166 | * | ||
167 | * returns 0 if successful, or a negative error code. | ||
168 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
169 | * NB: call with spinlock held! | ||
170 | */ | ||
171 | static int vx_read_status(vx_core_t *chip, struct vx_rmh *rmh) | ||
172 | { | ||
173 | int i, err, val, size; | ||
174 | |||
175 | /* no read necessary? */ | ||
176 | if (rmh->DspStat == RMH_SSIZE_FIXED && rmh->LgStat == 0) | ||
177 | return 0; | ||
178 | |||
179 | /* Wait for RX full (with timeout protection) | ||
180 | * The first word of status is in RX | ||
181 | */ | ||
182 | err = vx_wait_for_rx_full(chip); | ||
183 | if (err < 0) | ||
184 | return err; | ||
185 | |||
186 | /* Read RX */ | ||
187 | val = vx_inb(chip, RXH) << 16; | ||
188 | val |= vx_inb(chip, RXM) << 8; | ||
189 | val |= vx_inb(chip, RXL); | ||
190 | |||
191 | /* If status given by DSP, let's decode its size */ | ||
192 | switch (rmh->DspStat) { | ||
193 | case RMH_SSIZE_ARG: | ||
194 | size = val & 0xff; | ||
195 | rmh->Stat[0] = val & 0xffff00; | ||
196 | rmh->LgStat = size + 1; | ||
197 | break; | ||
198 | case RMH_SSIZE_MASK: | ||
199 | /* Let's count the arg numbers from a mask */ | ||
200 | rmh->Stat[0] = val; | ||
201 | size = 0; | ||
202 | while (val) { | ||
203 | if (val & 0x01) | ||
204 | size++; | ||
205 | val >>= 1; | ||
206 | } | ||
207 | rmh->LgStat = size + 1; | ||
208 | break; | ||
209 | default: | ||
210 | /* else retrieve the status length given by the driver */ | ||
211 | size = rmh->LgStat; | ||
212 | rmh->Stat[0] = val; /* Val is the status 1st word */ | ||
213 | size--; /* hence adjust remaining length */ | ||
214 | break; | ||
215 | } | ||
216 | |||
217 | if (size < 1) | ||
218 | return 0; | ||
219 | snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL); | ||
220 | |||
221 | for (i = 1; i <= size; i++) { | ||
222 | /* trigger an irq MESS_WRITE_NEXT */ | ||
223 | err = vx_send_irq_dsp(chip, IRQ_MESS_WRITE_NEXT); | ||
224 | if (err < 0) | ||
225 | return err; | ||
226 | /* Wait for RX full (with timeout protection) */ | ||
227 | err = vx_wait_for_rx_full(chip); | ||
228 | if (err < 0) | ||
229 | return err; | ||
230 | rmh->Stat[i] = vx_inb(chip, RXH) << 16; | ||
231 | rmh->Stat[i] |= vx_inb(chip, RXM) << 8; | ||
232 | rmh->Stat[i] |= vx_inb(chip, RXL); | ||
233 | } | ||
234 | |||
235 | return vx_transfer_end(chip, IRQ_MESS_WRITE_END); | ||
236 | } | ||
237 | |||
238 | |||
239 | #define MASK_MORE_THAN_1_WORD_COMMAND 0x00008000 | ||
240 | #define MASK_1_WORD_COMMAND 0x00ff7fff | ||
241 | |||
242 | /* | ||
243 | * vx_send_msg_nolock - send a DSP message and read back the status | ||
244 | * @rmh: the rmh record to send and receive | ||
245 | * | ||
246 | * returns 0 if successful, or a negative error code. | ||
247 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
248 | * | ||
249 | * this function doesn't call spinlock at all. | ||
250 | */ | ||
251 | int vx_send_msg_nolock(vx_core_t *chip, struct vx_rmh *rmh) | ||
252 | { | ||
253 | int i, err; | ||
254 | |||
255 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
256 | return -EBUSY; | ||
257 | |||
258 | if ((err = vx_reset_chk(chip)) < 0) { | ||
259 | snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n"); | ||
260 | return err; | ||
261 | } | ||
262 | |||
263 | #if 0 | ||
264 | printk(KERN_DEBUG "rmh: cmd = 0x%06x, length = %d, stype = %d\n", | ||
265 | rmh->Cmd[0], rmh->LgCmd, rmh->DspStat); | ||
266 | if (rmh->LgCmd > 1) { | ||
267 | printk(KERN_DEBUG " "); | ||
268 | for (i = 1; i < rmh->LgCmd; i++) | ||
269 | printk("0x%06x ", rmh->Cmd[i]); | ||
270 | printk("\n"); | ||
271 | } | ||
272 | #endif | ||
273 | /* Check bit M is set according to length of the command */ | ||
274 | if (rmh->LgCmd > 1) | ||
275 | rmh->Cmd[0] |= MASK_MORE_THAN_1_WORD_COMMAND; | ||
276 | else | ||
277 | rmh->Cmd[0] &= MASK_1_WORD_COMMAND; | ||
278 | |||
279 | /* Wait for TX empty */ | ||
280 | if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) { | ||
281 | snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n"); | ||
282 | return err; | ||
283 | } | ||
284 | |||
285 | /* Write Cmd[0] */ | ||
286 | vx_outb(chip, TXH, (rmh->Cmd[0] >> 16) & 0xff); | ||
287 | vx_outb(chip, TXM, (rmh->Cmd[0] >> 8) & 0xff); | ||
288 | vx_outb(chip, TXL, rmh->Cmd[0] & 0xff); | ||
289 | |||
290 | /* Trigger irq MESSAGE */ | ||
291 | if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) { | ||
292 | snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n"); | ||
293 | return err; | ||
294 | } | ||
295 | |||
296 | /* Wait for CHK = 1 */ | ||
297 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
298 | return err; | ||
299 | |||
300 | /* If error, get error value from RX */ | ||
301 | if (vx_inb(chip, ISR) & ISR_ERR) { | ||
302 | if ((err = vx_wait_for_rx_full(chip)) < 0) { | ||
303 | snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n"); | ||
304 | return err; | ||
305 | } | ||
306 | err = vx_inb(chip, RXH) << 16; | ||
307 | err |= vx_inb(chip, RXM) << 8; | ||
308 | err |= vx_inb(chip, RXL); | ||
309 | snd_printd(KERN_DEBUG "msg got error = 0x%x at cmd[0]\n", err); | ||
310 | err = -(VX_ERR_MASK | err); | ||
311 | return err; | ||
312 | } | ||
313 | |||
314 | /* Send the other words */ | ||
315 | if (rmh->LgCmd > 1) { | ||
316 | for (i = 1; i < rmh->LgCmd; i++) { | ||
317 | /* Wait for TX ready */ | ||
318 | if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) { | ||
319 | snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n"); | ||
320 | return err; | ||
321 | } | ||
322 | |||
323 | /* Write Cmd[i] */ | ||
324 | vx_outb(chip, TXH, (rmh->Cmd[i] >> 16) & 0xff); | ||
325 | vx_outb(chip, TXM, (rmh->Cmd[i] >> 8) & 0xff); | ||
326 | vx_outb(chip, TXL, rmh->Cmd[i] & 0xff); | ||
327 | |||
328 | /* Trigger irq MESS_READ_NEXT */ | ||
329 | if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) { | ||
330 | snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n"); | ||
331 | return err; | ||
332 | } | ||
333 | } | ||
334 | /* Wait for TX empty */ | ||
335 | if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) { | ||
336 | snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n"); | ||
337 | return err; | ||
338 | } | ||
339 | /* End of transfer */ | ||
340 | err = vx_transfer_end(chip, IRQ_MESS_READ_END); | ||
341 | if (err < 0) | ||
342 | return err; | ||
343 | } | ||
344 | |||
345 | return vx_read_status(chip, rmh); | ||
346 | } | ||
347 | |||
348 | |||
349 | /* | ||
350 | * vx_send_msg - send a DSP message with spinlock | ||
351 | * @rmh: the rmh record to send and receive | ||
352 | * | ||
353 | * returns 0 if successful, or a negative error code. | ||
354 | * see vx_send_msg_nolock(). | ||
355 | */ | ||
356 | int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh) | ||
357 | { | ||
358 | unsigned long flags; | ||
359 | int err; | ||
360 | |||
361 | spin_lock_irqsave(&chip->lock, flags); | ||
362 | err = vx_send_msg_nolock(chip, rmh); | ||
363 | spin_unlock_irqrestore(&chip->lock, flags); | ||
364 | return err; | ||
365 | } | ||
366 | |||
367 | |||
368 | /* | ||
369 | * vx_send_rih_nolock - send an RIH to xilinx | ||
370 | * @cmd: the command to send | ||
371 | * | ||
372 | * returns 0 if successful, or a negative error code. | ||
373 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
374 | * | ||
375 | * this function doesn't call spinlock at all. | ||
376 | * | ||
377 | * unlike RMH, no command is sent to DSP. | ||
378 | */ | ||
379 | int vx_send_rih_nolock(vx_core_t *chip, int cmd) | ||
380 | { | ||
381 | int err; | ||
382 | |||
383 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
384 | return -EBUSY; | ||
385 | |||
386 | #if 0 | ||
387 | printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd); | ||
388 | #endif | ||
389 | if ((err = vx_reset_chk(chip)) < 0) | ||
390 | return err; | ||
391 | /* send the IRQ */ | ||
392 | if ((err = vx_send_irq_dsp(chip, cmd)) < 0) | ||
393 | return err; | ||
394 | /* Wait CHK = 1 */ | ||
395 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
396 | return err; | ||
397 | /* If error, read RX */ | ||
398 | if (vx_inb(chip, ISR) & ISR_ERR) { | ||
399 | if ((err = vx_wait_for_rx_full(chip)) < 0) | ||
400 | return err; | ||
401 | err = vx_inb(chip, RXH) << 16; | ||
402 | err |= vx_inb(chip, RXM) << 8; | ||
403 | err |= vx_inb(chip, RXL); | ||
404 | return -(VX_ERR_MASK | err); | ||
405 | } | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | |||
410 | /* | ||
411 | * vx_send_rih - send an RIH with spinlock | ||
412 | * @cmd: the command to send | ||
413 | * | ||
414 | * see vx_send_rih_nolock(). | ||
415 | */ | ||
416 | int vx_send_rih(vx_core_t *chip, int cmd) | ||
417 | { | ||
418 | unsigned long flags; | ||
419 | int err; | ||
420 | |||
421 | spin_lock_irqsave(&chip->lock, flags); | ||
422 | err = vx_send_rih_nolock(chip, cmd); | ||
423 | spin_unlock_irqrestore(&chip->lock, flags); | ||
424 | return err; | ||
425 | } | ||
426 | |||
427 | #define END_OF_RESET_WAIT_TIME 500 /* us */ | ||
428 | |||
429 | /** | ||
430 | * snd_vx_boot_xilinx - boot up the xilinx interface | ||
431 | * @boot: the boot record to load | ||
432 | */ | ||
433 | int snd_vx_load_boot_image(vx_core_t *chip, const struct firmware *boot) | ||
434 | { | ||
435 | unsigned int i; | ||
436 | int no_fillup = vx_has_new_dsp(chip); | ||
437 | |||
438 | /* check the length of boot image */ | ||
439 | snd_assert(boot->size > 0, return -EINVAL); | ||
440 | snd_assert(boot->size % 3 == 0, return -EINVAL); | ||
441 | #if 0 | ||
442 | { | ||
443 | /* more strict check */ | ||
444 | unsigned int c = ((u32)boot->data[0] << 16) | ((u32)boot->data[1] << 8) | boot->data[2]; | ||
445 | snd_assert(boot->size == (c + 2) * 3, return -EINVAL); | ||
446 | } | ||
447 | #endif | ||
448 | |||
449 | /* reset dsp */ | ||
450 | vx_reset_dsp(chip); | ||
451 | |||
452 | udelay(END_OF_RESET_WAIT_TIME); /* another wait? */ | ||
453 | |||
454 | /* download boot strap */ | ||
455 | for (i = 0; i < 0x600; i += 3) { | ||
456 | if (i >= boot->size) { | ||
457 | if (no_fillup) | ||
458 | break; | ||
459 | if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) { | ||
460 | snd_printk(KERN_ERR "dsp boot failed at %d\n", i); | ||
461 | return -EIO; | ||
462 | } | ||
463 | vx_outb(chip, TXH, 0); | ||
464 | vx_outb(chip, TXM, 0); | ||
465 | vx_outb(chip, TXL, 0); | ||
466 | } else { | ||
467 | unsigned char *image = boot->data + i; | ||
468 | if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) { | ||
469 | snd_printk(KERN_ERR "dsp boot failed at %d\n", i); | ||
470 | return -EIO; | ||
471 | } | ||
472 | vx_outb(chip, TXH, image[0]); | ||
473 | vx_outb(chip, TXM, image[1]); | ||
474 | vx_outb(chip, TXL, image[2]); | ||
475 | } | ||
476 | } | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * vx_test_irq_src - query the source of interrupts | ||
482 | * | ||
483 | * called from irq handler only | ||
484 | */ | ||
485 | static int vx_test_irq_src(vx_core_t *chip, unsigned int *ret) | ||
486 | { | ||
487 | int err; | ||
488 | |||
489 | vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT); | ||
490 | spin_lock(&chip->lock); | ||
491 | err = vx_send_msg_nolock(chip, &chip->irq_rmh); | ||
492 | if (err < 0) | ||
493 | *ret = 0; | ||
494 | else | ||
495 | *ret = chip->irq_rmh.Stat[0]; | ||
496 | spin_unlock(&chip->lock); | ||
497 | return err; | ||
498 | } | ||
499 | |||
500 | |||
501 | /* | ||
502 | * vx_interrupt - soft irq handler | ||
503 | */ | ||
504 | static void vx_interrupt(unsigned long private_data) | ||
505 | { | ||
506 | vx_core_t *chip = (vx_core_t *) private_data; | ||
507 | unsigned int events; | ||
508 | |||
509 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
510 | return; | ||
511 | |||
512 | if (vx_test_irq_src(chip, &events) < 0) | ||
513 | return; | ||
514 | |||
515 | #if 0 | ||
516 | if (events & 0x000800) | ||
517 | printk(KERN_ERR "DSP Stream underrun ! IRQ events = 0x%x\n", events); | ||
518 | #endif | ||
519 | // printk(KERN_DEBUG "IRQ events = 0x%x\n", events); | ||
520 | |||
521 | /* We must prevent any application using this DSP | ||
522 | * and block any further request until the application | ||
523 | * either unregisters or reloads the DSP | ||
524 | */ | ||
525 | if (events & FATAL_DSP_ERROR) { | ||
526 | snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n"); | ||
527 | return; | ||
528 | } | ||
529 | |||
530 | /* The start on time code conditions are filled (ie the time code | ||
531 | * received by the board is equal to one of those given to it). | ||
532 | */ | ||
533 | if (events & TIME_CODE_EVENT_PENDING) | ||
534 | ; /* so far, nothing to do yet */ | ||
535 | |||
536 | /* The frequency has changed on the board (UER mode). */ | ||
537 | if (events & FREQUENCY_CHANGE_EVENT_PENDING) | ||
538 | vx_change_frequency(chip); | ||
539 | |||
540 | /* update the pcm streams */ | ||
541 | vx_pcm_update_intr(chip, events); | ||
542 | } | ||
543 | |||
544 | |||
545 | /** | ||
546 | * snd_vx_irq_handler - interrupt handler | ||
547 | */ | ||
548 | irqreturn_t snd_vx_irq_handler(int irq, void *dev, struct pt_regs *regs) | ||
549 | { | ||
550 | vx_core_t *chip = dev; | ||
551 | |||
552 | if (! (chip->chip_status & VX_STAT_CHIP_INIT) || | ||
553 | (chip->chip_status & VX_STAT_IS_STALE)) | ||
554 | return IRQ_NONE; | ||
555 | if (! vx_test_and_ack(chip)) | ||
556 | tasklet_hi_schedule(&chip->tq); | ||
557 | return IRQ_HANDLED; | ||
558 | } | ||
559 | |||
560 | |||
561 | /* | ||
562 | */ | ||
563 | static void vx_reset_board(vx_core_t *chip, int cold_reset) | ||
564 | { | ||
565 | snd_assert(chip->ops->reset_board, return); | ||
566 | |||
567 | /* current source, later sync'ed with target */ | ||
568 | chip->audio_source = VX_AUDIO_SRC_LINE; | ||
569 | if (cold_reset) { | ||
570 | chip->audio_source_target = chip->audio_source; | ||
571 | chip->clock_source = INTERNAL_QUARTZ; | ||
572 | chip->clock_mode = VX_CLOCK_MODE_AUTO; | ||
573 | chip->freq = 48000; | ||
574 | chip->uer_detected = VX_UER_MODE_NOT_PRESENT; | ||
575 | chip->uer_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; | ||
576 | } | ||
577 | |||
578 | chip->ops->reset_board(chip, cold_reset); | ||
579 | |||
580 | vx_reset_codec(chip, cold_reset); | ||
581 | |||
582 | vx_set_internal_clock(chip, chip->freq); | ||
583 | |||
584 | /* Reset the DSP */ | ||
585 | vx_reset_dsp(chip); | ||
586 | |||
587 | if (vx_is_pcmcia(chip)) { | ||
588 | /* Acknowledge any pending IRQ and reset the MEMIRQ flag. */ | ||
589 | vx_test_and_ack(chip); | ||
590 | vx_validate_irq(chip, 1); | ||
591 | } | ||
592 | |||
593 | /* init CBits */ | ||
594 | vx_set_iec958_status(chip, chip->uer_bits); | ||
595 | } | ||
596 | |||
597 | |||
598 | /* | ||
599 | * proc interface | ||
600 | */ | ||
601 | |||
602 | static void vx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
603 | { | ||
604 | vx_core_t *chip = entry->private_data; | ||
605 | static char *audio_src_vxp[] = { "Line", "Mic", "Digital" }; | ||
606 | static char *audio_src_vx2[] = { "Analog", "Analog", "Digital" }; | ||
607 | static char *clock_mode[] = { "Auto", "Internal", "External" }; | ||
608 | static char *clock_src[] = { "Internal", "External" }; | ||
609 | static char *uer_type[] = { "Consumer", "Professional", "Not Present" }; | ||
610 | |||
611 | snd_iprintf(buffer, "%s\n", chip->card->longname); | ||
612 | snd_iprintf(buffer, "Xilinx Firmware: %s\n", | ||
613 | chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No"); | ||
614 | snd_iprintf(buffer, "Device Initialized: %s\n", | ||
615 | chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No"); | ||
616 | snd_iprintf(buffer, "DSP audio info:"); | ||
617 | if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME) | ||
618 | snd_iprintf(buffer, " realtime"); | ||
619 | if (chip->audio_info & VX_AUDIO_INFO_OFFLINE) | ||
620 | snd_iprintf(buffer, " offline"); | ||
621 | if (chip->audio_info & VX_AUDIO_INFO_MPEG1) | ||
622 | snd_iprintf(buffer, " mpeg1"); | ||
623 | if (chip->audio_info & VX_AUDIO_INFO_MPEG2) | ||
624 | snd_iprintf(buffer, " mpeg2"); | ||
625 | if (chip->audio_info & VX_AUDIO_INFO_LINEAR_8) | ||
626 | snd_iprintf(buffer, " linear8"); | ||
627 | if (chip->audio_info & VX_AUDIO_INFO_LINEAR_16) | ||
628 | snd_iprintf(buffer, " linear16"); | ||
629 | if (chip->audio_info & VX_AUDIO_INFO_LINEAR_24) | ||
630 | snd_iprintf(buffer, " linear24"); | ||
631 | snd_iprintf(buffer, "\n"); | ||
632 | snd_iprintf(buffer, "Input Source: %s\n", vx_is_pcmcia(chip) ? | ||
633 | audio_src_vxp[chip->audio_source] : | ||
634 | audio_src_vx2[chip->audio_source]); | ||
635 | snd_iprintf(buffer, "Clock Mode: %s\n", clock_mode[chip->clock_mode]); | ||
636 | snd_iprintf(buffer, "Clock Source: %s\n", clock_src[chip->clock_source]); | ||
637 | snd_iprintf(buffer, "Frequency: %d\n", chip->freq); | ||
638 | snd_iprintf(buffer, "Detected Frequency: %d\n", chip->freq_detected); | ||
639 | snd_iprintf(buffer, "Detected UER type: %s\n", uer_type[chip->uer_detected]); | ||
640 | snd_iprintf(buffer, "Min/Max/Cur IBL: %d/%d/%d (granularity=%d)\n", | ||
641 | chip->ibl.min_size, chip->ibl.max_size, chip->ibl.size, | ||
642 | chip->ibl.granularity); | ||
643 | } | ||
644 | |||
645 | static void vx_proc_init(vx_core_t *chip) | ||
646 | { | ||
647 | snd_info_entry_t *entry; | ||
648 | |||
649 | if (! snd_card_proc_new(chip->card, "vx-status", &entry)) | ||
650 | snd_info_set_text_ops(entry, chip, 1024, vx_proc_read); | ||
651 | } | ||
652 | |||
653 | |||
654 | /** | ||
655 | * snd_vx_dsp_boot - load the DSP boot | ||
656 | */ | ||
657 | int snd_vx_dsp_boot(vx_core_t *chip, const struct firmware *boot) | ||
658 | { | ||
659 | int err; | ||
660 | int cold_reset = !(chip->chip_status & VX_STAT_DEVICE_INIT); | ||
661 | |||
662 | vx_reset_board(chip, cold_reset); | ||
663 | vx_validate_irq(chip, 0); | ||
664 | |||
665 | if ((err = snd_vx_load_boot_image(chip, boot)) < 0) | ||
666 | return err; | ||
667 | snd_vx_delay(chip, 10); | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * snd_vx_dsp_load - load the DSP image | ||
674 | */ | ||
675 | int snd_vx_dsp_load(vx_core_t *chip, const struct firmware *dsp) | ||
676 | { | ||
677 | unsigned int i; | ||
678 | int err; | ||
679 | unsigned int csum = 0; | ||
680 | unsigned char *image, *cptr; | ||
681 | |||
682 | snd_assert(dsp->size % 3 == 0, return -EINVAL); | ||
683 | |||
684 | vx_toggle_dac_mute(chip, 1); | ||
685 | |||
686 | /* Transfert data buffer from PC to DSP */ | ||
687 | for (i = 0; i < dsp->size; i += 3) { | ||
688 | image = dsp->data + i; | ||
689 | /* Wait DSP ready for a new read */ | ||
690 | if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) { | ||
691 | printk("dsp loading error at position %d\n", i); | ||
692 | return err; | ||
693 | } | ||
694 | cptr = image; | ||
695 | csum ^= *cptr; | ||
696 | csum = (csum >> 24) | (csum << 8); | ||
697 | vx_outb(chip, TXH, *cptr++); | ||
698 | csum ^= *cptr; | ||
699 | csum = (csum >> 24) | (csum << 8); | ||
700 | vx_outb(chip, TXM, *cptr++); | ||
701 | csum ^= *cptr; | ||
702 | csum = (csum >> 24) | (csum << 8); | ||
703 | vx_outb(chip, TXL, *cptr++); | ||
704 | } | ||
705 | snd_printdd(KERN_DEBUG "checksum = 0x%08x\n", csum); | ||
706 | |||
707 | snd_vx_delay(chip, 200); | ||
708 | |||
709 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
710 | return err; | ||
711 | |||
712 | vx_toggle_dac_mute(chip, 0); | ||
713 | |||
714 | vx_test_and_ack(chip); | ||
715 | vx_validate_irq(chip, 1); | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | #ifdef CONFIG_PM | ||
721 | /* | ||
722 | * suspend | ||
723 | */ | ||
724 | static int snd_vx_suspend(snd_card_t *card, pm_message_t state) | ||
725 | { | ||
726 | vx_core_t *chip = card->pm_private_data; | ||
727 | unsigned int i; | ||
728 | |||
729 | snd_assert(chip, return -EINVAL); | ||
730 | |||
731 | chip->chip_status |= VX_STAT_IN_SUSPEND; | ||
732 | for (i = 0; i < chip->hw->num_codecs; i++) | ||
733 | snd_pcm_suspend_all(chip->pcm[i]); | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * resume | ||
740 | */ | ||
741 | static int snd_vx_resume(snd_card_t *card) | ||
742 | { | ||
743 | vx_core_t *chip = card->pm_private_data; | ||
744 | int i, err; | ||
745 | |||
746 | snd_assert(chip, return -EINVAL); | ||
747 | |||
748 | chip->chip_status &= ~VX_STAT_CHIP_INIT; | ||
749 | |||
750 | for (i = 0; i < 4; i++) { | ||
751 | if (! chip->firmware[i]) | ||
752 | continue; | ||
753 | err = chip->ops->load_dsp(chip, i, chip->firmware[i]); | ||
754 | if (err < 0) { | ||
755 | snd_printk(KERN_ERR "vx: firmware resume error at DSP %d\n", i); | ||
756 | return -EIO; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | chip->chip_status |= VX_STAT_CHIP_INIT; | ||
761 | chip->chip_status &= ~VX_STAT_IN_SUSPEND; | ||
762 | |||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | #endif | ||
767 | |||
768 | /** | ||
769 | * snd_vx_create - constructor for vx_core_t | ||
770 | * @hw: hardware specific record | ||
771 | * | ||
772 | * this function allocates the instance and prepare for the hardware | ||
773 | * initialization. | ||
774 | * | ||
775 | * return the instance pointer if successful, NULL in error. | ||
776 | */ | ||
777 | vx_core_t *snd_vx_create(snd_card_t *card, struct snd_vx_hardware *hw, | ||
778 | struct snd_vx_ops *ops, | ||
779 | int extra_size) | ||
780 | { | ||
781 | vx_core_t *chip; | ||
782 | |||
783 | snd_assert(card && hw && ops, return NULL); | ||
784 | |||
785 | chip = kcalloc(1, sizeof(*chip) + extra_size, GFP_KERNEL); | ||
786 | if (! chip) { | ||
787 | snd_printk(KERN_ERR "vx_core: no memory\n"); | ||
788 | return NULL; | ||
789 | } | ||
790 | spin_lock_init(&chip->lock); | ||
791 | spin_lock_init(&chip->irq_lock); | ||
792 | chip->irq = -1; | ||
793 | chip->hw = hw; | ||
794 | chip->type = hw->type; | ||
795 | chip->ops = ops; | ||
796 | tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip); | ||
797 | init_MUTEX(&chip->mixer_mutex); | ||
798 | |||
799 | chip->card = card; | ||
800 | card->private_data = chip; | ||
801 | strcpy(card->driver, hw->name); | ||
802 | sprintf(card->shortname, "Digigram %s", hw->name); | ||
803 | |||
804 | snd_card_set_pm_callback(card, snd_vx_suspend, snd_vx_resume, chip); | ||
805 | |||
806 | vx_proc_init(chip); | ||
807 | |||
808 | return chip; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * module entries | ||
813 | */ | ||
814 | static int __init alsa_vx_core_init(void) | ||
815 | { | ||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | static void __exit alsa_vx_core_exit(void) | ||
820 | { | ||
821 | } | ||
822 | |||
823 | module_init(alsa_vx_core_init) | ||
824 | module_exit(alsa_vx_core_exit) | ||
825 | |||
826 | /* | ||
827 | * exports | ||
828 | */ | ||
829 | EXPORT_SYMBOL(snd_vx_check_reg_bit); | ||
830 | EXPORT_SYMBOL(snd_vx_create); | ||
831 | EXPORT_SYMBOL(snd_vx_setup_firmware); | ||
832 | EXPORT_SYMBOL(snd_vx_free_firmware); | ||
833 | EXPORT_SYMBOL(snd_vx_irq_handler); | ||
834 | EXPORT_SYMBOL(snd_vx_delay); | ||
835 | EXPORT_SYMBOL(snd_vx_dsp_boot); | ||
836 | EXPORT_SYMBOL(snd_vx_dsp_load); | ||
837 | EXPORT_SYMBOL(snd_vx_load_boot_image); | ||
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c new file mode 100644 index 000000000000..9a3dc3c3b3de --- /dev/null +++ b/sound/drivers/vx/vx_hwdep.c | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * DSP firmware management | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/device.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/hwdep.h> | ||
28 | #include <sound/vx_core.h> | ||
29 | |||
30 | #ifdef SND_VX_FW_LOADER | ||
31 | |||
32 | int snd_vx_setup_firmware(vx_core_t *chip) | ||
33 | { | ||
34 | static char *fw_files[VX_TYPE_NUMS][4] = { | ||
35 | [VX_TYPE_BOARD] = { | ||
36 | NULL, "x1_1_vx2.xlx", "bd56002.boot", "l_1_vx2.d56", | ||
37 | }, | ||
38 | [VX_TYPE_V2] = { | ||
39 | NULL, "x1_2_v22.xlx", "bd563v2.boot", "l_1_v22.d56", | ||
40 | }, | ||
41 | [VX_TYPE_MIC] = { | ||
42 | NULL, "x1_2_v22.xlx", "bd563v2.boot", "l_1_v22.d56", | ||
43 | }, | ||
44 | [VX_TYPE_VXPOCKET] = { | ||
45 | "bx_1_vxp.b56", "x1_1_vxp.xlx", "bd563s3.boot", "l_1_vxp.d56" | ||
46 | }, | ||
47 | [VX_TYPE_VXP440] = { | ||
48 | "bx_1_vp4.b56", "x1_1_vp4.xlx", "bd563s3.boot", "l_1_vp4.d56" | ||
49 | }, | ||
50 | }; | ||
51 | |||
52 | int i, err; | ||
53 | |||
54 | for (i = 0; i < 4; i++) { | ||
55 | char path[32]; | ||
56 | const struct firmware *fw; | ||
57 | if (! fw_files[chip->type][i]) | ||
58 | continue; | ||
59 | sprintf(path, "vx/%s", fw_files[chip->type][i]); | ||
60 | if (request_firmware(&fw, path, chip->dev)) { | ||
61 | snd_printk(KERN_ERR "vx: can't load firmware %s\n", path); | ||
62 | return -ENOENT; | ||
63 | } | ||
64 | err = chip->ops->load_dsp(chip, i, fw); | ||
65 | if (err < 0) { | ||
66 | release_firmware(fw); | ||
67 | return err; | ||
68 | } | ||
69 | if (i == 1) | ||
70 | chip->chip_status |= VX_STAT_XILINX_LOADED; | ||
71 | #ifdef CONFIG_PM | ||
72 | chip->firmware[i] = fw; | ||
73 | #else | ||
74 | release_firmware(fw); | ||
75 | #endif | ||
76 | } | ||
77 | |||
78 | /* ok, we reached to the last one */ | ||
79 | /* create the devices if not built yet */ | ||
80 | if ((err = snd_vx_pcm_new(chip)) < 0) | ||
81 | return err; | ||
82 | |||
83 | if ((err = snd_vx_mixer_new(chip)) < 0) | ||
84 | return err; | ||
85 | |||
86 | if (chip->ops->add_controls) | ||
87 | if ((err = chip->ops->add_controls(chip)) < 0) | ||
88 | return err; | ||
89 | |||
90 | chip->chip_status |= VX_STAT_DEVICE_INIT; | ||
91 | chip->chip_status |= VX_STAT_CHIP_INIT; | ||
92 | |||
93 | return snd_card_register(chip->card); | ||
94 | } | ||
95 | |||
96 | /* exported */ | ||
97 | void snd_vx_free_firmware(vx_core_t *chip) | ||
98 | { | ||
99 | #ifdef CONFIG_PM | ||
100 | int i; | ||
101 | for (i = 0; i < 4; i++) | ||
102 | release_firmware(chip->firmware[i]); | ||
103 | #endif | ||
104 | } | ||
105 | |||
106 | #else /* old style firmware loading */ | ||
107 | |||
108 | static int vx_hwdep_open(snd_hwdep_t *hw, struct file *file) | ||
109 | { | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int vx_hwdep_release(snd_hwdep_t *hw, struct file *file) | ||
114 | { | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int vx_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info) | ||
119 | { | ||
120 | static char *type_ids[VX_TYPE_NUMS] = { | ||
121 | [VX_TYPE_BOARD] = "vxboard", | ||
122 | [VX_TYPE_V2] = "vx222", | ||
123 | [VX_TYPE_MIC] = "vx222", | ||
124 | [VX_TYPE_VXPOCKET] = "vxpocket", | ||
125 | [VX_TYPE_VXP440] = "vxp440", | ||
126 | }; | ||
127 | vx_core_t *vx = hw->private_data; | ||
128 | |||
129 | snd_assert(type_ids[vx->type], return -EINVAL); | ||
130 | strcpy(info->id, type_ids[vx->type]); | ||
131 | if (vx_is_pcmcia(vx)) | ||
132 | info->num_dsps = 4; | ||
133 | else | ||
134 | info->num_dsps = 3; | ||
135 | if (vx->chip_status & VX_STAT_CHIP_INIT) | ||
136 | info->chip_ready = 1; | ||
137 | info->version = VX_DRIVER_VERSION; | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static void free_fw(const struct firmware *fw) | ||
142 | { | ||
143 | if (fw) { | ||
144 | vfree(fw->data); | ||
145 | kfree(fw); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | static int vx_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) | ||
150 | { | ||
151 | vx_core_t *vx = hw->private_data; | ||
152 | int index, err; | ||
153 | struct firmware *fw; | ||
154 | |||
155 | snd_assert(vx->ops->load_dsp, return -ENXIO); | ||
156 | |||
157 | fw = kmalloc(sizeof(*fw), GFP_KERNEL); | ||
158 | if (! fw) { | ||
159 | snd_printk(KERN_ERR "cannot allocate firmware\n"); | ||
160 | return -ENOMEM; | ||
161 | } | ||
162 | fw->size = dsp->length; | ||
163 | fw->data = vmalloc(fw->size); | ||
164 | if (! fw->data) { | ||
165 | snd_printk(KERN_ERR "cannot allocate firmware image (length=%d)\n", | ||
166 | (int)fw->size); | ||
167 | kfree(fw); | ||
168 | return -ENOMEM; | ||
169 | } | ||
170 | if (copy_from_user(fw->data, dsp->image, dsp->length)) { | ||
171 | free_fw(fw); | ||
172 | return -EFAULT; | ||
173 | } | ||
174 | |||
175 | index = dsp->index; | ||
176 | if (! vx_is_pcmcia(vx)) | ||
177 | index++; | ||
178 | err = vx->ops->load_dsp(vx, index, fw); | ||
179 | if (err < 0) { | ||
180 | free_fw(fw); | ||
181 | return err; | ||
182 | } | ||
183 | #ifdef CONFIG_PM | ||
184 | vx->firmware[index] = fw; | ||
185 | #else | ||
186 | free_fw(fw); | ||
187 | #endif | ||
188 | |||
189 | if (index == 1) | ||
190 | vx->chip_status |= VX_STAT_XILINX_LOADED; | ||
191 | if (index < 3) | ||
192 | return 0; | ||
193 | |||
194 | /* ok, we reached to the last one */ | ||
195 | /* create the devices if not built yet */ | ||
196 | if (! (vx->chip_status & VX_STAT_DEVICE_INIT)) { | ||
197 | if ((err = snd_vx_pcm_new(vx)) < 0) | ||
198 | return err; | ||
199 | |||
200 | if ((err = snd_vx_mixer_new(vx)) < 0) | ||
201 | return err; | ||
202 | |||
203 | if (vx->ops->add_controls) | ||
204 | if ((err = vx->ops->add_controls(vx)) < 0) | ||
205 | return err; | ||
206 | |||
207 | if ((err = snd_card_register(vx->card)) < 0) | ||
208 | return err; | ||
209 | |||
210 | vx->chip_status |= VX_STAT_DEVICE_INIT; | ||
211 | } | ||
212 | vx->chip_status |= VX_STAT_CHIP_INIT; | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | |||
217 | /* exported */ | ||
218 | int snd_vx_setup_firmware(vx_core_t *chip) | ||
219 | { | ||
220 | int err; | ||
221 | snd_hwdep_t *hw; | ||
222 | |||
223 | if ((err = snd_hwdep_new(chip->card, SND_VX_HWDEP_ID, 0, &hw)) < 0) | ||
224 | return err; | ||
225 | |||
226 | hw->iface = SNDRV_HWDEP_IFACE_VX; | ||
227 | hw->private_data = chip; | ||
228 | hw->ops.open = vx_hwdep_open; | ||
229 | hw->ops.release = vx_hwdep_release; | ||
230 | hw->ops.dsp_status = vx_hwdep_dsp_status; | ||
231 | hw->ops.dsp_load = vx_hwdep_dsp_load; | ||
232 | hw->exclusive = 1; | ||
233 | sprintf(hw->name, "VX Loader (%s)", chip->card->driver); | ||
234 | chip->hwdep = hw; | ||
235 | |||
236 | return snd_card_register(chip->card); | ||
237 | } | ||
238 | |||
239 | /* exported */ | ||
240 | void snd_vx_free_firmware(vx_core_t *chip) | ||
241 | { | ||
242 | #ifdef CONFIG_PM | ||
243 | int i; | ||
244 | for (i = 0; i < 4; i++) | ||
245 | free_fw(chip->firmware[i]); | ||
246 | #endif | ||
247 | } | ||
248 | |||
249 | #endif /* SND_VX_FW_LOADER */ | ||
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c new file mode 100644 index 000000000000..f00c88886460 --- /dev/null +++ b/sound/drivers/vx/vx_mixer.c | |||
@@ -0,0 +1,1000 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Common mixer part | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/control.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | |||
30 | /* | ||
31 | * write a codec data (24bit) | ||
32 | */ | ||
33 | static void vx_write_codec_reg(vx_core_t *chip, int codec, unsigned int data) | ||
34 | { | ||
35 | unsigned long flags; | ||
36 | |||
37 | snd_assert(chip->ops->write_codec, return); | ||
38 | |||
39 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
40 | return; | ||
41 | |||
42 | spin_lock_irqsave(&chip->lock, flags); | ||
43 | chip->ops->write_codec(chip, codec, data); | ||
44 | spin_unlock_irqrestore(&chip->lock, flags); | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Data type used to access the Codec | ||
49 | */ | ||
50 | typedef union { | ||
51 | u32 l; | ||
52 | #ifdef SNDRV_BIG_ENDIAN | ||
53 | struct w { | ||
54 | u16 h; | ||
55 | u16 l; | ||
56 | } w; | ||
57 | struct b { | ||
58 | u8 hh; | ||
59 | u8 mh; | ||
60 | u8 ml; | ||
61 | u8 ll; | ||
62 | } b; | ||
63 | #else /* LITTLE_ENDIAN */ | ||
64 | struct w { | ||
65 | u16 l; | ||
66 | u16 h; | ||
67 | } w; | ||
68 | struct b { | ||
69 | u8 ll; | ||
70 | u8 ml; | ||
71 | u8 mh; | ||
72 | u8 hh; | ||
73 | } b; | ||
74 | #endif | ||
75 | } vx_codec_data_t; | ||
76 | |||
77 | #define SET_CDC_DATA_SEL(di,s) ((di).b.mh = (u8) (s)) | ||
78 | #define SET_CDC_DATA_REG(di,r) ((di).b.ml = (u8) (r)) | ||
79 | #define SET_CDC_DATA_VAL(di,d) ((di).b.ll = (u8) (d)) | ||
80 | #define SET_CDC_DATA_INIT(di) ((di).l = 0L, SET_CDC_DATA_SEL(di,XX_CODEC_SELECTOR)) | ||
81 | |||
82 | /* | ||
83 | * set up codec register and write the value | ||
84 | * @codec: the codec id, 0 or 1 | ||
85 | * @reg: register index | ||
86 | * @val: data value | ||
87 | */ | ||
88 | static void vx_set_codec_reg(vx_core_t *chip, int codec, int reg, int val) | ||
89 | { | ||
90 | vx_codec_data_t data; | ||
91 | /* DAC control register */ | ||
92 | SET_CDC_DATA_INIT(data); | ||
93 | SET_CDC_DATA_REG(data, reg); | ||
94 | SET_CDC_DATA_VAL(data, val); | ||
95 | vx_write_codec_reg(chip, codec, data.l); | ||
96 | } | ||
97 | |||
98 | |||
99 | /* | ||
100 | * vx_set_analog_output_level - set the output attenuation level | ||
101 | * @codec: the output codec, 0 or 1. (1 for VXP440 only) | ||
102 | * @left: left output level, 0 = mute | ||
103 | * @right: right output level | ||
104 | */ | ||
105 | static void vx_set_analog_output_level(vx_core_t *chip, int codec, int left, int right) | ||
106 | { | ||
107 | left = chip->hw->output_level_max - left; | ||
108 | right = chip->hw->output_level_max - right; | ||
109 | |||
110 | if (chip->ops->akm_write) { | ||
111 | chip->ops->akm_write(chip, XX_CODEC_LEVEL_LEFT_REGISTER, left); | ||
112 | chip->ops->akm_write(chip, XX_CODEC_LEVEL_RIGHT_REGISTER, right); | ||
113 | } else { | ||
114 | /* convert to attenuation level: 0 = 0dB (max), 0xe3 = -113.5 dB (min) */ | ||
115 | vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_LEFT_REGISTER, left); | ||
116 | vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_RIGHT_REGISTER, right); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | |||
121 | /* | ||
122 | * vx_toggle_dac_mute - mute/unmute DAC | ||
123 | * @mute: 0 = unmute, 1 = mute | ||
124 | */ | ||
125 | |||
126 | #define DAC_ATTEN_MIN 0x08 | ||
127 | #define DAC_ATTEN_MAX 0x38 | ||
128 | |||
129 | void vx_toggle_dac_mute(vx_core_t *chip, int mute) | ||
130 | { | ||
131 | unsigned int i; | ||
132 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
133 | if (chip->ops->akm_write) | ||
134 | chip->ops->akm_write(chip, XX_CODEC_DAC_CONTROL_REGISTER, mute); /* XXX */ | ||
135 | else | ||
136 | vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, | ||
137 | mute ? DAC_ATTEN_MAX : DAC_ATTEN_MIN); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * vx_reset_codec - reset and initialize the codecs | ||
143 | */ | ||
144 | void vx_reset_codec(vx_core_t *chip, int cold_reset) | ||
145 | { | ||
146 | unsigned int i; | ||
147 | int port = chip->type >= VX_TYPE_VXPOCKET ? 0x75 : 0x65; | ||
148 | |||
149 | chip->ops->reset_codec(chip); | ||
150 | |||
151 | /* AKM codecs should be initialized in reset_codec callback */ | ||
152 | if (! chip->ops->akm_write) { | ||
153 | /* initialize old codecs */ | ||
154 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
155 | /* DAC control register (change level when zero crossing + mute) */ | ||
156 | vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, DAC_ATTEN_MAX); | ||
157 | /* ADC control register */ | ||
158 | vx_set_codec_reg(chip, i, XX_CODEC_ADC_CONTROL_REGISTER, 0x00); | ||
159 | /* Port mode register */ | ||
160 | vx_set_codec_reg(chip, i, XX_CODEC_PORT_MODE_REGISTER, port); | ||
161 | /* Clock control register */ | ||
162 | vx_set_codec_reg(chip, i, XX_CODEC_CLOCK_CONTROL_REGISTER, 0x00); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* mute analog output */ | ||
167 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
168 | chip->output_level[i][0] = 0; | ||
169 | chip->output_level[i][1] = 0; | ||
170 | vx_set_analog_output_level(chip, i, 0, 0); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * change the audio input source | ||
176 | * @src: the target source (VX_AUDIO_SRC_XXX) | ||
177 | */ | ||
178 | static void vx_change_audio_source(vx_core_t *chip, int src) | ||
179 | { | ||
180 | unsigned long flags; | ||
181 | |||
182 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
183 | return; | ||
184 | |||
185 | spin_lock_irqsave(&chip->lock, flags); | ||
186 | chip->ops->change_audio_source(chip, src); | ||
187 | spin_unlock_irqrestore(&chip->lock, flags); | ||
188 | } | ||
189 | |||
190 | |||
191 | /* | ||
192 | * change the audio source if necessary and possible | ||
193 | * returns 1 if the source is actually changed. | ||
194 | */ | ||
195 | int vx_sync_audio_source(vx_core_t *chip) | ||
196 | { | ||
197 | if (chip->audio_source_target == chip->audio_source || | ||
198 | chip->pcm_running) | ||
199 | return 0; | ||
200 | vx_change_audio_source(chip, chip->audio_source_target); | ||
201 | chip->audio_source = chip->audio_source_target; | ||
202 | return 1; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | * audio level, mute, monitoring | ||
208 | */ | ||
209 | struct vx_audio_level { | ||
210 | unsigned int has_level: 1; | ||
211 | unsigned int has_monitor_level: 1; | ||
212 | unsigned int has_mute: 1; | ||
213 | unsigned int has_monitor_mute: 1; | ||
214 | unsigned int mute; | ||
215 | unsigned int monitor_mute; | ||
216 | short level; | ||
217 | short monitor_level; | ||
218 | }; | ||
219 | |||
220 | static int vx_adjust_audio_level(vx_core_t *chip, int audio, int capture, | ||
221 | struct vx_audio_level *info) | ||
222 | { | ||
223 | struct vx_rmh rmh; | ||
224 | |||
225 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
226 | return -EBUSY; | ||
227 | |||
228 | vx_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST); | ||
229 | if (capture) | ||
230 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
231 | /* Add Audio IO mask */ | ||
232 | rmh.Cmd[1] = 1 << audio; | ||
233 | rmh.Cmd[2] = 0; | ||
234 | if (info->has_level) { | ||
235 | rmh.Cmd[0] |= VALID_AUDIO_IO_DIGITAL_LEVEL; | ||
236 | rmh.Cmd[2] |= info->level; | ||
237 | } | ||
238 | if (info->has_monitor_level) { | ||
239 | rmh.Cmd[0] |= VALID_AUDIO_IO_MONITORING_LEVEL; | ||
240 | rmh.Cmd[2] |= ((unsigned int)info->monitor_level << 10); | ||
241 | } | ||
242 | if (info->has_mute) { | ||
243 | rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_LEVEL; | ||
244 | if (info->mute) | ||
245 | rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_LEVEL; | ||
246 | } | ||
247 | if (info->has_monitor_mute) { | ||
248 | /* validate flag for M2 at least to unmute it */ | ||
249 | rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_MONITORING_1 | VALID_AUDIO_IO_MUTE_MONITORING_2; | ||
250 | if (info->monitor_mute) | ||
251 | rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_MONITORING_1; | ||
252 | } | ||
253 | |||
254 | return vx_send_msg(chip, &rmh); | ||
255 | } | ||
256 | |||
257 | |||
258 | #if 0 // not used | ||
259 | static int vx_read_audio_level(vx_core_t *chip, int audio, int capture, | ||
260 | struct vx_audio_level *info) | ||
261 | { | ||
262 | int err; | ||
263 | struct vx_rmh rmh; | ||
264 | |||
265 | memset(info, 0, sizeof(*info)); | ||
266 | vx_init_rmh(&rmh, CMD_GET_AUDIO_LEVELS); | ||
267 | if (capture) | ||
268 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
269 | /* Add Audio IO mask */ | ||
270 | rmh.Cmd[1] = 1 << audio; | ||
271 | err = vx_send_msg(chip, &rmh); | ||
272 | if (err < 0) | ||
273 | return err; | ||
274 | info.level = rmh.Stat[0] & MASK_DSP_WORD_LEVEL; | ||
275 | info.monitor_level = (rmh.Stat[0] >> 10) & MASK_DSP_WORD_LEVEL; | ||
276 | info.mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_LEVEL) ? 1 : 0; | ||
277 | info.monitor_mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_MONITORING_1) ? 1 : 0; | ||
278 | return 0; | ||
279 | } | ||
280 | #endif // not used | ||
281 | |||
282 | /* | ||
283 | * set the monitoring level and mute state of the given audio | ||
284 | * no more static, because must be called from vx_pcm to demute monitoring | ||
285 | */ | ||
286 | int vx_set_monitor_level(vx_core_t *chip, int audio, int level, int active) | ||
287 | { | ||
288 | struct vx_audio_level info; | ||
289 | |||
290 | memset(&info, 0, sizeof(info)); | ||
291 | info.has_monitor_level = 1; | ||
292 | info.monitor_level = level; | ||
293 | info.has_monitor_mute = 1; | ||
294 | info.monitor_mute = !active; | ||
295 | chip->audio_monitor[audio] = level; | ||
296 | chip->audio_monitor_active[audio] = active; | ||
297 | return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */ | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * set the mute status of the given audio | ||
303 | */ | ||
304 | static int vx_set_audio_switch(vx_core_t *chip, int audio, int active) | ||
305 | { | ||
306 | struct vx_audio_level info; | ||
307 | |||
308 | memset(&info, 0, sizeof(info)); | ||
309 | info.has_mute = 1; | ||
310 | info.mute = !active; | ||
311 | chip->audio_active[audio] = active; | ||
312 | return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */ | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * set the mute status of the given audio | ||
317 | */ | ||
318 | static int vx_set_audio_gain(vx_core_t *chip, int audio, int capture, int level) | ||
319 | { | ||
320 | struct vx_audio_level info; | ||
321 | |||
322 | memset(&info, 0, sizeof(info)); | ||
323 | info.has_level = 1; | ||
324 | info.level = level; | ||
325 | chip->audio_gain[capture][audio] = level; | ||
326 | return vx_adjust_audio_level(chip, audio, capture, &info); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * reset all audio levels | ||
331 | */ | ||
332 | static void vx_reset_audio_levels(vx_core_t *chip) | ||
333 | { | ||
334 | unsigned int i, c; | ||
335 | struct vx_audio_level info; | ||
336 | |||
337 | memset(chip->audio_gain, 0, sizeof(chip->audio_gain)); | ||
338 | memset(chip->audio_active, 0, sizeof(chip->audio_active)); | ||
339 | memset(chip->audio_monitor, 0, sizeof(chip->audio_monitor)); | ||
340 | memset(chip->audio_monitor_active, 0, sizeof(chip->audio_monitor_active)); | ||
341 | |||
342 | for (c = 0; c < 2; c++) { | ||
343 | for (i = 0; i < chip->hw->num_ins * 2; i++) { | ||
344 | memset(&info, 0, sizeof(info)); | ||
345 | if (c == 0) { | ||
346 | info.has_monitor_level = 1; | ||
347 | info.has_mute = 1; | ||
348 | info.has_monitor_mute = 1; | ||
349 | } | ||
350 | info.has_level = 1; | ||
351 | info.level = CVAL_0DB; /* default: 0dB */ | ||
352 | vx_adjust_audio_level(chip, i, c, &info); | ||
353 | chip->audio_gain[c][i] = CVAL_0DB; | ||
354 | chip->audio_monitor[i] = CVAL_0DB; | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | |||
360 | /* | ||
361 | * VU, peak meter record | ||
362 | */ | ||
363 | |||
364 | #define VU_METER_CHANNELS 2 | ||
365 | |||
366 | struct vx_vu_meter { | ||
367 | int saturated; | ||
368 | int vu_level; | ||
369 | int peak_level; | ||
370 | }; | ||
371 | |||
372 | /* | ||
373 | * get the VU and peak meter values | ||
374 | * @audio: the audio index | ||
375 | * @capture: 0 = playback, 1 = capture operation | ||
376 | * @info: the array of vx_vu_meter records (size = 2). | ||
377 | */ | ||
378 | static int vx_get_audio_vu_meter(vx_core_t *chip, int audio, int capture, struct vx_vu_meter *info) | ||
379 | { | ||
380 | struct vx_rmh rmh; | ||
381 | int i, err; | ||
382 | |||
383 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
384 | return -EBUSY; | ||
385 | |||
386 | vx_init_rmh(&rmh, CMD_AUDIO_VU_PIC_METER); | ||
387 | rmh.LgStat += 2 * VU_METER_CHANNELS; | ||
388 | if (capture) | ||
389 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
390 | |||
391 | /* Add Audio IO mask */ | ||
392 | rmh.Cmd[1] = 0; | ||
393 | for (i = 0; i < VU_METER_CHANNELS; i++) | ||
394 | rmh.Cmd[1] |= 1 << (audio + i); | ||
395 | err = vx_send_msg(chip, &rmh); | ||
396 | if (err < 0) | ||
397 | return err; | ||
398 | /* Read response */ | ||
399 | for (i = 0; i < 2 * VU_METER_CHANNELS; i +=2) { | ||
400 | info->saturated = (rmh.Stat[0] & (1 << (audio + i))) ? 1 : 0; | ||
401 | info->vu_level = rmh.Stat[i + 1]; | ||
402 | info->peak_level = rmh.Stat[i + 2]; | ||
403 | info++; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | |||
409 | /* | ||
410 | * control API entries | ||
411 | */ | ||
412 | |||
413 | /* | ||
414 | * output level control | ||
415 | */ | ||
416 | static int vx_output_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
417 | { | ||
418 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
419 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
420 | uinfo->count = 2; | ||
421 | uinfo->value.integer.min = 0; | ||
422 | uinfo->value.integer.max = chip->hw->output_level_max; | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int vx_output_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
427 | { | ||
428 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
429 | int codec = kcontrol->id.index; | ||
430 | down(&chip->mixer_mutex); | ||
431 | ucontrol->value.integer.value[0] = chip->output_level[codec][0]; | ||
432 | ucontrol->value.integer.value[1] = chip->output_level[codec][1]; | ||
433 | up(&chip->mixer_mutex); | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static int vx_output_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
438 | { | ||
439 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
440 | int codec = kcontrol->id.index; | ||
441 | down(&chip->mixer_mutex); | ||
442 | if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] || | ||
443 | ucontrol->value.integer.value[1] != chip->output_level[codec][1]) { | ||
444 | vx_set_analog_output_level(chip, codec, | ||
445 | ucontrol->value.integer.value[0], | ||
446 | ucontrol->value.integer.value[1]); | ||
447 | chip->output_level[codec][0] = ucontrol->value.integer.value[0]; | ||
448 | chip->output_level[codec][1] = ucontrol->value.integer.value[1]; | ||
449 | up(&chip->mixer_mutex); | ||
450 | return 1; | ||
451 | } | ||
452 | up(&chip->mixer_mutex); | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static snd_kcontrol_new_t vx_control_output_level = { | ||
457 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
458 | .name = "Master Playback Volume", | ||
459 | .info = vx_output_level_info, | ||
460 | .get = vx_output_level_get, | ||
461 | .put = vx_output_level_put, | ||
462 | }; | ||
463 | |||
464 | /* | ||
465 | * audio source select | ||
466 | */ | ||
467 | static int vx_audio_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
468 | { | ||
469 | static char *texts_mic[3] = { | ||
470 | "Digital", "Line", "Mic" | ||
471 | }; | ||
472 | static char *texts_vx2[2] = { | ||
473 | "Digital", "Analog" | ||
474 | }; | ||
475 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
476 | |||
477 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
478 | uinfo->count = 1; | ||
479 | if (chip->type >= VX_TYPE_VXPOCKET) { | ||
480 | uinfo->value.enumerated.items = 3; | ||
481 | if (uinfo->value.enumerated.item > 2) | ||
482 | uinfo->value.enumerated.item = 2; | ||
483 | strcpy(uinfo->value.enumerated.name, | ||
484 | texts_mic[uinfo->value.enumerated.item]); | ||
485 | } else { | ||
486 | uinfo->value.enumerated.items = 2; | ||
487 | if (uinfo->value.enumerated.item > 1) | ||
488 | uinfo->value.enumerated.item = 1; | ||
489 | strcpy(uinfo->value.enumerated.name, | ||
490 | texts_vx2[uinfo->value.enumerated.item]); | ||
491 | } | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int vx_audio_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
496 | { | ||
497 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
498 | ucontrol->value.enumerated.item[0] = chip->audio_source_target; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int vx_audio_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
503 | { | ||
504 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
505 | down(&chip->mixer_mutex); | ||
506 | if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) { | ||
507 | chip->audio_source_target = ucontrol->value.enumerated.item[0]; | ||
508 | vx_sync_audio_source(chip); | ||
509 | up(&chip->mixer_mutex); | ||
510 | return 1; | ||
511 | } | ||
512 | up(&chip->mixer_mutex); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static snd_kcontrol_new_t vx_control_audio_src = { | ||
517 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
518 | .name = "Capture Source", | ||
519 | .info = vx_audio_src_info, | ||
520 | .get = vx_audio_src_get, | ||
521 | .put = vx_audio_src_put, | ||
522 | }; | ||
523 | |||
524 | /* | ||
525 | * clock mode selection | ||
526 | */ | ||
527 | static int vx_clock_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
528 | { | ||
529 | static char *texts[3] = { | ||
530 | "Auto", "Internal", "External" | ||
531 | }; | ||
532 | |||
533 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
534 | uinfo->count = 1; | ||
535 | uinfo->value.enumerated.items = 3; | ||
536 | if (uinfo->value.enumerated.item > 2) | ||
537 | uinfo->value.enumerated.item = 2; | ||
538 | strcpy(uinfo->value.enumerated.name, | ||
539 | texts[uinfo->value.enumerated.item]); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static int vx_clock_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
544 | { | ||
545 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
546 | ucontrol->value.enumerated.item[0] = chip->clock_mode; | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int vx_clock_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
551 | { | ||
552 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
553 | down(&chip->mixer_mutex); | ||
554 | if (chip->clock_mode != ucontrol->value.enumerated.item[0]) { | ||
555 | chip->clock_mode = ucontrol->value.enumerated.item[0]; | ||
556 | vx_set_clock(chip, chip->freq); | ||
557 | up(&chip->mixer_mutex); | ||
558 | return 1; | ||
559 | } | ||
560 | up(&chip->mixer_mutex); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static snd_kcontrol_new_t vx_control_clock_mode = { | ||
565 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
566 | .name = "Clock Mode", | ||
567 | .info = vx_clock_mode_info, | ||
568 | .get = vx_clock_mode_get, | ||
569 | .put = vx_clock_mode_put, | ||
570 | }; | ||
571 | |||
572 | /* | ||
573 | * Audio Gain | ||
574 | */ | ||
575 | static int vx_audio_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
576 | { | ||
577 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
578 | uinfo->count = 2; | ||
579 | uinfo->value.integer.min = 0; | ||
580 | uinfo->value.integer.max = CVAL_MAX; | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static int vx_audio_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
585 | { | ||
586 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
587 | int audio = kcontrol->private_value & 0xff; | ||
588 | int capture = (kcontrol->private_value >> 8) & 1; | ||
589 | |||
590 | down(&chip->mixer_mutex); | ||
591 | ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio]; | ||
592 | ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1]; | ||
593 | up(&chip->mixer_mutex); | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static int vx_audio_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
598 | { | ||
599 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
600 | int audio = kcontrol->private_value & 0xff; | ||
601 | int capture = (kcontrol->private_value >> 8) & 1; | ||
602 | |||
603 | down(&chip->mixer_mutex); | ||
604 | if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] || | ||
605 | ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) { | ||
606 | vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]); | ||
607 | vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]); | ||
608 | up(&chip->mixer_mutex); | ||
609 | return 1; | ||
610 | } | ||
611 | up(&chip->mixer_mutex); | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static int vx_audio_monitor_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
616 | { | ||
617 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
618 | int audio = kcontrol->private_value & 0xff; | ||
619 | |||
620 | down(&chip->mixer_mutex); | ||
621 | ucontrol->value.integer.value[0] = chip->audio_monitor[audio]; | ||
622 | ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1]; | ||
623 | up(&chip->mixer_mutex); | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static int vx_audio_monitor_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
628 | { | ||
629 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
630 | int audio = kcontrol->private_value & 0xff; | ||
631 | |||
632 | down(&chip->mixer_mutex); | ||
633 | if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] || | ||
634 | ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) { | ||
635 | vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0], | ||
636 | chip->audio_monitor_active[audio]); | ||
637 | vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1], | ||
638 | chip->audio_monitor_active[audio+1]); | ||
639 | up(&chip->mixer_mutex); | ||
640 | return 1; | ||
641 | } | ||
642 | up(&chip->mixer_mutex); | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | static int vx_audio_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
647 | { | ||
648 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
649 | uinfo->count = 2; | ||
650 | uinfo->value.integer.min = 0; | ||
651 | uinfo->value.integer.max = 1; | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int vx_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
656 | { | ||
657 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
658 | int audio = kcontrol->private_value & 0xff; | ||
659 | |||
660 | down(&chip->mixer_mutex); | ||
661 | ucontrol->value.integer.value[0] = chip->audio_active[audio]; | ||
662 | ucontrol->value.integer.value[1] = chip->audio_active[audio+1]; | ||
663 | up(&chip->mixer_mutex); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int vx_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
668 | { | ||
669 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
670 | int audio = kcontrol->private_value & 0xff; | ||
671 | |||
672 | down(&chip->mixer_mutex); | ||
673 | if (ucontrol->value.integer.value[0] != chip->audio_active[audio] || | ||
674 | ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) { | ||
675 | vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]); | ||
676 | vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]); | ||
677 | up(&chip->mixer_mutex); | ||
678 | return 1; | ||
679 | } | ||
680 | up(&chip->mixer_mutex); | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static int vx_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
685 | { | ||
686 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
687 | int audio = kcontrol->private_value & 0xff; | ||
688 | |||
689 | down(&chip->mixer_mutex); | ||
690 | ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio]; | ||
691 | ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1]; | ||
692 | up(&chip->mixer_mutex); | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static int vx_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
697 | { | ||
698 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
699 | int audio = kcontrol->private_value & 0xff; | ||
700 | |||
701 | down(&chip->mixer_mutex); | ||
702 | if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] || | ||
703 | ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) { | ||
704 | vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], | ||
705 | ucontrol->value.integer.value[0]); | ||
706 | vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], | ||
707 | ucontrol->value.integer.value[1]); | ||
708 | up(&chip->mixer_mutex); | ||
709 | return 1; | ||
710 | } | ||
711 | up(&chip->mixer_mutex); | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static snd_kcontrol_new_t vx_control_audio_gain = { | ||
716 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
717 | /* name will be filled later */ | ||
718 | .info = vx_audio_gain_info, | ||
719 | .get = vx_audio_gain_get, | ||
720 | .put = vx_audio_gain_put | ||
721 | }; | ||
722 | static snd_kcontrol_new_t vx_control_output_switch = { | ||
723 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
724 | .name = "PCM Playback Switch", | ||
725 | .info = vx_audio_sw_info, | ||
726 | .get = vx_audio_sw_get, | ||
727 | .put = vx_audio_sw_put | ||
728 | }; | ||
729 | static snd_kcontrol_new_t vx_control_monitor_gain = { | ||
730 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
731 | .name = "Monitoring Volume", | ||
732 | .info = vx_audio_gain_info, /* shared */ | ||
733 | .get = vx_audio_monitor_get, | ||
734 | .put = vx_audio_monitor_put | ||
735 | }; | ||
736 | static snd_kcontrol_new_t vx_control_monitor_switch = { | ||
737 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
738 | .name = "Monitoring Switch", | ||
739 | .info = vx_audio_sw_info, /* shared */ | ||
740 | .get = vx_monitor_sw_get, | ||
741 | .put = vx_monitor_sw_put | ||
742 | }; | ||
743 | |||
744 | |||
745 | /* | ||
746 | * IEC958 status bits | ||
747 | */ | ||
748 | static int vx_iec958_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
749 | { | ||
750 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
751 | uinfo->count = 1; | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int vx_iec958_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
756 | { | ||
757 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
758 | |||
759 | down(&chip->mixer_mutex); | ||
760 | ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff; | ||
761 | ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff; | ||
762 | ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff; | ||
763 | ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff; | ||
764 | up(&chip->mixer_mutex); | ||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int vx_iec958_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
769 | { | ||
770 | ucontrol->value.iec958.status[0] = 0xff; | ||
771 | ucontrol->value.iec958.status[1] = 0xff; | ||
772 | ucontrol->value.iec958.status[2] = 0xff; | ||
773 | ucontrol->value.iec958.status[3] = 0xff; | ||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static int vx_iec958_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
778 | { | ||
779 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
780 | unsigned int val; | ||
781 | |||
782 | val = (ucontrol->value.iec958.status[0] << 0) | | ||
783 | (ucontrol->value.iec958.status[1] << 8) | | ||
784 | (ucontrol->value.iec958.status[2] << 16) | | ||
785 | (ucontrol->value.iec958.status[3] << 24); | ||
786 | down(&chip->mixer_mutex); | ||
787 | if (chip->uer_bits != val) { | ||
788 | chip->uer_bits = val; | ||
789 | vx_set_iec958_status(chip, val); | ||
790 | up(&chip->mixer_mutex); | ||
791 | return 1; | ||
792 | } | ||
793 | up(&chip->mixer_mutex); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static snd_kcontrol_new_t vx_control_iec958_mask = { | ||
798 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
799 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
800 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), | ||
801 | .info = vx_iec958_info, /* shared */ | ||
802 | .get = vx_iec958_mask_get, | ||
803 | }; | ||
804 | |||
805 | static snd_kcontrol_new_t vx_control_iec958 = { | ||
806 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
807 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | ||
808 | .info = vx_iec958_info, | ||
809 | .get = vx_iec958_get, | ||
810 | .put = vx_iec958_put | ||
811 | }; | ||
812 | |||
813 | |||
814 | /* | ||
815 | * VU meter | ||
816 | */ | ||
817 | |||
818 | #define METER_MAX 0xff | ||
819 | #define METER_SHIFT 16 | ||
820 | |||
821 | static int vx_vu_meter_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
822 | { | ||
823 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
824 | uinfo->count = 2; | ||
825 | uinfo->value.integer.min = 0; | ||
826 | uinfo->value.integer.max = METER_MAX; | ||
827 | return 0; | ||
828 | } | ||
829 | |||
830 | static int vx_vu_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
831 | { | ||
832 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
833 | struct vx_vu_meter meter[2]; | ||
834 | int audio = kcontrol->private_value & 0xff; | ||
835 | int capture = (kcontrol->private_value >> 8) & 1; | ||
836 | |||
837 | vx_get_audio_vu_meter(chip, audio, capture, meter); | ||
838 | ucontrol->value.integer.value[0] = meter[0].vu_level >> METER_SHIFT; | ||
839 | ucontrol->value.integer.value[1] = meter[1].vu_level >> METER_SHIFT; | ||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static int vx_peak_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
844 | { | ||
845 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
846 | struct vx_vu_meter meter[2]; | ||
847 | int audio = kcontrol->private_value & 0xff; | ||
848 | int capture = (kcontrol->private_value >> 8) & 1; | ||
849 | |||
850 | vx_get_audio_vu_meter(chip, audio, capture, meter); | ||
851 | ucontrol->value.integer.value[0] = meter[0].peak_level >> METER_SHIFT; | ||
852 | ucontrol->value.integer.value[1] = meter[1].peak_level >> METER_SHIFT; | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | static int vx_saturation_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
857 | { | ||
858 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
859 | uinfo->count = 2; | ||
860 | uinfo->value.integer.min = 0; | ||
861 | uinfo->value.integer.max = 1; | ||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static int vx_saturation_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
866 | { | ||
867 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
868 | struct vx_vu_meter meter[2]; | ||
869 | int audio = kcontrol->private_value & 0xff; | ||
870 | |||
871 | vx_get_audio_vu_meter(chip, audio, 1, meter); /* capture only */ | ||
872 | ucontrol->value.integer.value[0] = meter[0].saturated; | ||
873 | ucontrol->value.integer.value[1] = meter[1].saturated; | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static snd_kcontrol_new_t vx_control_vu_meter = { | ||
878 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
879 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
880 | /* name will be filled later */ | ||
881 | .info = vx_vu_meter_info, | ||
882 | .get = vx_vu_meter_get, | ||
883 | }; | ||
884 | |||
885 | static snd_kcontrol_new_t vx_control_peak_meter = { | ||
886 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
887 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
888 | /* name will be filled later */ | ||
889 | .info = vx_vu_meter_info, /* shared */ | ||
890 | .get = vx_peak_meter_get, | ||
891 | }; | ||
892 | |||
893 | static snd_kcontrol_new_t vx_control_saturation = { | ||
894 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
895 | .name = "Input Saturation", | ||
896 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
897 | .info = vx_saturation_info, | ||
898 | .get = vx_saturation_get, | ||
899 | }; | ||
900 | |||
901 | |||
902 | |||
903 | /* | ||
904 | * | ||
905 | */ | ||
906 | |||
907 | int snd_vx_mixer_new(vx_core_t *chip) | ||
908 | { | ||
909 | unsigned int i, c; | ||
910 | int err; | ||
911 | snd_kcontrol_new_t temp; | ||
912 | snd_card_t *card = chip->card; | ||
913 | char name[32]; | ||
914 | |||
915 | strcpy(card->mixername, card->driver); | ||
916 | |||
917 | /* output level controls */ | ||
918 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
919 | temp = vx_control_output_level; | ||
920 | temp.index = i; | ||
921 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
922 | return err; | ||
923 | } | ||
924 | |||
925 | /* PCM volumes, switches, monitoring */ | ||
926 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
927 | int val = i * 2; | ||
928 | temp = vx_control_audio_gain; | ||
929 | temp.index = i; | ||
930 | temp.name = "PCM Playback Volume"; | ||
931 | temp.private_value = val; | ||
932 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
933 | return err; | ||
934 | temp = vx_control_output_switch; | ||
935 | temp.index = i; | ||
936 | temp.private_value = val; | ||
937 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
938 | return err; | ||
939 | temp = vx_control_monitor_gain; | ||
940 | temp.index = i; | ||
941 | temp.private_value = val; | ||
942 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
943 | return err; | ||
944 | temp = vx_control_monitor_switch; | ||
945 | temp.index = i; | ||
946 | temp.private_value = val; | ||
947 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
948 | return err; | ||
949 | } | ||
950 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
951 | temp = vx_control_audio_gain; | ||
952 | temp.index = i; | ||
953 | temp.name = "PCM Capture Volume"; | ||
954 | temp.private_value = (i * 2) | (1 << 8); | ||
955 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
956 | return err; | ||
957 | } | ||
958 | |||
959 | /* Audio source */ | ||
960 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0) | ||
961 | return err; | ||
962 | /* clock mode */ | ||
963 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0) | ||
964 | return err; | ||
965 | /* IEC958 controls */ | ||
966 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0) | ||
967 | return err; | ||
968 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0) | ||
969 | return err; | ||
970 | /* VU, peak, saturation meters */ | ||
971 | for (c = 0; c < 2; c++) { | ||
972 | static char *dir[2] = { "Output", "Input" }; | ||
973 | for (i = 0; i < chip->hw->num_ins; i++) { | ||
974 | int val = (i * 2) | (c << 8); | ||
975 | if (c == 1) { | ||
976 | temp = vx_control_saturation; | ||
977 | temp.index = i; | ||
978 | temp.private_value = val; | ||
979 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
980 | return err; | ||
981 | } | ||
982 | sprintf(name, "%s VU Meter", dir[c]); | ||
983 | temp = vx_control_vu_meter; | ||
984 | temp.index = i; | ||
985 | temp.name = name; | ||
986 | temp.private_value = val; | ||
987 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
988 | return err; | ||
989 | sprintf(name, "%s Peak Meter", dir[c]); | ||
990 | temp = vx_control_peak_meter; | ||
991 | temp.index = i; | ||
992 | temp.name = name; | ||
993 | temp.private_value = val; | ||
994 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
995 | return err; | ||
996 | } | ||
997 | } | ||
998 | vx_reset_audio_levels(chip); | ||
999 | return 0; | ||
1000 | } | ||
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c new file mode 100644 index 000000000000..98587176b327 --- /dev/null +++ b/sound/drivers/vx/vx_pcm.c | |||
@@ -0,0 +1,1312 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * PCM part | ||
5 | * | ||
6 | * Copyright (c) 2002,2003 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * | ||
23 | * STRATEGY | ||
24 | * for playback, we send series of "chunks", which size is equal with the | ||
25 | * IBL size, typically 126 samples. at each end of chunk, the end-of-buffer | ||
26 | * interrupt is notified, and the interrupt handler will feed the next chunk. | ||
27 | * | ||
28 | * the current position is calculated from the sample count RMH. | ||
29 | * pipe->transferred is the counter of data which has been already transferred. | ||
30 | * if this counter reaches to the period size, snd_pcm_period_elapsed() will | ||
31 | * be issued. | ||
32 | * | ||
33 | * for capture, the situation is much easier. | ||
34 | * to get a low latency response, we'll check the capture streams at each | ||
35 | * interrupt (capture stream has no EOB notification). if the pending | ||
36 | * data is accumulated to the period size, snd_pcm_period_elapsed() is | ||
37 | * called and the pointer is updated. | ||
38 | * | ||
39 | * the current point of read buffer is kept in pipe->hw_ptr. note that | ||
40 | * this is in bytes. | ||
41 | * | ||
42 | * | ||
43 | * TODO | ||
44 | * - linked trigger for full-duplex mode. | ||
45 | * - scheduled action on the stream. | ||
46 | */ | ||
47 | |||
48 | #include <sound/driver.h> | ||
49 | #include <linux/slab.h> | ||
50 | #include <linux/vmalloc.h> | ||
51 | #include <linux/delay.h> | ||
52 | #include <sound/core.h> | ||
53 | #include <sound/asoundef.h> | ||
54 | #include <sound/pcm.h> | ||
55 | #include <sound/vx_core.h> | ||
56 | #include "vx_cmd.h" | ||
57 | |||
58 | |||
59 | /* | ||
60 | * we use a vmalloc'ed (sg-)buffer | ||
61 | */ | ||
62 | |||
63 | /* get the physical page pointer on the given offset */ | ||
64 | static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) | ||
65 | { | ||
66 | void *pageptr = subs->runtime->dma_area + offset; | ||
67 | return vmalloc_to_page(pageptr); | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * allocate a buffer via vmalloc_32(). | ||
72 | * called from hw_params | ||
73 | * NOTE: this may be called not only once per pcm open! | ||
74 | */ | ||
75 | static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) | ||
76 | { | ||
77 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
78 | if (runtime->dma_area) { | ||
79 | /* already allocated */ | ||
80 | if (runtime->dma_bytes >= size) | ||
81 | return 0; /* already enough large */ | ||
82 | vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */ | ||
83 | } | ||
84 | runtime->dma_area = vmalloc_32(size); | ||
85 | if (! runtime->dma_area) | ||
86 | return -ENOMEM; | ||
87 | memset(runtime->dma_area, 0, size); | ||
88 | runtime->dma_bytes = size; | ||
89 | return 1; /* changed */ | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * free the buffer. | ||
94 | * called from hw_free callback | ||
95 | * NOTE: this may be called not only once per pcm open! | ||
96 | */ | ||
97 | static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) | ||
98 | { | ||
99 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
100 | if (runtime->dma_area) { | ||
101 | vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */ | ||
102 | runtime->dma_area = NULL; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | |||
108 | /* | ||
109 | * read three pending pcm bytes via inb() | ||
110 | */ | ||
111 | static void vx_pcm_read_per_bytes(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) | ||
112 | { | ||
113 | int offset = pipe->hw_ptr; | ||
114 | unsigned char *buf = (unsigned char *)(runtime->dma_area + offset); | ||
115 | *buf++ = vx_inb(chip, RXH); | ||
116 | if (++offset >= pipe->buffer_bytes) { | ||
117 | offset = 0; | ||
118 | buf = (unsigned char *)runtime->dma_area; | ||
119 | } | ||
120 | *buf++ = vx_inb(chip, RXM); | ||
121 | if (++offset >= pipe->buffer_bytes) { | ||
122 | offset = 0; | ||
123 | buf = (unsigned char *)runtime->dma_area; | ||
124 | } | ||
125 | *buf++ = vx_inb(chip, RXL); | ||
126 | if (++offset >= pipe->buffer_bytes) { | ||
127 | offset = 0; | ||
128 | buf = (unsigned char *)runtime->dma_area; | ||
129 | } | ||
130 | pipe->hw_ptr = offset; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * vx_set_pcx_time - convert from the PC time to the RMH status time. | ||
135 | * @pc_time: the pointer for the PC-time to set | ||
136 | * @dsp_time: the pointer for RMH status time array | ||
137 | */ | ||
138 | static void vx_set_pcx_time(vx_core_t *chip, pcx_time_t *pc_time, unsigned int *dsp_time) | ||
139 | { | ||
140 | dsp_time[0] = (unsigned int)((*pc_time) >> 24) & PCX_TIME_HI_MASK; | ||
141 | dsp_time[1] = (unsigned int)(*pc_time) & MASK_DSP_WORD; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * vx_set_differed_time - set the differed time if specified | ||
146 | * @rmh: the rmh record to modify | ||
147 | * @pipe: the pipe to be checked | ||
148 | * | ||
149 | * if the pipe is programmed with the differed time, set the DSP time | ||
150 | * on the rmh and changes its command length. | ||
151 | * | ||
152 | * returns the increase of the command length. | ||
153 | */ | ||
154 | static int vx_set_differed_time(vx_core_t *chip, struct vx_rmh *rmh, vx_pipe_t *pipe) | ||
155 | { | ||
156 | /* Update The length added to the RMH command by the timestamp */ | ||
157 | if (! (pipe->differed_type & DC_DIFFERED_DELAY)) | ||
158 | return 0; | ||
159 | |||
160 | /* Set the T bit */ | ||
161 | rmh->Cmd[0] |= DSP_DIFFERED_COMMAND_MASK; | ||
162 | |||
163 | /* Time stamp is the 1st following parameter */ | ||
164 | vx_set_pcx_time(chip, &pipe->pcx_time, &rmh->Cmd[1]); | ||
165 | |||
166 | /* Add the flags to a notified differed command */ | ||
167 | if (pipe->differed_type & DC_NOTIFY_DELAY) | ||
168 | rmh->Cmd[1] |= NOTIFY_MASK_TIME_HIGH ; | ||
169 | |||
170 | /* Add the flags to a multiple differed command */ | ||
171 | if (pipe->differed_type & DC_MULTIPLE_DELAY) | ||
172 | rmh->Cmd[1] |= MULTIPLE_MASK_TIME_HIGH; | ||
173 | |||
174 | /* Add the flags to a stream-time differed command */ | ||
175 | if (pipe->differed_type & DC_STREAM_TIME_DELAY) | ||
176 | rmh->Cmd[1] |= STREAM_MASK_TIME_HIGH; | ||
177 | |||
178 | rmh->LgCmd += 2; | ||
179 | return 2; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * vx_set_stream_format - send the stream format command | ||
184 | * @pipe: the affected pipe | ||
185 | * @data: format bitmask | ||
186 | */ | ||
187 | static int vx_set_stream_format(vx_core_t *chip, vx_pipe_t *pipe, unsigned int data) | ||
188 | { | ||
189 | struct vx_rmh rmh; | ||
190 | |||
191 | vx_init_rmh(&rmh, pipe->is_capture ? | ||
192 | CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT); | ||
193 | rmh.Cmd[0] |= pipe->number << FIELD_SIZE; | ||
194 | |||
195 | /* Command might be longer since we may have to add a timestamp */ | ||
196 | vx_set_differed_time(chip, &rmh, pipe); | ||
197 | |||
198 | rmh.Cmd[rmh.LgCmd] = (data & 0xFFFFFF00) >> 8; | ||
199 | rmh.Cmd[rmh.LgCmd + 1] = (data & 0xFF) << 16 /*| (datal & 0xFFFF00) >> 8*/; | ||
200 | rmh.LgCmd += 2; | ||
201 | |||
202 | return vx_send_msg(chip, &rmh); | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | * vx_set_format - set the format of a pipe | ||
208 | * @pipe: the affected pipe | ||
209 | * @runtime: pcm runtime instance to be referred | ||
210 | * | ||
211 | * returns 0 if successful, or a negative error code. | ||
212 | */ | ||
213 | static int vx_set_format(vx_core_t *chip, vx_pipe_t *pipe, | ||
214 | snd_pcm_runtime_t *runtime) | ||
215 | { | ||
216 | unsigned int header = HEADER_FMT_BASE; | ||
217 | |||
218 | if (runtime->channels == 1) | ||
219 | header |= HEADER_FMT_MONO; | ||
220 | if (snd_pcm_format_little_endian(runtime->format)) | ||
221 | header |= HEADER_FMT_INTEL; | ||
222 | if (runtime->rate < 32000 && runtime->rate > 11025) | ||
223 | header |= HEADER_FMT_UPTO32; | ||
224 | else if (runtime->rate <= 11025) | ||
225 | header |= HEADER_FMT_UPTO11; | ||
226 | |||
227 | switch (snd_pcm_format_physical_width(runtime->format)) { | ||
228 | // case 8: break; | ||
229 | case 16: header |= HEADER_FMT_16BITS; break; | ||
230 | case 24: header |= HEADER_FMT_24BITS; break; | ||
231 | default : | ||
232 | snd_BUG(); | ||
233 | return -EINVAL; | ||
234 | }; | ||
235 | |||
236 | return vx_set_stream_format(chip, pipe, header); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * set / query the IBL size | ||
241 | */ | ||
242 | static int vx_set_ibl(vx_core_t *chip, struct vx_ibl_info *info) | ||
243 | { | ||
244 | int err; | ||
245 | struct vx_rmh rmh; | ||
246 | |||
247 | vx_init_rmh(&rmh, CMD_IBL); | ||
248 | rmh.Cmd[0] |= info->size & 0x03ffff; | ||
249 | err = vx_send_msg(chip, &rmh); | ||
250 | if (err < 0) | ||
251 | return err; | ||
252 | info->size = rmh.Stat[0]; | ||
253 | info->max_size = rmh.Stat[1]; | ||
254 | info->min_size = rmh.Stat[2]; | ||
255 | info->granularity = rmh.Stat[3]; | ||
256 | snd_printdd(KERN_DEBUG "vx_set_ibl: size = %d, max = %d, min = %d, gran = %d\n", | ||
257 | info->size, info->max_size, info->min_size, info->granularity); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | |||
262 | /* | ||
263 | * vx_get_pipe_state - get the state of a pipe | ||
264 | * @pipe: the pipe to be checked | ||
265 | * @state: the pointer for the returned state | ||
266 | * | ||
267 | * checks the state of a given pipe, and stores the state (1 = running, | ||
268 | * 0 = paused) on the given pointer. | ||
269 | * | ||
270 | * called from trigger callback only | ||
271 | */ | ||
272 | static int vx_get_pipe_state(vx_core_t *chip, vx_pipe_t *pipe, int *state) | ||
273 | { | ||
274 | int err; | ||
275 | struct vx_rmh rmh; | ||
276 | |||
277 | vx_init_rmh(&rmh, CMD_PIPE_STATE); | ||
278 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
279 | err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
280 | if (! err) | ||
281 | *state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0; | ||
282 | return err; | ||
283 | } | ||
284 | |||
285 | |||
286 | /* | ||
287 | * vx_query_hbuffer_size - query available h-buffer size in bytes | ||
288 | * @pipe: the pipe to be checked | ||
289 | * | ||
290 | * return the available size on h-buffer in bytes, | ||
291 | * or a negative error code. | ||
292 | * | ||
293 | * NOTE: calling this function always switches to the stream mode. | ||
294 | * you'll need to disconnect the host to get back to the | ||
295 | * normal mode. | ||
296 | */ | ||
297 | static int vx_query_hbuffer_size(vx_core_t *chip, vx_pipe_t *pipe) | ||
298 | { | ||
299 | int result; | ||
300 | struct vx_rmh rmh; | ||
301 | |||
302 | vx_init_rmh(&rmh, CMD_SIZE_HBUFFER); | ||
303 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
304 | if (pipe->is_capture) | ||
305 | rmh.Cmd[0] |= 0x00000001; | ||
306 | result = vx_send_msg(chip, &rmh); | ||
307 | if (! result) | ||
308 | result = rmh.Stat[0] & 0xffff; | ||
309 | return result; | ||
310 | } | ||
311 | |||
312 | |||
313 | /* | ||
314 | * vx_pipe_can_start - query whether a pipe is ready for start | ||
315 | * @pipe: the pipe to be checked | ||
316 | * | ||
317 | * return 1 if ready, 0 if not ready, and negative value on error. | ||
318 | * | ||
319 | * called from trigger callback only | ||
320 | */ | ||
321 | static int vx_pipe_can_start(vx_core_t *chip, vx_pipe_t *pipe) | ||
322 | { | ||
323 | int err; | ||
324 | struct vx_rmh rmh; | ||
325 | |||
326 | vx_init_rmh(&rmh, CMD_CAN_START_PIPE); | ||
327 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
328 | rmh.Cmd[0] |= 1; | ||
329 | |||
330 | err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
331 | if (! err) { | ||
332 | if (rmh.Stat[0]) | ||
333 | err = 1; | ||
334 | } | ||
335 | return err; | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * vx_conf_pipe - tell the pipe to stand by and wait for IRQA. | ||
340 | * @pipe: the pipe to be configured | ||
341 | */ | ||
342 | static int vx_conf_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
343 | { | ||
344 | struct vx_rmh rmh; | ||
345 | |||
346 | vx_init_rmh(&rmh, CMD_CONF_PIPE); | ||
347 | if (pipe->is_capture) | ||
348 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
349 | rmh.Cmd[1] = 1 << pipe->number; | ||
350 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * vx_send_irqa - trigger IRQA | ||
355 | */ | ||
356 | static int vx_send_irqa(vx_core_t *chip) | ||
357 | { | ||
358 | struct vx_rmh rmh; | ||
359 | |||
360 | vx_init_rmh(&rmh, CMD_SEND_IRQA); | ||
361 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
362 | } | ||
363 | |||
364 | |||
365 | #define MAX_WAIT_FOR_DSP 250 | ||
366 | /* | ||
367 | * vx boards do not support inter-card sync, besides | ||
368 | * only 126 samples require to be prepared before a pipe can start | ||
369 | */ | ||
370 | #define CAN_START_DELAY 2 /* wait 2ms only before asking if the pipe is ready*/ | ||
371 | #define WAIT_STATE_DELAY 2 /* wait 2ms after irqA was requested and check if the pipe state toggled*/ | ||
372 | |||
373 | /* | ||
374 | * vx_toggle_pipe - start / pause a pipe | ||
375 | * @pipe: the pipe to be triggered | ||
376 | * @state: start = 1, pause = 0 | ||
377 | * | ||
378 | * called from trigger callback only | ||
379 | * | ||
380 | */ | ||
381 | static int vx_toggle_pipe(vx_core_t *chip, vx_pipe_t *pipe, int state) | ||
382 | { | ||
383 | int err, i, cur_state; | ||
384 | |||
385 | /* Check the pipe is not already in the requested state */ | ||
386 | if (vx_get_pipe_state(chip, pipe, &cur_state) < 0) | ||
387 | return -EBADFD; | ||
388 | if (state == cur_state) | ||
389 | return 0; | ||
390 | |||
391 | /* If a start is requested, ask the DSP to get prepared | ||
392 | * and wait for a positive acknowledge (when there are | ||
393 | * enough sound buffer for this pipe) | ||
394 | */ | ||
395 | if (state) { | ||
396 | for (i = 0 ; i < MAX_WAIT_FOR_DSP; i++) { | ||
397 | err = vx_pipe_can_start(chip, pipe); | ||
398 | if (err > 0) | ||
399 | break; | ||
400 | /* Wait for a few, before asking again | ||
401 | * to avoid flooding the DSP with our requests | ||
402 | */ | ||
403 | mdelay(1); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | if ((err = vx_conf_pipe(chip, pipe)) < 0) | ||
408 | return err; | ||
409 | |||
410 | if ((err = vx_send_irqa(chip)) < 0) | ||
411 | return err; | ||
412 | |||
413 | /* If it completes successfully, wait for the pipes | ||
414 | * reaching the expected state before returning | ||
415 | * Check one pipe only (since they are synchronous) | ||
416 | */ | ||
417 | for (i = 0; i < MAX_WAIT_FOR_DSP; i++) { | ||
418 | err = vx_get_pipe_state(chip, pipe, &cur_state); | ||
419 | if (err < 0 || cur_state == state) | ||
420 | break; | ||
421 | err = -EIO; | ||
422 | mdelay(1); | ||
423 | } | ||
424 | return err < 0 ? -EIO : 0; | ||
425 | } | ||
426 | |||
427 | |||
428 | /* | ||
429 | * vx_stop_pipe - stop a pipe | ||
430 | * @pipe: the pipe to be stopped | ||
431 | * | ||
432 | * called from trigger callback only | ||
433 | */ | ||
434 | static int vx_stop_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
435 | { | ||
436 | struct vx_rmh rmh; | ||
437 | vx_init_rmh(&rmh, CMD_STOP_PIPE); | ||
438 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
439 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
440 | } | ||
441 | |||
442 | |||
443 | /* | ||
444 | * vx_alloc_pipe - allocate a pipe and initialize the pipe instance | ||
445 | * @capture: 0 = playback, 1 = capture operation | ||
446 | * @audioid: the audio id to be assigned | ||
447 | * @num_audio: number of audio channels | ||
448 | * @pipep: the returned pipe instance | ||
449 | * | ||
450 | * return 0 on success, or a negative error code. | ||
451 | */ | ||
452 | static int vx_alloc_pipe(vx_core_t *chip, int capture, | ||
453 | int audioid, int num_audio, | ||
454 | vx_pipe_t **pipep) | ||
455 | { | ||
456 | int err; | ||
457 | vx_pipe_t *pipe; | ||
458 | struct vx_rmh rmh; | ||
459 | int data_mode; | ||
460 | |||
461 | *pipep = NULL; | ||
462 | vx_init_rmh(&rmh, CMD_RES_PIPE); | ||
463 | vx_set_pipe_cmd_params(&rmh, capture, audioid, num_audio); | ||
464 | #if 0 // NYI | ||
465 | if (underrun_skip_sound) | ||
466 | rmh.Cmd[0] |= BIT_SKIP_SOUND; | ||
467 | #endif // NYI | ||
468 | data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; | ||
469 | if (! capture && data_mode) | ||
470 | rmh.Cmd[0] |= BIT_DATA_MODE; | ||
471 | err = vx_send_msg(chip, &rmh); | ||
472 | if (err < 0) | ||
473 | return err; | ||
474 | |||
475 | /* initialize the pipe record */ | ||
476 | pipe = kcalloc(1, sizeof(*pipe), GFP_KERNEL); | ||
477 | if (! pipe) { | ||
478 | /* release the pipe */ | ||
479 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
480 | vx_set_pipe_cmd_params(&rmh, capture, audioid, 0); | ||
481 | vx_send_msg(chip, &rmh); | ||
482 | return -ENOMEM; | ||
483 | } | ||
484 | |||
485 | /* the pipe index should be identical with the audio index */ | ||
486 | pipe->number = audioid; | ||
487 | pipe->is_capture = capture; | ||
488 | pipe->channels = num_audio; | ||
489 | pipe->differed_type = 0; | ||
490 | pipe->pcx_time = 0; | ||
491 | pipe->data_mode = data_mode; | ||
492 | *pipep = pipe; | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | |||
498 | /* | ||
499 | * vx_free_pipe - release a pipe | ||
500 | * @pipe: pipe to be released | ||
501 | */ | ||
502 | static int vx_free_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
503 | { | ||
504 | struct vx_rmh rmh; | ||
505 | |||
506 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
507 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
508 | vx_send_msg(chip, &rmh); | ||
509 | |||
510 | kfree(pipe); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | |||
515 | /* | ||
516 | * vx_start_stream - start the stream | ||
517 | * | ||
518 | * called from trigger callback only | ||
519 | */ | ||
520 | static int vx_start_stream(vx_core_t *chip, vx_pipe_t *pipe) | ||
521 | { | ||
522 | struct vx_rmh rmh; | ||
523 | |||
524 | vx_init_rmh(&rmh, CMD_START_ONE_STREAM); | ||
525 | vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); | ||
526 | vx_set_differed_time(chip, &rmh, pipe); | ||
527 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
528 | } | ||
529 | |||
530 | |||
531 | /* | ||
532 | * vx_stop_stream - stop the stream | ||
533 | * | ||
534 | * called from trigger callback only | ||
535 | */ | ||
536 | static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe) | ||
537 | { | ||
538 | struct vx_rmh rmh; | ||
539 | |||
540 | vx_init_rmh(&rmh, CMD_STOP_STREAM); | ||
541 | vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); | ||
542 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
543 | } | ||
544 | |||
545 | |||
546 | /* | ||
547 | * playback hw information | ||
548 | */ | ||
549 | |||
550 | static snd_pcm_hardware_t vx_pcm_playback_hw = { | ||
551 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
552 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | | ||
553 | SNDRV_PCM_INFO_RESUME), | ||
554 | .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, | ||
555 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
556 | .rate_min = 5000, | ||
557 | .rate_max = 48000, | ||
558 | .channels_min = 1, | ||
559 | .channels_max = 2, | ||
560 | .buffer_bytes_max = (128*1024), | ||
561 | .period_bytes_min = 126, | ||
562 | .period_bytes_max = (128*1024), | ||
563 | .periods_min = 2, | ||
564 | .periods_max = VX_MAX_PERIODS, | ||
565 | .fifo_size = 126, | ||
566 | }; | ||
567 | |||
568 | |||
569 | static void vx_pcm_delayed_start(unsigned long arg); | ||
570 | |||
571 | /* | ||
572 | * vx_pcm_playback_open - open callback for playback | ||
573 | */ | ||
574 | static int vx_pcm_playback_open(snd_pcm_substream_t *subs) | ||
575 | { | ||
576 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
577 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
578 | vx_pipe_t *pipe = NULL; | ||
579 | unsigned int audio; | ||
580 | int err; | ||
581 | |||
582 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
583 | return -EBUSY; | ||
584 | |||
585 | audio = subs->pcm->device * 2; | ||
586 | snd_assert(audio < chip->audio_outs, return -EINVAL); | ||
587 | |||
588 | /* playback pipe may have been already allocated for monitoring */ | ||
589 | pipe = chip->playback_pipes[audio]; | ||
590 | if (! pipe) { | ||
591 | /* not allocated yet */ | ||
592 | err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */ | ||
593 | if (err < 0) | ||
594 | return err; | ||
595 | chip->playback_pipes[audio] = pipe; | ||
596 | } | ||
597 | /* open for playback */ | ||
598 | pipe->references++; | ||
599 | |||
600 | pipe->substream = subs; | ||
601 | tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); | ||
602 | chip->playback_pipes[audio] = pipe; | ||
603 | |||
604 | runtime->hw = vx_pcm_playback_hw; | ||
605 | runtime->hw.period_bytes_min = chip->ibl.size; | ||
606 | runtime->private_data = pipe; | ||
607 | |||
608 | /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ | ||
609 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); | ||
610 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | /* | ||
616 | * vx_pcm_playback_close - close callback for playback | ||
617 | */ | ||
618 | static int vx_pcm_playback_close(snd_pcm_substream_t *subs) | ||
619 | { | ||
620 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
621 | vx_pipe_t *pipe; | ||
622 | |||
623 | if (! subs->runtime->private_data) | ||
624 | return -EINVAL; | ||
625 | |||
626 | pipe = subs->runtime->private_data; | ||
627 | |||
628 | if (--pipe->references == 0) { | ||
629 | chip->playback_pipes[pipe->number] = NULL; | ||
630 | vx_free_pipe(chip, pipe); | ||
631 | } | ||
632 | |||
633 | return 0; | ||
634 | |||
635 | } | ||
636 | |||
637 | |||
638 | /* | ||
639 | * vx_notify_end_of_buffer - send "end-of-buffer" notifier at the given pipe | ||
640 | * @pipe: the pipe to notify | ||
641 | * | ||
642 | * NB: call with a certain lock. | ||
643 | */ | ||
644 | static int vx_notify_end_of_buffer(vx_core_t *chip, vx_pipe_t *pipe) | ||
645 | { | ||
646 | int err; | ||
647 | struct vx_rmh rmh; /* use a temporary rmh here */ | ||
648 | |||
649 | /* Toggle Dsp Host Interface into Message mode */ | ||
650 | vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); | ||
651 | vx_init_rmh(&rmh, CMD_NOTIFY_END_OF_BUFFER); | ||
652 | vx_set_stream_cmd_params(&rmh, 0, pipe->number); | ||
653 | err = vx_send_msg_nolock(chip, &rmh); | ||
654 | if (err < 0) | ||
655 | return err; | ||
656 | /* Toggle Dsp Host Interface back to sound transfer mode */ | ||
657 | vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * vx_pcm_playback_transfer_chunk - transfer a single chunk | ||
663 | * @subs: substream | ||
664 | * @pipe: the pipe to transfer | ||
665 | * @size: chunk size in bytes | ||
666 | * | ||
667 | * transfer a single buffer chunk. EOB notificaton is added after that. | ||
668 | * called from the interrupt handler, too. | ||
669 | * | ||
670 | * return 0 if ok. | ||
671 | */ | ||
672 | static int vx_pcm_playback_transfer_chunk(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe, int size) | ||
673 | { | ||
674 | int space, err = 0; | ||
675 | |||
676 | space = vx_query_hbuffer_size(chip, pipe); | ||
677 | if (space < 0) { | ||
678 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
679 | vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); | ||
680 | snd_printd("error hbuffer\n"); | ||
681 | return space; | ||
682 | } | ||
683 | if (space < size) { | ||
684 | vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); | ||
685 | snd_printd("no enough hbuffer space %d\n", space); | ||
686 | return -EIO; /* XRUN */ | ||
687 | } | ||
688 | |||
689 | /* we don't need irqsave here, because this function | ||
690 | * is called from either trigger callback or irq handler | ||
691 | */ | ||
692 | spin_lock(&chip->lock); | ||
693 | vx_pseudo_dma_write(chip, runtime, pipe, size); | ||
694 | err = vx_notify_end_of_buffer(chip, pipe); | ||
695 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
696 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
697 | spin_unlock(&chip->lock); | ||
698 | return err; | ||
699 | } | ||
700 | |||
701 | /* | ||
702 | * update the position of the given pipe. | ||
703 | * pipe->position is updated and wrapped within the buffer size. | ||
704 | * pipe->transferred is updated, too, but the size is not wrapped, | ||
705 | * so that the caller can check the total transferred size later | ||
706 | * (to call snd_pcm_period_elapsed). | ||
707 | */ | ||
708 | static int vx_update_pipe_position(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) | ||
709 | { | ||
710 | struct vx_rmh rmh; | ||
711 | int err, update; | ||
712 | u64 count; | ||
713 | |||
714 | vx_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT); | ||
715 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
716 | err = vx_send_msg(chip, &rmh); | ||
717 | if (err < 0) | ||
718 | return err; | ||
719 | |||
720 | count = ((u64)(rmh.Stat[0] & 0xfffff) << 24) | (u64)rmh.Stat[1]; | ||
721 | update = (int)(count - pipe->cur_count); | ||
722 | pipe->cur_count = count; | ||
723 | pipe->position += update; | ||
724 | if (pipe->position >= (int)runtime->buffer_size) | ||
725 | pipe->position %= runtime->buffer_size; | ||
726 | pipe->transferred += update; | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | /* | ||
731 | * transfer the pending playback buffer data to DSP | ||
732 | * called from interrupt handler | ||
733 | */ | ||
734 | static void vx_pcm_playback_transfer(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe, int nchunks) | ||
735 | { | ||
736 | int i, err; | ||
737 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
738 | |||
739 | if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) | ||
740 | return; | ||
741 | for (i = 0; i < nchunks; i++) { | ||
742 | if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe, | ||
743 | chip->ibl.size)) < 0) | ||
744 | return; | ||
745 | } | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * update the playback position and call snd_pcm_period_elapsed() if necessary | ||
750 | * called from interrupt handler | ||
751 | */ | ||
752 | static void vx_pcm_playback_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe) | ||
753 | { | ||
754 | int err; | ||
755 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
756 | |||
757 | if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) { | ||
758 | if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0) | ||
759 | return; | ||
760 | if (pipe->transferred >= (int)runtime->period_size) { | ||
761 | pipe->transferred %= runtime->period_size; | ||
762 | snd_pcm_period_elapsed(subs); | ||
763 | } | ||
764 | } | ||
765 | } | ||
766 | |||
767 | /* | ||
768 | * start the stream and pipe. | ||
769 | * this function is called from tasklet, which is invoked by the trigger | ||
770 | * START callback. | ||
771 | */ | ||
772 | static void vx_pcm_delayed_start(unsigned long arg) | ||
773 | { | ||
774 | snd_pcm_substream_t *subs = (snd_pcm_substream_t *)arg; | ||
775 | vx_core_t *chip = subs->pcm->private_data; | ||
776 | vx_pipe_t *pipe = subs->runtime->private_data; | ||
777 | int err; | ||
778 | |||
779 | /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ | ||
780 | |||
781 | if ((err = vx_start_stream(chip, pipe)) < 0) { | ||
782 | snd_printk(KERN_ERR "vx: cannot start stream\n"); | ||
783 | return; | ||
784 | } | ||
785 | if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) { | ||
786 | snd_printk(KERN_ERR "vx: cannot start pipe\n"); | ||
787 | return; | ||
788 | } | ||
789 | /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/ | ||
790 | } | ||
791 | |||
792 | /* | ||
793 | * vx_pcm_playback_trigger - trigger callback for playback | ||
794 | */ | ||
795 | static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd) | ||
796 | { | ||
797 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
798 | vx_pipe_t *pipe = subs->runtime->private_data; | ||
799 | int err; | ||
800 | |||
801 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
802 | return -EBUSY; | ||
803 | |||
804 | switch (cmd) { | ||
805 | case SNDRV_PCM_TRIGGER_START: | ||
806 | case SNDRV_PCM_TRIGGER_RESUME: | ||
807 | if (! pipe->is_capture) | ||
808 | vx_pcm_playback_transfer(chip, subs, pipe, 2); | ||
809 | /* FIXME: | ||
810 | * we trigger the pipe using tasklet, so that the interrupts are | ||
811 | * issued surely after the trigger is completed. | ||
812 | */ | ||
813 | tasklet_hi_schedule(&pipe->start_tq); | ||
814 | chip->pcm_running++; | ||
815 | pipe->running = 1; | ||
816 | break; | ||
817 | case SNDRV_PCM_TRIGGER_STOP: | ||
818 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
819 | vx_toggle_pipe(chip, pipe, 0); | ||
820 | vx_stop_pipe(chip, pipe); | ||
821 | vx_stop_stream(chip, pipe); | ||
822 | chip->pcm_running--; | ||
823 | pipe->running = 0; | ||
824 | break; | ||
825 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
826 | if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0) | ||
827 | return err; | ||
828 | break; | ||
829 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
830 | if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) | ||
831 | return err; | ||
832 | break; | ||
833 | default: | ||
834 | return -EINVAL; | ||
835 | } | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * vx_pcm_playback_pointer - pointer callback for playback | ||
841 | */ | ||
842 | static snd_pcm_uframes_t vx_pcm_playback_pointer(snd_pcm_substream_t *subs) | ||
843 | { | ||
844 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
845 | vx_pipe_t *pipe = runtime->private_data; | ||
846 | return pipe->position; | ||
847 | } | ||
848 | |||
849 | /* | ||
850 | * vx_pcm_hw_params - hw_params callback for playback and capture | ||
851 | */ | ||
852 | static int vx_pcm_hw_params(snd_pcm_substream_t *subs, | ||
853 | snd_pcm_hw_params_t *hw_params) | ||
854 | { | ||
855 | return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * vx_pcm_hw_free - hw_free callback for playback and capture | ||
860 | */ | ||
861 | static int vx_pcm_hw_free(snd_pcm_substream_t *subs) | ||
862 | { | ||
863 | return snd_pcm_free_vmalloc_buffer(subs); | ||
864 | } | ||
865 | |||
866 | /* | ||
867 | * vx_pcm_prepare - prepare callback for playback and capture | ||
868 | */ | ||
869 | static int vx_pcm_prepare(snd_pcm_substream_t *subs) | ||
870 | { | ||
871 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
872 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
873 | vx_pipe_t *pipe = runtime->private_data; | ||
874 | int err, data_mode; | ||
875 | // int max_size, nchunks; | ||
876 | |||
877 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
878 | return -EBUSY; | ||
879 | |||
880 | data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; | ||
881 | if (data_mode != pipe->data_mode && ! pipe->is_capture) { | ||
882 | /* IEC958 status (raw-mode) was changed */ | ||
883 | /* we reopen the pipe */ | ||
884 | struct vx_rmh rmh; | ||
885 | snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode); | ||
886 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
887 | vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0); | ||
888 | if ((err = vx_send_msg(chip, &rmh)) < 0) | ||
889 | return err; | ||
890 | vx_init_rmh(&rmh, CMD_RES_PIPE); | ||
891 | vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels); | ||
892 | if (data_mode) | ||
893 | rmh.Cmd[0] |= BIT_DATA_MODE; | ||
894 | if ((err = vx_send_msg(chip, &rmh)) < 0) | ||
895 | return err; | ||
896 | pipe->data_mode = data_mode; | ||
897 | } | ||
898 | |||
899 | if (chip->pcm_running && chip->freq != runtime->rate) { | ||
900 | snd_printk(KERN_ERR "vx: cannot set different clock %d from the current %d\n", runtime->rate, chip->freq); | ||
901 | return -EINVAL; | ||
902 | } | ||
903 | vx_set_clock(chip, runtime->rate); | ||
904 | |||
905 | if ((err = vx_set_format(chip, pipe, runtime)) < 0) | ||
906 | return err; | ||
907 | |||
908 | if (vx_is_pcmcia(chip)) { | ||
909 | pipe->align = 2; /* 16bit word */ | ||
910 | } else { | ||
911 | pipe->align = 4; /* 32bit word */ | ||
912 | } | ||
913 | |||
914 | pipe->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size); | ||
915 | pipe->period_bytes = frames_to_bytes(runtime, runtime->period_size); | ||
916 | pipe->hw_ptr = 0; | ||
917 | |||
918 | /* set the timestamp */ | ||
919 | vx_update_pipe_position(chip, runtime, pipe); | ||
920 | /* clear again */ | ||
921 | pipe->transferred = 0; | ||
922 | pipe->position = 0; | ||
923 | |||
924 | pipe->prepared = 1; | ||
925 | |||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | |||
930 | /* | ||
931 | * operators for PCM playback | ||
932 | */ | ||
933 | static snd_pcm_ops_t vx_pcm_playback_ops = { | ||
934 | .open = vx_pcm_playback_open, | ||
935 | .close = vx_pcm_playback_close, | ||
936 | .ioctl = snd_pcm_lib_ioctl, | ||
937 | .hw_params = vx_pcm_hw_params, | ||
938 | .hw_free = vx_pcm_hw_free, | ||
939 | .prepare = vx_pcm_prepare, | ||
940 | .trigger = vx_pcm_trigger, | ||
941 | .pointer = vx_pcm_playback_pointer, | ||
942 | .page = snd_pcm_get_vmalloc_page, | ||
943 | }; | ||
944 | |||
945 | |||
946 | /* | ||
947 | * playback hw information | ||
948 | */ | ||
949 | |||
950 | static snd_pcm_hardware_t vx_pcm_capture_hw = { | ||
951 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
952 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | | ||
953 | SNDRV_PCM_INFO_RESUME), | ||
954 | .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, | ||
955 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
956 | .rate_min = 5000, | ||
957 | .rate_max = 48000, | ||
958 | .channels_min = 1, | ||
959 | .channels_max = 2, | ||
960 | .buffer_bytes_max = (128*1024), | ||
961 | .period_bytes_min = 126, | ||
962 | .period_bytes_max = (128*1024), | ||
963 | .periods_min = 2, | ||
964 | .periods_max = VX_MAX_PERIODS, | ||
965 | .fifo_size = 126, | ||
966 | }; | ||
967 | |||
968 | |||
969 | /* | ||
970 | * vx_pcm_capture_open - open callback for capture | ||
971 | */ | ||
972 | static int vx_pcm_capture_open(snd_pcm_substream_t *subs) | ||
973 | { | ||
974 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
975 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
976 | vx_pipe_t *pipe; | ||
977 | vx_pipe_t *pipe_out_monitoring = NULL; | ||
978 | unsigned int audio; | ||
979 | int err; | ||
980 | |||
981 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
982 | return -EBUSY; | ||
983 | |||
984 | audio = subs->pcm->device * 2; | ||
985 | snd_assert(audio < chip->audio_ins, return -EINVAL); | ||
986 | err = vx_alloc_pipe(chip, 1, audio, 2, &pipe); | ||
987 | if (err < 0) | ||
988 | return err; | ||
989 | pipe->substream = subs; | ||
990 | tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); | ||
991 | chip->capture_pipes[audio] = pipe; | ||
992 | |||
993 | /* check if monitoring is needed */ | ||
994 | if (chip->audio_monitor_active[audio]) { | ||
995 | pipe_out_monitoring = chip->playback_pipes[audio]; | ||
996 | if (! pipe_out_monitoring) { | ||
997 | /* allocate a pipe */ | ||
998 | err = vx_alloc_pipe(chip, 0, audio, 2, &pipe_out_monitoring); | ||
999 | if (err < 0) | ||
1000 | return err; | ||
1001 | chip->playback_pipes[audio] = pipe_out_monitoring; | ||
1002 | } | ||
1003 | pipe_out_monitoring->references++; | ||
1004 | /* | ||
1005 | if an output pipe is available, it's audios still may need to be | ||
1006 | unmuted. hence we'll have to call a mixer entry point. | ||
1007 | */ | ||
1008 | vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], chip->audio_monitor_active[audio]); | ||
1009 | /* assuming stereo */ | ||
1010 | vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], chip->audio_monitor_active[audio+1]); | ||
1011 | } | ||
1012 | |||
1013 | pipe->monitoring_pipe = pipe_out_monitoring; /* default value NULL */ | ||
1014 | |||
1015 | runtime->hw = vx_pcm_capture_hw; | ||
1016 | runtime->hw.period_bytes_min = chip->ibl.size; | ||
1017 | runtime->private_data = pipe; | ||
1018 | |||
1019 | /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ | ||
1020 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); | ||
1021 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | /* | ||
1027 | * vx_pcm_capture_close - close callback for capture | ||
1028 | */ | ||
1029 | static int vx_pcm_capture_close(snd_pcm_substream_t *subs) | ||
1030 | { | ||
1031 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
1032 | vx_pipe_t *pipe; | ||
1033 | vx_pipe_t *pipe_out_monitoring; | ||
1034 | |||
1035 | if (! subs->runtime->private_data) | ||
1036 | return -EINVAL; | ||
1037 | pipe = subs->runtime->private_data; | ||
1038 | chip->capture_pipes[pipe->number] = NULL; | ||
1039 | |||
1040 | pipe_out_monitoring = pipe->monitoring_pipe; | ||
1041 | |||
1042 | /* | ||
1043 | if an output pipe is attached to this input, | ||
1044 | check if it needs to be released. | ||
1045 | */ | ||
1046 | if (pipe_out_monitoring) { | ||
1047 | if (--pipe_out_monitoring->references == 0) { | ||
1048 | vx_free_pipe(chip, pipe_out_monitoring); | ||
1049 | chip->playback_pipes[pipe->number] = NULL; | ||
1050 | pipe->monitoring_pipe = NULL; | ||
1051 | } | ||
1052 | } | ||
1053 | |||
1054 | vx_free_pipe(chip, pipe); | ||
1055 | return 0; | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | |||
1060 | #define DMA_READ_ALIGN 6 /* hardware alignment for read */ | ||
1061 | |||
1062 | /* | ||
1063 | * vx_pcm_capture_update - update the capture buffer | ||
1064 | */ | ||
1065 | static void vx_pcm_capture_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe) | ||
1066 | { | ||
1067 | int size, space, count; | ||
1068 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
1069 | |||
1070 | if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) | ||
1071 | return; | ||
1072 | |||
1073 | size = runtime->buffer_size - snd_pcm_capture_avail(runtime); | ||
1074 | if (! size) | ||
1075 | return; | ||
1076 | size = frames_to_bytes(runtime, size); | ||
1077 | space = vx_query_hbuffer_size(chip, pipe); | ||
1078 | if (space < 0) | ||
1079 | goto _error; | ||
1080 | if (size > space) | ||
1081 | size = space; | ||
1082 | size = (size / 3) * 3; /* align to 3 bytes */ | ||
1083 | if (size < DMA_READ_ALIGN) | ||
1084 | goto _error; | ||
1085 | |||
1086 | /* keep the last 6 bytes, they will be read after disconnection */ | ||
1087 | count = size - DMA_READ_ALIGN; | ||
1088 | /* read bytes until the current pointer reaches to the aligned position | ||
1089 | * for word-transfer | ||
1090 | */ | ||
1091 | while (count > 0) { | ||
1092 | if ((pipe->hw_ptr % pipe->align) == 0) | ||
1093 | break; | ||
1094 | if (vx_wait_for_rx_full(chip) < 0) | ||
1095 | goto _error; | ||
1096 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1097 | count -= 3; | ||
1098 | } | ||
1099 | if (count > 0) { | ||
1100 | /* ok, let's accelerate! */ | ||
1101 | int align = pipe->align * 3; | ||
1102 | space = (count / align) * align; | ||
1103 | vx_pseudo_dma_read(chip, runtime, pipe, space); | ||
1104 | count -= space; | ||
1105 | } | ||
1106 | /* read the rest of bytes */ | ||
1107 | while (count > 0) { | ||
1108 | if (vx_wait_for_rx_full(chip) < 0) | ||
1109 | goto _error; | ||
1110 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1111 | count -= 3; | ||
1112 | } | ||
1113 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
1114 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
1115 | /* read the last pending 6 bytes */ | ||
1116 | count = DMA_READ_ALIGN; | ||
1117 | while (count > 0) { | ||
1118 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1119 | count -= 3; | ||
1120 | } | ||
1121 | /* update the position */ | ||
1122 | pipe->transferred += size; | ||
1123 | if (pipe->transferred >= pipe->period_bytes) { | ||
1124 | pipe->transferred %= pipe->period_bytes; | ||
1125 | snd_pcm_period_elapsed(subs); | ||
1126 | } | ||
1127 | return; | ||
1128 | |||
1129 | _error: | ||
1130 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
1131 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
1132 | return; | ||
1133 | } | ||
1134 | |||
1135 | /* | ||
1136 | * vx_pcm_capture_pointer - pointer callback for capture | ||
1137 | */ | ||
1138 | static snd_pcm_uframes_t vx_pcm_capture_pointer(snd_pcm_substream_t *subs) | ||
1139 | { | ||
1140 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
1141 | vx_pipe_t *pipe = runtime->private_data; | ||
1142 | return bytes_to_frames(runtime, pipe->hw_ptr); | ||
1143 | } | ||
1144 | |||
1145 | /* | ||
1146 | * operators for PCM capture | ||
1147 | */ | ||
1148 | static snd_pcm_ops_t vx_pcm_capture_ops = { | ||
1149 | .open = vx_pcm_capture_open, | ||
1150 | .close = vx_pcm_capture_close, | ||
1151 | .ioctl = snd_pcm_lib_ioctl, | ||
1152 | .hw_params = vx_pcm_hw_params, | ||
1153 | .hw_free = vx_pcm_hw_free, | ||
1154 | .prepare = vx_pcm_prepare, | ||
1155 | .trigger = vx_pcm_trigger, | ||
1156 | .pointer = vx_pcm_capture_pointer, | ||
1157 | .page = snd_pcm_get_vmalloc_page, | ||
1158 | }; | ||
1159 | |||
1160 | |||
1161 | /* | ||
1162 | * interrupt handler for pcm streams | ||
1163 | */ | ||
1164 | void vx_pcm_update_intr(vx_core_t *chip, unsigned int events) | ||
1165 | { | ||
1166 | unsigned int i; | ||
1167 | vx_pipe_t *pipe; | ||
1168 | |||
1169 | #define EVENT_MASK (END_OF_BUFFER_EVENTS_PENDING|ASYNC_EVENTS_PENDING) | ||
1170 | |||
1171 | if (events & EVENT_MASK) { | ||
1172 | vx_init_rmh(&chip->irq_rmh, CMD_ASYNC); | ||
1173 | if (events & ASYNC_EVENTS_PENDING) | ||
1174 | chip->irq_rmh.Cmd[0] |= 0x00000001; /* SEL_ASYNC_EVENTS */ | ||
1175 | if (events & END_OF_BUFFER_EVENTS_PENDING) | ||
1176 | chip->irq_rmh.Cmd[0] |= 0x00000002; /* SEL_END_OF_BUF_EVENTS */ | ||
1177 | |||
1178 | if (vx_send_msg(chip, &chip->irq_rmh) < 0) { | ||
1179 | snd_printdd(KERN_ERR "msg send error!!\n"); | ||
1180 | return; | ||
1181 | } | ||
1182 | |||
1183 | i = 1; | ||
1184 | while (i < chip->irq_rmh.LgStat) { | ||
1185 | int p, buf, capture, eob; | ||
1186 | p = chip->irq_rmh.Stat[i] & MASK_FIRST_FIELD; | ||
1187 | capture = (chip->irq_rmh.Stat[i] & 0x400000) ? 1 : 0; | ||
1188 | eob = (chip->irq_rmh.Stat[i] & 0x800000) ? 1 : 0; | ||
1189 | i++; | ||
1190 | if (events & ASYNC_EVENTS_PENDING) | ||
1191 | i++; | ||
1192 | buf = 1; /* force to transfer */ | ||
1193 | if (events & END_OF_BUFFER_EVENTS_PENDING) { | ||
1194 | if (eob) | ||
1195 | buf = chip->irq_rmh.Stat[i]; | ||
1196 | i++; | ||
1197 | } | ||
1198 | if (capture) | ||
1199 | continue; | ||
1200 | snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,); | ||
1201 | pipe = chip->playback_pipes[p]; | ||
1202 | if (pipe && pipe->substream) { | ||
1203 | vx_pcm_playback_update(chip, pipe->substream, pipe); | ||
1204 | vx_pcm_playback_transfer(chip, pipe->substream, pipe, buf); | ||
1205 | } | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | /* update the capture pcm pointers as frequently as possible */ | ||
1210 | for (i = 0; i < chip->audio_ins; i++) { | ||
1211 | pipe = chip->capture_pipes[i]; | ||
1212 | if (pipe && pipe->substream) | ||
1213 | vx_pcm_capture_update(chip, pipe->substream, pipe); | ||
1214 | } | ||
1215 | } | ||
1216 | |||
1217 | |||
1218 | /* | ||
1219 | * vx_init_audio_io - check the availabe audio i/o and allocate pipe arrays | ||
1220 | */ | ||
1221 | static int vx_init_audio_io(vx_core_t *chip) | ||
1222 | { | ||
1223 | struct vx_rmh rmh; | ||
1224 | int preferred; | ||
1225 | |||
1226 | vx_init_rmh(&rmh, CMD_SUPPORTED); | ||
1227 | if (vx_send_msg(chip, &rmh) < 0) { | ||
1228 | snd_printk(KERN_ERR "vx: cannot get the supported audio data\n"); | ||
1229 | return -ENXIO; | ||
1230 | } | ||
1231 | |||
1232 | chip->audio_outs = rmh.Stat[0] & MASK_FIRST_FIELD; | ||
1233 | chip->audio_ins = (rmh.Stat[0] >> (FIELD_SIZE*2)) & MASK_FIRST_FIELD; | ||
1234 | chip->audio_info = rmh.Stat[1]; | ||
1235 | |||
1236 | /* allocate pipes */ | ||
1237 | chip->playback_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_outs, GFP_KERNEL); | ||
1238 | chip->capture_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_ins, GFP_KERNEL); | ||
1239 | if (! chip->playback_pipes || ! chip->capture_pipes) | ||
1240 | return -ENOMEM; | ||
1241 | |||
1242 | memset(chip->playback_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_outs); | ||
1243 | memset(chip->capture_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_ins); | ||
1244 | |||
1245 | preferred = chip->ibl.size; | ||
1246 | chip->ibl.size = 0; | ||
1247 | vx_set_ibl(chip, &chip->ibl); /* query the info */ | ||
1248 | if (preferred > 0) { | ||
1249 | chip->ibl.size = ((preferred + chip->ibl.granularity - 1) / chip->ibl.granularity) * chip->ibl.granularity; | ||
1250 | if (chip->ibl.size > chip->ibl.max_size) | ||
1251 | chip->ibl.size = chip->ibl.max_size; | ||
1252 | } else | ||
1253 | chip->ibl.size = chip->ibl.min_size; /* set to the minimum */ | ||
1254 | vx_set_ibl(chip, &chip->ibl); | ||
1255 | |||
1256 | return 0; | ||
1257 | } | ||
1258 | |||
1259 | |||
1260 | /* | ||
1261 | * free callback for pcm | ||
1262 | */ | ||
1263 | static void snd_vx_pcm_free(snd_pcm_t *pcm) | ||
1264 | { | ||
1265 | vx_core_t *chip = pcm->private_data; | ||
1266 | chip->pcm[pcm->device] = NULL; | ||
1267 | if (chip->playback_pipes) { | ||
1268 | kfree(chip->playback_pipes); | ||
1269 | chip->playback_pipes = NULL; | ||
1270 | } | ||
1271 | if (chip->capture_pipes) { | ||
1272 | kfree(chip->capture_pipes); | ||
1273 | chip->capture_pipes = NULL; | ||
1274 | } | ||
1275 | } | ||
1276 | |||
1277 | /* | ||
1278 | * snd_vx_pcm_new - create and initialize a pcm | ||
1279 | */ | ||
1280 | int snd_vx_pcm_new(vx_core_t *chip) | ||
1281 | { | ||
1282 | snd_pcm_t *pcm; | ||
1283 | unsigned int i; | ||
1284 | int err; | ||
1285 | |||
1286 | if ((err = vx_init_audio_io(chip)) < 0) | ||
1287 | return err; | ||
1288 | |||
1289 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
1290 | unsigned int outs, ins; | ||
1291 | outs = chip->audio_outs > i * 2 ? 1 : 0; | ||
1292 | ins = chip->audio_ins > i * 2 ? 1 : 0; | ||
1293 | if (! outs && ! ins) | ||
1294 | break; | ||
1295 | err = snd_pcm_new(chip->card, "VX PCM", i, | ||
1296 | outs, ins, &pcm); | ||
1297 | if (err < 0) | ||
1298 | return err; | ||
1299 | if (outs) | ||
1300 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops); | ||
1301 | if (ins) | ||
1302 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops); | ||
1303 | |||
1304 | pcm->private_data = chip; | ||
1305 | pcm->private_free = snd_vx_pcm_free; | ||
1306 | pcm->info_flags = 0; | ||
1307 | strcpy(pcm->name, chip->card->shortname); | ||
1308 | chip->pcm[i] = pcm; | ||
1309 | } | ||
1310 | |||
1311 | return 0; | ||
1312 | } | ||
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c new file mode 100644 index 000000000000..18114713c3b3 --- /dev/null +++ b/sound/drivers/vx/vx_uer.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * IEC958 stuff | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | |||
30 | /* | ||
31 | * vx_modify_board_clock - tell the board that its clock has been modified | ||
32 | * @sync: DSP needs to resynchronize its FIFO | ||
33 | */ | ||
34 | static int vx_modify_board_clock(vx_core_t *chip, int sync) | ||
35 | { | ||
36 | struct vx_rmh rmh; | ||
37 | |||
38 | vx_init_rmh(&rmh, CMD_MODIFY_CLOCK); | ||
39 | /* Ask the DSP to resynchronize its FIFO. */ | ||
40 | if (sync) | ||
41 | rmh.Cmd[0] |= CMD_MODIFY_CLOCK_S_BIT; | ||
42 | return vx_send_msg(chip, &rmh); | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * vx_modify_board_inputs - resync audio inputs | ||
47 | */ | ||
48 | static int vx_modify_board_inputs(vx_core_t *chip) | ||
49 | { | ||
50 | struct vx_rmh rmh; | ||
51 | |||
52 | vx_init_rmh(&rmh, CMD_RESYNC_AUDIO_INPUTS); | ||
53 | rmh.Cmd[0] |= 1 << 0; /* reference: AUDIO 0 */ | ||
54 | return vx_send_msg(chip, &rmh); | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * vx_read_one_cbit - read one bit from UER config | ||
59 | * @index: the bit index | ||
60 | * returns 0 or 1. | ||
61 | */ | ||
62 | static int vx_read_one_cbit(vx_core_t *chip, int index) | ||
63 | { | ||
64 | unsigned long flags; | ||
65 | int val; | ||
66 | spin_lock_irqsave(&chip->lock, flags); | ||
67 | if (chip->type >= VX_TYPE_VXPOCKET) { | ||
68 | vx_outb(chip, CSUER, 1); /* read */ | ||
69 | vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); | ||
70 | val = (vx_inb(chip, RUER) >> 7) & 0x01; | ||
71 | } else { | ||
72 | vx_outl(chip, CSUER, 1); /* read */ | ||
73 | vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); | ||
74 | val = (vx_inl(chip, RUER) >> 7) & 0x01; | ||
75 | } | ||
76 | spin_unlock_irqrestore(&chip->lock, flags); | ||
77 | return val; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * vx_write_one_cbit - write one bit to UER config | ||
82 | * @index: the bit index | ||
83 | * @val: bit value, 0 or 1 | ||
84 | */ | ||
85 | static void vx_write_one_cbit(vx_core_t *chip, int index, int val) | ||
86 | { | ||
87 | unsigned long flags; | ||
88 | val = !!val; /* 0 or 1 */ | ||
89 | spin_lock_irqsave(&chip->lock, flags); | ||
90 | if (vx_is_pcmcia(chip)) { | ||
91 | vx_outb(chip, CSUER, 0); /* write */ | ||
92 | vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); | ||
93 | } else { | ||
94 | vx_outl(chip, CSUER, 0); /* write */ | ||
95 | vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); | ||
96 | } | ||
97 | spin_unlock_irqrestore(&chip->lock, flags); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * vx_read_uer_status - read the current UER status | ||
102 | * @mode: pointer to store the UER mode, VX_UER_MODE_XXX | ||
103 | * | ||
104 | * returns the frequency of UER, or 0 if not sync, | ||
105 | * or a negative error code. | ||
106 | */ | ||
107 | static int vx_read_uer_status(vx_core_t *chip, int *mode) | ||
108 | { | ||
109 | int val, freq; | ||
110 | |||
111 | /* Default values */ | ||
112 | freq = 0; | ||
113 | |||
114 | /* Read UER status */ | ||
115 | if (vx_is_pcmcia(chip)) | ||
116 | val = vx_inb(chip, CSUER); | ||
117 | else | ||
118 | val = vx_inl(chip, CSUER); | ||
119 | if (val < 0) | ||
120 | return val; | ||
121 | /* If clock is present, read frequency */ | ||
122 | if (val & VX_SUER_CLOCK_PRESENT_MASK) { | ||
123 | switch (val & VX_SUER_FREQ_MASK) { | ||
124 | case VX_SUER_FREQ_32KHz_MASK: | ||
125 | freq = 32000; | ||
126 | break; | ||
127 | case VX_SUER_FREQ_44KHz_MASK: | ||
128 | freq = 44100; | ||
129 | break; | ||
130 | case VX_SUER_FREQ_48KHz_MASK: | ||
131 | freq = 48000; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | if (val & VX_SUER_DATA_PRESENT_MASK) | ||
136 | /* bit 0 corresponds to consumer/professional bit */ | ||
137 | *mode = vx_read_one_cbit(chip, 0) ? | ||
138 | VX_UER_MODE_PROFESSIONAL : VX_UER_MODE_CONSUMER; | ||
139 | else | ||
140 | *mode = VX_UER_MODE_NOT_PRESENT; | ||
141 | |||
142 | return freq; | ||
143 | } | ||
144 | |||
145 | |||
146 | /* | ||
147 | * compute the sample clock value from frequency | ||
148 | * | ||
149 | * The formula is as follows: | ||
150 | * | ||
151 | * HexFreq = (dword) ((double) ((double) 28224000 / (double) Frequency)) | ||
152 | * switch ( HexFreq & 0x00000F00 ) | ||
153 | * case 0x00000100: ; | ||
154 | * case 0x00000200: | ||
155 | * case 0x00000300: HexFreq -= 0x00000201 ; | ||
156 | * case 0x00000400: | ||
157 | * case 0x00000500: | ||
158 | * case 0x00000600: | ||
159 | * case 0x00000700: HexFreq = (dword) (((double) 28224000 / (double) (Frequency*2)) - 1) | ||
160 | * default : HexFreq = (dword) ((double) 28224000 / (double) (Frequency*4)) - 0x000001FF | ||
161 | */ | ||
162 | |||
163 | static int vx_calc_clock_from_freq(vx_core_t *chip, int freq) | ||
164 | { | ||
165 | #define XX_FECH48000 0x0000004B | ||
166 | #define XX_FECH32000 0x00000171 | ||
167 | #define XX_FECH24000 0x0000024B | ||
168 | #define XX_FECH16000 0x00000371 | ||
169 | #define XX_FECH12000 0x0000044B | ||
170 | #define XX_FECH8000 0x00000571 | ||
171 | #define XX_FECH44100 0x0000007F | ||
172 | #define XX_FECH29400 0x0000016F | ||
173 | #define XX_FECH22050 0x0000027F | ||
174 | #define XX_FECH14000 0x000003EF | ||
175 | #define XX_FECH11025 0x0000047F | ||
176 | #define XX_FECH7350 0x000005BF | ||
177 | |||
178 | switch (freq) { | ||
179 | case 48000: return XX_FECH48000; | ||
180 | case 44100: return XX_FECH44100; | ||
181 | case 32000: return XX_FECH32000; | ||
182 | case 29400: return XX_FECH29400; | ||
183 | case 24000: return XX_FECH24000; | ||
184 | case 22050: return XX_FECH22050; | ||
185 | case 16000: return XX_FECH16000; | ||
186 | case 14000: return XX_FECH14000; | ||
187 | case 12000: return XX_FECH12000; | ||
188 | case 11025: return XX_FECH11025; | ||
189 | case 8000: return XX_FECH8000; | ||
190 | case 7350: return XX_FECH7350; | ||
191 | default: return freq; /* The value is already correct */ | ||
192 | } | ||
193 | } | ||
194 | |||
195 | |||
196 | /* | ||
197 | * vx_change_clock_source - change the clock source | ||
198 | * @source: the new source | ||
199 | */ | ||
200 | static void vx_change_clock_source(vx_core_t *chip, int source) | ||
201 | { | ||
202 | unsigned long flags; | ||
203 | |||
204 | /* we mute DAC to prevent clicks */ | ||
205 | vx_toggle_dac_mute(chip, 1); | ||
206 | spin_lock_irqsave(&chip->lock, flags); | ||
207 | chip->ops->set_clock_source(chip, source); | ||
208 | chip->clock_source = source; | ||
209 | spin_unlock_irqrestore(&chip->lock, flags); | ||
210 | /* unmute */ | ||
211 | vx_toggle_dac_mute(chip, 0); | ||
212 | } | ||
213 | |||
214 | |||
215 | /* | ||
216 | * set the internal clock | ||
217 | */ | ||
218 | void vx_set_internal_clock(vx_core_t *chip, unsigned int freq) | ||
219 | { | ||
220 | int clock; | ||
221 | unsigned long flags; | ||
222 | /* Get real clock value */ | ||
223 | clock = vx_calc_clock_from_freq(chip, freq); | ||
224 | snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq); | ||
225 | spin_lock_irqsave(&chip->lock, flags); | ||
226 | if (vx_is_pcmcia(chip)) { | ||
227 | vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f); | ||
228 | vx_outb(chip, LOFREQ, clock & 0xff); | ||
229 | } else { | ||
230 | vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f); | ||
231 | vx_outl(chip, LOFREQ, clock & 0xff); | ||
232 | } | ||
233 | spin_unlock_irqrestore(&chip->lock, flags); | ||
234 | } | ||
235 | |||
236 | |||
237 | /* | ||
238 | * set the iec958 status bits | ||
239 | * @bits: 32-bit status bits | ||
240 | */ | ||
241 | void vx_set_iec958_status(vx_core_t *chip, unsigned int bits) | ||
242 | { | ||
243 | int i; | ||
244 | |||
245 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
246 | return; | ||
247 | |||
248 | for (i = 0; i < 32; i++) | ||
249 | vx_write_one_cbit(chip, i, bits & (1 << i)); | ||
250 | } | ||
251 | |||
252 | |||
253 | /* | ||
254 | * vx_set_clock - change the clock and audio source if necessary | ||
255 | */ | ||
256 | int vx_set_clock(vx_core_t *chip, unsigned int freq) | ||
257 | { | ||
258 | int src_changed = 0; | ||
259 | |||
260 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
261 | return 0; | ||
262 | |||
263 | /* change the audio source if possible */ | ||
264 | vx_sync_audio_source(chip); | ||
265 | |||
266 | if (chip->clock_mode == VX_CLOCK_MODE_EXTERNAL || | ||
267 | (chip->clock_mode == VX_CLOCK_MODE_AUTO && | ||
268 | chip->audio_source == VX_AUDIO_SRC_DIGITAL)) { | ||
269 | if (chip->clock_source != UER_SYNC) { | ||
270 | vx_change_clock_source(chip, UER_SYNC); | ||
271 | mdelay(6); | ||
272 | src_changed = 1; | ||
273 | } | ||
274 | } else if (chip->clock_mode == VX_CLOCK_MODE_INTERNAL || | ||
275 | (chip->clock_mode == VX_CLOCK_MODE_AUTO && | ||
276 | chip->audio_source != VX_AUDIO_SRC_DIGITAL)) { | ||
277 | if (chip->clock_source != INTERNAL_QUARTZ) { | ||
278 | vx_change_clock_source(chip, INTERNAL_QUARTZ); | ||
279 | src_changed = 1; | ||
280 | } | ||
281 | if (chip->freq == freq) | ||
282 | return 0; | ||
283 | vx_set_internal_clock(chip, freq); | ||
284 | if (src_changed) | ||
285 | vx_modify_board_inputs(chip); | ||
286 | } | ||
287 | if (chip->freq == freq) | ||
288 | return 0; | ||
289 | chip->freq = freq; | ||
290 | vx_modify_board_clock(chip, 1); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | |||
295 | /* | ||
296 | * vx_change_frequency - called from interrupt handler | ||
297 | */ | ||
298 | int vx_change_frequency(vx_core_t *chip) | ||
299 | { | ||
300 | int freq; | ||
301 | |||
302 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
303 | return 0; | ||
304 | |||
305 | if (chip->clock_source == INTERNAL_QUARTZ) | ||
306 | return 0; | ||
307 | /* | ||
308 | * Read the real UER board frequency | ||
309 | */ | ||
310 | freq = vx_read_uer_status(chip, &chip->uer_detected); | ||
311 | if (freq < 0) | ||
312 | return freq; | ||
313 | /* | ||
314 | * The frequency computed by the DSP is good and | ||
315 | * is different from the previous computed. | ||
316 | */ | ||
317 | if (freq == 48000 || freq == 44100 || freq == 32000) | ||
318 | chip->freq_detected = freq; | ||
319 | |||
320 | return 0; | ||
321 | } | ||