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