| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Driver for Digigram pcxhr compatible soundcards | 
|  | 3 | * | 
|  | 4 | * hwdep device manager | 
|  | 5 | * | 
|  | 6 | * Copyright (c) 2004 by Digigram <alsa@digigram.com> | 
|  | 7 | * | 
|  | 8 | *   This program is free software; you can redistribute it and/or modify | 
|  | 9 | *   it under the terms of the GNU General Public License as published by | 
|  | 10 | *   the Free Software Foundation; either version 2 of the License, or | 
|  | 11 | *   (at your option) any later version. | 
|  | 12 | * | 
|  | 13 | *   This program is distributed in the hope that it will be useful, | 
|  | 14 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 15 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 16 | *   GNU General Public License for more details. | 
|  | 17 | * | 
|  | 18 | *   You should have received a copy of the GNU General Public License | 
|  | 19 | *   along with this program; if not, write to the Free Software | 
|  | 20 | *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
|  | 21 | */ | 
|  | 22 |  | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 23 | #include <linux/interrupt.h> | 
|  | 24 | #include <linux/vmalloc.h> | 
|  | 25 | #include <linux/firmware.h> | 
|  | 26 | #include <linux/pci.h> | 
| Paul Gortmaker | da155d5 | 2011-07-15 12:38:28 -0400 | [diff] [blame] | 27 | #include <linux/module.h> | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 28 | #include <asm/io.h> | 
|  | 29 | #include <sound/core.h> | 
|  | 30 | #include <sound/hwdep.h> | 
|  | 31 | #include "pcxhr.h" | 
|  | 32 | #include "pcxhr_mixer.h" | 
|  | 33 | #include "pcxhr_hwdep.h" | 
|  | 34 | #include "pcxhr_core.h" | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 35 | #include "pcxhr_mix22.h" | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 36 |  | 
|  | 37 |  | 
|  | 38 | #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) | 
|  | 39 | #if !defined(CONFIG_USE_PCXHRLOADER) && !defined(CONFIG_SND_PCXHR) /* built-in kernel */ | 
|  | 40 | #define SND_PCXHR_FW_LOADER	/* use the standard firmware loader */ | 
|  | 41 | #endif | 
|  | 42 | #endif | 
|  | 43 |  | 
|  | 44 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 45 | static int pcxhr_sub_init(struct pcxhr_mgr *mgr); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 46 | /* | 
|  | 47 | * get basic information and init pcxhr card | 
|  | 48 | */ | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 49 | static int pcxhr_init_board(struct pcxhr_mgr *mgr) | 
|  | 50 | { | 
|  | 51 | int err; | 
|  | 52 | struct pcxhr_rmh rmh; | 
|  | 53 | int card_streams; | 
|  | 54 |  | 
|  | 55 | /* calc the number of all streams used */ | 
|  | 56 | if (mgr->mono_capture) | 
|  | 57 | card_streams = mgr->capture_chips * 2; | 
|  | 58 | else | 
|  | 59 | card_streams = mgr->capture_chips; | 
|  | 60 | card_streams += mgr->playback_chips * PCXHR_PLAYBACK_STREAMS; | 
|  | 61 |  | 
|  | 62 | /* enable interrupts */ | 
|  | 63 | pcxhr_enable_dsp(mgr); | 
|  | 64 |  | 
|  | 65 | pcxhr_init_rmh(&rmh, CMD_SUPPORTED); | 
|  | 66 | err = pcxhr_send_msg(mgr, &rmh); | 
|  | 67 | if (err) | 
|  | 68 | return err; | 
|  | 69 | /* test 8 or 12 phys out */ | 
| Takashi Iwai | da3cec3 | 2008-08-08 17:12:14 +0200 | [diff] [blame] | 70 | if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2) | 
|  | 71 | return -EINVAL; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 72 | /* test 8 or 2 phys in */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 73 | if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) < | 
| Takashi Iwai | da3cec3 | 2008-08-08 17:12:14 +0200 | [diff] [blame] | 74 | mgr->capture_chips * 2) | 
|  | 75 | return -EINVAL; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 76 | /* test max nb substream per board */ | 
| Takashi Iwai | da3cec3 | 2008-08-08 17:12:14 +0200 | [diff] [blame] | 77 | if ((rmh.stat[1] & 0x5F) < card_streams) | 
|  | 78 | return -EINVAL; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 79 | /* test max nb substream per pipe */ | 
| Takashi Iwai | da3cec3 | 2008-08-08 17:12:14 +0200 | [diff] [blame] | 80 | if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS) | 
|  | 81 | return -EINVAL; | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 82 | snd_printdd("supported formats : playback=%x capture=%x\n", | 
|  | 83 | rmh.stat[2], rmh.stat[3]); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 84 |  | 
|  | 85 | pcxhr_init_rmh(&rmh, CMD_VERSION); | 
|  | 86 | /* firmware num for DSP */ | 
|  | 87 | rmh.cmd[0] |= mgr->firmware_num; | 
|  | 88 | /* transfer granularity in samples (should be multiple of 48) */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 89 | rmh.cmd[1] = (1<<23) + mgr->granularity; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 90 | rmh.cmd_len = 2; | 
|  | 91 | err = pcxhr_send_msg(mgr, &rmh); | 
|  | 92 | if (err) | 
|  | 93 | return err; | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 94 | snd_printdd("PCXHR DSP version is %d.%d.%d\n", (rmh.stat[0]>>16)&0xff, | 
|  | 95 | (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 96 | mgr->dsp_version = rmh.stat[0]; | 
|  | 97 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 98 | if (mgr->is_hr_stereo) | 
|  | 99 | err = hr222_sub_init(mgr); | 
|  | 100 | else | 
|  | 101 | err = pcxhr_sub_init(mgr); | 
|  | 102 | return err; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | static int pcxhr_sub_init(struct pcxhr_mgr *mgr) | 
|  | 106 | { | 
|  | 107 | int err; | 
|  | 108 | struct pcxhr_rmh rmh; | 
|  | 109 |  | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 110 | /* get options */ | 
|  | 111 | pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); | 
|  | 112 | rmh.cmd[0] |= IO_NUM_REG_STATUS; | 
|  | 113 | rmh.cmd[1]  = REG_STATUS_OPTIONS; | 
|  | 114 | rmh.cmd_len = 2; | 
|  | 115 | err = pcxhr_send_msg(mgr, &rmh); | 
|  | 116 | if (err) | 
|  | 117 | return err; | 
|  | 118 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 119 | if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) == | 
|  | 120 | REG_STATUS_OPT_ANALOG_BOARD) | 
|  | 121 | mgr->board_has_analog = 1;	/* analog addon board found */ | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 122 |  | 
|  | 123 | /* unmute inputs */ | 
|  | 124 | err = pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS, | 
|  | 125 | REG_CONT_UNMUTE_INPUTS, NULL); | 
|  | 126 | if (err) | 
|  | 127 | return err; | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 128 | /* unmute outputs (a write to IO_NUM_REG_MUTE_OUT mutes!) */ | 
|  | 129 | pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 130 | rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT; | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 131 | if (DSP_EXT_CMD_SET(mgr)) { | 
|  | 132 | rmh.cmd[1]  = 1;	/* unmute digital plugs */ | 
|  | 133 | rmh.cmd_len = 2; | 
|  | 134 | } | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 135 | err = pcxhr_send_msg(mgr, &rmh); | 
|  | 136 | return err; | 
|  | 137 | } | 
|  | 138 |  | 
|  | 139 | void pcxhr_reset_board(struct pcxhr_mgr *mgr) | 
|  | 140 | { | 
|  | 141 | struct pcxhr_rmh rmh; | 
|  | 142 |  | 
|  | 143 | if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) { | 
|  | 144 | /* mute outputs */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 145 | if (!mgr->is_hr_stereo) { | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 146 | /* a read to IO_NUM_REG_MUTE_OUT register unmutes! */ | 
|  | 147 | pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); | 
|  | 148 | rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT; | 
|  | 149 | pcxhr_send_msg(mgr, &rmh); | 
|  | 150 | /* mute inputs */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 151 | pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS, | 
|  | 152 | 0, NULL); | 
|  | 153 | } | 
|  | 154 | /* stereo cards mute with reset of dsp */ | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 155 | } | 
|  | 156 | /* reset pcxhr dsp */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 157 | if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX)) | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 158 | pcxhr_reset_dsp(mgr); | 
|  | 159 | /* reset second xilinx */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 160 | if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_XLX_COM_INDEX)) { | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 161 | pcxhr_reset_xilinx_com(mgr); | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 162 | mgr->dsp_loaded = 1; | 
|  | 163 | } | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 164 | return; | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 |  | 
|  | 168 | /* | 
|  | 169 | *  allocate a playback/capture pipe (pcmp0/pcmc0) | 
|  | 170 | */ | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 171 | static int pcxhr_dsp_allocate_pipe(struct pcxhr_mgr *mgr, | 
|  | 172 | struct pcxhr_pipe *pipe, | 
|  | 173 | int is_capture, int pin) | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 174 | { | 
|  | 175 | int stream_count, audio_count; | 
|  | 176 | int err; | 
|  | 177 | struct pcxhr_rmh rmh; | 
|  | 178 |  | 
|  | 179 | if (is_capture) { | 
|  | 180 | stream_count = 1; | 
|  | 181 | if (mgr->mono_capture) | 
|  | 182 | audio_count = 1; | 
|  | 183 | else | 
|  | 184 | audio_count = 2; | 
|  | 185 | } else { | 
|  | 186 | stream_count = PCXHR_PLAYBACK_STREAMS; | 
|  | 187 | audio_count = 2;	/* always stereo */ | 
|  | 188 | } | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 189 | snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n", | 
|  | 190 | pin, is_capture ? 'c' : 'p'); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 191 | pipe->is_capture = is_capture; | 
|  | 192 | pipe->first_audio = pin; | 
|  | 193 | /* define pipe (P_PCM_ONLY_MASK (0x020000) is not necessary) */ | 
|  | 194 | pcxhr_init_rmh(&rmh, CMD_RES_PIPE); | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 195 | pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin, | 
|  | 196 | audio_count, stream_count); | 
|  | 197 | rmh.cmd[1] |= 0x020000; /* add P_PCM_ONLY_MASK */ | 
|  | 198 | if (DSP_EXT_CMD_SET(mgr)) { | 
|  | 199 | /* add channel mask to command */ | 
|  | 200 | rmh.cmd[rmh.cmd_len++] = (audio_count == 1) ? 0x01 : 0x03; | 
|  | 201 | } | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 202 | err = pcxhr_send_msg(mgr, &rmh); | 
|  | 203 | if (err < 0) { | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 204 | snd_printk(KERN_ERR "error pipe allocation " | 
|  | 205 | "(CMD_RES_PIPE) err=%x!\n", err); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 206 | return err; | 
|  | 207 | } | 
|  | 208 | pipe->status = PCXHR_PIPE_DEFINED; | 
|  | 209 |  | 
|  | 210 | return 0; | 
|  | 211 | } | 
|  | 212 |  | 
|  | 213 | /* | 
|  | 214 | *  free playback/capture pipe (pcmp0/pcmc0) | 
|  | 215 | */ | 
|  | 216 | #if 0 | 
|  | 217 | static int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe) | 
|  | 218 | { | 
|  | 219 | struct pcxhr_rmh rmh; | 
|  | 220 | int capture_mask = 0; | 
|  | 221 | int playback_mask = 0; | 
|  | 222 | int err = 0; | 
|  | 223 |  | 
|  | 224 | if (pipe->is_capture) | 
|  | 225 | capture_mask  = (1 << pipe->first_audio); | 
|  | 226 | else | 
|  | 227 | playback_mask = (1 << pipe->first_audio); | 
|  | 228 |  | 
|  | 229 | /* stop one pipe */ | 
|  | 230 | err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0); | 
|  | 231 | if (err < 0) | 
|  | 232 | snd_printk(KERN_ERR "error stopping pipe!\n"); | 
|  | 233 | /* release the pipe */ | 
|  | 234 | pcxhr_init_rmh(&rmh, CMD_FREE_PIPE); | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 235 | pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio, | 
|  | 236 | 0, 0); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 237 | err = pcxhr_send_msg(mgr, &rmh); | 
|  | 238 | if (err < 0) | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 239 | snd_printk(KERN_ERR "error pipe release " | 
|  | 240 | "(CMD_FREE_PIPE) err(%x)\n", err); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 241 | pipe->status = PCXHR_PIPE_UNDEFINED; | 
|  | 242 | return err; | 
|  | 243 | } | 
|  | 244 | #endif | 
|  | 245 |  | 
|  | 246 |  | 
|  | 247 | static int pcxhr_config_pipes(struct pcxhr_mgr *mgr) | 
|  | 248 | { | 
|  | 249 | int err, i, j; | 
|  | 250 | struct snd_pcxhr *chip; | 
|  | 251 | struct pcxhr_pipe *pipe; | 
|  | 252 |  | 
|  | 253 | /* allocate the pipes on the dsp */ | 
|  | 254 | for (i = 0; i < mgr->num_cards; i++) { | 
|  | 255 | chip = mgr->chip[i]; | 
|  | 256 | if (chip->nb_streams_play) { | 
|  | 257 | pipe = &chip->playback_pipe; | 
|  | 258 | err = pcxhr_dsp_allocate_pipe( mgr, pipe, 0, i*2); | 
|  | 259 | if (err) | 
|  | 260 | return err; | 
|  | 261 | for(j = 0; j < chip->nb_streams_play; j++) | 
|  | 262 | chip->playback_stream[j].pipe = pipe; | 
|  | 263 | } | 
|  | 264 | for (j = 0; j < chip->nb_streams_capt; j++) { | 
|  | 265 | pipe = &chip->capture_pipe[j]; | 
|  | 266 | err = pcxhr_dsp_allocate_pipe(mgr, pipe, 1, i*2 + j); | 
|  | 267 | if (err) | 
|  | 268 | return err; | 
|  | 269 | chip->capture_stream[j].pipe = pipe; | 
|  | 270 | } | 
|  | 271 | } | 
|  | 272 | return 0; | 
|  | 273 | } | 
|  | 274 |  | 
|  | 275 | static int pcxhr_start_pipes(struct pcxhr_mgr *mgr) | 
|  | 276 | { | 
|  | 277 | int i, j; | 
|  | 278 | struct snd_pcxhr *chip; | 
|  | 279 | int playback_mask = 0; | 
|  | 280 | int capture_mask = 0; | 
|  | 281 |  | 
|  | 282 | /* start all the pipes on the dsp */ | 
|  | 283 | for (i = 0; i < mgr->num_cards; i++) { | 
|  | 284 | chip = mgr->chip[i]; | 
|  | 285 | if (chip->nb_streams_play) | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 286 | playback_mask |= 1 << chip->playback_pipe.first_audio; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 287 | for (j = 0; j < chip->nb_streams_capt; j++) | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 288 | capture_mask |= 1 << chip->capture_pipe[j].first_audio; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 289 | } | 
|  | 290 | return pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1); | 
|  | 291 | } | 
|  | 292 |  | 
|  | 293 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 294 | static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, | 
|  | 295 | const struct firmware *dsp) | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 296 | { | 
|  | 297 | int err, card_index; | 
|  | 298 |  | 
|  | 299 | snd_printdd("loading dsp [%d] size = %Zd\n", index, dsp->size); | 
|  | 300 |  | 
|  | 301 | switch (index) { | 
|  | 302 | case PCXHR_FIRMWARE_XLX_INT_INDEX: | 
|  | 303 | pcxhr_reset_xilinx_com(mgr); | 
|  | 304 | return pcxhr_load_xilinx_binary(mgr, dsp, 0); | 
|  | 305 |  | 
|  | 306 | case PCXHR_FIRMWARE_XLX_COM_INDEX: | 
|  | 307 | pcxhr_reset_xilinx_com(mgr); | 
|  | 308 | return pcxhr_load_xilinx_binary(mgr, dsp, 1); | 
|  | 309 |  | 
|  | 310 | case PCXHR_FIRMWARE_DSP_EPRM_INDEX: | 
|  | 311 | pcxhr_reset_dsp(mgr); | 
|  | 312 | return pcxhr_load_eeprom_binary(mgr, dsp); | 
|  | 313 |  | 
|  | 314 | case PCXHR_FIRMWARE_DSP_BOOT_INDEX: | 
|  | 315 | return pcxhr_load_boot_binary(mgr, dsp); | 
|  | 316 |  | 
|  | 317 | case PCXHR_FIRMWARE_DSP_MAIN_INDEX: | 
|  | 318 | err = pcxhr_load_dsp_binary(mgr, dsp); | 
|  | 319 | if (err) | 
|  | 320 | return err; | 
|  | 321 | break;	/* continue with first init */ | 
|  | 322 | default: | 
|  | 323 | snd_printk(KERN_ERR "wrong file index\n"); | 
|  | 324 | return -EFAULT; | 
|  | 325 | } /* end of switch file index*/ | 
|  | 326 |  | 
|  | 327 | /* first communication with embedded */ | 
|  | 328 | err = pcxhr_init_board(mgr); | 
|  | 329 | if (err < 0) { | 
|  | 330 | snd_printk(KERN_ERR "pcxhr could not be set up\n"); | 
|  | 331 | return err; | 
|  | 332 | } | 
|  | 333 | err = pcxhr_config_pipes(mgr); | 
|  | 334 | if (err < 0) { | 
|  | 335 | snd_printk(KERN_ERR "pcxhr pipes could not be set up\n"); | 
|  | 336 | return err; | 
|  | 337 | } | 
|  | 338 | /* create devices and mixer in accordance with HW options*/ | 
|  | 339 | for (card_index = 0; card_index < mgr->num_cards; card_index++) { | 
|  | 340 | struct snd_pcxhr *chip = mgr->chip[card_index]; | 
|  | 341 |  | 
|  | 342 | if ((err = pcxhr_create_pcm(chip)) < 0) | 
|  | 343 | return err; | 
|  | 344 |  | 
|  | 345 | if (card_index == 0) { | 
|  | 346 | if ((err = pcxhr_create_mixer(chip->mgr)) < 0) | 
|  | 347 | return err; | 
|  | 348 | } | 
|  | 349 | if ((err = snd_card_register(chip->card)) < 0) | 
|  | 350 | return err; | 
|  | 351 | } | 
|  | 352 | err = pcxhr_start_pipes(mgr); | 
|  | 353 | if (err < 0) { | 
|  | 354 | snd_printk(KERN_ERR "pcxhr pipes could not be started\n"); | 
|  | 355 | return err; | 
|  | 356 | } | 
|  | 357 | snd_printdd("pcxhr firmware downloaded and successfully set up\n"); | 
|  | 358 |  | 
|  | 359 | return 0; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | /* | 
|  | 363 | * fw loader entry | 
|  | 364 | */ | 
|  | 365 | #ifdef SND_PCXHR_FW_LOADER | 
|  | 366 |  | 
|  | 367 | int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) | 
|  | 368 | { | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 369 | static char *fw_files[][5] = { | 
| Markus Bollinger | ade9b2f | 2008-12-01 15:31:06 +0100 | [diff] [blame] | 370 | [0] = { "xlxint.dat", "xlxc882hr.dat", | 
|  | 371 | "dspe882.e56", "dspb882hr.b56", "dspd882.d56" }, | 
|  | 372 | [1] = { "xlxint.dat", "xlxc882e.dat", | 
|  | 373 | "dspe882.e56", "dspb882e.b56", "dspd882.d56" }, | 
|  | 374 | [2] = { "xlxint.dat", "xlxc1222hr.dat", | 
|  | 375 | "dspe882.e56", "dspb1222hr.b56", "dspd1222.d56" }, | 
|  | 376 | [3] = { "xlxint.dat", "xlxc1222e.dat", | 
|  | 377 | "dspe882.e56", "dspb1222e.b56", "dspd1222.d56" }, | 
|  | 378 | [4] = { NULL, "xlxc222.dat", | 
|  | 379 | "dspe924.e56", "dspb924.b56", "dspd222.d56" }, | 
|  | 380 | [5] = { NULL, "xlxc924.dat", | 
|  | 381 | "dspe924.e56", "dspb924.b56", "dspd222.d56" }, | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 382 | }; | 
|  | 383 | char path[32]; | 
|  | 384 |  | 
|  | 385 | const struct firmware *fw_entry; | 
|  | 386 | int i, err; | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 387 | int fw_set = mgr->fw_file_set; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 388 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 389 | for (i = 0; i < 5; i++) { | 
|  | 390 | if (!fw_files[fw_set][i]) | 
|  | 391 | continue; | 
|  | 392 | sprintf(path, "pcxhr/%s", fw_files[fw_set][i]); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 393 | if (request_firmware(&fw_entry, path, &mgr->pci->dev)) { | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 394 | snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n", | 
|  | 395 | path); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 396 | return -ENOENT; | 
|  | 397 | } | 
|  | 398 | /* fake hwdep dsp record */ | 
|  | 399 | err = pcxhr_dsp_load(mgr, i, fw_entry); | 
|  | 400 | release_firmware(fw_entry); | 
|  | 401 | if (err < 0) | 
|  | 402 | return err; | 
|  | 403 | mgr->dsp_loaded |= 1 << i; | 
|  | 404 | } | 
|  | 405 | return 0; | 
|  | 406 | } | 
|  | 407 |  | 
| Markus Bollinger | ade9b2f | 2008-12-01 15:31:06 +0100 | [diff] [blame] | 408 | MODULE_FIRMWARE("pcxhr/xlxint.dat"); | 
|  | 409 | MODULE_FIRMWARE("pcxhr/xlxc882hr.dat"); | 
|  | 410 | MODULE_FIRMWARE("pcxhr/xlxc882e.dat"); | 
|  | 411 | MODULE_FIRMWARE("pcxhr/dspe882.e56"); | 
|  | 412 | MODULE_FIRMWARE("pcxhr/dspb882hr.b56"); | 
|  | 413 | MODULE_FIRMWARE("pcxhr/dspb882e.b56"); | 
|  | 414 | MODULE_FIRMWARE("pcxhr/dspd882.d56"); | 
| Clemens Ladisch | 7e0af29 | 2007-05-03 17:59:54 +0200 | [diff] [blame] | 415 |  | 
| Markus Bollinger | ade9b2f | 2008-12-01 15:31:06 +0100 | [diff] [blame] | 416 | MODULE_FIRMWARE("pcxhr/xlxc1222hr.dat"); | 
|  | 417 | MODULE_FIRMWARE("pcxhr/xlxc1222e.dat"); | 
|  | 418 | MODULE_FIRMWARE("pcxhr/dspb1222hr.b56"); | 
|  | 419 | MODULE_FIRMWARE("pcxhr/dspb1222e.b56"); | 
|  | 420 | MODULE_FIRMWARE("pcxhr/dspd1222.d56"); | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 421 |  | 
| Markus Bollinger | ade9b2f | 2008-12-01 15:31:06 +0100 | [diff] [blame] | 422 | MODULE_FIRMWARE("pcxhr/xlxc222.dat"); | 
|  | 423 | MODULE_FIRMWARE("pcxhr/xlxc924.dat"); | 
|  | 424 | MODULE_FIRMWARE("pcxhr/dspe924.e56"); | 
|  | 425 | MODULE_FIRMWARE("pcxhr/dspb924.b56"); | 
|  | 426 | MODULE_FIRMWARE("pcxhr/dspd222.d56"); | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 427 |  | 
|  | 428 |  | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 429 | #else /* old style firmware loading */ | 
|  | 430 |  | 
|  | 431 | /* pcxhr hwdep interface id string */ | 
|  | 432 | #define PCXHR_HWDEP_ID       "pcxhr loader" | 
|  | 433 |  | 
|  | 434 |  | 
|  | 435 | static int pcxhr_hwdep_dsp_status(struct snd_hwdep *hw, | 
|  | 436 | struct snd_hwdep_dsp_status *info) | 
|  | 437 | { | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 438 | struct pcxhr_mgr *mgr = hw->private_data; | 
|  | 439 | sprintf(info->id, "pcxhr%d", mgr->fw_file_set); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 440 | info->num_dsps = PCXHR_FIRMWARE_FILES_MAX_INDEX; | 
|  | 441 |  | 
|  | 442 | if (hw->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) | 
|  | 443 | info->chip_ready = 1; | 
|  | 444 |  | 
|  | 445 | info->version = PCXHR_DRIVER_VERSION; | 
|  | 446 | return 0; | 
|  | 447 | } | 
|  | 448 |  | 
|  | 449 | static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw, | 
|  | 450 | struct snd_hwdep_dsp_image *dsp) | 
|  | 451 | { | 
|  | 452 | struct pcxhr_mgr *mgr = hw->private_data; | 
|  | 453 | int err; | 
|  | 454 | struct firmware fw; | 
|  | 455 |  | 
|  | 456 | fw.size = dsp->length; | 
|  | 457 | fw.data = vmalloc(fw.size); | 
|  | 458 | if (! fw.data) { | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 459 | snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image " | 
|  | 460 | "(%lu bytes)\n", (unsigned long)fw.size); | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 461 | return -ENOMEM; | 
|  | 462 | } | 
| David Howells | 67852dc | 2008-07-08 17:45:58 +0100 | [diff] [blame] | 463 | if (copy_from_user((void *)fw.data, dsp->image, dsp->length)) { | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 464 | vfree(fw.data); | 
|  | 465 | return -EFAULT; | 
|  | 466 | } | 
|  | 467 | err = pcxhr_dsp_load(mgr, dsp->index, &fw); | 
|  | 468 | vfree(fw.data); | 
|  | 469 | if (err < 0) | 
|  | 470 | return err; | 
|  | 471 | mgr->dsp_loaded |= 1 << dsp->index; | 
|  | 472 | return 0; | 
|  | 473 | } | 
|  | 474 |  | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 475 | int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) | 
|  | 476 | { | 
|  | 477 | int err; | 
|  | 478 | struct snd_hwdep *hw; | 
|  | 479 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 480 | /* only create hwdep interface for first cardX | 
|  | 481 | * (see "index" module parameter) | 
|  | 482 | */ | 
|  | 483 | err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw); | 
|  | 484 | if (err < 0) | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 485 | return err; | 
|  | 486 |  | 
|  | 487 | hw->iface = SNDRV_HWDEP_IFACE_PCXHR; | 
|  | 488 | hw->private_data = mgr; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 489 | hw->ops.dsp_status = pcxhr_hwdep_dsp_status; | 
|  | 490 | hw->ops.dsp_load = pcxhr_hwdep_dsp_load; | 
|  | 491 | hw->exclusive = 1; | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 492 | /* stereo cards don't need fw_file_0 -> dsp_loaded = 1 */ | 
|  | 493 | hw->dsp_loaded = mgr->is_hr_stereo ? 1 : 0; | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 494 | mgr->dsp_loaded = 0; | 
|  | 495 | sprintf(hw->name, PCXHR_HWDEP_ID); | 
|  | 496 |  | 
| Markus Bollinger | 7628700 | 2008-11-25 12:28:06 +0100 | [diff] [blame] | 497 | err = snd_card_register(mgr->chip[0]->card); | 
|  | 498 | if (err < 0) | 
| Markus Bollinger | e12229b | 2005-12-06 13:55:26 +0100 | [diff] [blame] | 499 | return err; | 
|  | 500 | return 0; | 
|  | 501 | } | 
|  | 502 |  | 
|  | 503 | #endif /* SND_PCXHR_FW_LOADER */ |