brintos

brintos / linux-shallow public Read only

0
0
Text · 16.8 KiB · 689c0f9 Raw
587 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Digigram miXart soundcards4 *5 * DSP firmware management6 *7 * Copyright (c) 2003 by Digigram <alsa@digigram.com>8 */9 10#include <linux/interrupt.h>11#include <linux/pci.h>12#include <linux/firmware.h>13#include <linux/vmalloc.h>14#include <linux/slab.h>15#include <linux/module.h>16#include <linux/io.h>17#include <sound/core.h>18#include "mixart.h"19#include "mixart_mixer.h"20#include "mixart_core.h"21#include "mixart_hwdep.h"22 23 24/**25 * mixart_wait_nice_for_register_value - wait for a value on a peudo register,26 * exit with a timeout27 *28 * @mgr: pointer to miXart manager structure29 * @offset: unsigned pseudo_register base + offset of value30 * @is_egal: wait for the equal value31 * @value: value32 * @timeout: timeout in centisenconds33 */34static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,35					       u32 offset, int is_egal,36					       u32 value, unsigned long timeout)37{38	unsigned long end_time = jiffies + (timeout * HZ / 100);39	u32 read;40 41	do {	/* we may take too long time in this loop.42		 * so give controls back to kernel if needed.43		 */44		cond_resched();45 46		read = readl_be( MIXART_MEM( mgr, offset ));47		if(is_egal) {48			if(read == value) return 0;49		}50		else { /* wait for different value */51			if(read != value) return 0;52		}53	} while ( time_after_eq(end_time, jiffies) );54 55	return -EBUSY;56}57 58 59/*60  structures needed to upload elf code packets 61 */62struct snd_mixart_elf32_ehdr {63	u8      e_ident[16];64	__be16  e_type;65	__be16  e_machine;66	__be32  e_version;67	__be32  e_entry;68	__be32  e_phoff;69	__be32  e_shoff;70	__be32  e_flags;71	__be16  e_ehsize;72	__be16  e_phentsize;73	__be16  e_phnum;74	__be16  e_shentsize;75	__be16  e_shnum;76	__be16  e_shstrndx;77};78 79struct snd_mixart_elf32_phdr {80	__be32  p_type;81	__be32  p_offset;82	__be32  p_vaddr;83	__be32  p_paddr;84	__be32  p_filesz;85	__be32  p_memsz;86	__be32  p_flags;87	__be32  p_align;88};89 90static int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp )91{92	char                    elf32_magic_number[4] = {0x7f,'E','L','F'};93	struct snd_mixart_elf32_ehdr *elf_header;94	int                     i;95 96	elf_header = (struct snd_mixart_elf32_ehdr *)dsp->data;97	for( i=0; i<4; i++ )98		if ( elf32_magic_number[i] != elf_header->e_ident[i] )99			return -EINVAL;100 101	if( elf_header->e_phoff != 0 ) {102		struct snd_mixart_elf32_phdr     elf_programheader;103 104		for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {105			u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));106 107			memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );108 109			if(elf_programheader.p_type != 0) {110				if( elf_programheader.p_filesz != 0 ) {111					memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),112						     dsp->data + be32_to_cpu( elf_programheader.p_offset ),113						     be32_to_cpu( elf_programheader.p_filesz ));114				}115			}116		}117	}118	return 0;119}120 121/*122 * get basic information and init miXart123 */124 125/* audio IDs for request to the board */126#define MIXART_FIRST_ANA_AUDIO_ID       0127#define MIXART_FIRST_DIG_AUDIO_ID       8128 129static int mixart_enum_connectors(struct mixart_mgr *mgr)130{131	u32 k;132	int err;133	struct mixart_msg request;134	struct mixart_enum_connector_resp *connector;135	struct mixart_audio_info_req  *audio_info_req;136	struct mixart_audio_info_resp *audio_info;137 138	connector = kmalloc(sizeof(*connector), GFP_KERNEL);139	audio_info_req = kmalloc(sizeof(*audio_info_req), GFP_KERNEL);140	audio_info = kmalloc(sizeof(*audio_info), GFP_KERNEL);141	if (! connector || ! audio_info_req || ! audio_info) {142		err = -ENOMEM;143		goto __error;144	}145 146	audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;147	audio_info_req->micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;148	audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX;149 150	request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;151	request.uid = (struct mixart_uid){0,0};  /* board num = 0 */152	request.data = NULL;153	request.size = 0;154 155	err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);156	if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {157		dev_err(&mgr->pci->dev,158			"error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");159		err = -EINVAL;160		goto __error;161	}162 163	for(k=0; k < connector->uid_count; k++) {164		struct mixart_pipe *pipe;165 166		if(k < MIXART_FIRST_DIG_AUDIO_ID) {167			pipe = &mgr->chip[k/2]->pipe_out_ana;168		} else {169			pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;170		}171		if(k & 1) {172			pipe->uid_right_connector = connector->uid[k];   /* odd */173		} else {174			pipe->uid_left_connector = connector->uid[k];    /* even */175		}176 177		/* dev_dbg(&mgr->pci->dev, "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */178 179		/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */180		request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;181		request.uid = connector->uid[k];182		request.data = audio_info_req;183		request.size = sizeof(*audio_info_req);184 185		err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);186		if( err < 0 ) {187			dev_err(&mgr->pci->dev,188				"error MSG_CONNECTOR_GET_AUDIO_INFO\n");189			goto __error;190		}191		/*dev_dbg(&mgr->pci->dev, "play  analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/192	}193 194	request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;195	request.uid = (struct mixart_uid){0,0};  /* board num = 0 */196	request.data = NULL;197	request.size = 0;198 199	err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);200	if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {201		dev_err(&mgr->pci->dev,202			"error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");203		err = -EINVAL;204		goto __error;205	}206 207	for(k=0; k < connector->uid_count; k++) {208		struct mixart_pipe *pipe;209 210		if(k < MIXART_FIRST_DIG_AUDIO_ID) {211			pipe = &mgr->chip[k/2]->pipe_in_ana;212		} else {213			pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;214		}215		if(k & 1) {216			pipe->uid_right_connector = connector->uid[k];   /* odd */217		} else {218			pipe->uid_left_connector = connector->uid[k];    /* even */219		}220 221		/* dev_dbg(&mgr->pci->dev, "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */222 223		/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */224		request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;225		request.uid = connector->uid[k];226		request.data = audio_info_req;227		request.size = sizeof(*audio_info_req);228 229		err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);230		if( err < 0 ) {231			dev_err(&mgr->pci->dev,232				"error MSG_CONNECTOR_GET_AUDIO_INFO\n");233			goto __error;234		}235		/*dev_dbg(&mgr->pci->dev, "rec  analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/236	}237	err = 0;238 239 __error:240	kfree(connector);241	kfree(audio_info_req);242	kfree(audio_info);243 244	return err;245}246 247static int mixart_enum_physio(struct mixart_mgr *mgr)248{249	u32 k;250	int err;251	struct mixart_msg request;252	struct mixart_uid get_console_mgr;253	struct mixart_return_uid console_mgr;254	struct mixart_uid_enumeration phys_io;255 256	/* get the uid for the console manager */257	get_console_mgr.object_id = 0;258	get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */259 260	request.message_id = MSG_CONSOLE_GET_CLOCK_UID;261	request.uid = get_console_mgr;262	request.data = &get_console_mgr;263	request.size = sizeof(get_console_mgr);264 265	err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);266 267	if( (err < 0) || (console_mgr.error_code != 0) ) {268		dev_dbg(&mgr->pci->dev,269			"error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n",270			console_mgr.error_code);271		return -EINVAL;272	}273 274	/* used later for clock issues ! */275	mgr->uid_console_manager = console_mgr.uid;276 277	request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;278	request.uid = (struct mixart_uid){0,0};279	request.data = &console_mgr.uid;280	request.size = sizeof(console_mgr.uid);281 282	err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);283	if( (err < 0) || ( phys_io.error_code != 0 ) ) {284		dev_err(&mgr->pci->dev,285			"error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n",286			err, phys_io.error_code);287		return -EINVAL;288	}289 290	/* min 2 phys io per card (analog in + analog out) */291	if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)292		return -EINVAL;293 294	for(k=0; k<mgr->num_cards; k++) {295		mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];296		mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k]; 297	}298 299	return 0;300}301 302 303static int mixart_first_init(struct mixart_mgr *mgr)304{305	u32 k;306	int err;307	struct mixart_msg request;308 309	err = mixart_enum_connectors(mgr);310	if (err < 0)311		return err;312 313	err = mixart_enum_physio(mgr);314	if (err < 0)315		return err;316 317	/* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */318	/* though why not here */319	request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;320	request.uid = (struct mixart_uid){0,0};321	request.data = NULL;322	request.size = 0;323	/* this command has no data. response is a 32 bit status */324	err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);325	if( (err < 0) || (k != 0) ) {326		dev_err(&mgr->pci->dev, "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");327		return err == 0 ? -EINVAL : err;328	}329 330	return 0;331}332 333 334/* firmware base addresses (when hard coded) */335#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS   0x00600000336 337static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmware *dsp)338{339	int           err, card_index;340	u32           status_xilinx, status_elf, status_daught;341	u32           val;342 343	/* read motherboard xilinx status */344	status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));345	/* read elf status */346	status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));347	/* read daughterboard xilinx status */348	status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));349 350	/* motherboard xilinx status 5 will say that the board is performing a reset */351	if (status_xilinx == 5) {352		dev_err(&mgr->pci->dev, "miXart is resetting !\n");353		return -EAGAIN; /* try again later */354	}355 356	switch (index)   {357	case MIXART_MOTHERBOARD_XLX_INDEX:358 359		/* xilinx already loaded ? */ 360		if (status_xilinx == 4) {361			dev_dbg(&mgr->pci->dev, "xilinx is already loaded !\n");362			return 0;363		}364		/* the status should be 0 == "idle" */365		if (status_xilinx != 0) {366			dev_err(&mgr->pci->dev,367				"xilinx load error ! status = %d\n",368				   status_xilinx);369			return -EIO; /* modprob -r may help ? */370		}371 372		/* check xilinx validity */373		if (((u32*)(dsp->data))[0] == 0xffffffff)374			return -EINVAL;375		if (dsp->size % 4)376			return -EINVAL;377 378		/* set xilinx status to copying */379		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));380 381		/* setup xilinx base address */382		writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));383		/* setup code size for xilinx file */384		writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));385 386		/* copy xilinx code */387		memcpy_toio(  MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS),  dsp->data,  dsp->size);388    389		/* set xilinx status to copy finished */390		writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));391 392		/* return, because no further processing needed */393		return 0;394 395	case MIXART_MOTHERBOARD_ELF_INDEX:396 397		if (status_elf == 4) {398			dev_dbg(&mgr->pci->dev, "elf file already loaded !\n");399			return 0;400		}401 402		/* the status should be 0 == "idle" */403		if (status_elf != 0) {404			dev_err(&mgr->pci->dev,405				"elf load error ! status = %d\n",406				   status_elf);407			return -EIO; /* modprob -r may help ? */408		}409 410		/* wait for xilinx status == 4 */411		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */412		if (err < 0) {413			dev_err(&mgr->pci->dev, "xilinx was not loaded or "414				   "could not be started\n");415			return err;416		}417 418		/* init some data on the card */419		writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */420		writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) );         /* reset pointer to flow table on miXart */421 422		/* set elf status to copying */423		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));424 425		/* process the copying of the elf packets */426		err = mixart_load_elf( mgr, dsp );427		if (err < 0) return err;428 429		/* set elf status to copy finished */430		writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));431 432		/* wait for elf status == 4 */433		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */434		if (err < 0) {435			dev_err(&mgr->pci->dev, "elf could not be started\n");436			return err;437		}438 439		/* miXart waits at this point on the pointer to the flow table */440		writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */441 442		return 0;  /* return, another xilinx file has to be loaded before */443 444	case MIXART_AESEBUBOARD_XLX_INDEX:445	default:446 447		/* elf and xilinx should be loaded */448		if (status_elf != 4 || status_xilinx != 4) {449			dev_err(&mgr->pci->dev, "xilinx or elf not "450			       "successfully loaded\n");451			return -EIO; /* modprob -r may help ? */452		}453 454		/* wait for daughter detection != 0 */455		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */456		if (err < 0) {457			dev_err(&mgr->pci->dev, "error starting elf file\n");458			return err;459		}460 461		/* the board type can now be retrieved */462		mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));463 464		if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)465			break;  /* no daughter board; the file does not have to be loaded, continue after the switch */466 467		/* only if aesebu daughter board presence (elf code must run)  */ 468		if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )469			return -EINVAL;470 471		/* daughter should be idle */472		if (status_daught != 0) {473			dev_err(&mgr->pci->dev,474				"daughter load error ! status = %d\n",475			       status_daught);476			return -EIO; /* modprob -r may help ? */477		}478 479		/* check daughterboard xilinx validity */480		if (((u32*)(dsp->data))[0] == 0xffffffff)481			return -EINVAL;482		if (dsp->size % 4)483			return -EINVAL;484 485		/* inform mixart about the size of the file */486		writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));487 488		/* set daughterboard status to 1 */489		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));490 491		/* wait for status == 2 */492		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */493		if (err < 0) {494			dev_err(&mgr->pci->dev, "daughter board load error\n");495			return err;496		}497 498		/* get the address where to write the file */499		val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));500		if (!val)501			return -EINVAL;502 503		/* copy daughterboard xilinx code */504		memcpy_toio(  MIXART_MEM( mgr, val),  dsp->data,  dsp->size);505 506		/* set daughterboard status to 4 */507		writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));508 509		/* continue with init */510		break;511	} /* end of switch file index*/512 513        /* wait for daughter status == 3 */514        err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */515        if (err < 0) {516		dev_err(&mgr->pci->dev,517			   "daughter board could not be initialised\n");518		return err;519	}520 521	/* init mailbox (communication with embedded) */522	snd_mixart_init_mailbox(mgr);523 524	/* first communication with embedded */525	err = mixart_first_init(mgr);526        if (err < 0) {527		dev_err(&mgr->pci->dev, "miXart could not be set up\n");528		return err;529	}530 531       	/* create devices and mixer in accordance with HW options*/532        for (card_index = 0; card_index < mgr->num_cards; card_index++) {533		struct snd_mixart *chip = mgr->chip[card_index];534 535		err = snd_mixart_create_pcm(chip);536		if (err < 0)537			return err;538 539		if (card_index == 0) {540			err = snd_mixart_create_mixer(chip->mgr);541			if (err < 0)542	        		return err;543		}544 545		err = snd_card_register(chip->card);546		if (err < 0)547			return err;548	}549 550	dev_dbg(&mgr->pci->dev,551		"miXart firmware downloaded and successfully set up\n");552 553	return 0;554}555 556 557int snd_mixart_setup_firmware(struct mixart_mgr *mgr)558{559	static const char * const fw_files[3] = {560		"miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"561	};562	char path[32];563 564	const struct firmware *fw_entry;565	int i, err;566 567	for (i = 0; i < 3; i++) {568		sprintf(path, "mixart/%s", fw_files[i]);569		if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {570			dev_err(&mgr->pci->dev,571				"miXart: can't load firmware %s\n", path);572			return -ENOENT;573		}574		/* fake hwdep dsp record */575		err = mixart_dsp_load(mgr, i, fw_entry);576		release_firmware(fw_entry);577		if (err < 0)578			return err;579		mgr->dsp_loaded |= 1 << i;580	}581	return 0;582}583 584MODULE_FIRMWARE("mixart/miXart8.xlx");585MODULE_FIRMWARE("mixart/miXart8.elf");586MODULE_FIRMWARE("mixart/miXart8AES.xlx");587