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