18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Digigram pcxhr compatible soundcards
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * hwdep device manager
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2004 by Digigram <alsa@digigram.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
128c2ecf20Sopenharmony_ci#include <linux/firmware.h>
138c2ecf20Sopenharmony_ci#include <linux/pci.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <sound/core.h>
178c2ecf20Sopenharmony_ci#include <sound/hwdep.h>
188c2ecf20Sopenharmony_ci#include "pcxhr.h"
198c2ecf20Sopenharmony_ci#include "pcxhr_mixer.h"
208c2ecf20Sopenharmony_ci#include "pcxhr_hwdep.h"
218c2ecf20Sopenharmony_ci#include "pcxhr_core.h"
228c2ecf20Sopenharmony_ci#include "pcxhr_mix22.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int pcxhr_sub_init(struct pcxhr_mgr *mgr);
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * get basic information and init pcxhr card
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_cistatic int pcxhr_init_board(struct pcxhr_mgr *mgr)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	int err;
328c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
338c2ecf20Sopenharmony_ci	int card_streams;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* calc the number of all streams used */
368c2ecf20Sopenharmony_ci	if (mgr->mono_capture)
378c2ecf20Sopenharmony_ci		card_streams = mgr->capture_chips * 2;
388c2ecf20Sopenharmony_ci	else
398c2ecf20Sopenharmony_ci		card_streams = mgr->capture_chips;
408c2ecf20Sopenharmony_ci	card_streams += mgr->playback_chips * PCXHR_PLAYBACK_STREAMS;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* enable interrupts */
438c2ecf20Sopenharmony_ci	pcxhr_enable_dsp(mgr);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_SUPPORTED);
468c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
478c2ecf20Sopenharmony_ci	if (err)
488c2ecf20Sopenharmony_ci		return err;
498c2ecf20Sopenharmony_ci	/* test 4, 8 or 12 phys out */
508c2ecf20Sopenharmony_ci	if ((rmh.stat[0] & MASK_FIRST_FIELD) < mgr->playback_chips * 2)
518c2ecf20Sopenharmony_ci		return -EINVAL;
528c2ecf20Sopenharmony_ci	/* test 4, 8 or 2 phys in */
538c2ecf20Sopenharmony_ci	if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) <
548c2ecf20Sopenharmony_ci	    mgr->capture_chips * 2)
558c2ecf20Sopenharmony_ci		return -EINVAL;
568c2ecf20Sopenharmony_ci	/* test max nb substream per board */
578c2ecf20Sopenharmony_ci	if ((rmh.stat[1] & 0x5F) < card_streams)
588c2ecf20Sopenharmony_ci		return -EINVAL;
598c2ecf20Sopenharmony_ci	/* test max nb substream per pipe */
608c2ecf20Sopenharmony_ci	if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS)
618c2ecf20Sopenharmony_ci		return -EINVAL;
628c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev,
638c2ecf20Sopenharmony_ci		"supported formats : playback=%x capture=%x\n",
648c2ecf20Sopenharmony_ci		    rmh.stat[2], rmh.stat[3]);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_VERSION);
678c2ecf20Sopenharmony_ci	/* firmware num for DSP */
688c2ecf20Sopenharmony_ci	rmh.cmd[0] |= mgr->firmware_num;
698c2ecf20Sopenharmony_ci	/* transfer granularity in samples (should be multiple of 48) */
708c2ecf20Sopenharmony_ci	rmh.cmd[1] = (1<<23) + mgr->granularity;
718c2ecf20Sopenharmony_ci	rmh.cmd_len = 2;
728c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
738c2ecf20Sopenharmony_ci	if (err)
748c2ecf20Sopenharmony_ci		return err;
758c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev,
768c2ecf20Sopenharmony_ci		"PCXHR DSP version is %d.%d.%d\n", (rmh.stat[0]>>16)&0xff,
778c2ecf20Sopenharmony_ci		    (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
788c2ecf20Sopenharmony_ci	mgr->dsp_version = rmh.stat[0];
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (mgr->is_hr_stereo)
818c2ecf20Sopenharmony_ci		err = hr222_sub_init(mgr);
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		err = pcxhr_sub_init(mgr);
848c2ecf20Sopenharmony_ci	return err;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int pcxhr_sub_init(struct pcxhr_mgr *mgr)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int err;
908c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* get options */
938c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
948c2ecf20Sopenharmony_ci	rmh.cmd[0] |= IO_NUM_REG_STATUS;
958c2ecf20Sopenharmony_ci	rmh.cmd[1]  = REG_STATUS_OPTIONS;
968c2ecf20Sopenharmony_ci	rmh.cmd_len = 2;
978c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
988c2ecf20Sopenharmony_ci	if (err)
998c2ecf20Sopenharmony_ci		return err;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) ==
1028c2ecf20Sopenharmony_ci	    REG_STATUS_OPT_ANALOG_BOARD)
1038c2ecf20Sopenharmony_ci		mgr->board_has_analog = 1;	/* analog addon board found */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* unmute inputs */
1068c2ecf20Sopenharmony_ci	err = pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
1078c2ecf20Sopenharmony_ci					  REG_CONT_UNMUTE_INPUTS, NULL);
1088c2ecf20Sopenharmony_ci	if (err)
1098c2ecf20Sopenharmony_ci		return err;
1108c2ecf20Sopenharmony_ci	/* unmute outputs (a write to IO_NUM_REG_MUTE_OUT mutes!) */
1118c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
1128c2ecf20Sopenharmony_ci	rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
1138c2ecf20Sopenharmony_ci	if (DSP_EXT_CMD_SET(mgr)) {
1148c2ecf20Sopenharmony_ci		rmh.cmd[1]  = 1;	/* unmute digital plugs */
1158c2ecf20Sopenharmony_ci		rmh.cmd_len = 2;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
1188c2ecf20Sopenharmony_ci	return err;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid pcxhr_reset_board(struct pcxhr_mgr *mgr)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
1268c2ecf20Sopenharmony_ci		/* mute outputs */
1278c2ecf20Sopenharmony_ci	    if (!mgr->is_hr_stereo) {
1288c2ecf20Sopenharmony_ci		/* a read to IO_NUM_REG_MUTE_OUT register unmutes! */
1298c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
1308c2ecf20Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
1318c2ecf20Sopenharmony_ci		pcxhr_send_msg(mgr, &rmh);
1328c2ecf20Sopenharmony_ci		/* mute inputs */
1338c2ecf20Sopenharmony_ci		pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
1348c2ecf20Sopenharmony_ci					    0, NULL);
1358c2ecf20Sopenharmony_ci	    }
1368c2ecf20Sopenharmony_ci		/* stereo cards mute with reset of dsp */
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci	/* reset pcxhr dsp */
1398c2ecf20Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX))
1408c2ecf20Sopenharmony_ci		pcxhr_reset_dsp(mgr);
1418c2ecf20Sopenharmony_ci	/* reset second xilinx */
1428c2ecf20Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_XLX_COM_INDEX)) {
1438c2ecf20Sopenharmony_ci		pcxhr_reset_xilinx_com(mgr);
1448c2ecf20Sopenharmony_ci		mgr->dsp_loaded = 1;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	return;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci *  allocate a playback/capture pipe (pcmp0/pcmc0)
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cistatic int pcxhr_dsp_allocate_pipe(struct pcxhr_mgr *mgr,
1548c2ecf20Sopenharmony_ci				   struct pcxhr_pipe *pipe,
1558c2ecf20Sopenharmony_ci				   int is_capture, int pin)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int stream_count, audio_count;
1588c2ecf20Sopenharmony_ci	int err;
1598c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (is_capture) {
1628c2ecf20Sopenharmony_ci		stream_count = 1;
1638c2ecf20Sopenharmony_ci		if (mgr->mono_capture)
1648c2ecf20Sopenharmony_ci			audio_count = 1;
1658c2ecf20Sopenharmony_ci		else
1668c2ecf20Sopenharmony_ci			audio_count = 2;
1678c2ecf20Sopenharmony_ci	} else {
1688c2ecf20Sopenharmony_ci		stream_count = PCXHR_PLAYBACK_STREAMS;
1698c2ecf20Sopenharmony_ci		audio_count = 2;	/* always stereo */
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "snd_add_ref_pipe pin(%d) pcm%c0\n",
1728c2ecf20Sopenharmony_ci		    pin, is_capture ? 'c' : 'p');
1738c2ecf20Sopenharmony_ci	pipe->is_capture = is_capture;
1748c2ecf20Sopenharmony_ci	pipe->first_audio = pin;
1758c2ecf20Sopenharmony_ci	/* define pipe (P_PCM_ONLY_MASK (0x020000) is not necessary) */
1768c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_RES_PIPE);
1778c2ecf20Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin,
1788c2ecf20Sopenharmony_ci				  audio_count, stream_count);
1798c2ecf20Sopenharmony_ci	rmh.cmd[1] |= 0x020000; /* add P_PCM_ONLY_MASK */
1808c2ecf20Sopenharmony_ci	if (DSP_EXT_CMD_SET(mgr)) {
1818c2ecf20Sopenharmony_ci		/* add channel mask to command */
1828c2ecf20Sopenharmony_ci	  rmh.cmd[rmh.cmd_len++] = (audio_count == 1) ? 0x01 : 0x03;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
1858c2ecf20Sopenharmony_ci	if (err < 0) {
1868c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "error pipe allocation "
1878c2ecf20Sopenharmony_ci			   "(CMD_RES_PIPE) err=%x!\n", err);
1888c2ecf20Sopenharmony_ci		return err;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci	pipe->status = PCXHR_PIPE_DEFINED;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci *  free playback/capture pipe (pcmp0/pcmc0)
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_ci#if 0
1998c2ecf20Sopenharmony_cistatic int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
2028c2ecf20Sopenharmony_ci	int capture_mask = 0;
2038c2ecf20Sopenharmony_ci	int playback_mask = 0;
2048c2ecf20Sopenharmony_ci	int err = 0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (pipe->is_capture)
2078c2ecf20Sopenharmony_ci		capture_mask  = (1 << pipe->first_audio);
2088c2ecf20Sopenharmony_ci	else
2098c2ecf20Sopenharmony_ci		playback_mask = (1 << pipe->first_audio);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* stop one pipe */
2128c2ecf20Sopenharmony_ci	err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0);
2138c2ecf20Sopenharmony_ci	if (err < 0)
2148c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "error stopping pipe!\n");
2158c2ecf20Sopenharmony_ci	/* release the pipe */
2168c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_FREE_PIPE);
2178c2ecf20Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio,
2188c2ecf20Sopenharmony_ci				  0, 0);
2198c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
2208c2ecf20Sopenharmony_ci	if (err < 0)
2218c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "error pipe release "
2228c2ecf20Sopenharmony_ci			   "(CMD_FREE_PIPE) err(%x)\n", err);
2238c2ecf20Sopenharmony_ci	pipe->status = PCXHR_PIPE_UNDEFINED;
2248c2ecf20Sopenharmony_ci	return err;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci#endif
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int pcxhr_config_pipes(struct pcxhr_mgr *mgr)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int err, i, j;
2328c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip;
2338c2ecf20Sopenharmony_ci	struct pcxhr_pipe *pipe;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* allocate the pipes on the dsp */
2368c2ecf20Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
2378c2ecf20Sopenharmony_ci		chip = mgr->chip[i];
2388c2ecf20Sopenharmony_ci		if (chip->nb_streams_play) {
2398c2ecf20Sopenharmony_ci			pipe = &chip->playback_pipe;
2408c2ecf20Sopenharmony_ci			err = pcxhr_dsp_allocate_pipe( mgr, pipe, 0, i*2);
2418c2ecf20Sopenharmony_ci			if (err)
2428c2ecf20Sopenharmony_ci				return err;
2438c2ecf20Sopenharmony_ci			for(j = 0; j < chip->nb_streams_play; j++)
2448c2ecf20Sopenharmony_ci				chip->playback_stream[j].pipe = pipe;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
2478c2ecf20Sopenharmony_ci			pipe = &chip->capture_pipe[j];
2488c2ecf20Sopenharmony_ci			err = pcxhr_dsp_allocate_pipe(mgr, pipe, 1, i*2 + j);
2498c2ecf20Sopenharmony_ci			if (err)
2508c2ecf20Sopenharmony_ci				return err;
2518c2ecf20Sopenharmony_ci			chip->capture_stream[j].pipe = pipe;
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int pcxhr_start_pipes(struct pcxhr_mgr *mgr)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	int i, j;
2608c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip;
2618c2ecf20Sopenharmony_ci	int playback_mask = 0;
2628c2ecf20Sopenharmony_ci	int capture_mask = 0;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* start all the pipes on the dsp */
2658c2ecf20Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
2668c2ecf20Sopenharmony_ci		chip = mgr->chip[i];
2678c2ecf20Sopenharmony_ci		if (chip->nb_streams_play)
2688c2ecf20Sopenharmony_ci			playback_mask |= 1 << chip->playback_pipe.first_audio;
2698c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++)
2708c2ecf20Sopenharmony_ci			capture_mask |= 1 << chip->capture_pipe[j].first_audio;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci	return pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
2778c2ecf20Sopenharmony_ci			  const struct firmware *dsp)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	int err, card_index;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev,
2828c2ecf20Sopenharmony_ci		"loading dsp [%d] size = %zd\n", index, dsp->size);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	switch (index) {
2858c2ecf20Sopenharmony_ci	case PCXHR_FIRMWARE_XLX_INT_INDEX:
2868c2ecf20Sopenharmony_ci		pcxhr_reset_xilinx_com(mgr);
2878c2ecf20Sopenharmony_ci		return pcxhr_load_xilinx_binary(mgr, dsp, 0);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	case PCXHR_FIRMWARE_XLX_COM_INDEX:
2908c2ecf20Sopenharmony_ci		pcxhr_reset_xilinx_com(mgr);
2918c2ecf20Sopenharmony_ci		return pcxhr_load_xilinx_binary(mgr, dsp, 1);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	case PCXHR_FIRMWARE_DSP_EPRM_INDEX:
2948c2ecf20Sopenharmony_ci		pcxhr_reset_dsp(mgr);
2958c2ecf20Sopenharmony_ci		return pcxhr_load_eeprom_binary(mgr, dsp);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	case PCXHR_FIRMWARE_DSP_BOOT_INDEX:
2988c2ecf20Sopenharmony_ci		return pcxhr_load_boot_binary(mgr, dsp);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	case PCXHR_FIRMWARE_DSP_MAIN_INDEX:
3018c2ecf20Sopenharmony_ci		err = pcxhr_load_dsp_binary(mgr, dsp);
3028c2ecf20Sopenharmony_ci		if (err)
3038c2ecf20Sopenharmony_ci			return err;
3048c2ecf20Sopenharmony_ci		break;	/* continue with first init */
3058c2ecf20Sopenharmony_ci	default:
3068c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "wrong file index\n");
3078c2ecf20Sopenharmony_ci		return -EFAULT;
3088c2ecf20Sopenharmony_ci	} /* end of switch file index*/
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* first communication with embedded */
3118c2ecf20Sopenharmony_ci	err = pcxhr_init_board(mgr);
3128c2ecf20Sopenharmony_ci        if (err < 0) {
3138c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "pcxhr could not be set up\n");
3148c2ecf20Sopenharmony_ci		return err;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	err = pcxhr_config_pipes(mgr);
3178c2ecf20Sopenharmony_ci        if (err < 0) {
3188c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "pcxhr pipes could not be set up\n");
3198c2ecf20Sopenharmony_ci		return err;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci       	/* create devices and mixer in accordance with HW options*/
3228c2ecf20Sopenharmony_ci        for (card_index = 0; card_index < mgr->num_cards; card_index++) {
3238c2ecf20Sopenharmony_ci		struct snd_pcxhr *chip = mgr->chip[card_index];
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		if ((err = pcxhr_create_pcm(chip)) < 0)
3268c2ecf20Sopenharmony_ci			return err;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		if (card_index == 0) {
3298c2ecf20Sopenharmony_ci			if ((err = pcxhr_create_mixer(chip->mgr)) < 0)
3308c2ecf20Sopenharmony_ci				return err;
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci		if ((err = snd_card_register(chip->card)) < 0)
3338c2ecf20Sopenharmony_ci			return err;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci	err = pcxhr_start_pipes(mgr);
3368c2ecf20Sopenharmony_ci        if (err < 0) {
3378c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "pcxhr pipes could not be started\n");
3388c2ecf20Sopenharmony_ci		return err;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev,
3418c2ecf20Sopenharmony_ci		"pcxhr firmware downloaded and successfully set up\n");
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/*
3478c2ecf20Sopenharmony_ci * fw loader entry
3488c2ecf20Sopenharmony_ci */
3498c2ecf20Sopenharmony_ciint pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	static const char * const fw_files[][5] = {
3528c2ecf20Sopenharmony_ci	[0] = { "xlxint.dat", "xlxc882hr.dat",
3538c2ecf20Sopenharmony_ci		"dspe882.e56", "dspb882hr.b56", "dspd882.d56" },
3548c2ecf20Sopenharmony_ci	[1] = { "xlxint.dat", "xlxc882e.dat",
3558c2ecf20Sopenharmony_ci		"dspe882.e56", "dspb882e.b56", "dspd882.d56" },
3568c2ecf20Sopenharmony_ci	[2] = { "xlxint.dat", "xlxc1222hr.dat",
3578c2ecf20Sopenharmony_ci		"dspe882.e56", "dspb1222hr.b56", "dspd1222.d56" },
3588c2ecf20Sopenharmony_ci	[3] = { "xlxint.dat", "xlxc1222e.dat",
3598c2ecf20Sopenharmony_ci		"dspe882.e56", "dspb1222e.b56", "dspd1222.d56" },
3608c2ecf20Sopenharmony_ci	[4] = { NULL, "xlxc222.dat",
3618c2ecf20Sopenharmony_ci		"dspe924.e56", "dspb924.b56", "dspd222.d56" },
3628c2ecf20Sopenharmony_ci	[5] = { NULL, "xlxc924.dat",
3638c2ecf20Sopenharmony_ci		"dspe924.e56", "dspb924.b56", "dspd222.d56" },
3648c2ecf20Sopenharmony_ci	};
3658c2ecf20Sopenharmony_ci	char path[32];
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	const struct firmware *fw_entry;
3688c2ecf20Sopenharmony_ci	int i, err;
3698c2ecf20Sopenharmony_ci	int fw_set = mgr->fw_file_set;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
3728c2ecf20Sopenharmony_ci		if (!fw_files[fw_set][i])
3738c2ecf20Sopenharmony_ci			continue;
3748c2ecf20Sopenharmony_ci		sprintf(path, "pcxhr/%s", fw_files[fw_set][i]);
3758c2ecf20Sopenharmony_ci		if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
3768c2ecf20Sopenharmony_ci			dev_err(&mgr->pci->dev,
3778c2ecf20Sopenharmony_ci				"pcxhr: can't load firmware %s\n",
3788c2ecf20Sopenharmony_ci				   path);
3798c2ecf20Sopenharmony_ci			return -ENOENT;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci		/* fake hwdep dsp record */
3828c2ecf20Sopenharmony_ci		err = pcxhr_dsp_load(mgr, i, fw_entry);
3838c2ecf20Sopenharmony_ci		release_firmware(fw_entry);
3848c2ecf20Sopenharmony_ci		if (err < 0)
3858c2ecf20Sopenharmony_ci			return err;
3868c2ecf20Sopenharmony_ci		mgr->dsp_loaded |= 1 << i;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxint.dat");
3928c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxc882hr.dat");
3938c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxc882e.dat");
3948c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspe882.e56");
3958c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspb882hr.b56");
3968c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspb882e.b56");
3978c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspd882.d56");
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxc1222hr.dat");
4008c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxc1222e.dat");
4018c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspb1222hr.b56");
4028c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspb1222e.b56");
4038c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspd1222.d56");
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxc222.dat");
4068c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/xlxc924.dat");
4078c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspe924.e56");
4088c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspb924.b56");
4098c2ecf20Sopenharmony_ciMODULE_FIRMWARE("pcxhr/dspd222.d56");
410