Merge trivial low-risk suspend hotkey bugzilla-5918 into release
[linux-2.6] / sound / pci / pcxhr / pcxhr_hwdep.c
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
23 #include <sound/driver.h>
24 #include <linux/interrupt.h>
25 #include <linux/vmalloc.h>
26 #include <linux/firmware.h>
27 #include <linux/pci.h>
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"
35
36
37 #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
38 #if !defined(CONFIG_USE_PCXHRLOADER) && !defined(CONFIG_SND_PCXHR) /* built-in kernel */
39 #define SND_PCXHR_FW_LOADER     /* use the standard firmware loader */
40 #endif
41 #endif
42
43
44 /*
45  * get basic information and init pcxhr card
46  */
47
48 static int pcxhr_init_board(struct pcxhr_mgr *mgr)
49 {
50         int err;
51         struct pcxhr_rmh rmh;
52         int card_streams;
53
54         /* calc the number of all streams used */
55         if (mgr->mono_capture)
56                 card_streams = mgr->capture_chips * 2;
57         else
58                 card_streams = mgr->capture_chips;
59         card_streams += mgr->playback_chips * PCXHR_PLAYBACK_STREAMS;
60
61         /* enable interrupts */
62         pcxhr_enable_dsp(mgr);
63
64         pcxhr_init_rmh(&rmh, CMD_SUPPORTED);
65         err = pcxhr_send_msg(mgr, &rmh);
66         if (err)
67                 return err;
68         /* test 8 or 12 phys out */
69         snd_assert((rmh.stat[0] & MASK_FIRST_FIELD) == mgr->playback_chips*2,
70                    return -EINVAL);
71         /* test 8 or 2 phys in */
72         snd_assert(((rmh.stat[0] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD) ==
73                    mgr->capture_chips * 2, return -EINVAL);
74         /* test max nb substream per board */
75         snd_assert((rmh.stat[1] & 0x5F) >= card_streams, return -EINVAL);
76         /* test max nb substream per pipe */
77         snd_assert(((rmh.stat[1]>>7)&0x5F) >= PCXHR_PLAYBACK_STREAMS, return -EINVAL);
78
79         pcxhr_init_rmh(&rmh, CMD_VERSION);
80         /* firmware num for DSP */
81         rmh.cmd[0] |= mgr->firmware_num;
82         /* transfer granularity in samples (should be multiple of 48) */
83         rmh.cmd[1] = (1<<23) + PCXHR_GRANULARITY;
84         rmh.cmd_len = 2;
85         err = pcxhr_send_msg(mgr, &rmh);
86         if (err)
87                 return err;
88         snd_printdd("PCXHR DSP version is %d.%d.%d\n",
89                     (rmh.stat[0]>>16)&0xff, (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
90         mgr->dsp_version = rmh.stat[0];
91
92         /* get options */
93         pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
94         rmh.cmd[0] |= IO_NUM_REG_STATUS;
95         rmh.cmd[1]  = REG_STATUS_OPTIONS;
96         rmh.cmd_len = 2;
97         err = pcxhr_send_msg(mgr, &rmh);
98         if (err)
99                 return err;
100
101         if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) == REG_STATUS_OPT_ANALOG_BOARD)
102                 mgr->board_has_analog = 1;      /* analog addon board available */
103         else
104                 /* analog addon board not available -> no support for instance */
105                 return -EINVAL; 
106
107         /* unmute inputs */
108         err = pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
109                                           REG_CONT_UNMUTE_INPUTS, NULL);
110         if (err)
111                 return err;
112         /* unmute outputs */
113         pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* a write to IO_NUM_REG_MUTE_OUT mutes! */
114         rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
115         err = pcxhr_send_msg(mgr, &rmh);
116         return err;
117 }
118
119 void pcxhr_reset_board(struct pcxhr_mgr *mgr)
120 {
121         struct pcxhr_rmh rmh;
122
123         if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
124                 /* mute outputs */
125                 /* a read to IO_NUM_REG_MUTE_OUT register unmutes! */
126                 pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
127                 rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
128                 pcxhr_send_msg(mgr, &rmh);
129                 /* mute inputs */
130                 pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS, 0, NULL);
131         }
132         /* reset pcxhr dsp */
133         if (mgr->dsp_loaded & ( 1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX))
134                 pcxhr_reset_dsp(mgr);
135         /* reset second xilinx */
136         if (mgr->dsp_loaded & ( 1 << PCXHR_FIRMWARE_XLX_COM_INDEX))
137                 pcxhr_reset_xilinx_com(mgr);
138         return;
139 }
140
141
142 /*
143  *  allocate a playback/capture pipe (pcmp0/pcmc0)
144  */
145 static int pcxhr_dsp_allocate_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe,
146                                     int is_capture, int pin)
147 {
148         int stream_count, audio_count;
149         int err;
150         struct pcxhr_rmh rmh;
151
152         if (is_capture) {
153                 stream_count = 1;
154                 if (mgr->mono_capture)
155                         audio_count = 1;
156                 else
157                         audio_count = 2;
158         } else {
159                 stream_count = PCXHR_PLAYBACK_STREAMS;
160                 audio_count = 2;        /* always stereo */
161         }
162         snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n", pin, is_capture ? 'c' : 'p');
163         pipe->is_capture = is_capture;
164         pipe->first_audio = pin;
165         /* define pipe (P_PCM_ONLY_MASK (0x020000) is not necessary) */
166         pcxhr_init_rmh(&rmh, CMD_RES_PIPE);
167         pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin, audio_count, stream_count); 
168         err = pcxhr_send_msg(mgr, &rmh);
169         if (err < 0) {
170                 snd_printk(KERN_ERR "error pipe allocation (CMD_RES_PIPE) err=%x!\n", err );
171                 return err;
172         }
173         pipe->status = PCXHR_PIPE_DEFINED;
174
175         return 0;
176 }
177
178 /*
179  *  free playback/capture pipe (pcmp0/pcmc0)
180  */
181 #if 0
182 static int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe)
183 {
184         struct pcxhr_rmh rmh;
185         int capture_mask = 0;
186         int playback_mask = 0;
187         int err = 0;
188
189         if (pipe->is_capture)
190                 capture_mask  = (1 << pipe->first_audio);
191         else
192                 playback_mask = (1 << pipe->first_audio);
193
194         /* stop one pipe */
195         err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0);
196         if (err < 0)
197                 snd_printk(KERN_ERR "error stopping pipe!\n");
198         /* release the pipe */
199         pcxhr_init_rmh(&rmh, CMD_FREE_PIPE);
200         pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio, 0, 0);
201         err = pcxhr_send_msg(mgr, &rmh);
202         if (err < 0)
203                 snd_printk(KERN_ERR "error pipe release (CMD_FREE_PIPE) err(%x)\n", err);
204         pipe->status = PCXHR_PIPE_UNDEFINED;
205         return err;
206 }
207 #endif
208
209
210 static int pcxhr_config_pipes(struct pcxhr_mgr *mgr)
211 {
212         int err, i, j;
213         struct snd_pcxhr *chip;
214         struct pcxhr_pipe *pipe;
215
216         /* allocate the pipes on the dsp */
217         for (i = 0; i < mgr->num_cards; i++) {
218                 chip = mgr->chip[i];
219                 if (chip->nb_streams_play) {
220                         pipe = &chip->playback_pipe;
221                         err = pcxhr_dsp_allocate_pipe( mgr, pipe, 0, i*2);
222                         if (err)
223                                 return err;
224                         for(j = 0; j < chip->nb_streams_play; j++)
225                                 chip->playback_stream[j].pipe = pipe;
226                 }
227                 for (j = 0; j < chip->nb_streams_capt; j++) {
228                         pipe = &chip->capture_pipe[j];
229                         err = pcxhr_dsp_allocate_pipe(mgr, pipe, 1, i*2 + j);
230                         if (err)
231                                 return err;
232                         chip->capture_stream[j].pipe = pipe;
233                 }
234         }
235         return 0;
236 }
237
238 static int pcxhr_start_pipes(struct pcxhr_mgr *mgr)
239 {
240         int i, j;
241         struct snd_pcxhr *chip;
242         int playback_mask = 0;
243         int capture_mask = 0;
244
245         /* start all the pipes on the dsp */
246         for (i = 0; i < mgr->num_cards; i++) {
247                 chip = mgr->chip[i];
248                 if (chip->nb_streams_play)
249                         playback_mask |= (1 << chip->playback_pipe.first_audio);
250                 for (j = 0; j < chip->nb_streams_capt; j++)
251                         capture_mask |= (1 << chip->capture_pipe[j].first_audio);
252         }
253         return pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
254 }
255
256
257 static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, const struct firmware *dsp)
258 {
259         int err, card_index;
260
261         snd_printdd("loading dsp [%d] size = %Zd\n", index, dsp->size);
262
263         switch (index) {
264         case PCXHR_FIRMWARE_XLX_INT_INDEX:
265                 pcxhr_reset_xilinx_com(mgr);
266                 return pcxhr_load_xilinx_binary(mgr, dsp, 0);
267
268         case PCXHR_FIRMWARE_XLX_COM_INDEX:
269                 pcxhr_reset_xilinx_com(mgr);
270                 return pcxhr_load_xilinx_binary(mgr, dsp, 1);
271
272         case PCXHR_FIRMWARE_DSP_EPRM_INDEX:
273                 pcxhr_reset_dsp(mgr);
274                 return pcxhr_load_eeprom_binary(mgr, dsp);
275
276         case PCXHR_FIRMWARE_DSP_BOOT_INDEX:
277                 return pcxhr_load_boot_binary(mgr, dsp);
278
279         case PCXHR_FIRMWARE_DSP_MAIN_INDEX:
280                 err = pcxhr_load_dsp_binary(mgr, dsp);
281                 if (err)
282                         return err;
283                 break;  /* continue with first init */
284         default:
285                 snd_printk(KERN_ERR "wrong file index\n");
286                 return -EFAULT;
287         } /* end of switch file index*/
288
289         /* first communication with embedded */
290         err = pcxhr_init_board(mgr);
291         if (err < 0) {
292                 snd_printk(KERN_ERR "pcxhr could not be set up\n");
293                 return err;
294         }
295         err = pcxhr_config_pipes(mgr);
296         if (err < 0) {
297                 snd_printk(KERN_ERR "pcxhr pipes could not be set up\n");
298                 return err;
299         }
300         /* create devices and mixer in accordance with HW options*/
301         for (card_index = 0; card_index < mgr->num_cards; card_index++) {
302                 struct snd_pcxhr *chip = mgr->chip[card_index];
303
304                 if ((err = pcxhr_create_pcm(chip)) < 0)
305                         return err;
306
307                 if (card_index == 0) {
308                         if ((err = pcxhr_create_mixer(chip->mgr)) < 0)
309                                 return err;
310                 }
311                 if ((err = snd_card_register(chip->card)) < 0)
312                         return err;
313         }
314         err = pcxhr_start_pipes(mgr);
315         if (err < 0) {
316                 snd_printk(KERN_ERR "pcxhr pipes could not be started\n");
317                 return err;
318         }
319         snd_printdd("pcxhr firmware downloaded and successfully set up\n");
320
321         return 0;
322 }
323
324 /*
325  * fw loader entry
326  */
327 #ifdef SND_PCXHR_FW_LOADER
328
329 int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
330 {
331         static char *fw_files[5] = {
332                 "xi_1_882.dat",
333                 "xc_1_882.dat",
334                 "e321_512.e56",
335                 "b321_512.b56",
336                 "d321_512.d56"
337         };
338         char path[32];
339
340         const struct firmware *fw_entry;
341         int i, err;
342
343         for (i = 0; i < ARRAY_SIZE(fw_files); i++) {
344                 sprintf(path, "pcxhr/%s", fw_files[i]);
345                 if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
346                         snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n", path);
347                         return -ENOENT;
348                 }
349                 /* fake hwdep dsp record */
350                 err = pcxhr_dsp_load(mgr, i, fw_entry);
351                 release_firmware(fw_entry);
352                 if (err < 0)
353                         return err;
354                 mgr->dsp_loaded |= 1 << i;
355         }
356         return 0;
357 }
358
359 #else /* old style firmware loading */
360
361 /* pcxhr hwdep interface id string */
362 #define PCXHR_HWDEP_ID       "pcxhr loader"
363
364
365 static int pcxhr_hwdep_dsp_status(struct snd_hwdep *hw,
366                                   struct snd_hwdep_dsp_status *info)
367 {
368         strcpy(info->id, "pcxhr");
369         info->num_dsps = PCXHR_FIRMWARE_FILES_MAX_INDEX;
370
371         if (hw->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))
372                 info->chip_ready = 1;
373
374         info->version = PCXHR_DRIVER_VERSION;
375         return 0;
376 }
377
378 static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw,
379                                 struct snd_hwdep_dsp_image *dsp)
380 {
381         struct pcxhr_mgr *mgr = hw->private_data;
382         int err;
383         struct firmware fw;
384
385         fw.size = dsp->length;
386         fw.data = vmalloc(fw.size);
387         if (! fw.data) {
388                 snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image (%lu bytes)\n",
389                            (unsigned long)fw.size);
390                 return -ENOMEM;
391         }
392         if (copy_from_user(fw.data, dsp->image, dsp->length)) {
393                 vfree(fw.data);
394                 return -EFAULT;
395         }
396         err = pcxhr_dsp_load(mgr, dsp->index, &fw);
397         vfree(fw.data);
398         if (err < 0)
399                 return err;
400         mgr->dsp_loaded |= 1 << dsp->index;
401         return 0;
402 }
403
404 static int pcxhr_hwdep_open(struct snd_hwdep *hw, struct file *file)
405 {
406         return 0;
407 }
408
409 static int pcxhr_hwdep_release(struct snd_hwdep *hw, struct file *file)
410 {
411         return 0;
412 }
413
414 int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
415 {
416         int err;
417         struct snd_hwdep *hw;
418
419         /* only create hwdep interface for first cardX (see "index" module parameter)*/
420         if ((err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw)) < 0)
421                 return err;
422
423         hw->iface = SNDRV_HWDEP_IFACE_PCXHR;
424         hw->private_data = mgr;
425         hw->ops.open = pcxhr_hwdep_open;
426         hw->ops.release = pcxhr_hwdep_release;
427         hw->ops.dsp_status = pcxhr_hwdep_dsp_status;
428         hw->ops.dsp_load = pcxhr_hwdep_dsp_load;
429         hw->exclusive = 1;
430         mgr->dsp_loaded = 0;
431         sprintf(hw->name, PCXHR_HWDEP_ID);
432
433         if ((err = snd_card_register(mgr->chip[0]->card)) < 0)
434                 return err;
435         return 0;
436 }
437
438 #endif /* SND_PCXHR_FW_LOADER */