11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Driver for Digigram miXart soundcards
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * main file with alsa callbacks
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Copyright (c) 2003 by Digigram <alsa@digigram.com>
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds #include <linux/init.h>
121da177e4SLinus Torvalds #include <linux/interrupt.h>
131da177e4SLinus Torvalds #include <linux/pci.h>
149d2f928dSTobias Klauser #include <linux/dma-mapping.h>
1565a77217SPaul Gortmaker #include <linux/module.h>
1662932df8SIngo Molnar #include <linux/mutex.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
18910638aeSMatthias Gehre
191da177e4SLinus Torvalds #include <sound/core.h>
201da177e4SLinus Torvalds #include <sound/initval.h>
211da177e4SLinus Torvalds #include <sound/info.h>
221da177e4SLinus Torvalds #include <sound/control.h>
231da177e4SLinus Torvalds #include <sound/pcm.h>
241da177e4SLinus Torvalds #include <sound/pcm_params.h>
251da177e4SLinus Torvalds #include "mixart.h"
261da177e4SLinus Torvalds #include "mixart_hwdep.h"
271da177e4SLinus Torvalds #include "mixart_core.h"
281da177e4SLinus Torvalds #include "mixart_mixer.h"
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds #define CARD_NAME "miXart"
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds MODULE_AUTHOR("Digigram <alsa@digigram.com>");
331da177e4SLinus Torvalds MODULE_DESCRIPTION("Digigram " CARD_NAME);
341da177e4SLinus Torvalds MODULE_LICENSE("GPL");
351da177e4SLinus Torvalds
361da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
371da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
38a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444);
411da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
421da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444);
431da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
441da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444);
451da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds */
491da177e4SLinus Torvalds
509baa3c34SBenoit Taine static const struct pci_device_id snd_mixart_ids[] = {
5128d27aaeSJoe Perches { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */
521da177e4SLinus Torvalds { 0, }
531da177e4SLinus Torvalds };
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, snd_mixart_ids);
561da177e4SLinus Torvalds
571da177e4SLinus Torvalds
mixart_set_pipe_state(struct mixart_mgr * mgr,struct mixart_pipe * pipe,int start)5867b48b88STakashi Iwai static int mixart_set_pipe_state(struct mixart_mgr *mgr,
5967b48b88STakashi Iwai struct mixart_pipe *pipe, int start)
601da177e4SLinus Torvalds {
6167b48b88STakashi Iwai struct mixart_group_state_req group_state;
6267b48b88STakashi Iwai struct mixart_group_state_resp group_state_resp;
6367b48b88STakashi Iwai struct mixart_msg request;
641da177e4SLinus Torvalds int err;
651da177e4SLinus Torvalds u32 system_msg_uid;
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds switch(pipe->status) {
681da177e4SLinus Torvalds case PIPE_RUNNING:
691da177e4SLinus Torvalds case PIPE_CLOCK_SET:
701da177e4SLinus Torvalds if(start) return 0; /* already started */
711da177e4SLinus Torvalds break;
721da177e4SLinus Torvalds case PIPE_STOPPED:
731da177e4SLinus Torvalds if(!start) return 0; /* already stopped */
741da177e4SLinus Torvalds break;
751da177e4SLinus Torvalds default:
766414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
776414e35dSTakashi Iwai "error mixart_set_pipe_state called with wrong pipe->status!\n");
781da177e4SLinus Torvalds return -EINVAL; /* function called with wrong pipe status */
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds
811da177e4SLinus Torvalds system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */
841da177e4SLinus Torvalds
851da177e4SLinus Torvalds request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD;
8667b48b88STakashi Iwai request.uid = (struct mixart_uid){0,0};
871da177e4SLinus Torvalds request.data = &system_msg_uid;
881da177e4SLinus Torvalds request.size = sizeof(system_msg_uid);
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
911da177e4SLinus Torvalds if(err) {
926414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
936414e35dSTakashi Iwai "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
941da177e4SLinus Torvalds return err;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds /* start or stop the pipe (1 pipe) */
981da177e4SLinus Torvalds
991da177e4SLinus Torvalds memset(&group_state, 0, sizeof(group_state));
1001da177e4SLinus Torvalds group_state.pipe_count = 1;
101*4040fc51SGustavo A. R. Silva group_state.pipe_uid = pipe->group_uid;
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds if(start)
1041da177e4SLinus Torvalds request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
1051da177e4SLinus Torvalds else
1061da177e4SLinus Torvalds request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET;
1071da177e4SLinus Torvalds
10867b48b88STakashi Iwai request.uid = pipe->group_uid; /*(struct mixart_uid){0,0};*/
1091da177e4SLinus Torvalds request.data = &group_state;
1101da177e4SLinus Torvalds request.size = sizeof(group_state);
1111da177e4SLinus Torvalds
1121da177e4SLinus Torvalds err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
1131da177e4SLinus Torvalds if (err < 0 || group_state_resp.txx_status != 0) {
1146414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
1156414e35dSTakashi Iwai "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n",
1166414e35dSTakashi Iwai err, group_state_resp.txx_status);
1171da177e4SLinus Torvalds return -EINVAL;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds if(start) {
1218cf3968cSDan Carpenter u32 stat = 0;
1221da177e4SLinus Torvalds
1231da177e4SLinus Torvalds group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
1241da177e4SLinus Torvalds
1251da177e4SLinus Torvalds err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
1261da177e4SLinus Torvalds if (err < 0 || group_state_resp.txx_status != 0) {
1276414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
1286414e35dSTakashi Iwai "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n",
1296414e35dSTakashi Iwai err, group_state_resp.txx_status);
1301da177e4SLinus Torvalds return -EINVAL;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds /* in case of start send a synchro top */
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
13667b48b88STakashi Iwai request.uid = (struct mixart_uid){0,0};
1371da177e4SLinus Torvalds request.data = NULL;
1381da177e4SLinus Torvalds request.size = 0;
1391da177e4SLinus Torvalds
1401da177e4SLinus Torvalds err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
1411da177e4SLinus Torvalds if (err < 0 || stat != 0) {
1426414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
1436414e35dSTakashi Iwai "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n",
1446414e35dSTakashi Iwai err, stat);
1451da177e4SLinus Torvalds return -EINVAL;
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds
1481da177e4SLinus Torvalds pipe->status = PIPE_RUNNING;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds else /* !start */
1511da177e4SLinus Torvalds pipe->status = PIPE_STOPPED;
1521da177e4SLinus Torvalds
1531da177e4SLinus Torvalds return 0;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds
1561da177e4SLinus Torvalds
mixart_set_clock(struct mixart_mgr * mgr,struct mixart_pipe * pipe,unsigned int rate)15767b48b88STakashi Iwai static int mixart_set_clock(struct mixart_mgr *mgr,
15867b48b88STakashi Iwai struct mixart_pipe *pipe, unsigned int rate)
1591da177e4SLinus Torvalds {
16067b48b88STakashi Iwai struct mixart_msg request;
16167b48b88STakashi Iwai struct mixart_clock_properties clock_properties;
16267b48b88STakashi Iwai struct mixart_clock_properties_resp clock_prop_resp;
1631da177e4SLinus Torvalds int err;
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds switch(pipe->status) {
1661da177e4SLinus Torvalds case PIPE_CLOCK_SET:
1671da177e4SLinus Torvalds break;
1681da177e4SLinus Torvalds case PIPE_RUNNING:
1691da177e4SLinus Torvalds if(rate != 0)
1701da177e4SLinus Torvalds break;
171c0dbbdadSGustavo A. R. Silva fallthrough;
1721da177e4SLinus Torvalds default:
1731da177e4SLinus Torvalds if(rate == 0)
1741da177e4SLinus Torvalds return 0; /* nothing to do */
1751da177e4SLinus Torvalds else {
1766414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
1776414e35dSTakashi Iwai "error mixart_set_clock(%d) called with wrong pipe->status !\n",
1786414e35dSTakashi Iwai rate);
1791da177e4SLinus Torvalds return -EINVAL;
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds memset(&clock_properties, 0, sizeof(clock_properties));
1841da177e4SLinus Torvalds clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK;
1851da177e4SLinus Torvalds clock_properties.clock_mode = CM_STANDALONE;
1861da177e4SLinus Torvalds clock_properties.frequency = rate;
1871da177e4SLinus Torvalds clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
188*4040fc51SGustavo A. R. Silva clock_properties.uid_caller = pipe->group_uid;
1891da177e4SLinus Torvalds
1906414e35dSTakashi Iwai dev_dbg(&mgr->pci->dev, "mixart_set_clock to %d kHz\n", rate);
1911da177e4SLinus Torvalds
1921da177e4SLinus Torvalds request.message_id = MSG_CLOCK_SET_PROPERTIES;
1931da177e4SLinus Torvalds request.uid = mgr->uid_console_manager;
1941da177e4SLinus Torvalds request.data = &clock_properties;
1951da177e4SLinus Torvalds request.size = sizeof(clock_properties);
1961da177e4SLinus Torvalds
1971da177e4SLinus Torvalds err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp);
1981da177e4SLinus Torvalds if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) {
1996414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
2006414e35dSTakashi Iwai "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n",
2016414e35dSTakashi Iwai err, clock_prop_resp.status, clock_prop_resp.clock_mode);
2021da177e4SLinus Torvalds return -EINVAL;
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds if(rate) pipe->status = PIPE_CLOCK_SET;
2061da177e4SLinus Torvalds else pipe->status = PIPE_RUNNING;
2071da177e4SLinus Torvalds
2081da177e4SLinus Torvalds return 0;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds
2111da177e4SLinus Torvalds
2121da177e4SLinus Torvalds /*
2131da177e4SLinus Torvalds * Allocate or reference output pipe for analog IOs (pcmp0/1)
2141da177e4SLinus Torvalds */
21567b48b88STakashi Iwai struct mixart_pipe *
snd_mixart_add_ref_pipe(struct snd_mixart * chip,int pcm_number,int capture,int monitoring)21667b48b88STakashi Iwai snd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture,
21767b48b88STakashi Iwai int monitoring)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds int stream_count;
22067b48b88STakashi Iwai struct mixart_pipe *pipe;
22167b48b88STakashi Iwai struct mixart_msg request;
2221da177e4SLinus Torvalds
2231da177e4SLinus Torvalds if(capture) {
2241da177e4SLinus Torvalds if (pcm_number == MIXART_PCM_ANALOG) {
2251da177e4SLinus Torvalds pipe = &(chip->pipe_in_ana); /* analog inputs */
2261da177e4SLinus Torvalds } else {
2271da177e4SLinus Torvalds pipe = &(chip->pipe_in_dig); /* digital inputs */
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP;
2301da177e4SLinus Torvalds stream_count = MIXART_CAPTURE_STREAMS;
2311da177e4SLinus Torvalds } else {
2321da177e4SLinus Torvalds if (pcm_number == MIXART_PCM_ANALOG) {
2331da177e4SLinus Torvalds pipe = &(chip->pipe_out_ana); /* analog outputs */
2341da177e4SLinus Torvalds } else {
2351da177e4SLinus Torvalds pipe = &(chip->pipe_out_dig); /* digital outputs */
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds request.message_id = MSG_STREAM_ADD_INPUT_GROUP;
2381da177e4SLinus Torvalds stream_count = MIXART_PLAYBACK_STREAMS;
2391da177e4SLinus Torvalds }
2401da177e4SLinus Torvalds
2411da177e4SLinus Torvalds /* a new stream is opened and there are already all streams in use */
2421da177e4SLinus Torvalds if( (monitoring == 0) && (pipe->references >= stream_count) ) {
2431da177e4SLinus Torvalds return NULL;
2441da177e4SLinus Torvalds }
2451da177e4SLinus Torvalds
2461da177e4SLinus Torvalds /* pipe is not yet defined */
2471da177e4SLinus Torvalds if( pipe->status == PIPE_UNDEFINED ) {
2481da177e4SLinus Torvalds int err, i;
2491da177e4SLinus Torvalds struct {
25067b48b88STakashi Iwai struct mixart_streaming_group_req sgroup_req;
25167b48b88STakashi Iwai struct mixart_streaming_group sgroup_resp;
2521da177e4SLinus Torvalds } *buf;
2531da177e4SLinus Torvalds
2546414e35dSTakashi Iwai dev_dbg(chip->card->dev,
2556414e35dSTakashi Iwai "add_ref_pipe audio chip(%d) pcm(%d)\n",
2566414e35dSTakashi Iwai chip->chip_idx, pcm_number);
2571da177e4SLinus Torvalds
2581da177e4SLinus Torvalds buf = kmalloc(sizeof(*buf), GFP_KERNEL);
2591da177e4SLinus Torvalds if (!buf)
2601da177e4SLinus Torvalds return NULL;
2611da177e4SLinus Torvalds
26267b48b88STakashi Iwai request.uid = (struct mixart_uid){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */
2631da177e4SLinus Torvalds request.data = &buf->sgroup_req;
2641da177e4SLinus Torvalds request.size = sizeof(buf->sgroup_req);
2651da177e4SLinus Torvalds
2661da177e4SLinus Torvalds memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req));
2671da177e4SLinus Torvalds
2681da177e4SLinus Torvalds buf->sgroup_req.stream_count = stream_count;
2691da177e4SLinus Torvalds buf->sgroup_req.channel_count = 2;
2701da177e4SLinus Torvalds buf->sgroup_req.latency = 256;
2711da177e4SLinus Torvalds buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */
2721da177e4SLinus Torvalds
2731da177e4SLinus Torvalds for (i=0; i<stream_count; i++) {
2741da177e4SLinus Torvalds int j;
2751da177e4SLinus Torvalds struct mixart_flowinfo *flowinfo;
2761da177e4SLinus Torvalds struct mixart_bufferinfo *bufferinfo;
2771da177e4SLinus Torvalds
2781da177e4SLinus Torvalds /* we don't yet know the format, so config 16 bit pcm audio for instance */
2791da177e4SLinus Torvalds buf->sgroup_req.stream_info[i].size_max_byte_frame = 1024;
2801da177e4SLinus Torvalds buf->sgroup_req.stream_info[i].size_max_sample_frame = 256;
2811da177e4SLinus Torvalds buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */
2821da177e4SLinus Torvalds
2831da177e4SLinus Torvalds /* find the right bufferinfo_array */
2841da177e4SLinus Torvalds j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i;
2851da177e4SLinus Torvalds if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */
2861da177e4SLinus Torvalds
2871da177e4SLinus Torvalds buf->sgroup_req.flow_entry[i] = j;
2881da177e4SLinus Torvalds
2891da177e4SLinus Torvalds flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area;
29067b48b88STakashi Iwai flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(struct mixart_bufferinfo));
2911da177e4SLinus Torvalds flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */
2921da177e4SLinus Torvalds
2931da177e4SLinus Torvalds bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
2941da177e4SLinus Torvalds bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */
2951da177e4SLinus Torvalds bufferinfo[j].available_length = 0; /* buffer is not yet allocated */
2961da177e4SLinus Torvalds
2971da177e4SLinus Torvalds /* construct the identifier of the stream buffer received in the interrupts ! */
2981da177e4SLinus Torvalds bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i;
2991da177e4SLinus Torvalds if(capture) {
3001da177e4SLinus Torvalds bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK;
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds }
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp);
3051da177e4SLinus Torvalds if((err < 0) || (buf->sgroup_resp.status != 0)) {
3066414e35dSTakashi Iwai dev_err(chip->card->dev,
3076414e35dSTakashi Iwai "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n",
3086414e35dSTakashi Iwai err, buf->sgroup_resp.status);
3091da177e4SLinus Torvalds kfree(buf);
3101da177e4SLinus Torvalds return NULL;
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds
3131da177e4SLinus Torvalds pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */
3141da177e4SLinus Torvalds pipe->stream_count = buf->sgroup_resp.stream_count;
3151da177e4SLinus Torvalds /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds pipe->status = PIPE_STOPPED;
3181da177e4SLinus Torvalds kfree(buf);
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds
3211da177e4SLinus Torvalds if(monitoring) pipe->monitoring = 1;
3221da177e4SLinus Torvalds else pipe->references++;
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds return pipe;
3251da177e4SLinus Torvalds }
3261da177e4SLinus Torvalds
3271da177e4SLinus Torvalds
snd_mixart_kill_ref_pipe(struct mixart_mgr * mgr,struct mixart_pipe * pipe,int monitoring)32867b48b88STakashi Iwai int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
32967b48b88STakashi Iwai struct mixart_pipe *pipe, int monitoring)
3301da177e4SLinus Torvalds {
3311da177e4SLinus Torvalds int err = 0;
3321da177e4SLinus Torvalds
3331da177e4SLinus Torvalds if(pipe->status == PIPE_UNDEFINED)
3341da177e4SLinus Torvalds return 0;
3351da177e4SLinus Torvalds
3361da177e4SLinus Torvalds if(monitoring)
3371da177e4SLinus Torvalds pipe->monitoring = 0;
3381da177e4SLinus Torvalds else
3391da177e4SLinus Torvalds pipe->references--;
3401da177e4SLinus Torvalds
3411da177e4SLinus Torvalds if((pipe->references <= 0) && (pipe->monitoring == 0)) {
3421da177e4SLinus Torvalds
34367b48b88STakashi Iwai struct mixart_msg request;
34467b48b88STakashi Iwai struct mixart_delete_group_resp delete_resp;
3451da177e4SLinus Torvalds
3461da177e4SLinus Torvalds /* release the clock */
3471da177e4SLinus Torvalds err = mixart_set_clock( mgr, pipe, 0);
3481da177e4SLinus Torvalds if( err < 0 ) {
3496414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
3506414e35dSTakashi Iwai "mixart_set_clock(0) return error!\n");
3511da177e4SLinus Torvalds }
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds /* stop the pipe */
3541da177e4SLinus Torvalds err = mixart_set_pipe_state(mgr, pipe, 0);
3551da177e4SLinus Torvalds if( err < 0 ) {
3566414e35dSTakashi Iwai dev_err(&mgr->pci->dev, "error stopping pipe!\n");
3571da177e4SLinus Torvalds }
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds request.message_id = MSG_STREAM_DELETE_GROUP;
36067b48b88STakashi Iwai request.uid = (struct mixart_uid){0,0};
3611da177e4SLinus Torvalds request.data = &pipe->group_uid; /* the streaming group ! */
3621da177e4SLinus Torvalds request.size = sizeof(pipe->group_uid);
3631da177e4SLinus Torvalds
3641da177e4SLinus Torvalds /* delete the pipe */
3651da177e4SLinus Torvalds err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
3661da177e4SLinus Torvalds if ((err < 0) || (delete_resp.status != 0)) {
3676414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
3686414e35dSTakashi Iwai "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n",
3696414e35dSTakashi Iwai err, delete_resp.status);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds
37267b48b88STakashi Iwai pipe->group_uid = (struct mixart_uid){0,0};
3731da177e4SLinus Torvalds pipe->stream_count = 0;
3741da177e4SLinus Torvalds pipe->status = PIPE_UNDEFINED;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds
3771da177e4SLinus Torvalds return err;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds
mixart_set_stream_state(struct mixart_stream * stream,int start)38067b48b88STakashi Iwai static int mixart_set_stream_state(struct mixart_stream *stream, int start)
3811da177e4SLinus Torvalds {
38267b48b88STakashi Iwai struct snd_mixart *chip;
38367b48b88STakashi Iwai struct mixart_stream_state_req stream_state_req;
38467b48b88STakashi Iwai struct mixart_msg request;
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds if(!stream->substream)
3871da177e4SLinus Torvalds return -EINVAL;
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds memset(&stream_state_req, 0, sizeof(stream_state_req));
3901da177e4SLinus Torvalds stream_state_req.stream_count = 1;
3911da177e4SLinus Torvalds stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
3921da177e4SLinus Torvalds stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;
3931da177e4SLinus Torvalds
3941da177e4SLinus Torvalds if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3951da177e4SLinus Torvalds request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
3961da177e4SLinus Torvalds else
3971da177e4SLinus Torvalds request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;
3981da177e4SLinus Torvalds
39967b48b88STakashi Iwai request.uid = (struct mixart_uid){0,0};
4001da177e4SLinus Torvalds request.data = &stream_state_req;
4011da177e4SLinus Torvalds request.size = sizeof(stream_state_req);
4021da177e4SLinus Torvalds
4031da177e4SLinus Torvalds stream->abs_period_elapsed = 0; /* reset stream pos */
4041da177e4SLinus Torvalds stream->buf_periods = 0;
4051da177e4SLinus Torvalds stream->buf_period_frag = 0;
4061da177e4SLinus Torvalds
4071da177e4SLinus Torvalds chip = snd_pcm_substream_chip(stream->substream);
4081da177e4SLinus Torvalds
4091da177e4SLinus Torvalds return snd_mixart_send_msg_nonblock(chip->mgr, &request);
4101da177e4SLinus Torvalds }
4111da177e4SLinus Torvalds
4121da177e4SLinus Torvalds /*
4131da177e4SLinus Torvalds * Trigger callback
4141da177e4SLinus Torvalds */
4151da177e4SLinus Torvalds
snd_mixart_trigger(struct snd_pcm_substream * subs,int cmd)41667b48b88STakashi Iwai static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
4171da177e4SLinus Torvalds {
41867b48b88STakashi Iwai struct mixart_stream *stream = subs->runtime->private_data;
4191da177e4SLinus Torvalds
4201da177e4SLinus Torvalds switch (cmd) {
4211da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START:
4221da177e4SLinus Torvalds
4236414e35dSTakashi Iwai dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_START\n");
4241da177e4SLinus Torvalds
4251da177e4SLinus Torvalds /* START_STREAM */
4261da177e4SLinus Torvalds if( mixart_set_stream_state(stream, 1) )
4271da177e4SLinus Torvalds return -EINVAL;
4281da177e4SLinus Torvalds
4291da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_RUNNING;
4301da177e4SLinus Torvalds
4311da177e4SLinus Torvalds break;
4321da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP:
4331da177e4SLinus Torvalds
4341da177e4SLinus Torvalds /* STOP_STREAM */
4351da177e4SLinus Torvalds if( mixart_set_stream_state(stream, 0) )
4361da177e4SLinus Torvalds return -EINVAL;
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_OPEN;
4391da177e4SLinus Torvalds
4406414e35dSTakashi Iwai dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
4411da177e4SLinus Torvalds
4421da177e4SLinus Torvalds break;
4431da177e4SLinus Torvalds
4441da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4451da177e4SLinus Torvalds /* TODO */
4461da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_PAUSE;
4476414e35dSTakashi Iwai dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_PUSH\n");
4481da177e4SLinus Torvalds break;
4491da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4501da177e4SLinus Torvalds /* TODO */
4511da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_RUNNING;
4526414e35dSTakashi Iwai dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_RELEASE\n");
4531da177e4SLinus Torvalds break;
4541da177e4SLinus Torvalds default:
4551da177e4SLinus Torvalds return -EINVAL;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds return 0;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds
mixart_sync_nonblock_events(struct mixart_mgr * mgr)46067b48b88STakashi Iwai static int mixart_sync_nonblock_events(struct mixart_mgr *mgr)
4611da177e4SLinus Torvalds {
462ef21ca24SNishanth Aravamudan unsigned long timeout = jiffies + HZ;
4631da177e4SLinus Torvalds while (atomic_read(&mgr->msg_processed) > 0) {
464ef21ca24SNishanth Aravamudan if (time_after(jiffies, timeout)) {
4656414e35dSTakashi Iwai dev_err(&mgr->pci->dev,
4666414e35dSTakashi Iwai "mixart: cannot process nonblock events!\n");
4671da177e4SLinus Torvalds return -EBUSY;
4681da177e4SLinus Torvalds }
4698433a509SNishanth Aravamudan schedule_timeout_uninterruptible(1);
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds return 0;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds
4741da177e4SLinus Torvalds /*
4751da177e4SLinus Torvalds * prepare callback for all pcms
4761da177e4SLinus Torvalds */
snd_mixart_prepare(struct snd_pcm_substream * subs)47767b48b88STakashi Iwai static int snd_mixart_prepare(struct snd_pcm_substream *subs)
4781da177e4SLinus Torvalds {
47967b48b88STakashi Iwai struct snd_mixart *chip = snd_pcm_substream_chip(subs);
48067b48b88STakashi Iwai struct mixart_stream *stream = subs->runtime->private_data;
4811da177e4SLinus Torvalds
4827bb2acb7SJohn Anthony Kazos Jr /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */
4831da177e4SLinus Torvalds
4846414e35dSTakashi Iwai dev_dbg(chip->card->dev, "snd_mixart_prepare\n");
4851da177e4SLinus Torvalds
4861da177e4SLinus Torvalds mixart_sync_nonblock_events(chip->mgr);
4871da177e4SLinus Torvalds
4881da177e4SLinus Torvalds /* only the first stream can choose the sample rate */
4891da177e4SLinus Torvalds /* the further opened streams will be limited to its frequency (see open) */
4901da177e4SLinus Torvalds if(chip->mgr->ref_count_rate == 1)
4911da177e4SLinus Torvalds chip->mgr->sample_rate = subs->runtime->rate;
4921da177e4SLinus Torvalds
4931da177e4SLinus Torvalds /* set the clock only once (first stream) on the same pipe */
4941da177e4SLinus Torvalds if(stream->pipe->references == 1) {
4951da177e4SLinus Torvalds if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
4961da177e4SLinus Torvalds return -EINVAL;
4971da177e4SLinus Torvalds }
4981da177e4SLinus Torvalds
4991da177e4SLinus Torvalds return 0;
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds
5021da177e4SLinus Torvalds
mixart_set_format(struct mixart_stream * stream,snd_pcm_format_t format)50367b48b88STakashi Iwai static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t format)
5041da177e4SLinus Torvalds {
5051da177e4SLinus Torvalds int err;
50667b48b88STakashi Iwai struct snd_mixart *chip;
50767b48b88STakashi Iwai struct mixart_msg request;
50867b48b88STakashi Iwai struct mixart_stream_param_desc stream_param;
50967b48b88STakashi Iwai struct mixart_return_uid resp;
5101da177e4SLinus Torvalds
5111da177e4SLinus Torvalds chip = snd_pcm_substream_chip(stream->substream);
5121da177e4SLinus Torvalds
5131da177e4SLinus Torvalds memset(&stream_param, 0, sizeof(stream_param));
5141da177e4SLinus Torvalds
5151da177e4SLinus Torvalds stream_param.coding_type = CT_LINEAR;
5161da177e4SLinus Torvalds stream_param.number_of_channel = stream->channels;
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds stream_param.sampling_freq = chip->mgr->sample_rate;
5191da177e4SLinus Torvalds if(stream_param.sampling_freq == 0)
5201da177e4SLinus Torvalds stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */
5211da177e4SLinus Torvalds
5221da177e4SLinus Torvalds switch(format){
5231da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_U8:
5241da177e4SLinus Torvalds stream_param.sample_type = ST_INTEGER_8;
5251da177e4SLinus Torvalds stream_param.sample_size = 8;
5261da177e4SLinus Torvalds break;
5271da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_S16_LE:
5281da177e4SLinus Torvalds stream_param.sample_type = ST_INTEGER_16LE;
5291da177e4SLinus Torvalds stream_param.sample_size = 16;
5301da177e4SLinus Torvalds break;
5311da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_S16_BE:
5321da177e4SLinus Torvalds stream_param.sample_type = ST_INTEGER_16BE;
5331da177e4SLinus Torvalds stream_param.sample_size = 16;
5341da177e4SLinus Torvalds break;
5351da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_S24_3LE:
5361da177e4SLinus Torvalds stream_param.sample_type = ST_INTEGER_24LE;
5371da177e4SLinus Torvalds stream_param.sample_size = 24;
5381da177e4SLinus Torvalds break;
5391da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_S24_3BE:
5401da177e4SLinus Torvalds stream_param.sample_type = ST_INTEGER_24BE;
5411da177e4SLinus Torvalds stream_param.sample_size = 24;
5421da177e4SLinus Torvalds break;
5431da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_FLOAT_LE:
5441da177e4SLinus Torvalds stream_param.sample_type = ST_FLOATING_POINT_32LE;
5451da177e4SLinus Torvalds stream_param.sample_size = 32;
5461da177e4SLinus Torvalds break;
5471da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_FLOAT_BE:
5481da177e4SLinus Torvalds stream_param.sample_type = ST_FLOATING_POINT_32BE;
5491da177e4SLinus Torvalds stream_param.sample_size = 32;
5501da177e4SLinus Torvalds break;
5511da177e4SLinus Torvalds default:
5526414e35dSTakashi Iwai dev_err(chip->card->dev,
5536414e35dSTakashi Iwai "error mixart_set_format() : unknown format\n");
5541da177e4SLinus Torvalds return -EINVAL;
5551da177e4SLinus Torvalds }
5561da177e4SLinus Torvalds
5576414e35dSTakashi Iwai dev_dbg(chip->card->dev,
5586414e35dSTakashi Iwai "set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
5591da177e4SLinus Torvalds stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds /* TODO: what else to configure ? */
5621da177e4SLinus Torvalds /* stream_param.samples_per_frame = 2; */
5631da177e4SLinus Torvalds /* stream_param.bytes_per_frame = 4; */
5641da177e4SLinus Torvalds /* stream_param.bytes_per_sample = 2; */
5651da177e4SLinus Torvalds
5661da177e4SLinus Torvalds stream_param.pipe_count = 1; /* set to 1 */
5671da177e4SLinus Torvalds stream_param.stream_count = 1; /* set to 1 */
568*4040fc51SGustavo A. R. Silva stream_param.stream_desc.uid_pipe = stream->pipe->group_uid;
569*4040fc51SGustavo A. R. Silva stream_param.stream_desc.stream_idx = stream->substream->number;
5701da177e4SLinus Torvalds
5711da177e4SLinus Torvalds request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
57267b48b88STakashi Iwai request.uid = (struct mixart_uid){0,0};
5731da177e4SLinus Torvalds request.data = &stream_param;
5741da177e4SLinus Torvalds request.size = sizeof(stream_param);
5751da177e4SLinus Torvalds
5761da177e4SLinus Torvalds err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
5771da177e4SLinus Torvalds if((err < 0) || resp.error_code) {
5786414e35dSTakashi Iwai dev_err(chip->card->dev,
5796414e35dSTakashi Iwai "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n",
5806414e35dSTakashi Iwai err, resp.error_code);
5811da177e4SLinus Torvalds return -EINVAL;
5821da177e4SLinus Torvalds }
5831da177e4SLinus Torvalds return 0;
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds
5861da177e4SLinus Torvalds
5871da177e4SLinus Torvalds /*
5881da177e4SLinus Torvalds * HW_PARAMS callback for all pcms
5891da177e4SLinus Torvalds */
snd_mixart_hw_params(struct snd_pcm_substream * subs,struct snd_pcm_hw_params * hw)59067b48b88STakashi Iwai static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
59167b48b88STakashi Iwai struct snd_pcm_hw_params *hw)
5921da177e4SLinus Torvalds {
59367b48b88STakashi Iwai struct snd_mixart *chip = snd_pcm_substream_chip(subs);
59467b48b88STakashi Iwai struct mixart_mgr *mgr = chip->mgr;
59567b48b88STakashi Iwai struct mixart_stream *stream = subs->runtime->private_data;
5961da177e4SLinus Torvalds snd_pcm_format_t format;
5971da177e4SLinus Torvalds int err;
5981da177e4SLinus Torvalds int channels;
5991da177e4SLinus Torvalds
6001da177e4SLinus Torvalds /* set up channels */
6011da177e4SLinus Torvalds channels = params_channels(hw);
6021da177e4SLinus Torvalds
6031da177e4SLinus Torvalds /* set up format for the stream */
6041da177e4SLinus Torvalds format = params_format(hw);
6051da177e4SLinus Torvalds
60662932df8SIngo Molnar mutex_lock(&mgr->setup_mutex);
6071da177e4SLinus Torvalds
6081da177e4SLinus Torvalds /* update the stream levels */
6091da177e4SLinus Torvalds if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
6101da177e4SLinus Torvalds int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
6111da177e4SLinus Torvalds if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
6121da177e4SLinus Torvalds mixart_update_playback_stream_level(chip, is_aes, subs->number);
6131da177e4SLinus Torvalds else
6141da177e4SLinus Torvalds mixart_update_capture_stream_level( chip, is_aes);
6151da177e4SLinus Torvalds }
6161da177e4SLinus Torvalds
6171da177e4SLinus Torvalds stream->channels = channels;
6181da177e4SLinus Torvalds
6191da177e4SLinus Torvalds /* set the format to the board */
6201da177e4SLinus Torvalds err = mixart_set_format(stream, format);
6211da177e4SLinus Torvalds if(err < 0) {
62282f5d571SJiri Slaby mutex_unlock(&mgr->setup_mutex);
6231da177e4SLinus Torvalds return err;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds
626c6312f39STakashi Iwai if (subs->runtime->buffer_changed) {
6271da177e4SLinus Torvalds struct mixart_bufferinfo *bufferinfo;
6281da177e4SLinus Torvalds int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
6291da177e4SLinus Torvalds if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
6301da177e4SLinus Torvalds i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds
6331da177e4SLinus Torvalds bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
6341da177e4SLinus Torvalds bufferinfo[i].buffer_address = subs->runtime->dma_addr;
6351da177e4SLinus Torvalds bufferinfo[i].available_length = subs->runtime->dma_bytes;
6361da177e4SLinus Torvalds /* bufferinfo[i].buffer_id is already defined */
6371da177e4SLinus Torvalds
6386414e35dSTakashi Iwai dev_dbg(chip->card->dev,
6396414e35dSTakashi Iwai "snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n",
6406414e35dSTakashi Iwai i, bufferinfo[i].buffer_address,
6411da177e4SLinus Torvalds bufferinfo[i].available_length,
6421da177e4SLinus Torvalds subs->number);
6431da177e4SLinus Torvalds }
64462932df8SIngo Molnar mutex_unlock(&mgr->setup_mutex);
6451da177e4SLinus Torvalds
646c6312f39STakashi Iwai return 0;
6471da177e4SLinus Torvalds }
6481da177e4SLinus Torvalds
snd_mixart_hw_free(struct snd_pcm_substream * subs)64967b48b88STakashi Iwai static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
6501da177e4SLinus Torvalds {
65167b48b88STakashi Iwai struct snd_mixart *chip = snd_pcm_substream_chip(subs);
6521da177e4SLinus Torvalds mixart_sync_nonblock_events(chip->mgr);
6531da177e4SLinus Torvalds return 0;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds
6561da177e4SLinus Torvalds
6571da177e4SLinus Torvalds
6581da177e4SLinus Torvalds /*
6591da177e4SLinus Torvalds * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
6601da177e4SLinus Torvalds */
6617d4edd42SBhumika Goyal static const struct snd_pcm_hardware snd_mixart_analog_caps =
6621da177e4SLinus Torvalds {
6631da177e4SLinus Torvalds .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
664b83f346bSClemens Ladisch SNDRV_PCM_INFO_MMAP_VALID |
6651da177e4SLinus Torvalds SNDRV_PCM_INFO_PAUSE),
6661da177e4SLinus Torvalds .formats = ( SNDRV_PCM_FMTBIT_U8 |
6671da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
6681da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
6691da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
6701da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
6711da177e4SLinus Torvalds .rate_min = 8000,
6721da177e4SLinus Torvalds .rate_max = 48000,
6731da177e4SLinus Torvalds .channels_min = 1,
6741da177e4SLinus Torvalds .channels_max = 2,
6751da177e4SLinus Torvalds .buffer_bytes_max = (32*1024),
6761da177e4SLinus Torvalds .period_bytes_min = 256, /* 256 frames U8 mono*/
6771da177e4SLinus Torvalds .period_bytes_max = (16*1024),
6781da177e4SLinus Torvalds .periods_min = 2,
6791da177e4SLinus Torvalds .periods_max = (32*1024/256),
6801da177e4SLinus Torvalds };
6811da177e4SLinus Torvalds
6827d4edd42SBhumika Goyal static const struct snd_pcm_hardware snd_mixart_digital_caps =
6831da177e4SLinus Torvalds {
6841da177e4SLinus Torvalds .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
685b83f346bSClemens Ladisch SNDRV_PCM_INFO_MMAP_VALID |
6861da177e4SLinus Torvalds SNDRV_PCM_INFO_PAUSE),
6871da177e4SLinus Torvalds .formats = ( SNDRV_PCM_FMTBIT_U8 |
6881da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
6891da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
6901da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
6911da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
6921da177e4SLinus Torvalds .rate_min = 32000,
6931da177e4SLinus Torvalds .rate_max = 48000,
6941da177e4SLinus Torvalds .channels_min = 1,
6951da177e4SLinus Torvalds .channels_max = 2,
6961da177e4SLinus Torvalds .buffer_bytes_max = (32*1024),
6971da177e4SLinus Torvalds .period_bytes_min = 256, /* 256 frames U8 mono*/
6981da177e4SLinus Torvalds .period_bytes_max = (16*1024),
6991da177e4SLinus Torvalds .periods_min = 2,
7001da177e4SLinus Torvalds .periods_max = (32*1024/256),
7011da177e4SLinus Torvalds };
7021da177e4SLinus Torvalds
7031da177e4SLinus Torvalds
snd_mixart_playback_open(struct snd_pcm_substream * subs)70467b48b88STakashi Iwai static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
7051da177e4SLinus Torvalds {
70667b48b88STakashi Iwai struct snd_mixart *chip = snd_pcm_substream_chip(subs);
70767b48b88STakashi Iwai struct mixart_mgr *mgr = chip->mgr;
70867b48b88STakashi Iwai struct snd_pcm_runtime *runtime = subs->runtime;
70967b48b88STakashi Iwai struct snd_pcm *pcm = subs->pcm;
71067b48b88STakashi Iwai struct mixart_stream *stream;
71167b48b88STakashi Iwai struct mixart_pipe *pipe;
7121da177e4SLinus Torvalds int err = 0;
7131da177e4SLinus Torvalds int pcm_number;
7141da177e4SLinus Torvalds
71562932df8SIngo Molnar mutex_lock(&mgr->setup_mutex);
7161da177e4SLinus Torvalds
7171da177e4SLinus Torvalds if ( pcm == chip->pcm ) {
7181da177e4SLinus Torvalds pcm_number = MIXART_PCM_ANALOG;
7191da177e4SLinus Torvalds runtime->hw = snd_mixart_analog_caps;
7201da177e4SLinus Torvalds } else {
721da3cec35STakashi Iwai snd_BUG_ON(pcm != chip->pcm_dig);
7221da177e4SLinus Torvalds pcm_number = MIXART_PCM_DIGITAL;
7231da177e4SLinus Torvalds runtime->hw = snd_mixart_digital_caps;
7241da177e4SLinus Torvalds }
7256414e35dSTakashi Iwai dev_dbg(chip->card->dev,
7266414e35dSTakashi Iwai "snd_mixart_playback_open C%d/P%d/Sub%d\n",
7276414e35dSTakashi Iwai chip->chip_idx, pcm_number, subs->number);
7281da177e4SLinus Torvalds
7291da177e4SLinus Torvalds /* get stream info */
7301da177e4SLinus Torvalds stream = &(chip->playback_stream[pcm_number][subs->number]);
7311da177e4SLinus Torvalds
7321da177e4SLinus Torvalds if (stream->status != MIXART_STREAM_STATUS_FREE){
7331da177e4SLinus Torvalds /* streams in use */
7346414e35dSTakashi Iwai dev_err(chip->card->dev,
7356414e35dSTakashi Iwai "snd_mixart_playback_open C%d/P%d/Sub%d in use\n",
7366414e35dSTakashi Iwai chip->chip_idx, pcm_number, subs->number);
7371da177e4SLinus Torvalds err = -EBUSY;
7381da177e4SLinus Torvalds goto _exit_open;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds
7411da177e4SLinus Torvalds /* get pipe pointer (out pipe) */
7421da177e4SLinus Torvalds pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
7431da177e4SLinus Torvalds
7441da177e4SLinus Torvalds if (pipe == NULL) {
7451da177e4SLinus Torvalds err = -EINVAL;
7461da177e4SLinus Torvalds goto _exit_open;
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds
7491da177e4SLinus Torvalds /* start the pipe if necessary */
7501da177e4SLinus Torvalds err = mixart_set_pipe_state(chip->mgr, pipe, 1);
7511da177e4SLinus Torvalds if( err < 0 ) {
7526414e35dSTakashi Iwai dev_err(chip->card->dev, "error starting pipe!\n");
7531da177e4SLinus Torvalds snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
7541da177e4SLinus Torvalds err = -EINVAL;
7551da177e4SLinus Torvalds goto _exit_open;
7561da177e4SLinus Torvalds }
7571da177e4SLinus Torvalds
7581da177e4SLinus Torvalds stream->pipe = pipe;
7591da177e4SLinus Torvalds stream->pcm_number = pcm_number;
7601da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_OPEN;
7611da177e4SLinus Torvalds stream->substream = subs;
7621da177e4SLinus Torvalds stream->channels = 0; /* not configured yet */
7631da177e4SLinus Torvalds
7641da177e4SLinus Torvalds runtime->private_data = stream;
7651da177e4SLinus Torvalds
7661da177e4SLinus Torvalds snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
7671da177e4SLinus Torvalds snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
7681da177e4SLinus Torvalds
7691da177e4SLinus Torvalds /* if a sample rate is already used, another stream cannot change */
7701da177e4SLinus Torvalds if(mgr->ref_count_rate++) {
7711da177e4SLinus Torvalds if(mgr->sample_rate) {
7721da177e4SLinus Torvalds runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
7731da177e4SLinus Torvalds }
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds
7761da177e4SLinus Torvalds _exit_open:
77762932df8SIngo Molnar mutex_unlock(&mgr->setup_mutex);
7781da177e4SLinus Torvalds
7791da177e4SLinus Torvalds return err;
7801da177e4SLinus Torvalds }
7811da177e4SLinus Torvalds
7821da177e4SLinus Torvalds
snd_mixart_capture_open(struct snd_pcm_substream * subs)78367b48b88STakashi Iwai static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
7841da177e4SLinus Torvalds {
78567b48b88STakashi Iwai struct snd_mixart *chip = snd_pcm_substream_chip(subs);
78667b48b88STakashi Iwai struct mixart_mgr *mgr = chip->mgr;
78767b48b88STakashi Iwai struct snd_pcm_runtime *runtime = subs->runtime;
78867b48b88STakashi Iwai struct snd_pcm *pcm = subs->pcm;
78967b48b88STakashi Iwai struct mixart_stream *stream;
79067b48b88STakashi Iwai struct mixart_pipe *pipe;
7911da177e4SLinus Torvalds int err = 0;
7921da177e4SLinus Torvalds int pcm_number;
7931da177e4SLinus Torvalds
79462932df8SIngo Molnar mutex_lock(&mgr->setup_mutex);
7951da177e4SLinus Torvalds
7961da177e4SLinus Torvalds if ( pcm == chip->pcm ) {
7971da177e4SLinus Torvalds pcm_number = MIXART_PCM_ANALOG;
7981da177e4SLinus Torvalds runtime->hw = snd_mixart_analog_caps;
7991da177e4SLinus Torvalds } else {
800da3cec35STakashi Iwai snd_BUG_ON(pcm != chip->pcm_dig);
8011da177e4SLinus Torvalds pcm_number = MIXART_PCM_DIGITAL;
8021da177e4SLinus Torvalds runtime->hw = snd_mixart_digital_caps;
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds
8051da177e4SLinus Torvalds runtime->hw.channels_min = 2; /* for instance, no mono */
8061da177e4SLinus Torvalds
8076414e35dSTakashi Iwai dev_dbg(chip->card->dev, "snd_mixart_capture_open C%d/P%d/Sub%d\n",
8086414e35dSTakashi Iwai chip->chip_idx, pcm_number, subs->number);
8091da177e4SLinus Torvalds
8101da177e4SLinus Torvalds /* get stream info */
8111da177e4SLinus Torvalds stream = &(chip->capture_stream[pcm_number]);
8121da177e4SLinus Torvalds
8131da177e4SLinus Torvalds if (stream->status != MIXART_STREAM_STATUS_FREE){
8141da177e4SLinus Torvalds /* streams in use */
8156414e35dSTakashi Iwai dev_err(chip->card->dev,
8166414e35dSTakashi Iwai "snd_mixart_capture_open C%d/P%d/Sub%d in use\n",
8176414e35dSTakashi Iwai chip->chip_idx, pcm_number, subs->number);
8181da177e4SLinus Torvalds err = -EBUSY;
8191da177e4SLinus Torvalds goto _exit_open;
8201da177e4SLinus Torvalds }
8211da177e4SLinus Torvalds
8221da177e4SLinus Torvalds /* get pipe pointer (in pipe) */
8231da177e4SLinus Torvalds pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
8241da177e4SLinus Torvalds
8251da177e4SLinus Torvalds if (pipe == NULL) {
8261da177e4SLinus Torvalds err = -EINVAL;
8271da177e4SLinus Torvalds goto _exit_open;
8281da177e4SLinus Torvalds }
8291da177e4SLinus Torvalds
8301da177e4SLinus Torvalds /* start the pipe if necessary */
8311da177e4SLinus Torvalds err = mixart_set_pipe_state(chip->mgr, pipe, 1);
8321da177e4SLinus Torvalds if( err < 0 ) {
8336414e35dSTakashi Iwai dev_err(chip->card->dev, "error starting pipe!\n");
8341da177e4SLinus Torvalds snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
8351da177e4SLinus Torvalds err = -EINVAL;
8361da177e4SLinus Torvalds goto _exit_open;
8371da177e4SLinus Torvalds }
8381da177e4SLinus Torvalds
8391da177e4SLinus Torvalds stream->pipe = pipe;
8401da177e4SLinus Torvalds stream->pcm_number = pcm_number;
8411da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_OPEN;
8421da177e4SLinus Torvalds stream->substream = subs;
8431da177e4SLinus Torvalds stream->channels = 0; /* not configured yet */
8441da177e4SLinus Torvalds
8451da177e4SLinus Torvalds runtime->private_data = stream;
8461da177e4SLinus Torvalds
8471da177e4SLinus Torvalds snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
8481da177e4SLinus Torvalds snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
8491da177e4SLinus Torvalds
8501da177e4SLinus Torvalds /* if a sample rate is already used, another stream cannot change */
8511da177e4SLinus Torvalds if(mgr->ref_count_rate++) {
8521da177e4SLinus Torvalds if(mgr->sample_rate) {
8531da177e4SLinus Torvalds runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
8541da177e4SLinus Torvalds }
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds
8571da177e4SLinus Torvalds _exit_open:
85862932df8SIngo Molnar mutex_unlock(&mgr->setup_mutex);
8591da177e4SLinus Torvalds
8601da177e4SLinus Torvalds return err;
8611da177e4SLinus Torvalds }
8621da177e4SLinus Torvalds
8631da177e4SLinus Torvalds
8641da177e4SLinus Torvalds
snd_mixart_close(struct snd_pcm_substream * subs)86567b48b88STakashi Iwai static int snd_mixart_close(struct snd_pcm_substream *subs)
8661da177e4SLinus Torvalds {
86767b48b88STakashi Iwai struct snd_mixart *chip = snd_pcm_substream_chip(subs);
86867b48b88STakashi Iwai struct mixart_mgr *mgr = chip->mgr;
86967b48b88STakashi Iwai struct mixart_stream *stream = subs->runtime->private_data;
8701da177e4SLinus Torvalds
87162932df8SIngo Molnar mutex_lock(&mgr->setup_mutex);
8721da177e4SLinus Torvalds
8736414e35dSTakashi Iwai dev_dbg(chip->card->dev, "snd_mixart_close C%d/P%d/Sub%d\n",
8746414e35dSTakashi Iwai chip->chip_idx, stream->pcm_number, subs->number);
8751da177e4SLinus Torvalds
8761da177e4SLinus Torvalds /* sample rate released */
8771da177e4SLinus Torvalds if(--mgr->ref_count_rate == 0) {
8781da177e4SLinus Torvalds mgr->sample_rate = 0;
8791da177e4SLinus Torvalds }
8801da177e4SLinus Torvalds
8811da177e4SLinus Torvalds /* delete pipe */
8821da177e4SLinus Torvalds if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
8831da177e4SLinus Torvalds
8846414e35dSTakashi Iwai dev_err(chip->card->dev,
8856414e35dSTakashi Iwai "error snd_mixart_kill_ref_pipe C%dP%d\n",
8866414e35dSTakashi Iwai chip->chip_idx, stream->pcm_number);
8871da177e4SLinus Torvalds }
8881da177e4SLinus Torvalds
8891da177e4SLinus Torvalds stream->pipe = NULL;
8901da177e4SLinus Torvalds stream->status = MIXART_STREAM_STATUS_FREE;
8911da177e4SLinus Torvalds stream->substream = NULL;
8921da177e4SLinus Torvalds
89362932df8SIngo Molnar mutex_unlock(&mgr->setup_mutex);
8941da177e4SLinus Torvalds return 0;
8951da177e4SLinus Torvalds }
8961da177e4SLinus Torvalds
8971da177e4SLinus Torvalds
snd_mixart_stream_pointer(struct snd_pcm_substream * subs)89867b48b88STakashi Iwai static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *subs)
8991da177e4SLinus Torvalds {
90067b48b88STakashi Iwai struct snd_pcm_runtime *runtime = subs->runtime;
90167b48b88STakashi Iwai struct mixart_stream *stream = runtime->private_data;
9021da177e4SLinus Torvalds
9031da177e4SLinus Torvalds return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
9041da177e4SLinus Torvalds }
9051da177e4SLinus Torvalds
9061da177e4SLinus Torvalds
9071da177e4SLinus Torvalds
9086769e988SJulia Lawall static const struct snd_pcm_ops snd_mixart_playback_ops = {
9091da177e4SLinus Torvalds .open = snd_mixart_playback_open,
9101da177e4SLinus Torvalds .close = snd_mixart_close,
9111da177e4SLinus Torvalds .prepare = snd_mixart_prepare,
9121da177e4SLinus Torvalds .hw_params = snd_mixart_hw_params,
9131da177e4SLinus Torvalds .hw_free = snd_mixart_hw_free,
9141da177e4SLinus Torvalds .trigger = snd_mixart_trigger,
9151da177e4SLinus Torvalds .pointer = snd_mixart_stream_pointer,
9161da177e4SLinus Torvalds };
9171da177e4SLinus Torvalds
9186769e988SJulia Lawall static const struct snd_pcm_ops snd_mixart_capture_ops = {
9191da177e4SLinus Torvalds .open = snd_mixart_capture_open,
9201da177e4SLinus Torvalds .close = snd_mixart_close,
9211da177e4SLinus Torvalds .prepare = snd_mixart_prepare,
9221da177e4SLinus Torvalds .hw_params = snd_mixart_hw_params,
9231da177e4SLinus Torvalds .hw_free = snd_mixart_hw_free,
9241da177e4SLinus Torvalds .trigger = snd_mixart_trigger,
9251da177e4SLinus Torvalds .pointer = snd_mixart_stream_pointer,
9261da177e4SLinus Torvalds };
9271da177e4SLinus Torvalds
preallocate_buffers(struct snd_mixart * chip,struct snd_pcm * pcm)92867b48b88STakashi Iwai static void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm)
9291da177e4SLinus Torvalds {
9301da177e4SLinus Torvalds #if 0
93167b48b88STakashi Iwai struct snd_pcm_substream *subs;
9321da177e4SLinus Torvalds int stream;
9331da177e4SLinus Torvalds
9341da177e4SLinus Torvalds for (stream = 0; stream < 2; stream++) {
9351da177e4SLinus Torvalds int idx = 0;
9361da177e4SLinus Torvalds for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
9371da177e4SLinus Torvalds /* set up the unique device id with the chip index */
9381da177e4SLinus Torvalds subs->dma_device.id = subs->pcm->device << 16 |
9391da177e4SLinus Torvalds subs->stream << 8 | (subs->number + 1) |
9401da177e4SLinus Torvalds (chip->chip_idx + 1) << 24;
9411da177e4SLinus Torvalds }
9421da177e4SLinus Torvalds #endif
943c6312f39STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
9446974f8adSTakashi Iwai &chip->mgr->pci->dev,
9456974f8adSTakashi Iwai 32*1024, 32*1024);
9461da177e4SLinus Torvalds }
9471da177e4SLinus Torvalds
9481da177e4SLinus Torvalds /*
9491da177e4SLinus Torvalds */
snd_mixart_pcm_analog(struct snd_mixart * chip)95067b48b88STakashi Iwai static int snd_mixart_pcm_analog(struct snd_mixart *chip)
9511da177e4SLinus Torvalds {
9521da177e4SLinus Torvalds int err;
95367b48b88STakashi Iwai struct snd_pcm *pcm;
9541da177e4SLinus Torvalds char name[32];
9551da177e4SLinus Torvalds
9561da177e4SLinus Torvalds sprintf(name, "miXart analog %d", chip->chip_idx);
95773debecfSTakashi Iwai err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
9581da177e4SLinus Torvalds MIXART_PLAYBACK_STREAMS,
95973debecfSTakashi Iwai MIXART_CAPTURE_STREAMS, &pcm);
96073debecfSTakashi Iwai if (err < 0) {
9616414e35dSTakashi Iwai dev_err(chip->card->dev,
9626414e35dSTakashi Iwai "cannot create the analog pcm %d\n", chip->chip_idx);
9631da177e4SLinus Torvalds return err;
9641da177e4SLinus Torvalds }
9651da177e4SLinus Torvalds
9661da177e4SLinus Torvalds pcm->private_data = chip;
9671da177e4SLinus Torvalds
9681da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
9691da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
9701da177e4SLinus Torvalds
9711da177e4SLinus Torvalds pcm->info_flags = 0;
9728d3a8b5cSTakashi Iwai pcm->nonatomic = true;
9731da177e4SLinus Torvalds strcpy(pcm->name, name);
9741da177e4SLinus Torvalds
9751da177e4SLinus Torvalds preallocate_buffers(chip, pcm);
9761da177e4SLinus Torvalds
9771da177e4SLinus Torvalds chip->pcm = pcm;
9781da177e4SLinus Torvalds return 0;
9791da177e4SLinus Torvalds }
9801da177e4SLinus Torvalds
9811da177e4SLinus Torvalds
9821da177e4SLinus Torvalds /*
9831da177e4SLinus Torvalds */
snd_mixart_pcm_digital(struct snd_mixart * chip)98467b48b88STakashi Iwai static int snd_mixart_pcm_digital(struct snd_mixart *chip)
9851da177e4SLinus Torvalds {
9861da177e4SLinus Torvalds int err;
98767b48b88STakashi Iwai struct snd_pcm *pcm;
9881da177e4SLinus Torvalds char name[32];
9891da177e4SLinus Torvalds
9901da177e4SLinus Torvalds sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
99173debecfSTakashi Iwai err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
9921da177e4SLinus Torvalds MIXART_PLAYBACK_STREAMS,
99373debecfSTakashi Iwai MIXART_CAPTURE_STREAMS, &pcm);
99473debecfSTakashi Iwai if (err < 0) {
9956414e35dSTakashi Iwai dev_err(chip->card->dev,
9966414e35dSTakashi Iwai "cannot create the digital pcm %d\n", chip->chip_idx);
9971da177e4SLinus Torvalds return err;
9981da177e4SLinus Torvalds }
9991da177e4SLinus Torvalds
10001da177e4SLinus Torvalds pcm->private_data = chip;
10011da177e4SLinus Torvalds
10021da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
10031da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
10041da177e4SLinus Torvalds
10051da177e4SLinus Torvalds pcm->info_flags = 0;
10068d3a8b5cSTakashi Iwai pcm->nonatomic = true;
10071da177e4SLinus Torvalds strcpy(pcm->name, name);
10081da177e4SLinus Torvalds
10091da177e4SLinus Torvalds preallocate_buffers(chip, pcm);
10101da177e4SLinus Torvalds
10111da177e4SLinus Torvalds chip->pcm_dig = pcm;
10121da177e4SLinus Torvalds return 0;
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds
snd_mixart_chip_free(struct snd_mixart * chip)101567b48b88STakashi Iwai static int snd_mixart_chip_free(struct snd_mixart *chip)
10161da177e4SLinus Torvalds {
10171da177e4SLinus Torvalds kfree(chip);
10181da177e4SLinus Torvalds return 0;
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds
snd_mixart_chip_dev_free(struct snd_device * device)102167b48b88STakashi Iwai static int snd_mixart_chip_dev_free(struct snd_device *device)
10221da177e4SLinus Torvalds {
102367b48b88STakashi Iwai struct snd_mixart *chip = device->device_data;
10241da177e4SLinus Torvalds return snd_mixart_chip_free(chip);
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds
10271da177e4SLinus Torvalds
10281da177e4SLinus Torvalds /*
10291da177e4SLinus Torvalds */
snd_mixart_create(struct mixart_mgr * mgr,struct snd_card * card,int idx)1030e23e7a14SBill Pemberton static int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
10311da177e4SLinus Torvalds {
10321da177e4SLinus Torvalds int err;
103367b48b88STakashi Iwai struct snd_mixart *chip;
1034efb0ad25STakashi Iwai static const struct snd_device_ops ops = {
10351da177e4SLinus Torvalds .dev_free = snd_mixart_chip_dev_free,
10361da177e4SLinus Torvalds };
10371da177e4SLinus Torvalds
103873f6a12eSJulia Lawall chip = kzalloc(sizeof(*chip), GFP_KERNEL);
1039a357f3d1SMarkus Elfring if (!chip)
10401da177e4SLinus Torvalds return -ENOMEM;
10411da177e4SLinus Torvalds
10421da177e4SLinus Torvalds chip->card = card;
10431da177e4SLinus Torvalds chip->chip_idx = idx;
10441da177e4SLinus Torvalds chip->mgr = mgr;
1045cefeaa50STakashi Iwai card->sync_irq = mgr->irq;
10461da177e4SLinus Torvalds
104773debecfSTakashi Iwai err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
104873debecfSTakashi Iwai if (err < 0) {
10491da177e4SLinus Torvalds snd_mixart_chip_free(chip);
10501da177e4SLinus Torvalds return err;
10511da177e4SLinus Torvalds }
10521da177e4SLinus Torvalds
105373f6a12eSJulia Lawall mgr->chip[idx] = chip;
10541da177e4SLinus Torvalds return 0;
10551da177e4SLinus Torvalds }
10561da177e4SLinus Torvalds
snd_mixart_create_pcm(struct snd_mixart * chip)105767b48b88STakashi Iwai int snd_mixart_create_pcm(struct snd_mixart* chip)
10581da177e4SLinus Torvalds {
10591da177e4SLinus Torvalds int err;
10601da177e4SLinus Torvalds
10611da177e4SLinus Torvalds err = snd_mixart_pcm_analog(chip);
10621da177e4SLinus Torvalds if (err < 0)
10631da177e4SLinus Torvalds return err;
10641da177e4SLinus Torvalds
10651da177e4SLinus Torvalds if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
10661da177e4SLinus Torvalds
10671da177e4SLinus Torvalds err = snd_mixart_pcm_digital(chip);
10681da177e4SLinus Torvalds if (err < 0)
10691da177e4SLinus Torvalds return err;
10701da177e4SLinus Torvalds }
10711da177e4SLinus Torvalds return err;
10721da177e4SLinus Torvalds }
10731da177e4SLinus Torvalds
10741da177e4SLinus Torvalds
10751da177e4SLinus Torvalds /*
10761da177e4SLinus Torvalds * release all the cards assigned to a manager instance
10771da177e4SLinus Torvalds */
snd_mixart_free(struct mixart_mgr * mgr)107867b48b88STakashi Iwai static int snd_mixart_free(struct mixart_mgr *mgr)
10791da177e4SLinus Torvalds {
10801da177e4SLinus Torvalds unsigned int i;
10811da177e4SLinus Torvalds
10821da177e4SLinus Torvalds for (i = 0; i < mgr->num_cards; i++) {
10831da177e4SLinus Torvalds if (mgr->chip[i])
10841da177e4SLinus Torvalds snd_card_free(mgr->chip[i]->card);
10851da177e4SLinus Torvalds }
10861da177e4SLinus Torvalds
10871da177e4SLinus Torvalds /* stop mailbox */
10881da177e4SLinus Torvalds snd_mixart_exit_mailbox(mgr);
10891da177e4SLinus Torvalds
10901da177e4SLinus Torvalds /* release irq */
10911da177e4SLinus Torvalds if (mgr->irq >= 0)
1092437a5a46STakashi Iwai free_irq(mgr->irq, mgr);
10931da177e4SLinus Torvalds
10941da177e4SLinus Torvalds /* reset board if some firmware was loaded */
10951da177e4SLinus Torvalds if(mgr->dsp_loaded) {
10961da177e4SLinus Torvalds snd_mixart_reset_board(mgr);
10976414e35dSTakashi Iwai dev_dbg(&mgr->pci->dev, "reset miXart !\n");
10981da177e4SLinus Torvalds }
10991da177e4SLinus Torvalds
11001da177e4SLinus Torvalds /* release the i/o ports */
1101ff6defa6SMarkus Elfring for (i = 0; i < 2; ++i)
11021da177e4SLinus Torvalds iounmap(mgr->mem[i].virt);
1103ff6defa6SMarkus Elfring
11041da177e4SLinus Torvalds pci_release_regions(mgr->pci);
11051da177e4SLinus Torvalds
11061da177e4SLinus Torvalds /* free flowarray */
11071da177e4SLinus Torvalds if(mgr->flowinfo.area) {
11081da177e4SLinus Torvalds snd_dma_free_pages(&mgr->flowinfo);
11091da177e4SLinus Torvalds mgr->flowinfo.area = NULL;
11101da177e4SLinus Torvalds }
11111da177e4SLinus Torvalds /* free bufferarray */
11121da177e4SLinus Torvalds if(mgr->bufferinfo.area) {
11131da177e4SLinus Torvalds snd_dma_free_pages(&mgr->bufferinfo);
11141da177e4SLinus Torvalds mgr->bufferinfo.area = NULL;
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds
11171da177e4SLinus Torvalds pci_disable_device(mgr->pci);
11181da177e4SLinus Torvalds kfree(mgr);
11191da177e4SLinus Torvalds return 0;
11201da177e4SLinus Torvalds }
11211da177e4SLinus Torvalds
11221da177e4SLinus Torvalds /*
11231da177e4SLinus Torvalds * proc interface
11241da177e4SLinus Torvalds */
11251da177e4SLinus Torvalds
11261da177e4SLinus Torvalds /*
11271da177e4SLinus Torvalds mixart_BA0 proc interface for BAR 0 - read callback
11281da177e4SLinus Torvalds */
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)112924e4a121STakashi Iwai static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry,
113024e4a121STakashi Iwai void *file_private_data,
11311da177e4SLinus Torvalds struct file *file, char __user *buf,
113224e4a121STakashi Iwai size_t count, loff_t pos)
11331da177e4SLinus Torvalds {
113467b48b88STakashi Iwai struct mixart_mgr *mgr = entry->private_data;
11351da177e4SLinus Torvalds
1136b0cc58a2SDan Carpenter count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
11371da177e4SLinus Torvalds if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count))
11381da177e4SLinus Torvalds return -EFAULT;
11391da177e4SLinus Torvalds return count;
11401da177e4SLinus Torvalds }
11411da177e4SLinus Torvalds
11421da177e4SLinus Torvalds /*
11431da177e4SLinus Torvalds mixart_BA1 proc interface for BAR 1 - read callback
11441da177e4SLinus Torvalds */
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)114524e4a121STakashi Iwai static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry,
114624e4a121STakashi Iwai void *file_private_data,
11471da177e4SLinus Torvalds struct file *file, char __user *buf,
114824e4a121STakashi Iwai size_t count, loff_t pos)
11491da177e4SLinus Torvalds {
115067b48b88STakashi Iwai struct mixart_mgr *mgr = entry->private_data;
11511da177e4SLinus Torvalds
1152b0cc58a2SDan Carpenter count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
11531da177e4SLinus Torvalds if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count))
11541da177e4SLinus Torvalds return -EFAULT;
11551da177e4SLinus Torvalds return count;
11561da177e4SLinus Torvalds }
11571da177e4SLinus Torvalds
1158d25ff268STakashi Iwai static const struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
11591da177e4SLinus Torvalds .read = snd_mixart_BA0_read,
11601da177e4SLinus Torvalds };
11611da177e4SLinus Torvalds
1162d25ff268STakashi Iwai static const struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
11631da177e4SLinus Torvalds .read = snd_mixart_BA1_read,
11641da177e4SLinus Torvalds };
11651da177e4SLinus Torvalds
11661da177e4SLinus Torvalds
snd_mixart_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)116767b48b88STakashi Iwai static void snd_mixart_proc_read(struct snd_info_entry *entry,
116867b48b88STakashi Iwai struct snd_info_buffer *buffer)
11691da177e4SLinus Torvalds {
117067b48b88STakashi Iwai struct snd_mixart *chip = entry->private_data;
11711da177e4SLinus Torvalds u32 ref;
11721da177e4SLinus Torvalds
11731da177e4SLinus Torvalds snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx);
11741da177e4SLinus Torvalds
11751da177e4SLinus Torvalds /* stats available when embedded OS is running */
11761da177e4SLinus Torvalds if (chip->mgr->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) {
11771da177e4SLinus Torvalds snd_iprintf(buffer, "- hardware -\n");
11781da177e4SLinus Torvalds switch (chip->mgr->board_type ) {
11791da177e4SLinus Torvalds case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break;
11801da177e4SLinus Torvalds case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break;
11811da177e4SLinus Torvalds case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break;
11821da177e4SLinus Torvalds default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break;
11831da177e4SLinus Torvalds }
11841da177e4SLinus Torvalds
11851da177e4SLinus Torvalds snd_iprintf(buffer, "- system load -\n");
11861da177e4SLinus Torvalds
11871da177e4SLinus Torvalds /* get perf reference */
11881da177e4SLinus Torvalds
11891da177e4SLinus Torvalds ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET));
11901da177e4SLinus Torvalds
11911da177e4SLinus Torvalds if (ref) {
11921da177e4SLinus Torvalds u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref;
11931da177e4SLinus Torvalds u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref;
11941da177e4SLinus Torvalds u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref;
11951da177e4SLinus Torvalds
11961da177e4SLinus Torvalds snd_iprintf(buffer, "\tstreaming : %d\n", streaming);
11971da177e4SLinus Torvalds snd_iprintf(buffer, "\tmailbox : %d\n", mailbox);
1198cf2fbdd2SMasanari Iida snd_iprintf(buffer, "\tinterrupts handling : %d\n\n", interr);
11991da177e4SLinus Torvalds }
12001da177e4SLinus Torvalds } /* endif elf loaded */
12011da177e4SLinus Torvalds }
12021da177e4SLinus Torvalds
snd_mixart_proc_init(struct snd_mixart * chip)1203e23e7a14SBill Pemberton static void snd_mixart_proc_init(struct snd_mixart *chip)
12041da177e4SLinus Torvalds {
120567b48b88STakashi Iwai struct snd_info_entry *entry;
12061da177e4SLinus Torvalds
12071da177e4SLinus Torvalds /* text interface to read perf and temp meters */
120847f2769bSTakashi Iwai snd_card_ro_proc_new(chip->card, "board_info", chip,
120947f2769bSTakashi Iwai snd_mixart_proc_read);
12101da177e4SLinus Torvalds
12111da177e4SLinus Torvalds if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
12121da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_DATA;
12131da177e4SLinus Torvalds entry->private_data = chip->mgr;
12141da177e4SLinus Torvalds entry->c.ops = &snd_mixart_proc_ops_BA0;
12151da177e4SLinus Torvalds entry->size = MIXART_BA0_SIZE;
12161da177e4SLinus Torvalds }
12171da177e4SLinus Torvalds if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) {
12181da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_DATA;
12191da177e4SLinus Torvalds entry->private_data = chip->mgr;
12201da177e4SLinus Torvalds entry->c.ops = &snd_mixart_proc_ops_BA1;
12211da177e4SLinus Torvalds entry->size = MIXART_BA1_SIZE;
12221da177e4SLinus Torvalds }
12231da177e4SLinus Torvalds }
12241da177e4SLinus Torvalds /* end of proc interface */
12251da177e4SLinus Torvalds
12261da177e4SLinus Torvalds
12271da177e4SLinus Torvalds /*
12281da177e4SLinus Torvalds * probe function - creates the card manager
12291da177e4SLinus Torvalds */
snd_mixart_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)1230e23e7a14SBill Pemberton static int snd_mixart_probe(struct pci_dev *pci,
12311da177e4SLinus Torvalds const struct pci_device_id *pci_id)
12321da177e4SLinus Torvalds {
12331da177e4SLinus Torvalds static int dev;
123467b48b88STakashi Iwai struct mixart_mgr *mgr;
12351da177e4SLinus Torvalds unsigned int i;
12361da177e4SLinus Torvalds int err;
12371da177e4SLinus Torvalds size_t size;
12381da177e4SLinus Torvalds
12391da177e4SLinus Torvalds /*
12401da177e4SLinus Torvalds */
12411da177e4SLinus Torvalds if (dev >= SNDRV_CARDS)
12421da177e4SLinus Torvalds return -ENODEV;
12431da177e4SLinus Torvalds if (! enable[dev]) {
12441da177e4SLinus Torvalds dev++;
12451da177e4SLinus Torvalds return -ENOENT;
12461da177e4SLinus Torvalds }
12471da177e4SLinus Torvalds
12481da177e4SLinus Torvalds /* enable PCI device */
124973debecfSTakashi Iwai err = pci_enable_device(pci);
125073debecfSTakashi Iwai if (err < 0)
12511da177e4SLinus Torvalds return err;
12521da177e4SLinus Torvalds pci_set_master(pci);
12531da177e4SLinus Torvalds
12541da177e4SLinus Torvalds /* check if we can restrict PCI DMA transfers to 32 bits */
1255412b979cSQuentin Lambert if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
12566414e35dSTakashi Iwai dev_err(&pci->dev,
12576414e35dSTakashi Iwai "architecture does not support 32bit PCI busmaster DMA\n");
12581da177e4SLinus Torvalds pci_disable_device(pci);
12591da177e4SLinus Torvalds return -ENXIO;
12601da177e4SLinus Torvalds }
12611da177e4SLinus Torvalds
12621da177e4SLinus Torvalds /*
12631da177e4SLinus Torvalds */
1264e560d8d8STakashi Iwai mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
12651da177e4SLinus Torvalds if (! mgr) {
12661da177e4SLinus Torvalds pci_disable_device(pci);
12671da177e4SLinus Torvalds return -ENOMEM;
12681da177e4SLinus Torvalds }
12691da177e4SLinus Torvalds
12701da177e4SLinus Torvalds mgr->pci = pci;
12711da177e4SLinus Torvalds mgr->irq = -1;
12721da177e4SLinus Torvalds
12731da177e4SLinus Torvalds /* resource assignment */
127473debecfSTakashi Iwai err = pci_request_regions(pci, CARD_NAME);
127573debecfSTakashi Iwai if (err < 0) {
12761da177e4SLinus Torvalds kfree(mgr);
12771da177e4SLinus Torvalds pci_disable_device(pci);
12781da177e4SLinus Torvalds return err;
12791da177e4SLinus Torvalds }
12801da177e4SLinus Torvalds for (i = 0; i < 2; i++) {
12811da177e4SLinus Torvalds mgr->mem[i].phys = pci_resource_start(pci, i);
12822f5ad54eSArjan van de Ven mgr->mem[i].virt = pci_ioremap_bar(pci, i);
1283f9ff161aSTakashi Iwai if (!mgr->mem[i].virt) {
12846414e35dSTakashi Iwai dev_err(&pci->dev, "unable to remap resource 0x%lx\n",
1285f9ff161aSTakashi Iwai mgr->mem[i].phys);
1286f9ff161aSTakashi Iwai snd_mixart_free(mgr);
1287f9ff161aSTakashi Iwai return -EBUSY;
1288f9ff161aSTakashi Iwai }
12891da177e4SLinus Torvalds }
12901da177e4SLinus Torvalds
12918d3a8b5cSTakashi Iwai if (request_threaded_irq(pci->irq, snd_mixart_interrupt,
12928d3a8b5cSTakashi Iwai snd_mixart_threaded_irq, IRQF_SHARED,
1293934c2b6dSTakashi Iwai KBUILD_MODNAME, mgr)) {
12946414e35dSTakashi Iwai dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
12951da177e4SLinus Torvalds snd_mixart_free(mgr);
12961da177e4SLinus Torvalds return -EBUSY;
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds mgr->irq = pci->irq;
12991da177e4SLinus Torvalds
13001da177e4SLinus Torvalds /* init mailbox */
13011da177e4SLinus Torvalds mgr->msg_fifo_readptr = 0;
13021da177e4SLinus Torvalds mgr->msg_fifo_writeptr = 0;
13031da177e4SLinus Torvalds
13048d3a8b5cSTakashi Iwai mutex_init(&mgr->lock);
13058d3a8b5cSTakashi Iwai mutex_init(&mgr->msg_lock);
13061da177e4SLinus Torvalds init_waitqueue_head(&mgr->msg_sleep);
13071da177e4SLinus Torvalds atomic_set(&mgr->msg_processed, 0);
13081da177e4SLinus Torvalds
13091da177e4SLinus Torvalds /* init setup mutex*/
131062932df8SIngo Molnar mutex_init(&mgr->setup_mutex);
13111da177e4SLinus Torvalds
13121da177e4SLinus Torvalds /* card assignment */
13131da177e4SLinus Torvalds mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */
13141da177e4SLinus Torvalds for (i = 0; i < mgr->num_cards; i++) {
131567b48b88STakashi Iwai struct snd_card *card;
13161da177e4SLinus Torvalds char tmpid[16];
13171da177e4SLinus Torvalds int idx;
13181da177e4SLinus Torvalds
13191da177e4SLinus Torvalds if (index[dev] < 0)
13201da177e4SLinus Torvalds idx = index[dev];
13211da177e4SLinus Torvalds else
13221da177e4SLinus Torvalds idx = index[dev] + i;
13231da177e4SLinus Torvalds snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
132460c5772bSTakashi Iwai err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
132560c5772bSTakashi Iwai 0, &card);
13261da177e4SLinus Torvalds
1327e58de7baSTakashi Iwai if (err < 0) {
13286414e35dSTakashi Iwai dev_err(&pci->dev, "cannot allocate the card %d\n", i);
13291da177e4SLinus Torvalds snd_mixart_free(mgr);
1330e58de7baSTakashi Iwai return err;
13311da177e4SLinus Torvalds }
13321da177e4SLinus Torvalds
13331da177e4SLinus Torvalds strcpy(card->driver, CARD_NAME);
1334c6e486ffSArnd Bergmann snprintf(card->shortname, sizeof(card->shortname),
1335c6e486ffSArnd Bergmann "Digigram miXart [PCM #%d]", i);
1336c6e486ffSArnd Bergmann snprintf(card->longname, sizeof(card->longname),
1337c6e486ffSArnd Bergmann "Digigram miXart at 0x%lx & 0x%lx, irq %i [PCM #%d]",
1338c6e486ffSArnd Bergmann mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq, i);
13391da177e4SLinus Torvalds
134073debecfSTakashi Iwai err = snd_mixart_create(mgr, card, i);
134173debecfSTakashi Iwai if (err < 0) {
134273f6a12eSJulia Lawall snd_card_free(card);
13431da177e4SLinus Torvalds snd_mixart_free(mgr);
13441da177e4SLinus Torvalds return err;
13451da177e4SLinus Torvalds }
13461da177e4SLinus Torvalds
13471da177e4SLinus Torvalds if(i==0) {
13481da177e4SLinus Torvalds /* init proc interface only for chip0 */
13491da177e4SLinus Torvalds snd_mixart_proc_init(mgr->chip[i]);
13501da177e4SLinus Torvalds }
13511da177e4SLinus Torvalds
135273debecfSTakashi Iwai err = snd_card_register(card);
135373debecfSTakashi Iwai if (err < 0) {
13541da177e4SLinus Torvalds snd_mixart_free(mgr);
13551da177e4SLinus Torvalds return err;
13561da177e4SLinus Torvalds }
13571da177e4SLinus Torvalds }
13581da177e4SLinus Torvalds
13591da177e4SLinus Torvalds /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */
13601da177e4SLinus Torvalds mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;
13611da177e4SLinus Torvalds
13621da177e4SLinus Torvalds /* create array of streaminfo */
136367b48b88STakashi Iwai size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
136467b48b88STakashi Iwai sizeof(struct mixart_flowinfo)) );
13656974f8adSTakashi Iwai if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
13661da177e4SLinus Torvalds size, &mgr->flowinfo) < 0) {
13671da177e4SLinus Torvalds snd_mixart_free(mgr);
13681da177e4SLinus Torvalds return -ENOMEM;
13691da177e4SLinus Torvalds }
13701da177e4SLinus Torvalds /* init streaminfo_array */
13711da177e4SLinus Torvalds memset(mgr->flowinfo.area, 0, size);
13721da177e4SLinus Torvalds
13731da177e4SLinus Torvalds /* create array of bufferinfo */
137467b48b88STakashi Iwai size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
137567b48b88STakashi Iwai sizeof(struct mixart_bufferinfo)) );
13766974f8adSTakashi Iwai if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
13771da177e4SLinus Torvalds size, &mgr->bufferinfo) < 0) {
13781da177e4SLinus Torvalds snd_mixart_free(mgr);
13791da177e4SLinus Torvalds return -ENOMEM;
13801da177e4SLinus Torvalds }
13811da177e4SLinus Torvalds /* init bufferinfo_array */
13821da177e4SLinus Torvalds memset(mgr->bufferinfo.area, 0, size);
13831da177e4SLinus Torvalds
13841da177e4SLinus Torvalds /* set up firmware */
13851da177e4SLinus Torvalds err = snd_mixart_setup_firmware(mgr);
13861da177e4SLinus Torvalds if (err < 0) {
13871da177e4SLinus Torvalds snd_mixart_free(mgr);
13881da177e4SLinus Torvalds return err;
13891da177e4SLinus Torvalds }
13901da177e4SLinus Torvalds
13911da177e4SLinus Torvalds pci_set_drvdata(pci, mgr);
13921da177e4SLinus Torvalds dev++;
13931da177e4SLinus Torvalds return 0;
13941da177e4SLinus Torvalds }
13951da177e4SLinus Torvalds
snd_mixart_remove(struct pci_dev * pci)1396e23e7a14SBill Pemberton static void snd_mixart_remove(struct pci_dev *pci)
13971da177e4SLinus Torvalds {
13981da177e4SLinus Torvalds snd_mixart_free(pci_get_drvdata(pci));
13991da177e4SLinus Torvalds }
14001da177e4SLinus Torvalds
1401e9f66d9bSTakashi Iwai static struct pci_driver mixart_driver = {
14023733e424STakashi Iwai .name = KBUILD_MODNAME,
14031da177e4SLinus Torvalds .id_table = snd_mixart_ids,
14041da177e4SLinus Torvalds .probe = snd_mixart_probe,
1405e23e7a14SBill Pemberton .remove = snd_mixart_remove,
14061da177e4SLinus Torvalds };
14071da177e4SLinus Torvalds
1408e9f66d9bSTakashi Iwai module_pci_driver(mixart_driver);
1409