18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Digigram miXart soundcards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * main file with alsa callbacks 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003 by Digigram <alsa@digigram.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <sound/core.h> 208c2ecf20Sopenharmony_ci#include <sound/initval.h> 218c2ecf20Sopenharmony_ci#include <sound/info.h> 228c2ecf20Sopenharmony_ci#include <sound/control.h> 238c2ecf20Sopenharmony_ci#include <sound/pcm.h> 248c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 258c2ecf20Sopenharmony_ci#include "mixart.h" 268c2ecf20Sopenharmony_ci#include "mixart_hwdep.h" 278c2ecf20Sopenharmony_ci#include "mixart_core.h" 288c2ecf20Sopenharmony_ci#include "mixart_mixer.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CARD_NAME "miXart" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Digigram <alsa@digigram.com>"); 338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Digigram " CARD_NAME); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 388c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 398c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); 438c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); 458c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_mixart_ids[] = { 528c2ecf20Sopenharmony_ci { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */ 538c2ecf20Sopenharmony_ci { 0, } 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_mixart_ids); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int mixart_set_pipe_state(struct mixart_mgr *mgr, 608c2ecf20Sopenharmony_ci struct mixart_pipe *pipe, int start) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct mixart_group_state_req group_state; 638c2ecf20Sopenharmony_ci struct mixart_group_state_resp group_state_resp; 648c2ecf20Sopenharmony_ci struct mixart_msg request; 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci u32 system_msg_uid; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci switch(pipe->status) { 698c2ecf20Sopenharmony_ci case PIPE_RUNNING: 708c2ecf20Sopenharmony_ci case PIPE_CLOCK_SET: 718c2ecf20Sopenharmony_ci if(start) return 0; /* already started */ 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case PIPE_STOPPED: 748c2ecf20Sopenharmony_ci if(!start) return 0; /* already stopped */ 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 788c2ecf20Sopenharmony_ci "error mixart_set_pipe_state called with wrong pipe->status!\n"); 798c2ecf20Sopenharmony_ci return -EINVAL; /* function called with wrong pipe status */ 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD; 878c2ecf20Sopenharmony_ci request.uid = (struct mixart_uid){0,0}; 888c2ecf20Sopenharmony_ci request.data = &system_msg_uid; 898c2ecf20Sopenharmony_ci request.size = sizeof(system_msg_uid); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid); 928c2ecf20Sopenharmony_ci if(err) { 938c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 948c2ecf20Sopenharmony_ci "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n"); 958c2ecf20Sopenharmony_ci return err; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* start or stop the pipe (1 pipe) */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci memset(&group_state, 0, sizeof(group_state)); 1018c2ecf20Sopenharmony_ci group_state.pipe_count = 1; 1028c2ecf20Sopenharmony_ci group_state.pipe_uid[0] = pipe->group_uid; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if(start) 1058c2ecf20Sopenharmony_ci request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET; 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci request.uid = pipe->group_uid; /*(struct mixart_uid){0,0};*/ 1108c2ecf20Sopenharmony_ci request.data = &group_state; 1118c2ecf20Sopenharmony_ci request.size = sizeof(group_state); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); 1148c2ecf20Sopenharmony_ci if (err < 0 || group_state_resp.txx_status != 0) { 1158c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 1168c2ecf20Sopenharmony_ci "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", 1178c2ecf20Sopenharmony_ci err, group_state_resp.txx_status); 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if(start) { 1228c2ecf20Sopenharmony_ci u32 stat = 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); 1278c2ecf20Sopenharmony_ci if (err < 0 || group_state_resp.txx_status != 0) { 1288c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 1298c2ecf20Sopenharmony_ci "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", 1308c2ecf20Sopenharmony_ci err, group_state_resp.txx_status); 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* in case of start send a synchro top */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; 1378c2ecf20Sopenharmony_ci request.uid = (struct mixart_uid){0,0}; 1388c2ecf20Sopenharmony_ci request.data = NULL; 1398c2ecf20Sopenharmony_ci request.size = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat); 1428c2ecf20Sopenharmony_ci if (err < 0 || stat != 0) { 1438c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 1448c2ecf20Sopenharmony_ci "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", 1458c2ecf20Sopenharmony_ci err, stat); 1468c2ecf20Sopenharmony_ci return -EINVAL; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pipe->status = PIPE_RUNNING; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci else /* !start */ 1528c2ecf20Sopenharmony_ci pipe->status = PIPE_STOPPED; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int mixart_set_clock(struct mixart_mgr *mgr, 1598c2ecf20Sopenharmony_ci struct mixart_pipe *pipe, unsigned int rate) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct mixart_msg request; 1628c2ecf20Sopenharmony_ci struct mixart_clock_properties clock_properties; 1638c2ecf20Sopenharmony_ci struct mixart_clock_properties_resp clock_prop_resp; 1648c2ecf20Sopenharmony_ci int err; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci switch(pipe->status) { 1678c2ecf20Sopenharmony_ci case PIPE_CLOCK_SET: 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case PIPE_RUNNING: 1708c2ecf20Sopenharmony_ci if(rate != 0) 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci fallthrough; 1738c2ecf20Sopenharmony_ci default: 1748c2ecf20Sopenharmony_ci if(rate == 0) 1758c2ecf20Sopenharmony_ci return 0; /* nothing to do */ 1768c2ecf20Sopenharmony_ci else { 1778c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 1788c2ecf20Sopenharmony_ci "error mixart_set_clock(%d) called with wrong pipe->status !\n", 1798c2ecf20Sopenharmony_ci rate); 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci memset(&clock_properties, 0, sizeof(clock_properties)); 1858c2ecf20Sopenharmony_ci clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK; 1868c2ecf20Sopenharmony_ci clock_properties.clock_mode = CM_STANDALONE; 1878c2ecf20Sopenharmony_ci clock_properties.frequency = rate; 1888c2ecf20Sopenharmony_ci clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */ 1898c2ecf20Sopenharmony_ci clock_properties.uid_caller[0] = pipe->group_uid; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dev_dbg(&mgr->pci->dev, "mixart_set_clock to %d kHz\n", rate); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci request.message_id = MSG_CLOCK_SET_PROPERTIES; 1948c2ecf20Sopenharmony_ci request.uid = mgr->uid_console_manager; 1958c2ecf20Sopenharmony_ci request.data = &clock_properties; 1968c2ecf20Sopenharmony_ci request.size = sizeof(clock_properties); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp); 1998c2ecf20Sopenharmony_ci if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) { 2008c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 2018c2ecf20Sopenharmony_ci "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", 2028c2ecf20Sopenharmony_ci err, clock_prop_resp.status, clock_prop_resp.clock_mode); 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if(rate) pipe->status = PIPE_CLOCK_SET; 2078c2ecf20Sopenharmony_ci else pipe->status = PIPE_RUNNING; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * Allocate or reference output pipe for analog IOs (pcmp0/1) 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistruct mixart_pipe * 2178c2ecf20Sopenharmony_cisnd_mixart_add_ref_pipe(struct snd_mixart *chip, int pcm_number, int capture, 2188c2ecf20Sopenharmony_ci int monitoring) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int stream_count; 2218c2ecf20Sopenharmony_ci struct mixart_pipe *pipe; 2228c2ecf20Sopenharmony_ci struct mixart_msg request; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if(capture) { 2258c2ecf20Sopenharmony_ci if (pcm_number == MIXART_PCM_ANALOG) { 2268c2ecf20Sopenharmony_ci pipe = &(chip->pipe_in_ana); /* analog inputs */ 2278c2ecf20Sopenharmony_ci } else { 2288c2ecf20Sopenharmony_ci pipe = &(chip->pipe_in_dig); /* digital inputs */ 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP; 2318c2ecf20Sopenharmony_ci stream_count = MIXART_CAPTURE_STREAMS; 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci if (pcm_number == MIXART_PCM_ANALOG) { 2348c2ecf20Sopenharmony_ci pipe = &(chip->pipe_out_ana); /* analog outputs */ 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci pipe = &(chip->pipe_out_dig); /* digital outputs */ 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci request.message_id = MSG_STREAM_ADD_INPUT_GROUP; 2398c2ecf20Sopenharmony_ci stream_count = MIXART_PLAYBACK_STREAMS; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* a new stream is opened and there are already all streams in use */ 2438c2ecf20Sopenharmony_ci if( (monitoring == 0) && (pipe->references >= stream_count) ) { 2448c2ecf20Sopenharmony_ci return NULL; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* pipe is not yet defined */ 2488c2ecf20Sopenharmony_ci if( pipe->status == PIPE_UNDEFINED ) { 2498c2ecf20Sopenharmony_ci int err, i; 2508c2ecf20Sopenharmony_ci struct { 2518c2ecf20Sopenharmony_ci struct mixart_streaming_group_req sgroup_req; 2528c2ecf20Sopenharmony_ci struct mixart_streaming_group sgroup_resp; 2538c2ecf20Sopenharmony_ci } *buf; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 2568c2ecf20Sopenharmony_ci "add_ref_pipe audio chip(%d) pcm(%d)\n", 2578c2ecf20Sopenharmony_ci chip->chip_idx, pcm_number); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci buf = kmalloc(sizeof(*buf), GFP_KERNEL); 2608c2ecf20Sopenharmony_ci if (!buf) 2618c2ecf20Sopenharmony_ci return NULL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci request.uid = (struct mixart_uid){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */ 2648c2ecf20Sopenharmony_ci request.data = &buf->sgroup_req; 2658c2ecf20Sopenharmony_ci request.size = sizeof(buf->sgroup_req); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req)); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci buf->sgroup_req.stream_count = stream_count; 2708c2ecf20Sopenharmony_ci buf->sgroup_req.channel_count = 2; 2718c2ecf20Sopenharmony_ci buf->sgroup_req.latency = 256; 2728c2ecf20Sopenharmony_ci buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci for (i=0; i<stream_count; i++) { 2758c2ecf20Sopenharmony_ci int j; 2768c2ecf20Sopenharmony_ci struct mixart_flowinfo *flowinfo; 2778c2ecf20Sopenharmony_ci struct mixart_bufferinfo *bufferinfo; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* we don't yet know the format, so config 16 bit pcm audio for instance */ 2808c2ecf20Sopenharmony_ci buf->sgroup_req.stream_info[i].size_max_byte_frame = 1024; 2818c2ecf20Sopenharmony_ci buf->sgroup_req.stream_info[i].size_max_sample_frame = 256; 2828c2ecf20Sopenharmony_ci buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* find the right bufferinfo_array */ 2858c2ecf20Sopenharmony_ci j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i; 2868c2ecf20Sopenharmony_ci if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */ 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci buf->sgroup_req.flow_entry[i] = j; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area; 2918c2ecf20Sopenharmony_ci flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(struct mixart_bufferinfo)); 2928c2ecf20Sopenharmony_ci flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */ 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; 2958c2ecf20Sopenharmony_ci bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */ 2968c2ecf20Sopenharmony_ci bufferinfo[j].available_length = 0; /* buffer is not yet allocated */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* construct the identifier of the stream buffer received in the interrupts ! */ 2998c2ecf20Sopenharmony_ci bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i; 3008c2ecf20Sopenharmony_ci if(capture) { 3018c2ecf20Sopenharmony_ci bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp); 3068c2ecf20Sopenharmony_ci if((err < 0) || (buf->sgroup_resp.status != 0)) { 3078c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 3088c2ecf20Sopenharmony_ci "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", 3098c2ecf20Sopenharmony_ci err, buf->sgroup_resp.status); 3108c2ecf20Sopenharmony_ci kfree(buf); 3118c2ecf20Sopenharmony_ci return NULL; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */ 3158c2ecf20Sopenharmony_ci pipe->stream_count = buf->sgroup_resp.stream_count; 3168c2ecf20Sopenharmony_ci /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */ 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci pipe->status = PIPE_STOPPED; 3198c2ecf20Sopenharmony_ci kfree(buf); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if(monitoring) pipe->monitoring = 1; 3238c2ecf20Sopenharmony_ci else pipe->references++; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return pipe; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ciint snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr, 3308c2ecf20Sopenharmony_ci struct mixart_pipe *pipe, int monitoring) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci int err = 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if(pipe->status == PIPE_UNDEFINED) 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if(monitoring) 3388c2ecf20Sopenharmony_ci pipe->monitoring = 0; 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci pipe->references--; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if((pipe->references <= 0) && (pipe->monitoring == 0)) { 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci struct mixart_msg request; 3458c2ecf20Sopenharmony_ci struct mixart_delete_group_resp delete_resp; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* release the clock */ 3488c2ecf20Sopenharmony_ci err = mixart_set_clock( mgr, pipe, 0); 3498c2ecf20Sopenharmony_ci if( err < 0 ) { 3508c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 3518c2ecf20Sopenharmony_ci "mixart_set_clock(0) return error!\n"); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* stop the pipe */ 3558c2ecf20Sopenharmony_ci err = mixart_set_pipe_state(mgr, pipe, 0); 3568c2ecf20Sopenharmony_ci if( err < 0 ) { 3578c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, "error stopping pipe!\n"); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci request.message_id = MSG_STREAM_DELETE_GROUP; 3618c2ecf20Sopenharmony_ci request.uid = (struct mixart_uid){0,0}; 3628c2ecf20Sopenharmony_ci request.data = &pipe->group_uid; /* the streaming group ! */ 3638c2ecf20Sopenharmony_ci request.size = sizeof(pipe->group_uid); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* delete the pipe */ 3668c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp); 3678c2ecf20Sopenharmony_ci if ((err < 0) || (delete_resp.status != 0)) { 3688c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 3698c2ecf20Sopenharmony_ci "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", 3708c2ecf20Sopenharmony_ci err, delete_resp.status); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci pipe->group_uid = (struct mixart_uid){0,0}; 3748c2ecf20Sopenharmony_ci pipe->stream_count = 0; 3758c2ecf20Sopenharmony_ci pipe->status = PIPE_UNDEFINED; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return err; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int mixart_set_stream_state(struct mixart_stream *stream, int start) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct snd_mixart *chip; 3848c2ecf20Sopenharmony_ci struct mixart_stream_state_req stream_state_req; 3858c2ecf20Sopenharmony_ci struct mixart_msg request; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if(!stream->substream) 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci memset(&stream_state_req, 0, sizeof(stream_state_req)); 3918c2ecf20Sopenharmony_ci stream_state_req.stream_count = 1; 3928c2ecf20Sopenharmony_ci stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid; 3938c2ecf20Sopenharmony_ci stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3968c2ecf20Sopenharmony_ci request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET; 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci request.uid = (struct mixart_uid){0,0}; 4018c2ecf20Sopenharmony_ci request.data = &stream_state_req; 4028c2ecf20Sopenharmony_ci request.size = sizeof(stream_state_req); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci stream->abs_period_elapsed = 0; /* reset stream pos */ 4058c2ecf20Sopenharmony_ci stream->buf_periods = 0; 4068c2ecf20Sopenharmony_ci stream->buf_period_frag = 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci chip = snd_pcm_substream_chip(stream->substream); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return snd_mixart_send_msg_nonblock(chip->mgr, &request); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* 4148c2ecf20Sopenharmony_ci * Trigger callback 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct mixart_stream *stream = subs->runtime->private_data; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci switch (cmd) { 4228c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_START\n"); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* START_STREAM */ 4278c2ecf20Sopenharmony_ci if( mixart_set_stream_state(stream, 1) ) 4288c2ecf20Sopenharmony_ci return -EINVAL; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_RUNNING; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* STOP_STREAM */ 4368c2ecf20Sopenharmony_ci if( mixart_set_stream_state(stream, 0) ) 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_OPEN; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_TRIGGER_STOP\n"); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 4468c2ecf20Sopenharmony_ci /* TODO */ 4478c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_PAUSE; 4488c2ecf20Sopenharmony_ci dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_PUSH\n"); 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 4518c2ecf20Sopenharmony_ci /* TODO */ 4528c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_RUNNING; 4538c2ecf20Sopenharmony_ci dev_dbg(subs->pcm->card->dev, "SNDRV_PCM_PAUSE_RELEASE\n"); 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci default: 4568c2ecf20Sopenharmony_ci return -EINVAL; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int mixart_sync_nonblock_events(struct mixart_mgr *mgr) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + HZ; 4648c2ecf20Sopenharmony_ci while (atomic_read(&mgr->msg_processed) > 0) { 4658c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 4668c2ecf20Sopenharmony_ci dev_err(&mgr->pci->dev, 4678c2ecf20Sopenharmony_ci "mixart: cannot process nonblock events!\n"); 4688c2ecf20Sopenharmony_ci return -EBUSY; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* 4768c2ecf20Sopenharmony_ci * prepare callback for all pcms 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_cistatic int snd_mixart_prepare(struct snd_pcm_substream *subs) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct snd_mixart *chip = snd_pcm_substream_chip(subs); 4818c2ecf20Sopenharmony_ci struct mixart_stream *stream = subs->runtime->private_data; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "snd_mixart_prepare\n"); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci mixart_sync_nonblock_events(chip->mgr); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* only the first stream can choose the sample rate */ 4908c2ecf20Sopenharmony_ci /* the further opened streams will be limited to its frequency (see open) */ 4918c2ecf20Sopenharmony_ci if(chip->mgr->ref_count_rate == 1) 4928c2ecf20Sopenharmony_ci chip->mgr->sample_rate = subs->runtime->rate; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* set the clock only once (first stream) on the same pipe */ 4958c2ecf20Sopenharmony_ci if(stream->pipe->references == 1) { 4968c2ecf20Sopenharmony_ci if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) ) 4978c2ecf20Sopenharmony_ci return -EINVAL; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t format) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci int err; 5078c2ecf20Sopenharmony_ci struct snd_mixart *chip; 5088c2ecf20Sopenharmony_ci struct mixart_msg request; 5098c2ecf20Sopenharmony_ci struct mixart_stream_param_desc stream_param; 5108c2ecf20Sopenharmony_ci struct mixart_return_uid resp; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci chip = snd_pcm_substream_chip(stream->substream); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci memset(&stream_param, 0, sizeof(stream_param)); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci stream_param.coding_type = CT_LINEAR; 5178c2ecf20Sopenharmony_ci stream_param.number_of_channel = stream->channels; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci stream_param.sampling_freq = chip->mgr->sample_rate; 5208c2ecf20Sopenharmony_ci if(stream_param.sampling_freq == 0) 5218c2ecf20Sopenharmony_ci stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */ 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci switch(format){ 5248c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_U8: 5258c2ecf20Sopenharmony_ci stream_param.sample_type = ST_INTEGER_8; 5268c2ecf20Sopenharmony_ci stream_param.sample_size = 8; 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 5298c2ecf20Sopenharmony_ci stream_param.sample_type = ST_INTEGER_16LE; 5308c2ecf20Sopenharmony_ci stream_param.sample_size = 16; 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 5338c2ecf20Sopenharmony_ci stream_param.sample_type = ST_INTEGER_16BE; 5348c2ecf20Sopenharmony_ci stream_param.sample_size = 16; 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3LE: 5378c2ecf20Sopenharmony_ci stream_param.sample_type = ST_INTEGER_24LE; 5388c2ecf20Sopenharmony_ci stream_param.sample_size = 24; 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3BE: 5418c2ecf20Sopenharmony_ci stream_param.sample_type = ST_INTEGER_24BE; 5428c2ecf20Sopenharmony_ci stream_param.sample_size = 24; 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_FLOAT_LE: 5458c2ecf20Sopenharmony_ci stream_param.sample_type = ST_FLOATING_POINT_32LE; 5468c2ecf20Sopenharmony_ci stream_param.sample_size = 32; 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_FLOAT_BE: 5498c2ecf20Sopenharmony_ci stream_param.sample_type = ST_FLOATING_POINT_32BE; 5508c2ecf20Sopenharmony_ci stream_param.sample_size = 32; 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci default: 5538c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 5548c2ecf20Sopenharmony_ci "error mixart_set_format() : unknown format\n"); 5558c2ecf20Sopenharmony_ci return -EINVAL; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 5598c2ecf20Sopenharmony_ci "set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n", 5608c2ecf20Sopenharmony_ci stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* TODO: what else to configure ? */ 5638c2ecf20Sopenharmony_ci /* stream_param.samples_per_frame = 2; */ 5648c2ecf20Sopenharmony_ci /* stream_param.bytes_per_frame = 4; */ 5658c2ecf20Sopenharmony_ci /* stream_param.bytes_per_sample = 2; */ 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci stream_param.pipe_count = 1; /* set to 1 */ 5688c2ecf20Sopenharmony_ci stream_param.stream_count = 1; /* set to 1 */ 5698c2ecf20Sopenharmony_ci stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid; 5708c2ecf20Sopenharmony_ci stream_param.stream_desc[0].stream_idx = stream->substream->number; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM; 5738c2ecf20Sopenharmony_ci request.uid = (struct mixart_uid){0,0}; 5748c2ecf20Sopenharmony_ci request.data = &stream_param; 5758c2ecf20Sopenharmony_ci request.size = sizeof(stream_param); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); 5788c2ecf20Sopenharmony_ci if((err < 0) || resp.error_code) { 5798c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 5808c2ecf20Sopenharmony_ci "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", 5818c2ecf20Sopenharmony_ci err, resp.error_code); 5828c2ecf20Sopenharmony_ci return -EINVAL; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/* 5898c2ecf20Sopenharmony_ci * HW_PARAMS callback for all pcms 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_cistatic int snd_mixart_hw_params(struct snd_pcm_substream *subs, 5928c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct snd_mixart *chip = snd_pcm_substream_chip(subs); 5958c2ecf20Sopenharmony_ci struct mixart_mgr *mgr = chip->mgr; 5968c2ecf20Sopenharmony_ci struct mixart_stream *stream = subs->runtime->private_data; 5978c2ecf20Sopenharmony_ci snd_pcm_format_t format; 5988c2ecf20Sopenharmony_ci int err; 5998c2ecf20Sopenharmony_ci int channels; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* set up channels */ 6028c2ecf20Sopenharmony_ci channels = params_channels(hw); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* set up format for the stream */ 6058c2ecf20Sopenharmony_ci format = params_format(hw); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci mutex_lock(&mgr->setup_mutex); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* update the stream levels */ 6108c2ecf20Sopenharmony_ci if( stream->pcm_number <= MIXART_PCM_DIGITAL ) { 6118c2ecf20Sopenharmony_ci int is_aes = stream->pcm_number > MIXART_PCM_ANALOG; 6128c2ecf20Sopenharmony_ci if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) 6138c2ecf20Sopenharmony_ci mixart_update_playback_stream_level(chip, is_aes, subs->number); 6148c2ecf20Sopenharmony_ci else 6158c2ecf20Sopenharmony_ci mixart_update_capture_stream_level( chip, is_aes); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci stream->channels = channels; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* set the format to the board */ 6218c2ecf20Sopenharmony_ci err = mixart_set_format(stream, format); 6228c2ecf20Sopenharmony_ci if(err < 0) { 6238c2ecf20Sopenharmony_ci mutex_unlock(&mgr->setup_mutex); 6248c2ecf20Sopenharmony_ci return err; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (subs->runtime->buffer_changed) { 6288c2ecf20Sopenharmony_ci struct mixart_bufferinfo *bufferinfo; 6298c2ecf20Sopenharmony_ci int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number; 6308c2ecf20Sopenharmony_ci if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) { 6318c2ecf20Sopenharmony_ci i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */ 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; 6358c2ecf20Sopenharmony_ci bufferinfo[i].buffer_address = subs->runtime->dma_addr; 6368c2ecf20Sopenharmony_ci bufferinfo[i].available_length = subs->runtime->dma_bytes; 6378c2ecf20Sopenharmony_ci /* bufferinfo[i].buffer_id is already defined */ 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 6408c2ecf20Sopenharmony_ci "snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", 6418c2ecf20Sopenharmony_ci i, bufferinfo[i].buffer_address, 6428c2ecf20Sopenharmony_ci bufferinfo[i].available_length, 6438c2ecf20Sopenharmony_ci subs->number); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci mutex_unlock(&mgr->setup_mutex); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int snd_mixart_hw_free(struct snd_pcm_substream *subs) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct snd_mixart *chip = snd_pcm_substream_chip(subs); 6538c2ecf20Sopenharmony_ci mixart_sync_nonblock_events(chip->mgr); 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/* 6608c2ecf20Sopenharmony_ci * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_mixart_analog_caps = 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 6658c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 6668c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE), 6678c2ecf20Sopenharmony_ci .formats = ( SNDRV_PCM_FMTBIT_U8 | 6688c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | 6698c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | 6708c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), 6718c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 6728c2ecf20Sopenharmony_ci .rate_min = 8000, 6738c2ecf20Sopenharmony_ci .rate_max = 48000, 6748c2ecf20Sopenharmony_ci .channels_min = 1, 6758c2ecf20Sopenharmony_ci .channels_max = 2, 6768c2ecf20Sopenharmony_ci .buffer_bytes_max = (32*1024), 6778c2ecf20Sopenharmony_ci .period_bytes_min = 256, /* 256 frames U8 mono*/ 6788c2ecf20Sopenharmony_ci .period_bytes_max = (16*1024), 6798c2ecf20Sopenharmony_ci .periods_min = 2, 6808c2ecf20Sopenharmony_ci .periods_max = (32*1024/256), 6818c2ecf20Sopenharmony_ci}; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_mixart_digital_caps = 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 6868c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 6878c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE), 6888c2ecf20Sopenharmony_ci .formats = ( SNDRV_PCM_FMTBIT_U8 | 6898c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | 6908c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | 6918c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), 6928c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 6938c2ecf20Sopenharmony_ci .rate_min = 32000, 6948c2ecf20Sopenharmony_ci .rate_max = 48000, 6958c2ecf20Sopenharmony_ci .channels_min = 1, 6968c2ecf20Sopenharmony_ci .channels_max = 2, 6978c2ecf20Sopenharmony_ci .buffer_bytes_max = (32*1024), 6988c2ecf20Sopenharmony_ci .period_bytes_min = 256, /* 256 frames U8 mono*/ 6998c2ecf20Sopenharmony_ci .period_bytes_max = (16*1024), 7008c2ecf20Sopenharmony_ci .periods_min = 2, 7018c2ecf20Sopenharmony_ci .periods_max = (32*1024/256), 7028c2ecf20Sopenharmony_ci}; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic int snd_mixart_playback_open(struct snd_pcm_substream *subs) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct snd_mixart *chip = snd_pcm_substream_chip(subs); 7088c2ecf20Sopenharmony_ci struct mixart_mgr *mgr = chip->mgr; 7098c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 7108c2ecf20Sopenharmony_ci struct snd_pcm *pcm = subs->pcm; 7118c2ecf20Sopenharmony_ci struct mixart_stream *stream; 7128c2ecf20Sopenharmony_ci struct mixart_pipe *pipe; 7138c2ecf20Sopenharmony_ci int err = 0; 7148c2ecf20Sopenharmony_ci int pcm_number; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci mutex_lock(&mgr->setup_mutex); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if ( pcm == chip->pcm ) { 7198c2ecf20Sopenharmony_ci pcm_number = MIXART_PCM_ANALOG; 7208c2ecf20Sopenharmony_ci runtime->hw = snd_mixart_analog_caps; 7218c2ecf20Sopenharmony_ci } else { 7228c2ecf20Sopenharmony_ci snd_BUG_ON(pcm != chip->pcm_dig); 7238c2ecf20Sopenharmony_ci pcm_number = MIXART_PCM_DIGITAL; 7248c2ecf20Sopenharmony_ci runtime->hw = snd_mixart_digital_caps; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 7278c2ecf20Sopenharmony_ci "snd_mixart_playback_open C%d/P%d/Sub%d\n", 7288c2ecf20Sopenharmony_ci chip->chip_idx, pcm_number, subs->number); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* get stream info */ 7318c2ecf20Sopenharmony_ci stream = &(chip->playback_stream[pcm_number][subs->number]); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (stream->status != MIXART_STREAM_STATUS_FREE){ 7348c2ecf20Sopenharmony_ci /* streams in use */ 7358c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 7368c2ecf20Sopenharmony_ci "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", 7378c2ecf20Sopenharmony_ci chip->chip_idx, pcm_number, subs->number); 7388c2ecf20Sopenharmony_ci err = -EBUSY; 7398c2ecf20Sopenharmony_ci goto _exit_open; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* get pipe pointer (out pipe) */ 7438c2ecf20Sopenharmony_ci pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (pipe == NULL) { 7468c2ecf20Sopenharmony_ci err = -EINVAL; 7478c2ecf20Sopenharmony_ci goto _exit_open; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* start the pipe if necessary */ 7518c2ecf20Sopenharmony_ci err = mixart_set_pipe_state(chip->mgr, pipe, 1); 7528c2ecf20Sopenharmony_ci if( err < 0 ) { 7538c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "error starting pipe!\n"); 7548c2ecf20Sopenharmony_ci snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); 7558c2ecf20Sopenharmony_ci err = -EINVAL; 7568c2ecf20Sopenharmony_ci goto _exit_open; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci stream->pipe = pipe; 7608c2ecf20Sopenharmony_ci stream->pcm_number = pcm_number; 7618c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_OPEN; 7628c2ecf20Sopenharmony_ci stream->substream = subs; 7638c2ecf20Sopenharmony_ci stream->channels = 0; /* not configured yet */ 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci runtime->private_data = stream; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 7688c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* if a sample rate is already used, another stream cannot change */ 7718c2ecf20Sopenharmony_ci if(mgr->ref_count_rate++) { 7728c2ecf20Sopenharmony_ci if(mgr->sample_rate) { 7738c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci _exit_open: 7788c2ecf20Sopenharmony_ci mutex_unlock(&mgr->setup_mutex); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci return err; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic int snd_mixart_capture_open(struct snd_pcm_substream *subs) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct snd_mixart *chip = snd_pcm_substream_chip(subs); 7878c2ecf20Sopenharmony_ci struct mixart_mgr *mgr = chip->mgr; 7888c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 7898c2ecf20Sopenharmony_ci struct snd_pcm *pcm = subs->pcm; 7908c2ecf20Sopenharmony_ci struct mixart_stream *stream; 7918c2ecf20Sopenharmony_ci struct mixart_pipe *pipe; 7928c2ecf20Sopenharmony_ci int err = 0; 7938c2ecf20Sopenharmony_ci int pcm_number; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci mutex_lock(&mgr->setup_mutex); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if ( pcm == chip->pcm ) { 7988c2ecf20Sopenharmony_ci pcm_number = MIXART_PCM_ANALOG; 7998c2ecf20Sopenharmony_ci runtime->hw = snd_mixart_analog_caps; 8008c2ecf20Sopenharmony_ci } else { 8018c2ecf20Sopenharmony_ci snd_BUG_ON(pcm != chip->pcm_dig); 8028c2ecf20Sopenharmony_ci pcm_number = MIXART_PCM_DIGITAL; 8038c2ecf20Sopenharmony_ci runtime->hw = snd_mixart_digital_caps; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci runtime->hw.channels_min = 2; /* for instance, no mono */ 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "snd_mixart_capture_open C%d/P%d/Sub%d\n", 8098c2ecf20Sopenharmony_ci chip->chip_idx, pcm_number, subs->number); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* get stream info */ 8128c2ecf20Sopenharmony_ci stream = &(chip->capture_stream[pcm_number]); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (stream->status != MIXART_STREAM_STATUS_FREE){ 8158c2ecf20Sopenharmony_ci /* streams in use */ 8168c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 8178c2ecf20Sopenharmony_ci "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", 8188c2ecf20Sopenharmony_ci chip->chip_idx, pcm_number, subs->number); 8198c2ecf20Sopenharmony_ci err = -EBUSY; 8208c2ecf20Sopenharmony_ci goto _exit_open; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* get pipe pointer (in pipe) */ 8248c2ecf20Sopenharmony_ci pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (pipe == NULL) { 8278c2ecf20Sopenharmony_ci err = -EINVAL; 8288c2ecf20Sopenharmony_ci goto _exit_open; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* start the pipe if necessary */ 8328c2ecf20Sopenharmony_ci err = mixart_set_pipe_state(chip->mgr, pipe, 1); 8338c2ecf20Sopenharmony_ci if( err < 0 ) { 8348c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "error starting pipe!\n"); 8358c2ecf20Sopenharmony_ci snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); 8368c2ecf20Sopenharmony_ci err = -EINVAL; 8378c2ecf20Sopenharmony_ci goto _exit_open; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci stream->pipe = pipe; 8418c2ecf20Sopenharmony_ci stream->pcm_number = pcm_number; 8428c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_OPEN; 8438c2ecf20Sopenharmony_ci stream->substream = subs; 8448c2ecf20Sopenharmony_ci stream->channels = 0; /* not configured yet */ 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci runtime->private_data = stream; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 8498c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* if a sample rate is already used, another stream cannot change */ 8528c2ecf20Sopenharmony_ci if(mgr->ref_count_rate++) { 8538c2ecf20Sopenharmony_ci if(mgr->sample_rate) { 8548c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci _exit_open: 8598c2ecf20Sopenharmony_ci mutex_unlock(&mgr->setup_mutex); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return err; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int snd_mixart_close(struct snd_pcm_substream *subs) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct snd_mixart *chip = snd_pcm_substream_chip(subs); 8698c2ecf20Sopenharmony_ci struct mixart_mgr *mgr = chip->mgr; 8708c2ecf20Sopenharmony_ci struct mixart_stream *stream = subs->runtime->private_data; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci mutex_lock(&mgr->setup_mutex); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "snd_mixart_close C%d/P%d/Sub%d\n", 8758c2ecf20Sopenharmony_ci chip->chip_idx, stream->pcm_number, subs->number); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* sample rate released */ 8788c2ecf20Sopenharmony_ci if(--mgr->ref_count_rate == 0) { 8798c2ecf20Sopenharmony_ci mgr->sample_rate = 0; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* delete pipe */ 8838c2ecf20Sopenharmony_ci if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) { 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 8868c2ecf20Sopenharmony_ci "error snd_mixart_kill_ref_pipe C%dP%d\n", 8878c2ecf20Sopenharmony_ci chip->chip_idx, stream->pcm_number); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci stream->pipe = NULL; 8918c2ecf20Sopenharmony_ci stream->status = MIXART_STREAM_STATUS_FREE; 8928c2ecf20Sopenharmony_ci stream->substream = NULL; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci mutex_unlock(&mgr->setup_mutex); 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *subs) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 9028c2ecf20Sopenharmony_ci struct mixart_stream *stream = runtime->private_data; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_mixart_playback_ops = { 9108c2ecf20Sopenharmony_ci .open = snd_mixart_playback_open, 9118c2ecf20Sopenharmony_ci .close = snd_mixart_close, 9128c2ecf20Sopenharmony_ci .prepare = snd_mixart_prepare, 9138c2ecf20Sopenharmony_ci .hw_params = snd_mixart_hw_params, 9148c2ecf20Sopenharmony_ci .hw_free = snd_mixart_hw_free, 9158c2ecf20Sopenharmony_ci .trigger = snd_mixart_trigger, 9168c2ecf20Sopenharmony_ci .pointer = snd_mixart_stream_pointer, 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_mixart_capture_ops = { 9208c2ecf20Sopenharmony_ci .open = snd_mixart_capture_open, 9218c2ecf20Sopenharmony_ci .close = snd_mixart_close, 9228c2ecf20Sopenharmony_ci .prepare = snd_mixart_prepare, 9238c2ecf20Sopenharmony_ci .hw_params = snd_mixart_hw_params, 9248c2ecf20Sopenharmony_ci .hw_free = snd_mixart_hw_free, 9258c2ecf20Sopenharmony_ci .trigger = snd_mixart_trigger, 9268c2ecf20Sopenharmony_ci .pointer = snd_mixart_stream_pointer, 9278c2ecf20Sopenharmony_ci}; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci#if 0 9328c2ecf20Sopenharmony_ci struct snd_pcm_substream *subs; 9338c2ecf20Sopenharmony_ci int stream; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) { 9368c2ecf20Sopenharmony_ci int idx = 0; 9378c2ecf20Sopenharmony_ci for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++) 9388c2ecf20Sopenharmony_ci /* set up the unique device id with the chip index */ 9398c2ecf20Sopenharmony_ci subs->dma_device.id = subs->pcm->device << 16 | 9408c2ecf20Sopenharmony_ci subs->stream << 8 | (subs->number + 1) | 9418c2ecf20Sopenharmony_ci (chip->chip_idx + 1) << 24; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci#endif 9448c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 9458c2ecf20Sopenharmony_ci &chip->mgr->pci->dev, 9468c2ecf20Sopenharmony_ci 32*1024, 32*1024); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/* 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_cistatic int snd_mixart_pcm_analog(struct snd_mixart *chip) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci int err; 9548c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 9558c2ecf20Sopenharmony_ci char name[32]; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci sprintf(name, "miXart analog %d", chip->chip_idx); 9588c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG, 9598c2ecf20Sopenharmony_ci MIXART_PLAYBACK_STREAMS, 9608c2ecf20Sopenharmony_ci MIXART_CAPTURE_STREAMS, &pcm)) < 0) { 9618c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 9628c2ecf20Sopenharmony_ci "cannot create the analog pcm %d\n", chip->chip_idx); 9638c2ecf20Sopenharmony_ci return err; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci pcm->private_data = chip; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); 9698c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci pcm->info_flags = 0; 9728c2ecf20Sopenharmony_ci pcm->nonatomic = true; 9738c2ecf20Sopenharmony_ci strcpy(pcm->name, name); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci preallocate_buffers(chip, pcm); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci chip->pcm = pcm; 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci/* 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_cistatic int snd_mixart_pcm_digital(struct snd_mixart *chip) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci int err; 9878c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 9888c2ecf20Sopenharmony_ci char name[32]; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci sprintf(name, "miXart AES/EBU %d", chip->chip_idx); 9918c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL, 9928c2ecf20Sopenharmony_ci MIXART_PLAYBACK_STREAMS, 9938c2ecf20Sopenharmony_ci MIXART_CAPTURE_STREAMS, &pcm)) < 0) { 9948c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 9958c2ecf20Sopenharmony_ci "cannot create the digital pcm %d\n", chip->chip_idx); 9968c2ecf20Sopenharmony_ci return err; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci pcm->private_data = chip; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); 10028c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci pcm->info_flags = 0; 10058c2ecf20Sopenharmony_ci pcm->nonatomic = true; 10068c2ecf20Sopenharmony_ci strcpy(pcm->name, name); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci preallocate_buffers(chip, pcm); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci chip->pcm_dig = pcm; 10118c2ecf20Sopenharmony_ci return 0; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic int snd_mixart_chip_free(struct snd_mixart *chip) 10158c2ecf20Sopenharmony_ci{ 10168c2ecf20Sopenharmony_ci kfree(chip); 10178c2ecf20Sopenharmony_ci return 0; 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic int snd_mixart_chip_dev_free(struct snd_device *device) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct snd_mixart *chip = device->device_data; 10238c2ecf20Sopenharmony_ci return snd_mixart_chip_free(chip); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci/* 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_cistatic int snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci int err; 10328c2ecf20Sopenharmony_ci struct snd_mixart *chip; 10338c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 10348c2ecf20Sopenharmony_ci .dev_free = snd_mixart_chip_dev_free, 10358c2ecf20Sopenharmony_ci }; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 10388c2ecf20Sopenharmony_ci if (!chip) 10398c2ecf20Sopenharmony_ci return -ENOMEM; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci chip->card = card; 10428c2ecf20Sopenharmony_ci chip->chip_idx = idx; 10438c2ecf20Sopenharmony_ci chip->mgr = mgr; 10448c2ecf20Sopenharmony_ci card->sync_irq = mgr->irq; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 10478c2ecf20Sopenharmony_ci snd_mixart_chip_free(chip); 10488c2ecf20Sopenharmony_ci return err; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci mgr->chip[idx] = chip; 10528c2ecf20Sopenharmony_ci return 0; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ciint snd_mixart_create_pcm(struct snd_mixart* chip) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci int err; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci err = snd_mixart_pcm_analog(chip); 10608c2ecf20Sopenharmony_ci if (err < 0) 10618c2ecf20Sopenharmony_ci return err; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci err = snd_mixart_pcm_digital(chip); 10668c2ecf20Sopenharmony_ci if (err < 0) 10678c2ecf20Sopenharmony_ci return err; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci return err; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci/* 10748c2ecf20Sopenharmony_ci * release all the cards assigned to a manager instance 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic int snd_mixart_free(struct mixart_mgr *mgr) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci unsigned int i; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci for (i = 0; i < mgr->num_cards; i++) { 10818c2ecf20Sopenharmony_ci if (mgr->chip[i]) 10828c2ecf20Sopenharmony_ci snd_card_free(mgr->chip[i]->card); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* stop mailbox */ 10868c2ecf20Sopenharmony_ci snd_mixart_exit_mailbox(mgr); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* release irq */ 10898c2ecf20Sopenharmony_ci if (mgr->irq >= 0) 10908c2ecf20Sopenharmony_ci free_irq(mgr->irq, mgr); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* reset board if some firmware was loaded */ 10938c2ecf20Sopenharmony_ci if(mgr->dsp_loaded) { 10948c2ecf20Sopenharmony_ci snd_mixart_reset_board(mgr); 10958c2ecf20Sopenharmony_ci dev_dbg(&mgr->pci->dev, "reset miXart !\n"); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* release the i/o ports */ 10998c2ecf20Sopenharmony_ci for (i = 0; i < 2; ++i) 11008c2ecf20Sopenharmony_ci iounmap(mgr->mem[i].virt); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci pci_release_regions(mgr->pci); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* free flowarray */ 11058c2ecf20Sopenharmony_ci if(mgr->flowinfo.area) { 11068c2ecf20Sopenharmony_ci snd_dma_free_pages(&mgr->flowinfo); 11078c2ecf20Sopenharmony_ci mgr->flowinfo.area = NULL; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci /* free bufferarray */ 11108c2ecf20Sopenharmony_ci if(mgr->bufferinfo.area) { 11118c2ecf20Sopenharmony_ci snd_dma_free_pages(&mgr->bufferinfo); 11128c2ecf20Sopenharmony_ci mgr->bufferinfo.area = NULL; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci pci_disable_device(mgr->pci); 11168c2ecf20Sopenharmony_ci kfree(mgr); 11178c2ecf20Sopenharmony_ci return 0; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci/* 11218c2ecf20Sopenharmony_ci * proc interface 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci/* 11258c2ecf20Sopenharmony_ci mixart_BA0 proc interface for BAR 0 - read callback 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_cistatic ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry, 11288c2ecf20Sopenharmony_ci void *file_private_data, 11298c2ecf20Sopenharmony_ci struct file *file, char __user *buf, 11308c2ecf20Sopenharmony_ci size_t count, loff_t pos) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct mixart_mgr *mgr = entry->private_data; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ 11358c2ecf20Sopenharmony_ci if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count)) 11368c2ecf20Sopenharmony_ci return -EFAULT; 11378c2ecf20Sopenharmony_ci return count; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci/* 11418c2ecf20Sopenharmony_ci mixart_BA1 proc interface for BAR 1 - read callback 11428c2ecf20Sopenharmony_ci */ 11438c2ecf20Sopenharmony_cistatic ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry, 11448c2ecf20Sopenharmony_ci void *file_private_data, 11458c2ecf20Sopenharmony_ci struct file *file, char __user *buf, 11468c2ecf20Sopenharmony_ci size_t count, loff_t pos) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci struct mixart_mgr *mgr = entry->private_data; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ 11518c2ecf20Sopenharmony_ci if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count)) 11528c2ecf20Sopenharmony_ci return -EFAULT; 11538c2ecf20Sopenharmony_ci return count; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic const struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = { 11578c2ecf20Sopenharmony_ci .read = snd_mixart_BA0_read, 11588c2ecf20Sopenharmony_ci}; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic const struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = { 11618c2ecf20Sopenharmony_ci .read = snd_mixart_BA1_read, 11628c2ecf20Sopenharmony_ci}; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic void snd_mixart_proc_read(struct snd_info_entry *entry, 11668c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct snd_mixart *chip = entry->private_data; 11698c2ecf20Sopenharmony_ci u32 ref; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* stats available when embedded OS is running */ 11748c2ecf20Sopenharmony_ci if (chip->mgr->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) { 11758c2ecf20Sopenharmony_ci snd_iprintf(buffer, "- hardware -\n"); 11768c2ecf20Sopenharmony_ci switch (chip->mgr->board_type ) { 11778c2ecf20Sopenharmony_ci case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break; 11788c2ecf20Sopenharmony_ci case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break; 11798c2ecf20Sopenharmony_ci case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break; 11808c2ecf20Sopenharmony_ci default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci snd_iprintf(buffer, "- system load -\n"); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* get perf reference */ 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET)); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (ref) { 11908c2ecf20Sopenharmony_ci u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref; 11918c2ecf20Sopenharmony_ci u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref; 11928c2ecf20Sopenharmony_ci u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\tstreaming : %d\n", streaming); 11958c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\tmailbox : %d\n", mailbox); 11968c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\tinterrupts handling : %d\n\n", interr); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci } /* endif elf loaded */ 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic void snd_mixart_proc_init(struct snd_mixart *chip) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* text interface to read perf and temp meters */ 12068c2ecf20Sopenharmony_ci snd_card_ro_proc_new(chip->card, "board_info", chip, 12078c2ecf20Sopenharmony_ci snd_mixart_proc_read); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) { 12108c2ecf20Sopenharmony_ci entry->content = SNDRV_INFO_CONTENT_DATA; 12118c2ecf20Sopenharmony_ci entry->private_data = chip->mgr; 12128c2ecf20Sopenharmony_ci entry->c.ops = &snd_mixart_proc_ops_BA0; 12138c2ecf20Sopenharmony_ci entry->size = MIXART_BA0_SIZE; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) { 12168c2ecf20Sopenharmony_ci entry->content = SNDRV_INFO_CONTENT_DATA; 12178c2ecf20Sopenharmony_ci entry->private_data = chip->mgr; 12188c2ecf20Sopenharmony_ci entry->c.ops = &snd_mixart_proc_ops_BA1; 12198c2ecf20Sopenharmony_ci entry->size = MIXART_BA1_SIZE; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci/* end of proc interface */ 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci/* 12268c2ecf20Sopenharmony_ci * probe function - creates the card manager 12278c2ecf20Sopenharmony_ci */ 12288c2ecf20Sopenharmony_cistatic int snd_mixart_probe(struct pci_dev *pci, 12298c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci static int dev; 12328c2ecf20Sopenharmony_ci struct mixart_mgr *mgr; 12338c2ecf20Sopenharmony_ci unsigned int i; 12348c2ecf20Sopenharmony_ci int err; 12358c2ecf20Sopenharmony_ci size_t size; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* 12388c2ecf20Sopenharmony_ci */ 12398c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 12408c2ecf20Sopenharmony_ci return -ENODEV; 12418c2ecf20Sopenharmony_ci if (! enable[dev]) { 12428c2ecf20Sopenharmony_ci dev++; 12438c2ecf20Sopenharmony_ci return -ENOENT; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* enable PCI device */ 12478c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 12488c2ecf20Sopenharmony_ci return err; 12498c2ecf20Sopenharmony_ci pci_set_master(pci); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci /* check if we can restrict PCI DMA transfers to 32 bits */ 12528c2ecf20Sopenharmony_ci if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { 12538c2ecf20Sopenharmony_ci dev_err(&pci->dev, 12548c2ecf20Sopenharmony_ci "architecture does not support 32bit PCI busmaster DMA\n"); 12558c2ecf20Sopenharmony_ci pci_disable_device(pci); 12568c2ecf20Sopenharmony_ci return -ENXIO; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* 12608c2ecf20Sopenharmony_ci */ 12618c2ecf20Sopenharmony_ci mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); 12628c2ecf20Sopenharmony_ci if (! mgr) { 12638c2ecf20Sopenharmony_ci pci_disable_device(pci); 12648c2ecf20Sopenharmony_ci return -ENOMEM; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci mgr->pci = pci; 12688c2ecf20Sopenharmony_ci mgr->irq = -1; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci /* resource assignment */ 12718c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { 12728c2ecf20Sopenharmony_ci kfree(mgr); 12738c2ecf20Sopenharmony_ci pci_disable_device(pci); 12748c2ecf20Sopenharmony_ci return err; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 12778c2ecf20Sopenharmony_ci mgr->mem[i].phys = pci_resource_start(pci, i); 12788c2ecf20Sopenharmony_ci mgr->mem[i].virt = pci_ioremap_bar(pci, i); 12798c2ecf20Sopenharmony_ci if (!mgr->mem[i].virt) { 12808c2ecf20Sopenharmony_ci dev_err(&pci->dev, "unable to remap resource 0x%lx\n", 12818c2ecf20Sopenharmony_ci mgr->mem[i].phys); 12828c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 12838c2ecf20Sopenharmony_ci return -EBUSY; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (request_threaded_irq(pci->irq, snd_mixart_interrupt, 12888c2ecf20Sopenharmony_ci snd_mixart_threaded_irq, IRQF_SHARED, 12898c2ecf20Sopenharmony_ci KBUILD_MODNAME, mgr)) { 12908c2ecf20Sopenharmony_ci dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq); 12918c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 12928c2ecf20Sopenharmony_ci return -EBUSY; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci mgr->irq = pci->irq; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* init mailbox */ 12978c2ecf20Sopenharmony_ci mgr->msg_fifo_readptr = 0; 12988c2ecf20Sopenharmony_ci mgr->msg_fifo_writeptr = 0; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci mutex_init(&mgr->lock); 13018c2ecf20Sopenharmony_ci mutex_init(&mgr->msg_lock); 13028c2ecf20Sopenharmony_ci init_waitqueue_head(&mgr->msg_sleep); 13038c2ecf20Sopenharmony_ci atomic_set(&mgr->msg_processed, 0); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci /* init setup mutex*/ 13068c2ecf20Sopenharmony_ci mutex_init(&mgr->setup_mutex); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* card assignment */ 13098c2ecf20Sopenharmony_ci mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */ 13108c2ecf20Sopenharmony_ci for (i = 0; i < mgr->num_cards; i++) { 13118c2ecf20Sopenharmony_ci struct snd_card *card; 13128c2ecf20Sopenharmony_ci char tmpid[16]; 13138c2ecf20Sopenharmony_ci int idx; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (index[dev] < 0) 13168c2ecf20Sopenharmony_ci idx = index[dev]; 13178c2ecf20Sopenharmony_ci else 13188c2ecf20Sopenharmony_ci idx = index[dev] + i; 13198c2ecf20Sopenharmony_ci snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i); 13208c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE, 13218c2ecf20Sopenharmony_ci 0, &card); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (err < 0) { 13248c2ecf20Sopenharmony_ci dev_err(&pci->dev, "cannot allocate the card %d\n", i); 13258c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 13268c2ecf20Sopenharmony_ci return err; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci strcpy(card->driver, CARD_NAME); 13308c2ecf20Sopenharmony_ci snprintf(card->shortname, sizeof(card->shortname), 13318c2ecf20Sopenharmony_ci "Digigram miXart [PCM #%d]", i); 13328c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 13338c2ecf20Sopenharmony_ci "Digigram miXart at 0x%lx & 0x%lx, irq %i [PCM #%d]", 13348c2ecf20Sopenharmony_ci mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq, i); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if ((err = snd_mixart_create(mgr, card, i)) < 0) { 13378c2ecf20Sopenharmony_ci snd_card_free(card); 13388c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 13398c2ecf20Sopenharmony_ci return err; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if(i==0) { 13438c2ecf20Sopenharmony_ci /* init proc interface only for chip0 */ 13448c2ecf20Sopenharmony_ci snd_mixart_proc_init(mgr->chip[i]); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 13488c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 13498c2ecf20Sopenharmony_ci return err; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */ 13548c2ecf20Sopenharmony_ci mgr->board_type = MIXART_DAUGHTER_TYPE_NONE; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* create array of streaminfo */ 13578c2ecf20Sopenharmony_ci size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * 13588c2ecf20Sopenharmony_ci sizeof(struct mixart_flowinfo)) ); 13598c2ecf20Sopenharmony_ci if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 13608c2ecf20Sopenharmony_ci size, &mgr->flowinfo) < 0) { 13618c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 13628c2ecf20Sopenharmony_ci return -ENOMEM; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci /* init streaminfo_array */ 13658c2ecf20Sopenharmony_ci memset(mgr->flowinfo.area, 0, size); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* create array of bufferinfo */ 13688c2ecf20Sopenharmony_ci size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * 13698c2ecf20Sopenharmony_ci sizeof(struct mixart_bufferinfo)) ); 13708c2ecf20Sopenharmony_ci if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 13718c2ecf20Sopenharmony_ci size, &mgr->bufferinfo) < 0) { 13728c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 13738c2ecf20Sopenharmony_ci return -ENOMEM; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci /* init bufferinfo_array */ 13768c2ecf20Sopenharmony_ci memset(mgr->bufferinfo.area, 0, size); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* set up firmware */ 13798c2ecf20Sopenharmony_ci err = snd_mixart_setup_firmware(mgr); 13808c2ecf20Sopenharmony_ci if (err < 0) { 13818c2ecf20Sopenharmony_ci snd_mixart_free(mgr); 13828c2ecf20Sopenharmony_ci return err; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci pci_set_drvdata(pci, mgr); 13868c2ecf20Sopenharmony_ci dev++; 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void snd_mixart_remove(struct pci_dev *pci) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci snd_mixart_free(pci_get_drvdata(pci)); 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_cistatic struct pci_driver mixart_driver = { 13968c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 13978c2ecf20Sopenharmony_ci .id_table = snd_mixart_ids, 13988c2ecf20Sopenharmony_ci .probe = snd_mixart_probe, 13998c2ecf20Sopenharmony_ci .remove = snd_mixart_remove, 14008c2ecf20Sopenharmony_ci}; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cimodule_pci_driver(mixart_driver); 1403