162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Digigram miXart soundcards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * DSP firmware management
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2003 by Digigram <alsa@digigram.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/firmware.h>
1362306a36Sopenharmony_ci#include <linux/vmalloc.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include "mixart.h"
1962306a36Sopenharmony_ci#include "mixart_mixer.h"
2062306a36Sopenharmony_ci#include "mixart_core.h"
2162306a36Sopenharmony_ci#include "mixart_hwdep.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * mixart_wait_nice_for_register_value - wait for a value on a peudo register,
2662306a36Sopenharmony_ci * exit with a timeout
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * @mgr: pointer to miXart manager structure
2962306a36Sopenharmony_ci * @offset: unsigned pseudo_register base + offset of value
3062306a36Sopenharmony_ci * @is_egal: wait for the equal value
3162306a36Sopenharmony_ci * @value: value
3262306a36Sopenharmony_ci * @timeout: timeout in centisenconds
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_cistatic int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,
3562306a36Sopenharmony_ci					       u32 offset, int is_egal,
3662306a36Sopenharmony_ci					       u32 value, unsigned long timeout)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	unsigned long end_time = jiffies + (timeout * HZ / 100);
3962306a36Sopenharmony_ci	u32 read;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	do {	/* we may take too long time in this loop.
4262306a36Sopenharmony_ci		 * so give controls back to kernel if needed.
4362306a36Sopenharmony_ci		 */
4462306a36Sopenharmony_ci		cond_resched();
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		read = readl_be( MIXART_MEM( mgr, offset ));
4762306a36Sopenharmony_ci		if(is_egal) {
4862306a36Sopenharmony_ci			if(read == value) return 0;
4962306a36Sopenharmony_ci		}
5062306a36Sopenharmony_ci		else { /* wait for different value */
5162306a36Sopenharmony_ci			if(read != value) return 0;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	} while ( time_after_eq(end_time, jiffies) );
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return -EBUSY;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci  structures needed to upload elf code packets
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_cistruct snd_mixart_elf32_ehdr {
6362306a36Sopenharmony_ci	u8      e_ident[16];
6462306a36Sopenharmony_ci	__be16  e_type;
6562306a36Sopenharmony_ci	__be16  e_machine;
6662306a36Sopenharmony_ci	__be32  e_version;
6762306a36Sopenharmony_ci	__be32  e_entry;
6862306a36Sopenharmony_ci	__be32  e_phoff;
6962306a36Sopenharmony_ci	__be32  e_shoff;
7062306a36Sopenharmony_ci	__be32  e_flags;
7162306a36Sopenharmony_ci	__be16  e_ehsize;
7262306a36Sopenharmony_ci	__be16  e_phentsize;
7362306a36Sopenharmony_ci	__be16  e_phnum;
7462306a36Sopenharmony_ci	__be16  e_shentsize;
7562306a36Sopenharmony_ci	__be16  e_shnum;
7662306a36Sopenharmony_ci	__be16  e_shstrndx;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct snd_mixart_elf32_phdr {
8062306a36Sopenharmony_ci	__be32  p_type;
8162306a36Sopenharmony_ci	__be32  p_offset;
8262306a36Sopenharmony_ci	__be32  p_vaddr;
8362306a36Sopenharmony_ci	__be32  p_paddr;
8462306a36Sopenharmony_ci	__be32  p_filesz;
8562306a36Sopenharmony_ci	__be32  p_memsz;
8662306a36Sopenharmony_ci	__be32  p_flags;
8762306a36Sopenharmony_ci	__be32  p_align;
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp )
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	char                    elf32_magic_number[4] = {0x7f,'E','L','F'};
9362306a36Sopenharmony_ci	struct snd_mixart_elf32_ehdr *elf_header;
9462306a36Sopenharmony_ci	int                     i;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	elf_header = (struct snd_mixart_elf32_ehdr *)dsp->data;
9762306a36Sopenharmony_ci	for( i=0; i<4; i++ )
9862306a36Sopenharmony_ci		if ( elf32_magic_number[i] != elf_header->e_ident[i] )
9962306a36Sopenharmony_ci			return -EINVAL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if( elf_header->e_phoff != 0 ) {
10262306a36Sopenharmony_ci		struct snd_mixart_elf32_phdr     elf_programheader;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {
10562306a36Sopenharmony_ci			u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci			memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci			if(elf_programheader.p_type != 0) {
11062306a36Sopenharmony_ci				if( elf_programheader.p_filesz != 0 ) {
11162306a36Sopenharmony_ci					memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
11262306a36Sopenharmony_ci						     dsp->data + be32_to_cpu( elf_programheader.p_offset ),
11362306a36Sopenharmony_ci						     be32_to_cpu( elf_programheader.p_filesz ));
11462306a36Sopenharmony_ci				}
11562306a36Sopenharmony_ci			}
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * get basic information and init miXart
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* audio IDs for request to the board */
12662306a36Sopenharmony_ci#define MIXART_FIRST_ANA_AUDIO_ID       0
12762306a36Sopenharmony_ci#define MIXART_FIRST_DIG_AUDIO_ID       8
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int mixart_enum_connectors(struct mixart_mgr *mgr)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	u32 k;
13262306a36Sopenharmony_ci	int err;
13362306a36Sopenharmony_ci	struct mixart_msg request;
13462306a36Sopenharmony_ci	struct mixart_enum_connector_resp *connector;
13562306a36Sopenharmony_ci	struct mixart_audio_info_req  *audio_info_req;
13662306a36Sopenharmony_ci	struct mixart_audio_info_resp *audio_info;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	connector = kmalloc(sizeof(*connector), GFP_KERNEL);
13962306a36Sopenharmony_ci	audio_info_req = kmalloc(sizeof(*audio_info_req), GFP_KERNEL);
14062306a36Sopenharmony_ci	audio_info = kmalloc(sizeof(*audio_info), GFP_KERNEL);
14162306a36Sopenharmony_ci	if (! connector || ! audio_info_req || ! audio_info) {
14262306a36Sopenharmony_ci		err = -ENOMEM;
14362306a36Sopenharmony_ci		goto __error;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;
14762306a36Sopenharmony_ci	audio_info_req->micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;
14862306a36Sopenharmony_ci	audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;
15162306a36Sopenharmony_ci	request.uid = (struct mixart_uid){0,0};  /* board num = 0 */
15262306a36Sopenharmony_ci	request.data = NULL;
15362306a36Sopenharmony_ci	request.size = 0;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
15662306a36Sopenharmony_ci	if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
15762306a36Sopenharmony_ci		dev_err(&mgr->pci->dev,
15862306a36Sopenharmony_ci			"error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
15962306a36Sopenharmony_ci		err = -EINVAL;
16062306a36Sopenharmony_ci		goto __error;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	for(k=0; k < connector->uid_count; k++) {
16462306a36Sopenharmony_ci		struct mixart_pipe *pipe;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		if(k < MIXART_FIRST_DIG_AUDIO_ID) {
16762306a36Sopenharmony_ci			pipe = &mgr->chip[k/2]->pipe_out_ana;
16862306a36Sopenharmony_ci		} else {
16962306a36Sopenharmony_ci			pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci		if(k & 1) {
17262306a36Sopenharmony_ci			pipe->uid_right_connector = connector->uid[k];   /* odd */
17362306a36Sopenharmony_ci		} else {
17462306a36Sopenharmony_ci			pipe->uid_left_connector = connector->uid[k];    /* even */
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		/* dev_dbg(&mgr->pci->dev, "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
18062306a36Sopenharmony_ci		request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
18162306a36Sopenharmony_ci		request.uid = connector->uid[k];
18262306a36Sopenharmony_ci		request.data = audio_info_req;
18362306a36Sopenharmony_ci		request.size = sizeof(*audio_info_req);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
18662306a36Sopenharmony_ci		if( err < 0 ) {
18762306a36Sopenharmony_ci			dev_err(&mgr->pci->dev,
18862306a36Sopenharmony_ci				"error MSG_CONNECTOR_GET_AUDIO_INFO\n");
18962306a36Sopenharmony_ci			goto __error;
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci		/*dev_dbg(&mgr->pci->dev, "play  analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;
19562306a36Sopenharmony_ci	request.uid = (struct mixart_uid){0,0};  /* board num = 0 */
19662306a36Sopenharmony_ci	request.data = NULL;
19762306a36Sopenharmony_ci	request.size = 0;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
20062306a36Sopenharmony_ci	if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
20162306a36Sopenharmony_ci		dev_err(&mgr->pci->dev,
20262306a36Sopenharmony_ci			"error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
20362306a36Sopenharmony_ci		err = -EINVAL;
20462306a36Sopenharmony_ci		goto __error;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	for(k=0; k < connector->uid_count; k++) {
20862306a36Sopenharmony_ci		struct mixart_pipe *pipe;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		if(k < MIXART_FIRST_DIG_AUDIO_ID) {
21162306a36Sopenharmony_ci			pipe = &mgr->chip[k/2]->pipe_in_ana;
21262306a36Sopenharmony_ci		} else {
21362306a36Sopenharmony_ci			pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci		if(k & 1) {
21662306a36Sopenharmony_ci			pipe->uid_right_connector = connector->uid[k];   /* odd */
21762306a36Sopenharmony_ci		} else {
21862306a36Sopenharmony_ci			pipe->uid_left_connector = connector->uid[k];    /* even */
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		/* dev_dbg(&mgr->pci->dev, "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
22462306a36Sopenharmony_ci		request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
22562306a36Sopenharmony_ci		request.uid = connector->uid[k];
22662306a36Sopenharmony_ci		request.data = audio_info_req;
22762306a36Sopenharmony_ci		request.size = sizeof(*audio_info_req);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
23062306a36Sopenharmony_ci		if( err < 0 ) {
23162306a36Sopenharmony_ci			dev_err(&mgr->pci->dev,
23262306a36Sopenharmony_ci				"error MSG_CONNECTOR_GET_AUDIO_INFO\n");
23362306a36Sopenharmony_ci			goto __error;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci		/*dev_dbg(&mgr->pci->dev, "rec  analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	err = 0;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci __error:
24062306a36Sopenharmony_ci	kfree(connector);
24162306a36Sopenharmony_ci	kfree(audio_info_req);
24262306a36Sopenharmony_ci	kfree(audio_info);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return err;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int mixart_enum_physio(struct mixart_mgr *mgr)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	u32 k;
25062306a36Sopenharmony_ci	int err;
25162306a36Sopenharmony_ci	struct mixart_msg request;
25262306a36Sopenharmony_ci	struct mixart_uid get_console_mgr;
25362306a36Sopenharmony_ci	struct mixart_return_uid console_mgr;
25462306a36Sopenharmony_ci	struct mixart_uid_enumeration phys_io;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* get the uid for the console manager */
25762306a36Sopenharmony_ci	get_console_mgr.object_id = 0;
25862306a36Sopenharmony_ci	get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	request.message_id = MSG_CONSOLE_GET_CLOCK_UID;
26162306a36Sopenharmony_ci	request.uid = get_console_mgr;
26262306a36Sopenharmony_ci	request.data = &get_console_mgr;
26362306a36Sopenharmony_ci	request.size = sizeof(get_console_mgr);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if( (err < 0) || (console_mgr.error_code != 0) ) {
26862306a36Sopenharmony_ci		dev_dbg(&mgr->pci->dev,
26962306a36Sopenharmony_ci			"error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n",
27062306a36Sopenharmony_ci			console_mgr.error_code);
27162306a36Sopenharmony_ci		return -EINVAL;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* used later for clock issues ! */
27562306a36Sopenharmony_ci	mgr->uid_console_manager = console_mgr.uid;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;
27862306a36Sopenharmony_ci	request.uid = (struct mixart_uid){0,0};
27962306a36Sopenharmony_ci	request.data = &console_mgr.uid;
28062306a36Sopenharmony_ci	request.size = sizeof(console_mgr.uid);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
28362306a36Sopenharmony_ci	if( (err < 0) || ( phys_io.error_code != 0 ) ) {
28462306a36Sopenharmony_ci		dev_err(&mgr->pci->dev,
28562306a36Sopenharmony_ci			"error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n",
28662306a36Sopenharmony_ci			err, phys_io.error_code);
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* min 2 phys io per card (analog in + analog out) */
29162306a36Sopenharmony_ci	if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	for(k=0; k<mgr->num_cards; k++) {
29562306a36Sopenharmony_ci		mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
29662306a36Sopenharmony_ci		mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k];
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int mixart_first_init(struct mixart_mgr *mgr)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	u32 k;
30662306a36Sopenharmony_ci	int err;
30762306a36Sopenharmony_ci	struct mixart_msg request;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	err = mixart_enum_connectors(mgr);
31062306a36Sopenharmony_ci	if (err < 0)
31162306a36Sopenharmony_ci		return err;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	err = mixart_enum_physio(mgr);
31462306a36Sopenharmony_ci	if (err < 0)
31562306a36Sopenharmony_ci		return err;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
31862306a36Sopenharmony_ci	/* though why not here */
31962306a36Sopenharmony_ci	request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
32062306a36Sopenharmony_ci	request.uid = (struct mixart_uid){0,0};
32162306a36Sopenharmony_ci	request.data = NULL;
32262306a36Sopenharmony_ci	request.size = 0;
32362306a36Sopenharmony_ci	/* this command has no data. response is a 32 bit status */
32462306a36Sopenharmony_ci	err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
32562306a36Sopenharmony_ci	if( (err < 0) || (k != 0) ) {
32662306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
32762306a36Sopenharmony_ci		return err == 0 ? -EINVAL : err;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return 0;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/* firmware base addresses (when hard coded) */
33562306a36Sopenharmony_ci#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS   0x00600000
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmware *dsp)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	int           err, card_index;
34062306a36Sopenharmony_ci	u32           status_xilinx, status_elf, status_daught;
34162306a36Sopenharmony_ci	u32           val;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* read motherboard xilinx status */
34462306a36Sopenharmony_ci	status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
34562306a36Sopenharmony_ci	/* read elf status */
34662306a36Sopenharmony_ci	status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
34762306a36Sopenharmony_ci	/* read daughterboard xilinx status */
34862306a36Sopenharmony_ci	status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* motherboard xilinx status 5 will say that the board is performing a reset */
35162306a36Sopenharmony_ci	if (status_xilinx == 5) {
35262306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "miXart is resetting !\n");
35362306a36Sopenharmony_ci		return -EAGAIN; /* try again later */
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	switch (index)   {
35762306a36Sopenharmony_ci	case MIXART_MOTHERBOARD_XLX_INDEX:
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		/* xilinx already loaded ? */
36062306a36Sopenharmony_ci		if (status_xilinx == 4) {
36162306a36Sopenharmony_ci			dev_dbg(&mgr->pci->dev, "xilinx is already loaded !\n");
36262306a36Sopenharmony_ci			return 0;
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci		/* the status should be 0 == "idle" */
36562306a36Sopenharmony_ci		if (status_xilinx != 0) {
36662306a36Sopenharmony_ci			dev_err(&mgr->pci->dev,
36762306a36Sopenharmony_ci				"xilinx load error ! status = %d\n",
36862306a36Sopenharmony_ci				   status_xilinx);
36962306a36Sopenharmony_ci			return -EIO; /* modprob -r may help ? */
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		/* check xilinx validity */
37362306a36Sopenharmony_ci		if (((u32*)(dsp->data))[0] == 0xffffffff)
37462306a36Sopenharmony_ci			return -EINVAL;
37562306a36Sopenharmony_ci		if (dsp->size % 4)
37662306a36Sopenharmony_ci			return -EINVAL;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		/* set xilinx status to copying */
37962306a36Sopenharmony_ci		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		/* setup xilinx base address */
38262306a36Sopenharmony_ci		writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));
38362306a36Sopenharmony_ci		/* setup code size for xilinx file */
38462306a36Sopenharmony_ci		writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		/* copy xilinx code */
38762306a36Sopenharmony_ci		memcpy_toio(  MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS),  dsp->data,  dsp->size);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		/* set xilinx status to copy finished */
39062306a36Sopenharmony_ci		writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		/* return, because no further processing needed */
39362306a36Sopenharmony_ci		return 0;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	case MIXART_MOTHERBOARD_ELF_INDEX:
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		if (status_elf == 4) {
39862306a36Sopenharmony_ci			dev_dbg(&mgr->pci->dev, "elf file already loaded !\n");
39962306a36Sopenharmony_ci			return 0;
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		/* the status should be 0 == "idle" */
40362306a36Sopenharmony_ci		if (status_elf != 0) {
40462306a36Sopenharmony_ci			dev_err(&mgr->pci->dev,
40562306a36Sopenharmony_ci				"elf load error ! status = %d\n",
40662306a36Sopenharmony_ci				   status_elf);
40762306a36Sopenharmony_ci			return -EIO; /* modprob -r may help ? */
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* wait for xilinx status == 4 */
41162306a36Sopenharmony_ci		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
41262306a36Sopenharmony_ci		if (err < 0) {
41362306a36Sopenharmony_ci			dev_err(&mgr->pci->dev, "xilinx was not loaded or "
41462306a36Sopenharmony_ci				   "could not be started\n");
41562306a36Sopenharmony_ci			return err;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		/* init some data on the card */
41962306a36Sopenharmony_ci		writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */
42062306a36Sopenharmony_ci		writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) );         /* reset pointer to flow table on miXart */
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		/* set elf status to copying */
42362306a36Sopenharmony_ci		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		/* process the copying of the elf packets */
42662306a36Sopenharmony_ci		err = mixart_load_elf( mgr, dsp );
42762306a36Sopenharmony_ci		if (err < 0) return err;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		/* set elf status to copy finished */
43062306a36Sopenharmony_ci		writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		/* wait for elf status == 4 */
43362306a36Sopenharmony_ci		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
43462306a36Sopenharmony_ci		if (err < 0) {
43562306a36Sopenharmony_ci			dev_err(&mgr->pci->dev, "elf could not be started\n");
43662306a36Sopenharmony_ci			return err;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		/* miXart waits at this point on the pointer to the flow table */
44062306a36Sopenharmony_ci		writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		return 0;  /* return, another xilinx file has to be loaded before */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	case MIXART_AESEBUBOARD_XLX_INDEX:
44562306a36Sopenharmony_ci	default:
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		/* elf and xilinx should be loaded */
44862306a36Sopenharmony_ci		if (status_elf != 4 || status_xilinx != 4) {
44962306a36Sopenharmony_ci			dev_err(&mgr->pci->dev, "xilinx or elf not "
45062306a36Sopenharmony_ci			       "successfully loaded\n");
45162306a36Sopenharmony_ci			return -EIO; /* modprob -r may help ? */
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		/* wait for daughter detection != 0 */
45562306a36Sopenharmony_ci		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
45662306a36Sopenharmony_ci		if (err < 0) {
45762306a36Sopenharmony_ci			dev_err(&mgr->pci->dev, "error starting elf file\n");
45862306a36Sopenharmony_ci			return err;
45962306a36Sopenharmony_ci		}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* the board type can now be retrieved */
46262306a36Sopenharmony_ci		mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)
46562306a36Sopenharmony_ci			break;  /* no daughter board; the file does not have to be loaded, continue after the switch */
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		/* only if aesebu daughter board presence (elf code must run)  */
46862306a36Sopenharmony_ci		if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )
46962306a36Sopenharmony_ci			return -EINVAL;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		/* daughter should be idle */
47262306a36Sopenharmony_ci		if (status_daught != 0) {
47362306a36Sopenharmony_ci			dev_err(&mgr->pci->dev,
47462306a36Sopenharmony_ci				"daughter load error ! status = %d\n",
47562306a36Sopenharmony_ci			       status_daught);
47662306a36Sopenharmony_ci			return -EIO; /* modprob -r may help ? */
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		/* check daughterboard xilinx validity */
48062306a36Sopenharmony_ci		if (((u32*)(dsp->data))[0] == 0xffffffff)
48162306a36Sopenharmony_ci			return -EINVAL;
48262306a36Sopenharmony_ci		if (dsp->size % 4)
48362306a36Sopenharmony_ci			return -EINVAL;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		/* inform mixart about the size of the file */
48662306a36Sopenharmony_ci		writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		/* set daughterboard status to 1 */
48962306a36Sopenharmony_ci		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		/* wait for status == 2 */
49262306a36Sopenharmony_ci		err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
49362306a36Sopenharmony_ci		if (err < 0) {
49462306a36Sopenharmony_ci			dev_err(&mgr->pci->dev, "daughter board load error\n");
49562306a36Sopenharmony_ci			return err;
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		/* get the address where to write the file */
49962306a36Sopenharmony_ci		val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
50062306a36Sopenharmony_ci		if (!val)
50162306a36Sopenharmony_ci			return -EINVAL;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		/* copy daughterboard xilinx code */
50462306a36Sopenharmony_ci		memcpy_toio(  MIXART_MEM( mgr, val),  dsp->data,  dsp->size);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* set daughterboard status to 4 */
50762306a36Sopenharmony_ci		writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		/* continue with init */
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci	} /* end of switch file index*/
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci        /* wait for daughter status == 3 */
51462306a36Sopenharmony_ci        err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
51562306a36Sopenharmony_ci        if (err < 0) {
51662306a36Sopenharmony_ci		dev_err(&mgr->pci->dev,
51762306a36Sopenharmony_ci			   "daughter board could not be initialised\n");
51862306a36Sopenharmony_ci		return err;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* init mailbox (communication with embedded) */
52262306a36Sopenharmony_ci	snd_mixart_init_mailbox(mgr);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* first communication with embedded */
52562306a36Sopenharmony_ci	err = mixart_first_init(mgr);
52662306a36Sopenharmony_ci        if (err < 0) {
52762306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "miXart could not be set up\n");
52862306a36Sopenharmony_ci		return err;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci       	/* create devices and mixer in accordance with HW options*/
53262306a36Sopenharmony_ci        for (card_index = 0; card_index < mgr->num_cards; card_index++) {
53362306a36Sopenharmony_ci		struct snd_mixart *chip = mgr->chip[card_index];
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		err = snd_mixart_create_pcm(chip);
53662306a36Sopenharmony_ci		if (err < 0)
53762306a36Sopenharmony_ci			return err;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		if (card_index == 0) {
54062306a36Sopenharmony_ci			err = snd_mixart_create_mixer(chip->mgr);
54162306a36Sopenharmony_ci			if (err < 0)
54262306a36Sopenharmony_ci	        		return err;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		err = snd_card_register(chip->card);
54662306a36Sopenharmony_ci		if (err < 0)
54762306a36Sopenharmony_ci			return err;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	dev_dbg(&mgr->pci->dev,
55162306a36Sopenharmony_ci		"miXart firmware downloaded and successfully set up\n");
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return 0;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ciint snd_mixart_setup_firmware(struct mixart_mgr *mgr)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	static const char * const fw_files[3] = {
56062306a36Sopenharmony_ci		"miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"
56162306a36Sopenharmony_ci	};
56262306a36Sopenharmony_ci	char path[32];
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	const struct firmware *fw_entry;
56562306a36Sopenharmony_ci	int i, err;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
56862306a36Sopenharmony_ci		sprintf(path, "mixart/%s", fw_files[i]);
56962306a36Sopenharmony_ci		if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
57062306a36Sopenharmony_ci			dev_err(&mgr->pci->dev,
57162306a36Sopenharmony_ci				"miXart: can't load firmware %s\n", path);
57262306a36Sopenharmony_ci			return -ENOENT;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci		/* fake hwdep dsp record */
57562306a36Sopenharmony_ci		err = mixart_dsp_load(mgr, i, fw_entry);
57662306a36Sopenharmony_ci		release_firmware(fw_entry);
57762306a36Sopenharmony_ci		if (err < 0)
57862306a36Sopenharmony_ci			return err;
57962306a36Sopenharmony_ci		mgr->dsp_loaded |= 1 << i;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	return 0;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciMODULE_FIRMWARE("mixart/miXart8.xlx");
58562306a36Sopenharmony_ciMODULE_FIRMWARE("mixart/miXart8.elf");
58662306a36Sopenharmony_ciMODULE_FIRMWARE("mixart/miXart8AES.xlx");
587