1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for Digigram miXart soundcards
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * main file with alsa callbacks
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Copyright (c) 2003 by Digigram <alsa@digigram.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/pci.h>
14*4882a593Smuzhiyun #include <linux/dma-mapping.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/mutex.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include <sound/core.h>
20*4882a593Smuzhiyun #include <sound/initval.h>
21*4882a593Smuzhiyun #include <sound/info.h>
22*4882a593Smuzhiyun #include <sound/control.h>
23*4882a593Smuzhiyun #include <sound/pcm.h>
24*4882a593Smuzhiyun #include <sound/pcm_params.h>
25*4882a593Smuzhiyun #include "mixart.h"
26*4882a593Smuzhiyun #include "mixart_hwdep.h"
27*4882a593Smuzhiyun #include "mixart_core.h"
28*4882a593Smuzhiyun #include "mixart_mixer.h"
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define CARD_NAME "miXart"
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun MODULE_AUTHOR("Digigram <alsa@digigram.com>");
33*4882a593Smuzhiyun MODULE_DESCRIPTION("Digigram " CARD_NAME);
34*4882a593Smuzhiyun MODULE_LICENSE("GPL");
35*4882a593Smuzhiyun MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
38*4882a593Smuzhiyun static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
39*4882a593Smuzhiyun static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun module_param_array(index, int, NULL, 0444);
42*4882a593Smuzhiyun MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
43*4882a593Smuzhiyun module_param_array(id, charp, NULL, 0444);
44*4882a593Smuzhiyun MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
45*4882a593Smuzhiyun module_param_array(enable, bool, NULL, 0444);
46*4882a593Smuzhiyun MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /*
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun static const struct pci_device_id snd_mixart_ids[] = {
52*4882a593Smuzhiyun { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */
53*4882a593Smuzhiyun { 0, }
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, snd_mixart_ids);
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun
mixart_set_pipe_state(struct mixart_mgr * mgr,struct mixart_pipe * pipe,int start)59*4882a593Smuzhiyun static int mixart_set_pipe_state(struct mixart_mgr *mgr,
60*4882a593Smuzhiyun struct mixart_pipe *pipe, int start)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun struct mixart_group_state_req group_state;
63*4882a593Smuzhiyun struct mixart_group_state_resp group_state_resp;
64*4882a593Smuzhiyun struct mixart_msg request;
65*4882a593Smuzhiyun int err;
66*4882a593Smuzhiyun u32 system_msg_uid;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun switch(pipe->status) {
69*4882a593Smuzhiyun case PIPE_RUNNING:
70*4882a593Smuzhiyun case PIPE_CLOCK_SET:
71*4882a593Smuzhiyun if(start) return 0; /* already started */
72*4882a593Smuzhiyun break;
73*4882a593Smuzhiyun case PIPE_STOPPED:
74*4882a593Smuzhiyun if(!start) return 0; /* already stopped */
75*4882a593Smuzhiyun break;
76*4882a593Smuzhiyun default:
77*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
78*4882a593Smuzhiyun "error mixart_set_pipe_state called with wrong pipe->status!\n");
79*4882a593Smuzhiyun return -EINVAL; /* function called with wrong pipe status */
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD;
87*4882a593Smuzhiyun request.uid = (struct mixart_uid){0,0};
88*4882a593Smuzhiyun request.data = &system_msg_uid;
89*4882a593Smuzhiyun request.size = sizeof(system_msg_uid);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
92*4882a593Smuzhiyun if(err) {
93*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
94*4882a593Smuzhiyun "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
95*4882a593Smuzhiyun return err;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /* start or stop the pipe (1 pipe) */
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun memset(&group_state, 0, sizeof(group_state));
101*4882a593Smuzhiyun group_state.pipe_count = 1;
102*4882a593Smuzhiyun group_state.pipe_uid[0] = pipe->group_uid;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun if(start)
105*4882a593Smuzhiyun request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
106*4882a593Smuzhiyun else
107*4882a593Smuzhiyun request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun request.uid = pipe->group_uid; /*(struct mixart_uid){0,0};*/
110*4882a593Smuzhiyun request.data = &group_state;
111*4882a593Smuzhiyun request.size = sizeof(group_state);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
114*4882a593Smuzhiyun if (err < 0 || group_state_resp.txx_status != 0) {
115*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
116*4882a593Smuzhiyun "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n",
117*4882a593Smuzhiyun err, group_state_resp.txx_status);
118*4882a593Smuzhiyun return -EINVAL;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun if(start) {
122*4882a593Smuzhiyun u32 stat = 0;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
127*4882a593Smuzhiyun if (err < 0 || group_state_resp.txx_status != 0) {
128*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
129*4882a593Smuzhiyun "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n",
130*4882a593Smuzhiyun err, group_state_resp.txx_status);
131*4882a593Smuzhiyun return -EINVAL;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun /* in case of start send a synchro top */
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
137*4882a593Smuzhiyun request.uid = (struct mixart_uid){0,0};
138*4882a593Smuzhiyun request.data = NULL;
139*4882a593Smuzhiyun request.size = 0;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
142*4882a593Smuzhiyun if (err < 0 || stat != 0) {
143*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
144*4882a593Smuzhiyun "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n",
145*4882a593Smuzhiyun err, stat);
146*4882a593Smuzhiyun return -EINVAL;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun pipe->status = PIPE_RUNNING;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun else /* !start */
152*4882a593Smuzhiyun pipe->status = PIPE_STOPPED;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun return 0;
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun
mixart_set_clock(struct mixart_mgr * mgr,struct mixart_pipe * pipe,unsigned int rate)158*4882a593Smuzhiyun static int mixart_set_clock(struct mixart_mgr *mgr,
159*4882a593Smuzhiyun struct mixart_pipe *pipe, unsigned int rate)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun struct mixart_msg request;
162*4882a593Smuzhiyun struct mixart_clock_properties clock_properties;
163*4882a593Smuzhiyun struct mixart_clock_properties_resp clock_prop_resp;
164*4882a593Smuzhiyun int err;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun switch(pipe->status) {
167*4882a593Smuzhiyun case PIPE_CLOCK_SET:
168*4882a593Smuzhiyun break;
169*4882a593Smuzhiyun case PIPE_RUNNING:
170*4882a593Smuzhiyun if(rate != 0)
171*4882a593Smuzhiyun break;
172*4882a593Smuzhiyun fallthrough;
173*4882a593Smuzhiyun default:
174*4882a593Smuzhiyun if(rate == 0)
175*4882a593Smuzhiyun return 0; /* nothing to do */
176*4882a593Smuzhiyun else {
177*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
178*4882a593Smuzhiyun "error mixart_set_clock(%d) called with wrong pipe->status !\n",
179*4882a593Smuzhiyun rate);
180*4882a593Smuzhiyun return -EINVAL;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun memset(&clock_properties, 0, sizeof(clock_properties));
185*4882a593Smuzhiyun clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK;
186*4882a593Smuzhiyun clock_properties.clock_mode = CM_STANDALONE;
187*4882a593Smuzhiyun clock_properties.frequency = rate;
188*4882a593Smuzhiyun clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
189*4882a593Smuzhiyun clock_properties.uid_caller[0] = pipe->group_uid;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun dev_dbg(&mgr->pci->dev, "mixart_set_clock to %d kHz\n", rate);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun request.message_id = MSG_CLOCK_SET_PROPERTIES;
194*4882a593Smuzhiyun request.uid = mgr->uid_console_manager;
195*4882a593Smuzhiyun request.data = &clock_properties;
196*4882a593Smuzhiyun request.size = sizeof(clock_properties);
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp);
199*4882a593Smuzhiyun if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) {
200*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
201*4882a593Smuzhiyun "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n",
202*4882a593Smuzhiyun err, clock_prop_resp.status, clock_prop_resp.clock_mode);
203*4882a593Smuzhiyun return -EINVAL;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if(rate) pipe->status = PIPE_CLOCK_SET;
207*4882a593Smuzhiyun else pipe->status = PIPE_RUNNING;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun return 0;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun /*
214*4882a593Smuzhiyun * Allocate or reference output pipe for analog IOs (pcmp0/1)
215*4882a593Smuzhiyun */
216*4882a593Smuzhiyun struct mixart_pipe *
snd_mixart_add_ref_pipe(struct snd_mixart * chip,int pcm_number,int capture,int monitoring)217*4882a593Smuzhiyun snd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture,
218*4882a593Smuzhiyun int monitoring)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun int stream_count;
221*4882a593Smuzhiyun struct mixart_pipe *pipe;
222*4882a593Smuzhiyun struct mixart_msg request;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun if(capture) {
225*4882a593Smuzhiyun if (pcm_number == MIXART_PCM_ANALOG) {
226*4882a593Smuzhiyun pipe = &(chip->pipe_in_ana); /* analog inputs */
227*4882a593Smuzhiyun } else {
228*4882a593Smuzhiyun pipe = &(chip->pipe_in_dig); /* digital inputs */
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP;
231*4882a593Smuzhiyun stream_count = MIXART_CAPTURE_STREAMS;
232*4882a593Smuzhiyun } else {
233*4882a593Smuzhiyun if (pcm_number == MIXART_PCM_ANALOG) {
234*4882a593Smuzhiyun pipe = &(chip->pipe_out_ana); /* analog outputs */
235*4882a593Smuzhiyun } else {
236*4882a593Smuzhiyun pipe = &(chip->pipe_out_dig); /* digital outputs */
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun request.message_id = MSG_STREAM_ADD_INPUT_GROUP;
239*4882a593Smuzhiyun stream_count = MIXART_PLAYBACK_STREAMS;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun /* a new stream is opened and there are already all streams in use */
243*4882a593Smuzhiyun if( (monitoring == 0) && (pipe->references >= stream_count) ) {
244*4882a593Smuzhiyun return NULL;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun /* pipe is not yet defined */
248*4882a593Smuzhiyun if( pipe->status == PIPE_UNDEFINED ) {
249*4882a593Smuzhiyun int err, i;
250*4882a593Smuzhiyun struct {
251*4882a593Smuzhiyun struct mixart_streaming_group_req sgroup_req;
252*4882a593Smuzhiyun struct mixart_streaming_group sgroup_resp;
253*4882a593Smuzhiyun } *buf;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun dev_dbg(chip->card->dev,
256*4882a593Smuzhiyun "add_ref_pipe audio chip(%d) pcm(%d)\n",
257*4882a593Smuzhiyun chip->chip_idx, pcm_number);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun buf = kmalloc(sizeof(*buf), GFP_KERNEL);
260*4882a593Smuzhiyun if (!buf)
261*4882a593Smuzhiyun return NULL;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun request.uid = (struct mixart_uid){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */
264*4882a593Smuzhiyun request.data = &buf->sgroup_req;
265*4882a593Smuzhiyun request.size = sizeof(buf->sgroup_req);
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req));
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun buf->sgroup_req.stream_count = stream_count;
270*4882a593Smuzhiyun buf->sgroup_req.channel_count = 2;
271*4882a593Smuzhiyun buf->sgroup_req.latency = 256;
272*4882a593Smuzhiyun buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun for (i=0; i<stream_count; i++) {
275*4882a593Smuzhiyun int j;
276*4882a593Smuzhiyun struct mixart_flowinfo *flowinfo;
277*4882a593Smuzhiyun struct mixart_bufferinfo *bufferinfo;
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun /* we don't yet know the format, so config 16 bit pcm audio for instance */
280*4882a593Smuzhiyun buf->sgroup_req.stream_info[i].size_max_byte_frame = 1024;
281*4882a593Smuzhiyun buf->sgroup_req.stream_info[i].size_max_sample_frame = 256;
282*4882a593Smuzhiyun buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun /* find the right bufferinfo_array */
285*4882a593Smuzhiyun j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i;
286*4882a593Smuzhiyun if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun buf->sgroup_req.flow_entry[i] = j;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area;
291*4882a593Smuzhiyun flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(struct mixart_bufferinfo));
292*4882a593Smuzhiyun flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
295*4882a593Smuzhiyun bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */
296*4882a593Smuzhiyun bufferinfo[j].available_length = 0; /* buffer is not yet allocated */
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun /* construct the identifier of the stream buffer received in the interrupts ! */
299*4882a593Smuzhiyun bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i;
300*4882a593Smuzhiyun if(capture) {
301*4882a593Smuzhiyun bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK;
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp);
306*4882a593Smuzhiyun if((err < 0) || (buf->sgroup_resp.status != 0)) {
307*4882a593Smuzhiyun dev_err(chip->card->dev,
308*4882a593Smuzhiyun "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n",
309*4882a593Smuzhiyun err, buf->sgroup_resp.status);
310*4882a593Smuzhiyun kfree(buf);
311*4882a593Smuzhiyun return NULL;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */
315*4882a593Smuzhiyun pipe->stream_count = buf->sgroup_resp.stream_count;
316*4882a593Smuzhiyun /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun pipe->status = PIPE_STOPPED;
319*4882a593Smuzhiyun kfree(buf);
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun if(monitoring) pipe->monitoring = 1;
323*4882a593Smuzhiyun else pipe->references++;
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun return pipe;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun
snd_mixart_kill_ref_pipe(struct mixart_mgr * mgr,struct mixart_pipe * pipe,int monitoring)329*4882a593Smuzhiyun int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
330*4882a593Smuzhiyun struct mixart_pipe *pipe, int monitoring)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun int err = 0;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun if(pipe->status == PIPE_UNDEFINED)
335*4882a593Smuzhiyun return 0;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun if(monitoring)
338*4882a593Smuzhiyun pipe->monitoring = 0;
339*4882a593Smuzhiyun else
340*4882a593Smuzhiyun pipe->references--;
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun if((pipe->references <= 0) && (pipe->monitoring == 0)) {
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun struct mixart_msg request;
345*4882a593Smuzhiyun struct mixart_delete_group_resp delete_resp;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun /* release the clock */
348*4882a593Smuzhiyun err = mixart_set_clock( mgr, pipe, 0);
349*4882a593Smuzhiyun if( err < 0 ) {
350*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
351*4882a593Smuzhiyun "mixart_set_clock(0) return error!\n");
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun /* stop the pipe */
355*4882a593Smuzhiyun err = mixart_set_pipe_state(mgr, pipe, 0);
356*4882a593Smuzhiyun if( err < 0 ) {
357*4882a593Smuzhiyun dev_err(&mgr->pci->dev, "error stopping pipe!\n");
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun request.message_id = MSG_STREAM_DELETE_GROUP;
361*4882a593Smuzhiyun request.uid = (struct mixart_uid){0,0};
362*4882a593Smuzhiyun request.data = &pipe->group_uid; /* the streaming group ! */
363*4882a593Smuzhiyun request.size = sizeof(pipe->group_uid);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun /* delete the pipe */
366*4882a593Smuzhiyun err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
367*4882a593Smuzhiyun if ((err < 0) || (delete_resp.status != 0)) {
368*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
369*4882a593Smuzhiyun "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n",
370*4882a593Smuzhiyun err, delete_resp.status);
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun pipe->group_uid = (struct mixart_uid){0,0};
374*4882a593Smuzhiyun pipe->stream_count = 0;
375*4882a593Smuzhiyun pipe->status = PIPE_UNDEFINED;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun return err;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun
mixart_set_stream_state(struct mixart_stream * stream,int start)381*4882a593Smuzhiyun static int mixart_set_stream_state(struct mixart_stream *stream, int start)
382*4882a593Smuzhiyun {
383*4882a593Smuzhiyun struct snd_mixart *chip;
384*4882a593Smuzhiyun struct mixart_stream_state_req stream_state_req;
385*4882a593Smuzhiyun struct mixart_msg request;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun if(!stream->substream)
388*4882a593Smuzhiyun return -EINVAL;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun memset(&stream_state_req, 0, sizeof(stream_state_req));
391*4882a593Smuzhiyun stream_state_req.stream_count = 1;
392*4882a593Smuzhiyun stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
393*4882a593Smuzhiyun stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
396*4882a593Smuzhiyun request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
397*4882a593Smuzhiyun else
398*4882a593Smuzhiyun request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun request.uid = (struct mixart_uid){0,0};
401*4882a593Smuzhiyun request.data = &stream_state_req;
402*4882a593Smuzhiyun request.size = sizeof(stream_state_req);
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun stream->abs_period_elapsed = 0; /* reset stream pos */
405*4882a593Smuzhiyun stream->buf_periods = 0;
406*4882a593Smuzhiyun stream->buf_period_frag = 0;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun chip = snd_pcm_substream_chip(stream->substream);
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun return snd_mixart_send_msg_nonblock(chip->mgr, &request);
411*4882a593Smuzhiyun }
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun /*
414*4882a593Smuzhiyun * Trigger callback
415*4882a593Smuzhiyun */
416*4882a593Smuzhiyun
snd_mixart_trigger(struct snd_pcm_substream * subs,int cmd)417*4882a593Smuzhiyun static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun struct mixart_stream *stream = subs->runtime->private_data;
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun switch (cmd) {
422*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_START:
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_START\n");
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /* START_STREAM */
427*4882a593Smuzhiyun if( mixart_set_stream_state(stream, 1) )
428*4882a593Smuzhiyun return -EINVAL;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_RUNNING;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun break;
433*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_STOP:
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun /* STOP_STREAM */
436*4882a593Smuzhiyun if( mixart_set_stream_state(stream, 0) )
437*4882a593Smuzhiyun return -EINVAL;
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_OPEN;
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun break;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
446*4882a593Smuzhiyun /* TODO */
447*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_PAUSE;
448*4882a593Smuzhiyun dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_PUSH\n");
449*4882a593Smuzhiyun break;
450*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
451*4882a593Smuzhiyun /* TODO */
452*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_RUNNING;
453*4882a593Smuzhiyun dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_RELEASE\n");
454*4882a593Smuzhiyun break;
455*4882a593Smuzhiyun default:
456*4882a593Smuzhiyun return -EINVAL;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun return 0;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
mixart_sync_nonblock_events(struct mixart_mgr * mgr)461*4882a593Smuzhiyun static int mixart_sync_nonblock_events(struct mixart_mgr *mgr)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun unsigned long timeout = jiffies + HZ;
464*4882a593Smuzhiyun while (atomic_read(&mgr->msg_processed) > 0) {
465*4882a593Smuzhiyun if (time_after(jiffies, timeout)) {
466*4882a593Smuzhiyun dev_err(&mgr->pci->dev,
467*4882a593Smuzhiyun "mixart: cannot process nonblock events!\n");
468*4882a593Smuzhiyun return -EBUSY;
469*4882a593Smuzhiyun }
470*4882a593Smuzhiyun schedule_timeout_uninterruptible(1);
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun return 0;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun /*
476*4882a593Smuzhiyun * prepare callback for all pcms
477*4882a593Smuzhiyun */
snd_mixart_prepare(struct snd_pcm_substream * subs)478*4882a593Smuzhiyun static int snd_mixart_prepare(struct snd_pcm_substream *subs)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun struct snd_mixart *chip = snd_pcm_substream_chip(subs);
481*4882a593Smuzhiyun struct mixart_stream *stream = subs->runtime->private_data;
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun dev_dbg(chip->card->dev, "snd_mixart_prepare\n");
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun mixart_sync_nonblock_events(chip->mgr);
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun /* only the first stream can choose the sample rate */
490*4882a593Smuzhiyun /* the further opened streams will be limited to its frequency (see open) */
491*4882a593Smuzhiyun if(chip->mgr->ref_count_rate == 1)
492*4882a593Smuzhiyun chip->mgr->sample_rate = subs->runtime->rate;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun /* set the clock only once (first stream) on the same pipe */
495*4882a593Smuzhiyun if(stream->pipe->references == 1) {
496*4882a593Smuzhiyun if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
497*4882a593Smuzhiyun return -EINVAL;
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun return 0;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun
mixart_set_format(struct mixart_stream * stream,snd_pcm_format_t format)504*4882a593Smuzhiyun static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t format)
505*4882a593Smuzhiyun {
506*4882a593Smuzhiyun int err;
507*4882a593Smuzhiyun struct snd_mixart *chip;
508*4882a593Smuzhiyun struct mixart_msg request;
509*4882a593Smuzhiyun struct mixart_stream_param_desc stream_param;
510*4882a593Smuzhiyun struct mixart_return_uid resp;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun chip = snd_pcm_substream_chip(stream->substream);
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun memset(&stream_param, 0, sizeof(stream_param));
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun stream_param.coding_type = CT_LINEAR;
517*4882a593Smuzhiyun stream_param.number_of_channel = stream->channels;
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun stream_param.sampling_freq = chip->mgr->sample_rate;
520*4882a593Smuzhiyun if(stream_param.sampling_freq == 0)
521*4882a593Smuzhiyun stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun switch(format){
524*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_U8:
525*4882a593Smuzhiyun stream_param.sample_type = ST_INTEGER_8;
526*4882a593Smuzhiyun stream_param.sample_size = 8;
527*4882a593Smuzhiyun break;
528*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S16_LE:
529*4882a593Smuzhiyun stream_param.sample_type = ST_INTEGER_16LE;
530*4882a593Smuzhiyun stream_param.sample_size = 16;
531*4882a593Smuzhiyun break;
532*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S16_BE:
533*4882a593Smuzhiyun stream_param.sample_type = ST_INTEGER_16BE;
534*4882a593Smuzhiyun stream_param.sample_size = 16;
535*4882a593Smuzhiyun break;
536*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S24_3LE:
537*4882a593Smuzhiyun stream_param.sample_type = ST_INTEGER_24LE;
538*4882a593Smuzhiyun stream_param.sample_size = 24;
539*4882a593Smuzhiyun break;
540*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_S24_3BE:
541*4882a593Smuzhiyun stream_param.sample_type = ST_INTEGER_24BE;
542*4882a593Smuzhiyun stream_param.sample_size = 24;
543*4882a593Smuzhiyun break;
544*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_FLOAT_LE:
545*4882a593Smuzhiyun stream_param.sample_type = ST_FLOATING_POINT_32LE;
546*4882a593Smuzhiyun stream_param.sample_size = 32;
547*4882a593Smuzhiyun break;
548*4882a593Smuzhiyun case SNDRV_PCM_FORMAT_FLOAT_BE:
549*4882a593Smuzhiyun stream_param.sample_type = ST_FLOATING_POINT_32BE;
550*4882a593Smuzhiyun stream_param.sample_size = 32;
551*4882a593Smuzhiyun break;
552*4882a593Smuzhiyun default:
553*4882a593Smuzhiyun dev_err(chip->card->dev,
554*4882a593Smuzhiyun "error mixart_set_format() : unknown format\n");
555*4882a593Smuzhiyun return -EINVAL;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun
558*4882a593Smuzhiyun dev_dbg(chip->card->dev,
559*4882a593Smuzhiyun "set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
560*4882a593Smuzhiyun stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun /* TODO: what else to configure ? */
563*4882a593Smuzhiyun /* stream_param.samples_per_frame = 2; */
564*4882a593Smuzhiyun /* stream_param.bytes_per_frame = 4; */
565*4882a593Smuzhiyun /* stream_param.bytes_per_sample = 2; */
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun stream_param.pipe_count = 1; /* set to 1 */
568*4882a593Smuzhiyun stream_param.stream_count = 1; /* set to 1 */
569*4882a593Smuzhiyun stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
570*4882a593Smuzhiyun stream_param.stream_desc[0].stream_idx = stream->substream->number;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
573*4882a593Smuzhiyun request.uid = (struct mixart_uid){0,0};
574*4882a593Smuzhiyun request.data = &stream_param;
575*4882a593Smuzhiyun request.size = sizeof(stream_param);
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
578*4882a593Smuzhiyun if((err < 0) || resp.error_code) {
579*4882a593Smuzhiyun dev_err(chip->card->dev,
580*4882a593Smuzhiyun "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n",
581*4882a593Smuzhiyun err, resp.error_code);
582*4882a593Smuzhiyun return -EINVAL;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun return 0;
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun /*
589*4882a593Smuzhiyun * HW_PARAMS callback for all pcms
590*4882a593Smuzhiyun */
snd_mixart_hw_params(struct snd_pcm_substream * subs,struct snd_pcm_hw_params * hw)591*4882a593Smuzhiyun static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
592*4882a593Smuzhiyun struct snd_pcm_hw_params *hw)
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun struct snd_mixart *chip = snd_pcm_substream_chip(subs);
595*4882a593Smuzhiyun struct mixart_mgr *mgr = chip->mgr;
596*4882a593Smuzhiyun struct mixart_stream *stream = subs->runtime->private_data;
597*4882a593Smuzhiyun snd_pcm_format_t format;
598*4882a593Smuzhiyun int err;
599*4882a593Smuzhiyun int channels;
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun /* set up channels */
602*4882a593Smuzhiyun channels = params_channels(hw);
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun /* set up format for the stream */
605*4882a593Smuzhiyun format = params_format(hw);
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun mutex_lock(&mgr->setup_mutex);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun /* update the stream levels */
610*4882a593Smuzhiyun if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
611*4882a593Smuzhiyun int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
612*4882a593Smuzhiyun if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
613*4882a593Smuzhiyun mixart_update_playback_stream_level(chip, is_aes, subs->number);
614*4882a593Smuzhiyun else
615*4882a593Smuzhiyun mixart_update_capture_stream_level( chip, is_aes);
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun stream->channels = channels;
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun /* set the format to the board */
621*4882a593Smuzhiyun err = mixart_set_format(stream, format);
622*4882a593Smuzhiyun if(err < 0) {
623*4882a593Smuzhiyun mutex_unlock(&mgr->setup_mutex);
624*4882a593Smuzhiyun return err;
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun if (subs->runtime->buffer_changed) {
628*4882a593Smuzhiyun struct mixart_bufferinfo *bufferinfo;
629*4882a593Smuzhiyun int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
630*4882a593Smuzhiyun if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
631*4882a593Smuzhiyun i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
635*4882a593Smuzhiyun bufferinfo[i].buffer_address = subs->runtime->dma_addr;
636*4882a593Smuzhiyun bufferinfo[i].available_length = subs->runtime->dma_bytes;
637*4882a593Smuzhiyun /* bufferinfo[i].buffer_id is already defined */
638*4882a593Smuzhiyun
639*4882a593Smuzhiyun dev_dbg(chip->card->dev,
640*4882a593Smuzhiyun "snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n",
641*4882a593Smuzhiyun i, bufferinfo[i].buffer_address,
642*4882a593Smuzhiyun bufferinfo[i].available_length,
643*4882a593Smuzhiyun subs->number);
644*4882a593Smuzhiyun }
645*4882a593Smuzhiyun mutex_unlock(&mgr->setup_mutex);
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun return 0;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
snd_mixart_hw_free(struct snd_pcm_substream * subs)650*4882a593Smuzhiyun static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
651*4882a593Smuzhiyun {
652*4882a593Smuzhiyun struct snd_mixart *chip = snd_pcm_substream_chip(subs);
653*4882a593Smuzhiyun mixart_sync_nonblock_events(chip->mgr);
654*4882a593Smuzhiyun return 0;
655*4882a593Smuzhiyun }
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun /*
660*4882a593Smuzhiyun * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
661*4882a593Smuzhiyun */
662*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_mixart_analog_caps =
663*4882a593Smuzhiyun {
664*4882a593Smuzhiyun .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
665*4882a593Smuzhiyun SNDRV_PCM_INFO_MMAP_VALID |
666*4882a593Smuzhiyun SNDRV_PCM_INFO_PAUSE),
667*4882a593Smuzhiyun .formats = ( SNDRV_PCM_FMTBIT_U8 |
668*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
669*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
670*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
671*4882a593Smuzhiyun .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
672*4882a593Smuzhiyun .rate_min = 8000,
673*4882a593Smuzhiyun .rate_max = 48000,
674*4882a593Smuzhiyun .channels_min = 1,
675*4882a593Smuzhiyun .channels_max = 2,
676*4882a593Smuzhiyun .buffer_bytes_max = (32*1024),
677*4882a593Smuzhiyun .period_bytes_min = 256, /* 256 frames U8 mono*/
678*4882a593Smuzhiyun .period_bytes_max = (16*1024),
679*4882a593Smuzhiyun .periods_min = 2,
680*4882a593Smuzhiyun .periods_max = (32*1024/256),
681*4882a593Smuzhiyun };
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_mixart_digital_caps =
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
686*4882a593Smuzhiyun SNDRV_PCM_INFO_MMAP_VALID |
687*4882a593Smuzhiyun SNDRV_PCM_INFO_PAUSE),
688*4882a593Smuzhiyun .formats = ( SNDRV_PCM_FMTBIT_U8 |
689*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
690*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
691*4882a593Smuzhiyun SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
692*4882a593Smuzhiyun .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
693*4882a593Smuzhiyun .rate_min = 32000,
694*4882a593Smuzhiyun .rate_max = 48000,
695*4882a593Smuzhiyun .channels_min = 1,
696*4882a593Smuzhiyun .channels_max = 2,
697*4882a593Smuzhiyun .buffer_bytes_max = (32*1024),
698*4882a593Smuzhiyun .period_bytes_min = 256, /* 256 frames U8 mono*/
699*4882a593Smuzhiyun .period_bytes_max = (16*1024),
700*4882a593Smuzhiyun .periods_min = 2,
701*4882a593Smuzhiyun .periods_max = (32*1024/256),
702*4882a593Smuzhiyun };
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun
snd_mixart_playback_open(struct snd_pcm_substream * subs)705*4882a593Smuzhiyun static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
706*4882a593Smuzhiyun {
707*4882a593Smuzhiyun struct snd_mixart *chip = snd_pcm_substream_chip(subs);
708*4882a593Smuzhiyun struct mixart_mgr *mgr = chip->mgr;
709*4882a593Smuzhiyun struct snd_pcm_runtime *runtime = subs->runtime;
710*4882a593Smuzhiyun struct snd_pcm *pcm = subs->pcm;
711*4882a593Smuzhiyun struct mixart_stream *stream;
712*4882a593Smuzhiyun struct mixart_pipe *pipe;
713*4882a593Smuzhiyun int err = 0;
714*4882a593Smuzhiyun int pcm_number;
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun mutex_lock(&mgr->setup_mutex);
717*4882a593Smuzhiyun
718*4882a593Smuzhiyun if ( pcm == chip->pcm ) {
719*4882a593Smuzhiyun pcm_number = MIXART_PCM_ANALOG;
720*4882a593Smuzhiyun runtime->hw = snd_mixart_analog_caps;
721*4882a593Smuzhiyun } else {
722*4882a593Smuzhiyun snd_BUG_ON(pcm != chip->pcm_dig);
723*4882a593Smuzhiyun pcm_number = MIXART_PCM_DIGITAL;
724*4882a593Smuzhiyun runtime->hw = snd_mixart_digital_caps;
725*4882a593Smuzhiyun }
726*4882a593Smuzhiyun dev_dbg(chip->card->dev,
727*4882a593Smuzhiyun "snd_mixart_playback_open C%d/P%d/Sub%d\n",
728*4882a593Smuzhiyun chip->chip_idx, pcm_number, subs->number);
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun /* get stream info */
731*4882a593Smuzhiyun stream = &(chip->playback_stream[pcm_number][subs->number]);
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun if (stream->status != MIXART_STREAM_STATUS_FREE){
734*4882a593Smuzhiyun /* streams in use */
735*4882a593Smuzhiyun dev_err(chip->card->dev,
736*4882a593Smuzhiyun "snd_mixart_playback_open C%d/P%d/Sub%d in use\n",
737*4882a593Smuzhiyun chip->chip_idx, pcm_number, subs->number);
738*4882a593Smuzhiyun err = -EBUSY;
739*4882a593Smuzhiyun goto _exit_open;
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun /* get pipe pointer (out pipe) */
743*4882a593Smuzhiyun pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun if (pipe == NULL) {
746*4882a593Smuzhiyun err = -EINVAL;
747*4882a593Smuzhiyun goto _exit_open;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun /* start the pipe if necessary */
751*4882a593Smuzhiyun err = mixart_set_pipe_state(chip->mgr, pipe, 1);
752*4882a593Smuzhiyun if( err < 0 ) {
753*4882a593Smuzhiyun dev_err(chip->card->dev, "error starting pipe!\n");
754*4882a593Smuzhiyun snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
755*4882a593Smuzhiyun err = -EINVAL;
756*4882a593Smuzhiyun goto _exit_open;
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun stream->pipe = pipe;
760*4882a593Smuzhiyun stream->pcm_number = pcm_number;
761*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_OPEN;
762*4882a593Smuzhiyun stream->substream = subs;
763*4882a593Smuzhiyun stream->channels = 0; /* not configured yet */
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun runtime->private_data = stream;
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
768*4882a593Smuzhiyun snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun /* if a sample rate is already used, another stream cannot change */
771*4882a593Smuzhiyun if(mgr->ref_count_rate++) {
772*4882a593Smuzhiyun if(mgr->sample_rate) {
773*4882a593Smuzhiyun runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
774*4882a593Smuzhiyun }
775*4882a593Smuzhiyun }
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun _exit_open:
778*4882a593Smuzhiyun mutex_unlock(&mgr->setup_mutex);
779*4882a593Smuzhiyun
780*4882a593Smuzhiyun return err;
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun
snd_mixart_capture_open(struct snd_pcm_substream * subs)784*4882a593Smuzhiyun static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
785*4882a593Smuzhiyun {
786*4882a593Smuzhiyun struct snd_mixart *chip = snd_pcm_substream_chip(subs);
787*4882a593Smuzhiyun struct mixart_mgr *mgr = chip->mgr;
788*4882a593Smuzhiyun struct snd_pcm_runtime *runtime = subs->runtime;
789*4882a593Smuzhiyun struct snd_pcm *pcm = subs->pcm;
790*4882a593Smuzhiyun struct mixart_stream *stream;
791*4882a593Smuzhiyun struct mixart_pipe *pipe;
792*4882a593Smuzhiyun int err = 0;
793*4882a593Smuzhiyun int pcm_number;
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun mutex_lock(&mgr->setup_mutex);
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun if ( pcm == chip->pcm ) {
798*4882a593Smuzhiyun pcm_number = MIXART_PCM_ANALOG;
799*4882a593Smuzhiyun runtime->hw = snd_mixart_analog_caps;
800*4882a593Smuzhiyun } else {
801*4882a593Smuzhiyun snd_BUG_ON(pcm != chip->pcm_dig);
802*4882a593Smuzhiyun pcm_number = MIXART_PCM_DIGITAL;
803*4882a593Smuzhiyun runtime->hw = snd_mixart_digital_caps;
804*4882a593Smuzhiyun }
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun runtime->hw.channels_min = 2; /* for instance, no mono */
807*4882a593Smuzhiyun
808*4882a593Smuzhiyun dev_dbg(chip->card->dev, "snd_mixart_capture_open C%d/P%d/Sub%d\n",
809*4882a593Smuzhiyun chip->chip_idx, pcm_number, subs->number);
810*4882a593Smuzhiyun
811*4882a593Smuzhiyun /* get stream info */
812*4882a593Smuzhiyun stream = &(chip->capture_stream[pcm_number]);
813*4882a593Smuzhiyun
814*4882a593Smuzhiyun if (stream->status != MIXART_STREAM_STATUS_FREE){
815*4882a593Smuzhiyun /* streams in use */
816*4882a593Smuzhiyun dev_err(chip->card->dev,
817*4882a593Smuzhiyun "snd_mixart_capture_open C%d/P%d/Sub%d in use\n",
818*4882a593Smuzhiyun chip->chip_idx, pcm_number, subs->number);
819*4882a593Smuzhiyun err = -EBUSY;
820*4882a593Smuzhiyun goto _exit_open;
821*4882a593Smuzhiyun }
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun /* get pipe pointer (in pipe) */
824*4882a593Smuzhiyun pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun if (pipe == NULL) {
827*4882a593Smuzhiyun err = -EINVAL;
828*4882a593Smuzhiyun goto _exit_open;
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun
831*4882a593Smuzhiyun /* start the pipe if necessary */
832*4882a593Smuzhiyun err = mixart_set_pipe_state(chip->mgr, pipe, 1);
833*4882a593Smuzhiyun if( err < 0 ) {
834*4882a593Smuzhiyun dev_err(chip->card->dev, "error starting pipe!\n");
835*4882a593Smuzhiyun snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
836*4882a593Smuzhiyun err = -EINVAL;
837*4882a593Smuzhiyun goto _exit_open;
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun
840*4882a593Smuzhiyun stream->pipe = pipe;
841*4882a593Smuzhiyun stream->pcm_number = pcm_number;
842*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_OPEN;
843*4882a593Smuzhiyun stream->substream = subs;
844*4882a593Smuzhiyun stream->channels = 0; /* not configured yet */
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun runtime->private_data = stream;
847*4882a593Smuzhiyun
848*4882a593Smuzhiyun snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
849*4882a593Smuzhiyun snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun /* if a sample rate is already used, another stream cannot change */
852*4882a593Smuzhiyun if(mgr->ref_count_rate++) {
853*4882a593Smuzhiyun if(mgr->sample_rate) {
854*4882a593Smuzhiyun runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
855*4882a593Smuzhiyun }
856*4882a593Smuzhiyun }
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun _exit_open:
859*4882a593Smuzhiyun mutex_unlock(&mgr->setup_mutex);
860*4882a593Smuzhiyun
861*4882a593Smuzhiyun return err;
862*4882a593Smuzhiyun }
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun
snd_mixart_close(struct snd_pcm_substream * subs)866*4882a593Smuzhiyun static int snd_mixart_close(struct snd_pcm_substream *subs)
867*4882a593Smuzhiyun {
868*4882a593Smuzhiyun struct snd_mixart *chip = snd_pcm_substream_chip(subs);
869*4882a593Smuzhiyun struct mixart_mgr *mgr = chip->mgr;
870*4882a593Smuzhiyun struct mixart_stream *stream = subs->runtime->private_data;
871*4882a593Smuzhiyun
872*4882a593Smuzhiyun mutex_lock(&mgr->setup_mutex);
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun dev_dbg(chip->card->dev, "snd_mixart_close C%d/P%d/Sub%d\n",
875*4882a593Smuzhiyun chip->chip_idx, stream->pcm_number, subs->number);
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun /* sample rate released */
878*4882a593Smuzhiyun if(--mgr->ref_count_rate == 0) {
879*4882a593Smuzhiyun mgr->sample_rate = 0;
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun /* delete pipe */
883*4882a593Smuzhiyun if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
884*4882a593Smuzhiyun
885*4882a593Smuzhiyun dev_err(chip->card->dev,
886*4882a593Smuzhiyun "error snd_mixart_kill_ref_pipe C%dP%d\n",
887*4882a593Smuzhiyun chip->chip_idx, stream->pcm_number);
888*4882a593Smuzhiyun }
889*4882a593Smuzhiyun
890*4882a593Smuzhiyun stream->pipe = NULL;
891*4882a593Smuzhiyun stream->status = MIXART_STREAM_STATUS_FREE;
892*4882a593Smuzhiyun stream->substream = NULL;
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun mutex_unlock(&mgr->setup_mutex);
895*4882a593Smuzhiyun return 0;
896*4882a593Smuzhiyun }
897*4882a593Smuzhiyun
898*4882a593Smuzhiyun
snd_mixart_stream_pointer(struct snd_pcm_substream * subs)899*4882a593Smuzhiyun static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *subs)
900*4882a593Smuzhiyun {
901*4882a593Smuzhiyun struct snd_pcm_runtime *runtime = subs->runtime;
902*4882a593Smuzhiyun struct mixart_stream *stream = runtime->private_data;
903*4882a593Smuzhiyun
904*4882a593Smuzhiyun return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
905*4882a593Smuzhiyun }
906*4882a593Smuzhiyun
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun static const struct snd_pcm_ops snd_mixart_playback_ops = {
910*4882a593Smuzhiyun .open = snd_mixart_playback_open,
911*4882a593Smuzhiyun .close = snd_mixart_close,
912*4882a593Smuzhiyun .prepare = snd_mixart_prepare,
913*4882a593Smuzhiyun .hw_params = snd_mixart_hw_params,
914*4882a593Smuzhiyun .hw_free = snd_mixart_hw_free,
915*4882a593Smuzhiyun .trigger = snd_mixart_trigger,
916*4882a593Smuzhiyun .pointer = snd_mixart_stream_pointer,
917*4882a593Smuzhiyun };
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun static const struct snd_pcm_ops snd_mixart_capture_ops = {
920*4882a593Smuzhiyun .open = snd_mixart_capture_open,
921*4882a593Smuzhiyun .close = snd_mixart_close,
922*4882a593Smuzhiyun .prepare = snd_mixart_prepare,
923*4882a593Smuzhiyun .hw_params = snd_mixart_hw_params,
924*4882a593Smuzhiyun .hw_free = snd_mixart_hw_free,
925*4882a593Smuzhiyun .trigger = snd_mixart_trigger,
926*4882a593Smuzhiyun .pointer = snd_mixart_stream_pointer,
927*4882a593Smuzhiyun };
928*4882a593Smuzhiyun
preallocate_buffers(struct snd_mixart * chip,struct snd_pcm * pcm)929*4882a593Smuzhiyun static void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm)
930*4882a593Smuzhiyun {
931*4882a593Smuzhiyun #if 0
932*4882a593Smuzhiyun struct snd_pcm_substream *subs;
933*4882a593Smuzhiyun int stream;
934*4882a593Smuzhiyun
935*4882a593Smuzhiyun for (stream = 0; stream < 2; stream++) {
936*4882a593Smuzhiyun int idx = 0;
937*4882a593Smuzhiyun for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
938*4882a593Smuzhiyun /* set up the unique device id with the chip index */
939*4882a593Smuzhiyun subs->dma_device.id = subs->pcm->device << 16 |
940*4882a593Smuzhiyun subs->stream << 8 | (subs->number + 1) |
941*4882a593Smuzhiyun (chip->chip_idx + 1) << 24;
942*4882a593Smuzhiyun }
943*4882a593Smuzhiyun #endif
944*4882a593Smuzhiyun snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
945*4882a593Smuzhiyun &chip->mgr->pci->dev,
946*4882a593Smuzhiyun 32*1024, 32*1024);
947*4882a593Smuzhiyun }
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun /*
950*4882a593Smuzhiyun */
snd_mixart_pcm_analog(struct snd_mixart * chip)951*4882a593Smuzhiyun static int snd_mixart_pcm_analog(struct snd_mixart *chip)
952*4882a593Smuzhiyun {
953*4882a593Smuzhiyun int err;
954*4882a593Smuzhiyun struct snd_pcm *pcm;
955*4882a593Smuzhiyun char name[32];
956*4882a593Smuzhiyun
957*4882a593Smuzhiyun sprintf(name, "miXart analog %d", chip->chip_idx);
958*4882a593Smuzhiyun if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
959*4882a593Smuzhiyun MIXART_PLAYBACK_STREAMS,
960*4882a593Smuzhiyun MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
961*4882a593Smuzhiyun dev_err(chip->card->dev,
962*4882a593Smuzhiyun "cannot create the analog pcm %d\n", chip->chip_idx);
963*4882a593Smuzhiyun return err;
964*4882a593Smuzhiyun }
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun pcm->private_data = chip;
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
969*4882a593Smuzhiyun snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun pcm->info_flags = 0;
972*4882a593Smuzhiyun pcm->nonatomic = true;
973*4882a593Smuzhiyun strcpy(pcm->name, name);
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun preallocate_buffers(chip, pcm);
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun chip->pcm = pcm;
978*4882a593Smuzhiyun return 0;
979*4882a593Smuzhiyun }
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun /*
983*4882a593Smuzhiyun */
snd_mixart_pcm_digital(struct snd_mixart * chip)984*4882a593Smuzhiyun static int snd_mixart_pcm_digital(struct snd_mixart *chip)
985*4882a593Smuzhiyun {
986*4882a593Smuzhiyun int err;
987*4882a593Smuzhiyun struct snd_pcm *pcm;
988*4882a593Smuzhiyun char name[32];
989*4882a593Smuzhiyun
990*4882a593Smuzhiyun sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
991*4882a593Smuzhiyun if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
992*4882a593Smuzhiyun MIXART_PLAYBACK_STREAMS,
993*4882a593Smuzhiyun MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
994*4882a593Smuzhiyun dev_err(chip->card->dev,
995*4882a593Smuzhiyun "cannot create the digital pcm %d\n", chip->chip_idx);
996*4882a593Smuzhiyun return err;
997*4882a593Smuzhiyun }
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun pcm->private_data = chip;
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
1002*4882a593Smuzhiyun snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun pcm->info_flags = 0;
1005*4882a593Smuzhiyun pcm->nonatomic = true;
1006*4882a593Smuzhiyun strcpy(pcm->name, name);
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun preallocate_buffers(chip, pcm);
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun chip->pcm_dig = pcm;
1011*4882a593Smuzhiyun return 0;
1012*4882a593Smuzhiyun }
1013*4882a593Smuzhiyun
snd_mixart_chip_free(struct snd_mixart * chip)1014*4882a593Smuzhiyun static int snd_mixart_chip_free(struct snd_mixart *chip)
1015*4882a593Smuzhiyun {
1016*4882a593Smuzhiyun kfree(chip);
1017*4882a593Smuzhiyun return 0;
1018*4882a593Smuzhiyun }
1019*4882a593Smuzhiyun
snd_mixart_chip_dev_free(struct snd_device * device)1020*4882a593Smuzhiyun static int snd_mixart_chip_dev_free(struct snd_device *device)
1021*4882a593Smuzhiyun {
1022*4882a593Smuzhiyun struct snd_mixart *chip = device->device_data;
1023*4882a593Smuzhiyun return snd_mixart_chip_free(chip);
1024*4882a593Smuzhiyun }
1025*4882a593Smuzhiyun
1026*4882a593Smuzhiyun
1027*4882a593Smuzhiyun /*
1028*4882a593Smuzhiyun */
snd_mixart_create(struct mixart_mgr * mgr,struct snd_card * card,int idx)1029*4882a593Smuzhiyun static int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
1030*4882a593Smuzhiyun {
1031*4882a593Smuzhiyun int err;
1032*4882a593Smuzhiyun struct snd_mixart *chip;
1033*4882a593Smuzhiyun static const struct snd_device_ops ops = {
1034*4882a593Smuzhiyun .dev_free = snd_mixart_chip_dev_free,
1035*4882a593Smuzhiyun };
1036*4882a593Smuzhiyun
1037*4882a593Smuzhiyun chip = kzalloc(sizeof(*chip), GFP_KERNEL);
1038*4882a593Smuzhiyun if (!chip)
1039*4882a593Smuzhiyun return -ENOMEM;
1040*4882a593Smuzhiyun
1041*4882a593Smuzhiyun chip->card = card;
1042*4882a593Smuzhiyun chip->chip_idx = idx;
1043*4882a593Smuzhiyun chip->mgr = mgr;
1044*4882a593Smuzhiyun card->sync_irq = mgr->irq;
1045*4882a593Smuzhiyun
1046*4882a593Smuzhiyun if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
1047*4882a593Smuzhiyun snd_mixart_chip_free(chip);
1048*4882a593Smuzhiyun return err;
1049*4882a593Smuzhiyun }
1050*4882a593Smuzhiyun
1051*4882a593Smuzhiyun mgr->chip[idx] = chip;
1052*4882a593Smuzhiyun return 0;
1053*4882a593Smuzhiyun }
1054*4882a593Smuzhiyun
snd_mixart_create_pcm(struct snd_mixart * chip)1055*4882a593Smuzhiyun int snd_mixart_create_pcm(struct snd_mixart* chip)
1056*4882a593Smuzhiyun {
1057*4882a593Smuzhiyun int err;
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun err = snd_mixart_pcm_analog(chip);
1060*4882a593Smuzhiyun if (err < 0)
1061*4882a593Smuzhiyun return err;
1062*4882a593Smuzhiyun
1063*4882a593Smuzhiyun if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
1064*4882a593Smuzhiyun
1065*4882a593Smuzhiyun err = snd_mixart_pcm_digital(chip);
1066*4882a593Smuzhiyun if (err < 0)
1067*4882a593Smuzhiyun return err;
1068*4882a593Smuzhiyun }
1069*4882a593Smuzhiyun return err;
1070*4882a593Smuzhiyun }
1071*4882a593Smuzhiyun
1072*4882a593Smuzhiyun
1073*4882a593Smuzhiyun /*
1074*4882a593Smuzhiyun * release all the cards assigned to a manager instance
1075*4882a593Smuzhiyun */
snd_mixart_free(struct mixart_mgr * mgr)1076*4882a593Smuzhiyun static int snd_mixart_free(struct mixart_mgr *mgr)
1077*4882a593Smuzhiyun {
1078*4882a593Smuzhiyun unsigned int i;
1079*4882a593Smuzhiyun
1080*4882a593Smuzhiyun for (i = 0; i < mgr->num_cards; i++) {
1081*4882a593Smuzhiyun if (mgr->chip[i])
1082*4882a593Smuzhiyun snd_card_free(mgr->chip[i]->card);
1083*4882a593Smuzhiyun }
1084*4882a593Smuzhiyun
1085*4882a593Smuzhiyun /* stop mailbox */
1086*4882a593Smuzhiyun snd_mixart_exit_mailbox(mgr);
1087*4882a593Smuzhiyun
1088*4882a593Smuzhiyun /* release irq */
1089*4882a593Smuzhiyun if (mgr->irq >= 0)
1090*4882a593Smuzhiyun free_irq(mgr->irq, mgr);
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun /* reset board if some firmware was loaded */
1093*4882a593Smuzhiyun if(mgr->dsp_loaded) {
1094*4882a593Smuzhiyun snd_mixart_reset_board(mgr);
1095*4882a593Smuzhiyun dev_dbg(&mgr->pci->dev, "reset miXart !\n");
1096*4882a593Smuzhiyun }
1097*4882a593Smuzhiyun
1098*4882a593Smuzhiyun /* release the i/o ports */
1099*4882a593Smuzhiyun for (i = 0; i < 2; ++i)
1100*4882a593Smuzhiyun iounmap(mgr->mem[i].virt);
1101*4882a593Smuzhiyun
1102*4882a593Smuzhiyun pci_release_regions(mgr->pci);
1103*4882a593Smuzhiyun
1104*4882a593Smuzhiyun /* free flowarray */
1105*4882a593Smuzhiyun if(mgr->flowinfo.area) {
1106*4882a593Smuzhiyun snd_dma_free_pages(&mgr->flowinfo);
1107*4882a593Smuzhiyun mgr->flowinfo.area = NULL;
1108*4882a593Smuzhiyun }
1109*4882a593Smuzhiyun /* free bufferarray */
1110*4882a593Smuzhiyun if(mgr->bufferinfo.area) {
1111*4882a593Smuzhiyun snd_dma_free_pages(&mgr->bufferinfo);
1112*4882a593Smuzhiyun mgr->bufferinfo.area = NULL;
1113*4882a593Smuzhiyun }
1114*4882a593Smuzhiyun
1115*4882a593Smuzhiyun pci_disable_device(mgr->pci);
1116*4882a593Smuzhiyun kfree(mgr);
1117*4882a593Smuzhiyun return 0;
1118*4882a593Smuzhiyun }
1119*4882a593Smuzhiyun
1120*4882a593Smuzhiyun /*
1121*4882a593Smuzhiyun * proc interface
1122*4882a593Smuzhiyun */
1123*4882a593Smuzhiyun
1124*4882a593Smuzhiyun /*
1125*4882a593Smuzhiyun mixart_BA0 proc interface for BAR 0 - read callback
1126*4882a593Smuzhiyun */
snd_mixart_BA0_read(struct snd_info_entry * entry,void * file_private_data,struct file * file,char __user * buf,size_t count,loff_t pos)1127*4882a593Smuzhiyun static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry,
1128*4882a593Smuzhiyun void *file_private_data,
1129*4882a593Smuzhiyun struct file *file, char __user *buf,
1130*4882a593Smuzhiyun size_t count, loff_t pos)
1131*4882a593Smuzhiyun {
1132*4882a593Smuzhiyun struct mixart_mgr *mgr = entry->private_data;
1133*4882a593Smuzhiyun
1134*4882a593Smuzhiyun count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
1135*4882a593Smuzhiyun if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count))
1136*4882a593Smuzhiyun return -EFAULT;
1137*4882a593Smuzhiyun return count;
1138*4882a593Smuzhiyun }
1139*4882a593Smuzhiyun
1140*4882a593Smuzhiyun /*
1141*4882a593Smuzhiyun mixart_BA1 proc interface for BAR 1 - read callback
1142*4882a593Smuzhiyun */
snd_mixart_BA1_read(struct snd_info_entry * entry,void * file_private_data,struct file * file,char __user * buf,size_t count,loff_t pos)1143*4882a593Smuzhiyun static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry,
1144*4882a593Smuzhiyun void *file_private_data,
1145*4882a593Smuzhiyun struct file *file, char __user *buf,
1146*4882a593Smuzhiyun size_t count, loff_t pos)
1147*4882a593Smuzhiyun {
1148*4882a593Smuzhiyun struct mixart_mgr *mgr = entry->private_data;
1149*4882a593Smuzhiyun
1150*4882a593Smuzhiyun count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
1151*4882a593Smuzhiyun if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count))
1152*4882a593Smuzhiyun return -EFAULT;
1153*4882a593Smuzhiyun return count;
1154*4882a593Smuzhiyun }
1155*4882a593Smuzhiyun
1156*4882a593Smuzhiyun static const struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
1157*4882a593Smuzhiyun .read = snd_mixart_BA0_read,
1158*4882a593Smuzhiyun };
1159*4882a593Smuzhiyun
1160*4882a593Smuzhiyun static const struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
1161*4882a593Smuzhiyun .read = snd_mixart_BA1_read,
1162*4882a593Smuzhiyun };
1163*4882a593Smuzhiyun
1164*4882a593Smuzhiyun
snd_mixart_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)1165*4882a593Smuzhiyun static void snd_mixart_proc_read(struct snd_info_entry *entry,
1166*4882a593Smuzhiyun struct snd_info_buffer *buffer)
1167*4882a593Smuzhiyun {
1168*4882a593Smuzhiyun struct snd_mixart *chip = entry->private_data;
1169*4882a593Smuzhiyun u32 ref;
1170*4882a593Smuzhiyun
1171*4882a593Smuzhiyun snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx);
1172*4882a593Smuzhiyun
1173*4882a593Smuzhiyun /* stats available when embedded OS is running */
1174*4882a593Smuzhiyun if (chip->mgr->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) {
1175*4882a593Smuzhiyun snd_iprintf(buffer, "- hardware -\n");
1176*4882a593Smuzhiyun switch (chip->mgr->board_type ) {
1177*4882a593Smuzhiyun case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break;
1178*4882a593Smuzhiyun case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break;
1179*4882a593Smuzhiyun case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break;
1180*4882a593Smuzhiyun default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break;
1181*4882a593Smuzhiyun }
1182*4882a593Smuzhiyun
1183*4882a593Smuzhiyun snd_iprintf(buffer, "- system load -\n");
1184*4882a593Smuzhiyun
1185*4882a593Smuzhiyun /* get perf reference */
1186*4882a593Smuzhiyun
1187*4882a593Smuzhiyun ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET));
1188*4882a593Smuzhiyun
1189*4882a593Smuzhiyun if (ref) {
1190*4882a593Smuzhiyun u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref;
1191*4882a593Smuzhiyun u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref;
1192*4882a593Smuzhiyun u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref;
1193*4882a593Smuzhiyun
1194*4882a593Smuzhiyun snd_iprintf(buffer, "\tstreaming : %d\n", streaming);
1195*4882a593Smuzhiyun snd_iprintf(buffer, "\tmailbox : %d\n", mailbox);
1196*4882a593Smuzhiyun snd_iprintf(buffer, "\tinterrupts handling : %d\n\n", interr);
1197*4882a593Smuzhiyun }
1198*4882a593Smuzhiyun } /* endif elf loaded */
1199*4882a593Smuzhiyun }
1200*4882a593Smuzhiyun
snd_mixart_proc_init(struct snd_mixart * chip)1201*4882a593Smuzhiyun static void snd_mixart_proc_init(struct snd_mixart *chip)
1202*4882a593Smuzhiyun {
1203*4882a593Smuzhiyun struct snd_info_entry *entry;
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyun /* text interface to read perf and temp meters */
1206*4882a593Smuzhiyun snd_card_ro_proc_new(chip->card, "board_info", chip,
1207*4882a593Smuzhiyun snd_mixart_proc_read);
1208*4882a593Smuzhiyun
1209*4882a593Smuzhiyun if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
1210*4882a593Smuzhiyun entry->content = SNDRV_INFO_CONTENT_DATA;
1211*4882a593Smuzhiyun entry->private_data = chip->mgr;
1212*4882a593Smuzhiyun entry->c.ops = &snd_mixart_proc_ops_BA0;
1213*4882a593Smuzhiyun entry->size = MIXART_BA0_SIZE;
1214*4882a593Smuzhiyun }
1215*4882a593Smuzhiyun if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) {
1216*4882a593Smuzhiyun entry->content = SNDRV_INFO_CONTENT_DATA;
1217*4882a593Smuzhiyun entry->private_data = chip->mgr;
1218*4882a593Smuzhiyun entry->c.ops = &snd_mixart_proc_ops_BA1;
1219*4882a593Smuzhiyun entry->size = MIXART_BA1_SIZE;
1220*4882a593Smuzhiyun }
1221*4882a593Smuzhiyun }
1222*4882a593Smuzhiyun /* end of proc interface */
1223*4882a593Smuzhiyun
1224*4882a593Smuzhiyun
1225*4882a593Smuzhiyun /*
1226*4882a593Smuzhiyun * probe function - creates the card manager
1227*4882a593Smuzhiyun */
snd_mixart_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)1228*4882a593Smuzhiyun static int snd_mixart_probe(struct pci_dev *pci,
1229*4882a593Smuzhiyun const struct pci_device_id *pci_id)
1230*4882a593Smuzhiyun {
1231*4882a593Smuzhiyun static int dev;
1232*4882a593Smuzhiyun struct mixart_mgr *mgr;
1233*4882a593Smuzhiyun unsigned int i;
1234*4882a593Smuzhiyun int err;
1235*4882a593Smuzhiyun size_t size;
1236*4882a593Smuzhiyun
1237*4882a593Smuzhiyun /*
1238*4882a593Smuzhiyun */
1239*4882a593Smuzhiyun if (dev >= SNDRV_CARDS)
1240*4882a593Smuzhiyun return -ENODEV;
1241*4882a593Smuzhiyun if (! enable[dev]) {
1242*4882a593Smuzhiyun dev++;
1243*4882a593Smuzhiyun return -ENOENT;
1244*4882a593Smuzhiyun }
1245*4882a593Smuzhiyun
1246*4882a593Smuzhiyun /* enable PCI device */
1247*4882a593Smuzhiyun if ((err = pci_enable_device(pci)) < 0)
1248*4882a593Smuzhiyun return err;
1249*4882a593Smuzhiyun pci_set_master(pci);
1250*4882a593Smuzhiyun
1251*4882a593Smuzhiyun /* check if we can restrict PCI DMA transfers to 32 bits */
1252*4882a593Smuzhiyun if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
1253*4882a593Smuzhiyun dev_err(&pci->dev,
1254*4882a593Smuzhiyun "architecture does not support 32bit PCI busmaster DMA\n");
1255*4882a593Smuzhiyun pci_disable_device(pci);
1256*4882a593Smuzhiyun return -ENXIO;
1257*4882a593Smuzhiyun }
1258*4882a593Smuzhiyun
1259*4882a593Smuzhiyun /*
1260*4882a593Smuzhiyun */
1261*4882a593Smuzhiyun mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
1262*4882a593Smuzhiyun if (! mgr) {
1263*4882a593Smuzhiyun pci_disable_device(pci);
1264*4882a593Smuzhiyun return -ENOMEM;
1265*4882a593Smuzhiyun }
1266*4882a593Smuzhiyun
1267*4882a593Smuzhiyun mgr->pci = pci;
1268*4882a593Smuzhiyun mgr->irq = -1;
1269*4882a593Smuzhiyun
1270*4882a593Smuzhiyun /* resource assignment */
1271*4882a593Smuzhiyun if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
1272*4882a593Smuzhiyun kfree(mgr);
1273*4882a593Smuzhiyun pci_disable_device(pci);
1274*4882a593Smuzhiyun return err;
1275*4882a593Smuzhiyun }
1276*4882a593Smuzhiyun for (i = 0; i < 2; i++) {
1277*4882a593Smuzhiyun mgr->mem[i].phys = pci_resource_start(pci, i);
1278*4882a593Smuzhiyun mgr->mem[i].virt = pci_ioremap_bar(pci, i);
1279*4882a593Smuzhiyun if (!mgr->mem[i].virt) {
1280*4882a593Smuzhiyun dev_err(&pci->dev, "unable to remap resource 0x%lx\n",
1281*4882a593Smuzhiyun mgr->mem[i].phys);
1282*4882a593Smuzhiyun snd_mixart_free(mgr);
1283*4882a593Smuzhiyun return -EBUSY;
1284*4882a593Smuzhiyun }
1285*4882a593Smuzhiyun }
1286*4882a593Smuzhiyun
1287*4882a593Smuzhiyun if (request_threaded_irq(pci->irq, snd_mixart_interrupt,
1288*4882a593Smuzhiyun snd_mixart_threaded_irq, IRQF_SHARED,
1289*4882a593Smuzhiyun KBUILD_MODNAME, mgr)) {
1290*4882a593Smuzhiyun dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
1291*4882a593Smuzhiyun snd_mixart_free(mgr);
1292*4882a593Smuzhiyun return -EBUSY;
1293*4882a593Smuzhiyun }
1294*4882a593Smuzhiyun mgr->irq = pci->irq;
1295*4882a593Smuzhiyun
1296*4882a593Smuzhiyun /* init mailbox */
1297*4882a593Smuzhiyun mgr->msg_fifo_readptr = 0;
1298*4882a593Smuzhiyun mgr->msg_fifo_writeptr = 0;
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun mutex_init(&mgr->lock);
1301*4882a593Smuzhiyun mutex_init(&mgr->msg_lock);
1302*4882a593Smuzhiyun init_waitqueue_head(&mgr->msg_sleep);
1303*4882a593Smuzhiyun atomic_set(&mgr->msg_processed, 0);
1304*4882a593Smuzhiyun
1305*4882a593Smuzhiyun /* init setup mutex*/
1306*4882a593Smuzhiyun mutex_init(&mgr->setup_mutex);
1307*4882a593Smuzhiyun
1308*4882a593Smuzhiyun /* card assignment */
1309*4882a593Smuzhiyun mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */
1310*4882a593Smuzhiyun for (i = 0; i < mgr->num_cards; i++) {
1311*4882a593Smuzhiyun struct snd_card *card;
1312*4882a593Smuzhiyun char tmpid[16];
1313*4882a593Smuzhiyun int idx;
1314*4882a593Smuzhiyun
1315*4882a593Smuzhiyun if (index[dev] < 0)
1316*4882a593Smuzhiyun idx = index[dev];
1317*4882a593Smuzhiyun else
1318*4882a593Smuzhiyun idx = index[dev] + i;
1319*4882a593Smuzhiyun snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
1320*4882a593Smuzhiyun err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
1321*4882a593Smuzhiyun 0, &card);
1322*4882a593Smuzhiyun
1323*4882a593Smuzhiyun if (err < 0) {
1324*4882a593Smuzhiyun dev_err(&pci->dev, "cannot allocate the card %d\n", i);
1325*4882a593Smuzhiyun snd_mixart_free(mgr);
1326*4882a593Smuzhiyun return err;
1327*4882a593Smuzhiyun }
1328*4882a593Smuzhiyun
1329*4882a593Smuzhiyun strcpy(card->driver, CARD_NAME);
1330*4882a593Smuzhiyun snprintf(card->shortname, sizeof(card->shortname),
1331*4882a593Smuzhiyun "Digigram miXart [PCM #%d]", i);
1332*4882a593Smuzhiyun snprintf(card->longname, sizeof(card->longname),
1333*4882a593Smuzhiyun "Digigram miXart at 0x%lx & 0x%lx, irq %i [PCM #%d]",
1334*4882a593Smuzhiyun mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq, i);
1335*4882a593Smuzhiyun
1336*4882a593Smuzhiyun if ((err = snd_mixart_create(mgr, card, i)) < 0) {
1337*4882a593Smuzhiyun snd_card_free(card);
1338*4882a593Smuzhiyun snd_mixart_free(mgr);
1339*4882a593Smuzhiyun return err;
1340*4882a593Smuzhiyun }
1341*4882a593Smuzhiyun
1342*4882a593Smuzhiyun if(i==0) {
1343*4882a593Smuzhiyun /* init proc interface only for chip0 */
1344*4882a593Smuzhiyun snd_mixart_proc_init(mgr->chip[i]);
1345*4882a593Smuzhiyun }
1346*4882a593Smuzhiyun
1347*4882a593Smuzhiyun if ((err = snd_card_register(card)) < 0) {
1348*4882a593Smuzhiyun snd_mixart_free(mgr);
1349*4882a593Smuzhiyun return err;
1350*4882a593Smuzhiyun }
1351*4882a593Smuzhiyun }
1352*4882a593Smuzhiyun
1353*4882a593Smuzhiyun /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */
1354*4882a593Smuzhiyun mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;
1355*4882a593Smuzhiyun
1356*4882a593Smuzhiyun /* create array of streaminfo */
1357*4882a593Smuzhiyun size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
1358*4882a593Smuzhiyun sizeof(struct mixart_flowinfo)) );
1359*4882a593Smuzhiyun if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
1360*4882a593Smuzhiyun size, &mgr->flowinfo) < 0) {
1361*4882a593Smuzhiyun snd_mixart_free(mgr);
1362*4882a593Smuzhiyun return -ENOMEM;
1363*4882a593Smuzhiyun }
1364*4882a593Smuzhiyun /* init streaminfo_array */
1365*4882a593Smuzhiyun memset(mgr->flowinfo.area, 0, size);
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun /* create array of bufferinfo */
1368*4882a593Smuzhiyun size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
1369*4882a593Smuzhiyun sizeof(struct mixart_bufferinfo)) );
1370*4882a593Smuzhiyun if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
1371*4882a593Smuzhiyun size, &mgr->bufferinfo) < 0) {
1372*4882a593Smuzhiyun snd_mixart_free(mgr);
1373*4882a593Smuzhiyun return -ENOMEM;
1374*4882a593Smuzhiyun }
1375*4882a593Smuzhiyun /* init bufferinfo_array */
1376*4882a593Smuzhiyun memset(mgr->bufferinfo.area, 0, size);
1377*4882a593Smuzhiyun
1378*4882a593Smuzhiyun /* set up firmware */
1379*4882a593Smuzhiyun err = snd_mixart_setup_firmware(mgr);
1380*4882a593Smuzhiyun if (err < 0) {
1381*4882a593Smuzhiyun snd_mixart_free(mgr);
1382*4882a593Smuzhiyun return err;
1383*4882a593Smuzhiyun }
1384*4882a593Smuzhiyun
1385*4882a593Smuzhiyun pci_set_drvdata(pci, mgr);
1386*4882a593Smuzhiyun dev++;
1387*4882a593Smuzhiyun return 0;
1388*4882a593Smuzhiyun }
1389*4882a593Smuzhiyun
snd_mixart_remove(struct pci_dev * pci)1390*4882a593Smuzhiyun static void snd_mixart_remove(struct pci_dev *pci)
1391*4882a593Smuzhiyun {
1392*4882a593Smuzhiyun snd_mixart_free(pci_get_drvdata(pci));
1393*4882a593Smuzhiyun }
1394*4882a593Smuzhiyun
1395*4882a593Smuzhiyun static struct pci_driver mixart_driver = {
1396*4882a593Smuzhiyun .name = KBUILD_MODNAME,
1397*4882a593Smuzhiyun .id_table = snd_mixart_ids,
1398*4882a593Smuzhiyun .probe = snd_mixart_probe,
1399*4882a593Smuzhiyun .remove = snd_mixart_remove,
1400*4882a593Smuzhiyun };
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun module_pci_driver(mixart_driver);
1403