162306a36Sopenharmony_ci/**************************************************************************** 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci Copyright Echo Digital Audio Corporation (c) 1998 - 2004 462306a36Sopenharmony_ci All rights reserved 562306a36Sopenharmony_ci www.echoaudio.com 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci This file is part of Echo Digital Audio's generic driver library. 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci Echo Digital Audio's generic driver library is free software; 1062306a36Sopenharmony_ci you can redistribute it and/or modify it under the terms of 1162306a36Sopenharmony_ci the GNU General Public License as published by the Free Software 1262306a36Sopenharmony_ci Foundation. 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci This program is distributed in the hope that it will be useful, 1562306a36Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 1662306a36Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1762306a36Sopenharmony_ci GNU General Public License for more details. 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci You should have received a copy of the GNU General Public License 2062306a36Sopenharmony_ci along with this program; if not, write to the Free Software 2162306a36Sopenharmony_ci Foundation, Inc., 59 Temple Place - Suite 330, Boston, 2262306a36Sopenharmony_ci MA 02111-1307, USA. 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci ************************************************************************* 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci Translation from C++ and adaptation for use in ALSA-Driver 2762306a36Sopenharmony_ci were made by Giuliano Pochini <pochini@shiny.it> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci****************************************************************************/ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#if PAGE_SIZE < 4096 3262306a36Sopenharmony_ci#error PAGE_SIZE is < 4k 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int restore_dsp_rettings(struct echoaudio *chip); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Some vector commands involve the DSP reading or writing data to and from the 3962306a36Sopenharmony_cicomm page; if you send one of these commands to the DSP, it will complete the 4062306a36Sopenharmony_cicommand and then write a non-zero value to the Handshake field in the 4162306a36Sopenharmony_cicomm page. This function waits for the handshake to show up. */ 4262306a36Sopenharmony_cistatic int wait_handshake(struct echoaudio *chip) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci int i; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Wait up to 20ms for the handshake from the DSP */ 4762306a36Sopenharmony_ci for (i = 0; i < HANDSHAKE_TIMEOUT; i++) { 4862306a36Sopenharmony_ci /* Look for the handshake value */ 4962306a36Sopenharmony_ci barrier(); 5062306a36Sopenharmony_ci if (chip->comm_page->handshake) { 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci udelay(1); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci dev_err(chip->card->dev, "wait_handshake(): Timeout waiting for DSP\n"); 5762306a36Sopenharmony_ci return -EBUSY; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Much of the interaction between the DSP and the driver is done via vector 6362306a36Sopenharmony_cicommands; send_vector writes a vector command to the DSP. Typically, this 6462306a36Sopenharmony_cicauses the DSP to read or write fields in the comm page. 6562306a36Sopenharmony_ciPCI posting is not required thanks to the handshake logic. */ 6662306a36Sopenharmony_cistatic int send_vector(struct echoaudio *chip, u32 command) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci wmb(); /* Flush all pending writes before sending the command */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Wait up to 100ms for the "vector busy" bit to be off */ 7362306a36Sopenharmony_ci for (i = 0; i < VECTOR_BUSY_TIMEOUT; i++) { 7462306a36Sopenharmony_ci if (!(get_dsp_register(chip, CHI32_VECTOR_REG) & 7562306a36Sopenharmony_ci CHI32_VECTOR_BUSY)) { 7662306a36Sopenharmony_ci set_dsp_register(chip, CHI32_VECTOR_REG, command); 7762306a36Sopenharmony_ci /*if (i) DE_ACT(("send_vector time: %d\n", i));*/ 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci udelay(1); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci dev_err(chip->card->dev, "timeout on send_vector\n"); 8462306a36Sopenharmony_ci return -EBUSY; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* write_dsp writes a 32-bit value to the DSP; this is used almost 9062306a36Sopenharmony_ciexclusively for loading the DSP. */ 9162306a36Sopenharmony_cistatic int write_dsp(struct echoaudio *chip, u32 data) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci u32 status, i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (i = 0; i < 10000000; i++) { /* timeout = 10s */ 9662306a36Sopenharmony_ci status = get_dsp_register(chip, CHI32_STATUS_REG); 9762306a36Sopenharmony_ci if ((status & CHI32_STATUS_HOST_WRITE_EMPTY) != 0) { 9862306a36Sopenharmony_ci set_dsp_register(chip, CHI32_DATA_REG, data); 9962306a36Sopenharmony_ci wmb(); /* write it immediately */ 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci udelay(1); 10362306a36Sopenharmony_ci cond_resched(); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci chip->bad_board = true; /* Set true until DSP re-loaded */ 10762306a36Sopenharmony_ci dev_dbg(chip->card->dev, "write_dsp: Set bad_board to true\n"); 10862306a36Sopenharmony_ci return -EIO; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* read_dsp reads a 32-bit value from the DSP; this is used almost 11462306a36Sopenharmony_ciexclusively for loading the DSP and checking the status of the ASIC. */ 11562306a36Sopenharmony_cistatic int read_dsp(struct echoaudio *chip, u32 *data) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci u32 status, i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < READ_DSP_TIMEOUT; i++) { 12062306a36Sopenharmony_ci status = get_dsp_register(chip, CHI32_STATUS_REG); 12162306a36Sopenharmony_ci if ((status & CHI32_STATUS_HOST_READ_FULL) != 0) { 12262306a36Sopenharmony_ci *data = get_dsp_register(chip, CHI32_DATA_REG); 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci udelay(1); 12662306a36Sopenharmony_ci cond_resched(); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci chip->bad_board = true; /* Set true until DSP re-loaded */ 13062306a36Sopenharmony_ci dev_err(chip->card->dev, "read_dsp: Set bad_board to true\n"); 13162306a36Sopenharmony_ci return -EIO; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/**************************************************************************** 13762306a36Sopenharmony_ci Firmware loading functions 13862306a36Sopenharmony_ci ****************************************************************************/ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* This function is used to read back the serial number from the DSP; 14162306a36Sopenharmony_cithis is triggered by the SET_COMMPAGE_ADDR command. 14262306a36Sopenharmony_ciOnly some early Echogals products have serial numbers in the ROM; 14362306a36Sopenharmony_cithe serial number is not used, but you still need to do this as 14462306a36Sopenharmony_cipart of the DSP load process. */ 14562306a36Sopenharmony_cistatic int read_sn(struct echoaudio *chip) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int i; 14862306a36Sopenharmony_ci u32 sn[6]; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 15162306a36Sopenharmony_ci if (read_dsp(chip, &sn[i])) { 15262306a36Sopenharmony_ci dev_err(chip->card->dev, 15362306a36Sopenharmony_ci "Failed to read serial number\n"); 15462306a36Sopenharmony_ci return -EIO; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci dev_dbg(chip->card->dev, 15862306a36Sopenharmony_ci "Read serial number %08x %08x %08x %08x %08x\n", 15962306a36Sopenharmony_ci sn[0], sn[1], sn[2], sn[3], sn[4]); 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#ifndef ECHOCARD_HAS_ASIC 16662306a36Sopenharmony_ci/* This card has no ASIC, just return ok */ 16762306a36Sopenharmony_cistatic inline int check_asic_status(struct echoaudio *chip) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci chip->asic_loaded = true; 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#endif /* !ECHOCARD_HAS_ASIC */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_ASIC 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* Load ASIC code - done after the DSP is loaded */ 18062306a36Sopenharmony_cistatic int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci const struct firmware *fw; 18362306a36Sopenharmony_ci int err; 18462306a36Sopenharmony_ci u32 i, size; 18562306a36Sopenharmony_ci u8 *code; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci err = get_firmware(&fw, chip, asic); 18862306a36Sopenharmony_ci if (err < 0) { 18962306a36Sopenharmony_ci dev_warn(chip->card->dev, "Firmware not found !\n"); 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci code = (u8 *)fw->data; 19462306a36Sopenharmony_ci size = fw->size; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Send the "Here comes the ASIC" command */ 19762306a36Sopenharmony_ci if (write_dsp(chip, cmd) < 0) 19862306a36Sopenharmony_ci goto la_error; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Write length of ASIC file in bytes */ 20162306a36Sopenharmony_ci if (write_dsp(chip, size) < 0) 20262306a36Sopenharmony_ci goto la_error; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci for (i = 0; i < size; i++) { 20562306a36Sopenharmony_ci if (write_dsp(chip, code[i]) < 0) 20662306a36Sopenharmony_ci goto la_error; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci free_firmware(fw, chip); 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cila_error: 21362306a36Sopenharmony_ci dev_err(chip->card->dev, "failed on write_dsp\n"); 21462306a36Sopenharmony_ci free_firmware(fw, chip); 21562306a36Sopenharmony_ci return -EIO; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_ASIC */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#ifdef DSP_56361 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* Install the resident loader for 56361 DSPs; The resident loader is on 22562306a36Sopenharmony_cithe EPROM on the board for 56301 DSP. The resident loader is a tiny little 22662306a36Sopenharmony_ciprogram that is used to load the real DSP code. */ 22762306a36Sopenharmony_cistatic int install_resident_loader(struct echoaudio *chip) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci u32 address; 23062306a36Sopenharmony_ci int index, words, i; 23162306a36Sopenharmony_ci u16 *code; 23262306a36Sopenharmony_ci u32 status; 23362306a36Sopenharmony_ci const struct firmware *fw; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 56361 cards only! This check is required by the old 56301-based 23662306a36Sopenharmony_ci Mona and Gina24 */ 23762306a36Sopenharmony_ci if (chip->device_id != DEVICE_ID_56361) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Look to see if the resident loader is present. If the resident 24162306a36Sopenharmony_ci loader is already installed, host flag 5 will be on. */ 24262306a36Sopenharmony_ci status = get_dsp_register(chip, CHI32_STATUS_REG); 24362306a36Sopenharmony_ci if (status & CHI32_STATUS_REG_HF5) { 24462306a36Sopenharmony_ci dev_dbg(chip->card->dev, 24562306a36Sopenharmony_ci "Resident loader already installed; status is 0x%x\n", 24662306a36Sopenharmony_ci status); 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci i = get_firmware(&fw, chip, FW_361_LOADER); 25162306a36Sopenharmony_ci if (i < 0) { 25262306a36Sopenharmony_ci dev_warn(chip->card->dev, "Firmware not found !\n"); 25362306a36Sopenharmony_ci return i; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* The DSP code is an array of 16 bit words. The array is divided up 25762306a36Sopenharmony_ci into sections. The first word of each section is the size in words, 25862306a36Sopenharmony_ci followed by the section type. 25962306a36Sopenharmony_ci Since DSP addresses and data are 24 bits wide, they each take up two 26062306a36Sopenharmony_ci 16 bit words in the array. 26162306a36Sopenharmony_ci This is a lot like the other loader loop, but it's not a loop, you 26262306a36Sopenharmony_ci don't write the memory type, and you don't write a zero at the end. */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Set DSP format bits for 24 bit mode */ 26562306a36Sopenharmony_ci set_dsp_register(chip, CHI32_CONTROL_REG, 26662306a36Sopenharmony_ci get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci code = (u16 *)fw->data; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Skip the header section; the first word in the array is the size 27162306a36Sopenharmony_ci of the first section, so the first real section of code is pointed 27262306a36Sopenharmony_ci to by Code[0]. */ 27362306a36Sopenharmony_ci index = code[0]; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Skip the section size, LRS block type, and DSP memory type */ 27662306a36Sopenharmony_ci index += 3; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Get the number of DSP words to write */ 27962306a36Sopenharmony_ci words = code[index++]; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Get the DSP address for this block; 24 bits, so build from two words */ 28262306a36Sopenharmony_ci address = ((u32)code[index] << 16) + code[index + 1]; 28362306a36Sopenharmony_ci index += 2; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Write the count to the DSP */ 28662306a36Sopenharmony_ci if (write_dsp(chip, words)) { 28762306a36Sopenharmony_ci dev_err(chip->card->dev, 28862306a36Sopenharmony_ci "install_resident_loader: Failed to write word count!\n"); 28962306a36Sopenharmony_ci goto irl_error; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci /* Write the DSP address */ 29262306a36Sopenharmony_ci if (write_dsp(chip, address)) { 29362306a36Sopenharmony_ci dev_err(chip->card->dev, 29462306a36Sopenharmony_ci "install_resident_loader: Failed to write DSP address!\n"); 29562306a36Sopenharmony_ci goto irl_error; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci /* Write out this block of code to the DSP */ 29862306a36Sopenharmony_ci for (i = 0; i < words; i++) { 29962306a36Sopenharmony_ci u32 data; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci data = ((u32)code[index] << 16) + code[index + 1]; 30262306a36Sopenharmony_ci if (write_dsp(chip, data)) { 30362306a36Sopenharmony_ci dev_err(chip->card->dev, 30462306a36Sopenharmony_ci "install_resident_loader: Failed to write DSP code\n"); 30562306a36Sopenharmony_ci goto irl_error; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci index += 2; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Wait for flag 5 to come up */ 31162306a36Sopenharmony_ci for (i = 0; i < 200; i++) { /* Timeout is 50us * 200 = 10ms */ 31262306a36Sopenharmony_ci udelay(50); 31362306a36Sopenharmony_ci status = get_dsp_register(chip, CHI32_STATUS_REG); 31462306a36Sopenharmony_ci if (status & CHI32_STATUS_REG_HF5) 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (i == 200) { 31962306a36Sopenharmony_ci dev_err(chip->card->dev, "Resident loader failed to set HF5\n"); 32062306a36Sopenharmony_ci goto irl_error; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci dev_dbg(chip->card->dev, "Resident loader successfully installed\n"); 32462306a36Sopenharmony_ci free_firmware(fw, chip); 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciirl_error: 32862306a36Sopenharmony_ci free_firmware(fw, chip); 32962306a36Sopenharmony_ci return -EIO; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci#endif /* DSP_56361 */ 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int load_dsp(struct echoaudio *chip, u16 *code) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci u32 address, data; 33862306a36Sopenharmony_ci int index, words, i; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (chip->dsp_code == code) { 34162306a36Sopenharmony_ci dev_warn(chip->card->dev, "DSP is already loaded!\n"); 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci chip->bad_board = true; /* Set true until DSP loaded */ 34562306a36Sopenharmony_ci chip->dsp_code = NULL; /* Current DSP code not loaded */ 34662306a36Sopenharmony_ci chip->asic_loaded = false; /* Loading the DSP code will reset the ASIC */ 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci dev_dbg(chip->card->dev, "load_dsp: Set bad_board to true\n"); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* If this board requires a resident loader, install it. */ 35162306a36Sopenharmony_ci#ifdef DSP_56361 35262306a36Sopenharmony_ci i = install_resident_loader(chip); 35362306a36Sopenharmony_ci if (i < 0) 35462306a36Sopenharmony_ci return i; 35562306a36Sopenharmony_ci#endif 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Send software reset command */ 35862306a36Sopenharmony_ci if (send_vector(chip, DSP_VC_RESET) < 0) { 35962306a36Sopenharmony_ci dev_err(chip->card->dev, 36062306a36Sopenharmony_ci "LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"); 36162306a36Sopenharmony_ci return -EIO; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci /* Delay 10us */ 36462306a36Sopenharmony_ci udelay(10); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Wait 10ms for HF3 to indicate that software reset is complete */ 36762306a36Sopenharmony_ci for (i = 0; i < 1000; i++) { /* Timeout is 10us * 1000 = 10ms */ 36862306a36Sopenharmony_ci if (get_dsp_register(chip, CHI32_STATUS_REG) & 36962306a36Sopenharmony_ci CHI32_STATUS_REG_HF3) 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci udelay(10); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (i == 1000) { 37562306a36Sopenharmony_ci dev_err(chip->card->dev, 37662306a36Sopenharmony_ci "load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"); 37762306a36Sopenharmony_ci return -EIO; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Set DSP format bits for 24 bit mode now that soft reset is done */ 38162306a36Sopenharmony_ci set_dsp_register(chip, CHI32_CONTROL_REG, 38262306a36Sopenharmony_ci get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Main loader loop */ 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci index = code[0]; 38762306a36Sopenharmony_ci for (;;) { 38862306a36Sopenharmony_ci int block_type, mem_type; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Total Block Size */ 39162306a36Sopenharmony_ci index++; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Block Type */ 39462306a36Sopenharmony_ci block_type = code[index]; 39562306a36Sopenharmony_ci if (block_type == 4) /* We're finished */ 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci index++; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Memory Type P=0,X=1,Y=2 */ 40162306a36Sopenharmony_ci mem_type = code[index++]; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Block Code Size */ 40462306a36Sopenharmony_ci words = code[index++]; 40562306a36Sopenharmony_ci if (words == 0) /* We're finished */ 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Start Address */ 40962306a36Sopenharmony_ci address = ((u32)code[index] << 16) + code[index + 1]; 41062306a36Sopenharmony_ci index += 2; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (write_dsp(chip, words) < 0) { 41362306a36Sopenharmony_ci dev_err(chip->card->dev, 41462306a36Sopenharmony_ci "load_dsp: failed to write number of DSP words\n"); 41562306a36Sopenharmony_ci return -EIO; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci if (write_dsp(chip, address) < 0) { 41862306a36Sopenharmony_ci dev_err(chip->card->dev, 41962306a36Sopenharmony_ci "load_dsp: failed to write DSP address\n"); 42062306a36Sopenharmony_ci return -EIO; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci if (write_dsp(chip, mem_type) < 0) { 42362306a36Sopenharmony_ci dev_err(chip->card->dev, 42462306a36Sopenharmony_ci "load_dsp: failed to write DSP memory type\n"); 42562306a36Sopenharmony_ci return -EIO; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci /* Code */ 42862306a36Sopenharmony_ci for (i = 0; i < words; i++, index+=2) { 42962306a36Sopenharmony_ci data = ((u32)code[index] << 16) + code[index + 1]; 43062306a36Sopenharmony_ci if (write_dsp(chip, data) < 0) { 43162306a36Sopenharmony_ci dev_err(chip->card->dev, 43262306a36Sopenharmony_ci "load_dsp: failed to write DSP data\n"); 43362306a36Sopenharmony_ci return -EIO; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (write_dsp(chip, 0) < 0) { /* We're done!!! */ 43962306a36Sopenharmony_ci dev_err(chip->card->dev, 44062306a36Sopenharmony_ci "load_dsp: Failed to write final zero\n"); 44162306a36Sopenharmony_ci return -EIO; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci udelay(10); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci for (i = 0; i < 5000; i++) { /* Timeout is 100us * 5000 = 500ms */ 44662306a36Sopenharmony_ci /* Wait for flag 4 - indicates that the DSP loaded OK */ 44762306a36Sopenharmony_ci if (get_dsp_register(chip, CHI32_STATUS_REG) & 44862306a36Sopenharmony_ci CHI32_STATUS_REG_HF4) { 44962306a36Sopenharmony_ci set_dsp_register(chip, CHI32_CONTROL_REG, 45062306a36Sopenharmony_ci get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) { 45362306a36Sopenharmony_ci dev_err(chip->card->dev, 45462306a36Sopenharmony_ci "load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"); 45562306a36Sopenharmony_ci return -EIO; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (write_dsp(chip, chip->comm_page_phys) < 0) { 45962306a36Sopenharmony_ci dev_err(chip->card->dev, 46062306a36Sopenharmony_ci "load_dsp: Failed to write comm page address\n"); 46162306a36Sopenharmony_ci return -EIO; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Get the serial number via slave mode. 46562306a36Sopenharmony_ci This is triggered by the SET_COMMPAGE_ADDR command. 46662306a36Sopenharmony_ci We don't actually use the serial number but we have to 46762306a36Sopenharmony_ci get it as part of the DSP init voodoo. */ 46862306a36Sopenharmony_ci if (read_sn(chip) < 0) { 46962306a36Sopenharmony_ci dev_err(chip->card->dev, 47062306a36Sopenharmony_ci "load_dsp: Failed to read serial number\n"); 47162306a36Sopenharmony_ci return -EIO; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci chip->dsp_code = code; /* Show which DSP code loaded */ 47562306a36Sopenharmony_ci chip->bad_board = false; /* DSP OK */ 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci udelay(100); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci dev_err(chip->card->dev, 48262306a36Sopenharmony_ci "load_dsp: DSP load timed out waiting for HF4\n"); 48362306a36Sopenharmony_ci return -EIO; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* load_firmware takes care of loading the DSP and any ASIC code. */ 48962306a36Sopenharmony_cistatic int load_firmware(struct echoaudio *chip) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci const struct firmware *fw; 49262306a36Sopenharmony_ci int box_type, err; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (snd_BUG_ON(!chip->comm_page)) 49562306a36Sopenharmony_ci return -EPERM; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* See if the ASIC is present and working - only if the DSP is already loaded */ 49862306a36Sopenharmony_ci if (chip->dsp_code) { 49962306a36Sopenharmony_ci box_type = check_asic_status(chip); 50062306a36Sopenharmony_ci if (box_type >= 0) 50162306a36Sopenharmony_ci return box_type; 50262306a36Sopenharmony_ci /* ASIC check failed; force the DSP to reload */ 50362306a36Sopenharmony_ci chip->dsp_code = NULL; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci err = get_firmware(&fw, chip, chip->dsp_code_to_load); 50762306a36Sopenharmony_ci if (err < 0) 50862306a36Sopenharmony_ci return err; 50962306a36Sopenharmony_ci err = load_dsp(chip, (u16 *)fw->data); 51062306a36Sopenharmony_ci free_firmware(fw, chip); 51162306a36Sopenharmony_ci if (err < 0) 51262306a36Sopenharmony_ci return err; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci box_type = load_asic(chip); 51562306a36Sopenharmony_ci if (box_type < 0) 51662306a36Sopenharmony_ci return box_type; /* error */ 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return box_type; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/**************************************************************************** 52462306a36Sopenharmony_ci Mixer functions 52562306a36Sopenharmony_ci ****************************************************************************/ 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci#if defined(ECHOCARD_HAS_INPUT_NOMINAL_LEVEL) || \ 52862306a36Sopenharmony_ci defined(ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL) 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */ 53162306a36Sopenharmony_cistatic int set_nominal_level(struct echoaudio *chip, u16 index, char consumer) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci if (snd_BUG_ON(index >= num_busses_out(chip) + num_busses_in(chip))) 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Wait for the handshake (OK even if ASIC is not loaded) */ 53762306a36Sopenharmony_ci if (wait_handshake(chip)) 53862306a36Sopenharmony_ci return -EIO; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci chip->nominal_level[index] = consumer; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (consumer) 54362306a36Sopenharmony_ci chip->comm_page->nominal_level_mask |= cpu_to_le32(1 << index); 54462306a36Sopenharmony_ci else 54562306a36Sopenharmony_ci chip->comm_page->nominal_level_mask &= ~cpu_to_le32(1 << index); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_*_NOMINAL_LEVEL */ 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* Set the gain for a single physical output channel (dB). */ 55562306a36Sopenharmony_cistatic int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci if (snd_BUG_ON(channel >= num_busses_out(chip))) 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (wait_handshake(chip)) 56162306a36Sopenharmony_ci return -EIO; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Save the new value */ 56462306a36Sopenharmony_ci chip->output_gain[channel] = gain; 56562306a36Sopenharmony_ci chip->comm_page->line_out_level[channel] = gain; 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR 57262306a36Sopenharmony_ci/* Set the monitor level from an input bus to an output bus. */ 57362306a36Sopenharmony_cistatic int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input, 57462306a36Sopenharmony_ci s8 gain) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci if (snd_BUG_ON(output >= num_busses_out(chip) || 57762306a36Sopenharmony_ci input >= num_busses_in(chip))) 57862306a36Sopenharmony_ci return -EINVAL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (wait_handshake(chip)) 58162306a36Sopenharmony_ci return -EIO; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci chip->monitor_gain[output][input] = gain; 58462306a36Sopenharmony_ci chip->comm_page->monitors[monitor_index(chip, output, input)] = gain; 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_MONITOR */ 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* Tell the DSP to read and update output, nominal & monitor levels in comm page. */ 59162306a36Sopenharmony_cistatic int update_output_line_level(struct echoaudio *chip) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci if (wait_handshake(chip)) 59462306a36Sopenharmony_ci return -EIO; 59562306a36Sopenharmony_ci clear_handshake(chip); 59662306a36Sopenharmony_ci return send_vector(chip, DSP_VC_UPDATE_OUTVOL); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* Tell the DSP to read and update input levels in comm page */ 60262306a36Sopenharmony_cistatic int update_input_line_level(struct echoaudio *chip) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (wait_handshake(chip)) 60562306a36Sopenharmony_ci return -EIO; 60662306a36Sopenharmony_ci clear_handshake(chip); 60762306a36Sopenharmony_ci return send_vector(chip, DSP_VC_UPDATE_INGAIN); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/* set_meters_on turns the meters on or off. If meters are turned on, the DSP 61362306a36Sopenharmony_ciwill write the meter and clock detect values to the comm page at about 30Hz */ 61462306a36Sopenharmony_cistatic void set_meters_on(struct echoaudio *chip, char on) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci if (on && !chip->meters_enabled) { 61762306a36Sopenharmony_ci send_vector(chip, DSP_VC_METERS_ON); 61862306a36Sopenharmony_ci chip->meters_enabled = 1; 61962306a36Sopenharmony_ci } else if (!on && chip->meters_enabled) { 62062306a36Sopenharmony_ci send_vector(chip, DSP_VC_METERS_OFF); 62162306a36Sopenharmony_ci chip->meters_enabled = 0; 62262306a36Sopenharmony_ci memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED, 62362306a36Sopenharmony_ci DSP_MAXPIPES); 62462306a36Sopenharmony_ci memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED, 62562306a36Sopenharmony_ci DSP_MAXPIPES); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* Fill out an the given array using the current values in the comm page. 63262306a36Sopenharmony_ciMeters are written in the comm page by the DSP in this order: 63362306a36Sopenharmony_ci Output busses 63462306a36Sopenharmony_ci Input busses 63562306a36Sopenharmony_ci Output pipes (vmixer cards only) 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ciThis function assumes there are no more than 16 in/out busses or pipes 63862306a36Sopenharmony_ciMeters is an array [3][16][2] of long. */ 63962306a36Sopenharmony_cistatic void get_audio_meters(struct echoaudio *chip, long *meters) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci unsigned int i, m, n; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for (i = 0 ; i < 96; i++) 64462306a36Sopenharmony_ci meters[i] = 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci for (m = 0, n = 0, i = 0; i < num_busses_out(chip); i++, m++) { 64762306a36Sopenharmony_ci meters[n++] = chip->comm_page->vu_meter[m]; 64862306a36Sopenharmony_ci meters[n++] = chip->comm_page->peak_meter[m]; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci#ifdef ECHOCARD_ECHO3G 65262306a36Sopenharmony_ci m = E3G_MAX_OUTPUTS; /* Skip unused meters */ 65362306a36Sopenharmony_ci#endif 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci for (n = 32, i = 0; i < num_busses_in(chip); i++, m++) { 65662306a36Sopenharmony_ci meters[n++] = chip->comm_page->vu_meter[m]; 65762306a36Sopenharmony_ci meters[n++] = chip->comm_page->peak_meter[m]; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER 66062306a36Sopenharmony_ci for (n = 64, i = 0; i < num_pipes_out(chip); i++, m++) { 66162306a36Sopenharmony_ci meters[n++] = chip->comm_page->vu_meter[m]; 66262306a36Sopenharmony_ci meters[n++] = chip->comm_page->peak_meter[m]; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci#endif 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int restore_dsp_rettings(struct echoaudio *chip) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci int i, o, err; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci err = check_asic_status(chip); 67462306a36Sopenharmony_ci if (err < 0) 67562306a36Sopenharmony_ci return err; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* Gina20/Darla20 only. Should be harmless for other cards. */ 67862306a36Sopenharmony_ci chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF; 67962306a36Sopenharmony_ci chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF; 68062306a36Sopenharmony_ci chip->comm_page->handshake = cpu_to_le32(0xffffffff); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Restore output busses */ 68362306a36Sopenharmony_ci for (i = 0; i < num_busses_out(chip); i++) { 68462306a36Sopenharmony_ci err = set_output_gain(chip, i, chip->output_gain[i]); 68562306a36Sopenharmony_ci if (err < 0) 68662306a36Sopenharmony_ci return err; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER 69062306a36Sopenharmony_ci for (i = 0; i < num_pipes_out(chip); i++) 69162306a36Sopenharmony_ci for (o = 0; o < num_busses_out(chip); o++) { 69262306a36Sopenharmony_ci err = set_vmixer_gain(chip, o, i, 69362306a36Sopenharmony_ci chip->vmixer_gain[o][i]); 69462306a36Sopenharmony_ci if (err < 0) 69562306a36Sopenharmony_ci return err; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci if (update_vmixer_level(chip) < 0) 69862306a36Sopenharmony_ci return -EIO; 69962306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */ 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR 70262306a36Sopenharmony_ci for (o = 0; o < num_busses_out(chip); o++) 70362306a36Sopenharmony_ci for (i = 0; i < num_busses_in(chip); i++) { 70462306a36Sopenharmony_ci err = set_monitor_gain(chip, o, i, 70562306a36Sopenharmony_ci chip->monitor_gain[o][i]); 70662306a36Sopenharmony_ci if (err < 0) 70762306a36Sopenharmony_ci return err; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_MONITOR */ 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN 71262306a36Sopenharmony_ci for (i = 0; i < num_busses_in(chip); i++) { 71362306a36Sopenharmony_ci err = set_input_gain(chip, i, chip->input_gain[i]); 71462306a36Sopenharmony_ci if (err < 0) 71562306a36Sopenharmony_ci return err; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_GAIN */ 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci err = update_output_line_level(chip); 72062306a36Sopenharmony_ci if (err < 0) 72162306a36Sopenharmony_ci return err; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci err = update_input_line_level(chip); 72462306a36Sopenharmony_ci if (err < 0) 72562306a36Sopenharmony_ci return err; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci err = set_sample_rate(chip, chip->sample_rate); 72862306a36Sopenharmony_ci if (err < 0) 72962306a36Sopenharmony_ci return err; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (chip->meters_enabled) { 73262306a36Sopenharmony_ci err = send_vector(chip, DSP_VC_METERS_ON); 73362306a36Sopenharmony_ci if (err < 0) 73462306a36Sopenharmony_ci return err; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH 73862306a36Sopenharmony_ci if (set_digital_mode(chip, chip->digital_mode) < 0) 73962306a36Sopenharmony_ci return -EIO; 74062306a36Sopenharmony_ci#endif 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 74362306a36Sopenharmony_ci if (set_professional_spdif(chip, chip->professional_spdif) < 0) 74462306a36Sopenharmony_ci return -EIO; 74562306a36Sopenharmony_ci#endif 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER 74862306a36Sopenharmony_ci if (set_phantom_power(chip, chip->phantom_power) < 0) 74962306a36Sopenharmony_ci return -EIO; 75062306a36Sopenharmony_ci#endif 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK 75362306a36Sopenharmony_ci /* set_input_clock() also restores automute setting */ 75462306a36Sopenharmony_ci if (set_input_clock(chip, chip->input_clock) < 0) 75562306a36Sopenharmony_ci return -EIO; 75662306a36Sopenharmony_ci#endif 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH 75962306a36Sopenharmony_ci if (set_output_clock(chip, chip->output_clock) < 0) 76062306a36Sopenharmony_ci return -EIO; 76162306a36Sopenharmony_ci#endif 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (wait_handshake(chip) < 0) 76462306a36Sopenharmony_ci return -EIO; 76562306a36Sopenharmony_ci clear_handshake(chip); 76662306a36Sopenharmony_ci if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0) 76762306a36Sopenharmony_ci return -EIO; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/**************************************************************************** 77562306a36Sopenharmony_ci Transport functions 77662306a36Sopenharmony_ci ****************************************************************************/ 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/* set_audio_format() sets the format of the audio data in host memory for 77962306a36Sopenharmony_cithis pipe. Note that _MS_ (mono-to-stereo) playback modes are not used by ALSA 78062306a36Sopenharmony_cibut they are here because they are just mono while capturing */ 78162306a36Sopenharmony_cistatic void set_audio_format(struct echoaudio *chip, u16 pipe_index, 78262306a36Sopenharmony_ci const struct audioformat *format) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci u16 dsp_format; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SS_16LE; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Look for super-interleave (no big-endian and 8 bits) */ 78962306a36Sopenharmony_ci if (format->interleave > 2) { 79062306a36Sopenharmony_ci switch (format->bits_per_sample) { 79162306a36Sopenharmony_ci case 16: 79262306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE; 79362306a36Sopenharmony_ci break; 79462306a36Sopenharmony_ci case 24: 79562306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE; 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case 32: 79862306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE; 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci dsp_format |= format->interleave; 80262306a36Sopenharmony_ci } else if (format->data_are_bigendian) { 80362306a36Sopenharmony_ci /* For big-endian data, only 32 bit samples are supported */ 80462306a36Sopenharmony_ci switch (format->interleave) { 80562306a36Sopenharmony_ci case 1: 80662306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_MM_32BE; 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 80962306a36Sopenharmony_ci case 2: 81062306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SS_32BE; 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci#endif 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci } else if (format->interleave == 1 && 81562306a36Sopenharmony_ci format->bits_per_sample == 32 && !format->mono_to_stereo) { 81662306a36Sopenharmony_ci /* 32 bit little-endian mono->mono case */ 81762306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_MM_32LE; 81862306a36Sopenharmony_ci } else { 81962306a36Sopenharmony_ci /* Handle the other little-endian formats */ 82062306a36Sopenharmony_ci switch (format->bits_per_sample) { 82162306a36Sopenharmony_ci case 8: 82262306a36Sopenharmony_ci if (format->interleave == 2) 82362306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SS_8; 82462306a36Sopenharmony_ci else 82562306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_MS_8; 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci default: 82862306a36Sopenharmony_ci case 16: 82962306a36Sopenharmony_ci if (format->interleave == 2) 83062306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SS_16LE; 83162306a36Sopenharmony_ci else 83262306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_MS_16LE; 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci case 24: 83562306a36Sopenharmony_ci if (format->interleave == 2) 83662306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SS_24LE; 83762306a36Sopenharmony_ci else 83862306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_MS_24LE; 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci case 32: 84162306a36Sopenharmony_ci if (format->interleave == 2) 84262306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_SS_32LE; 84362306a36Sopenharmony_ci else 84462306a36Sopenharmony_ci dsp_format = DSP_AUDIOFORM_MS_32LE; 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci dev_dbg(chip->card->dev, 84962306a36Sopenharmony_ci "set_audio_format[%d] = %x\n", pipe_index, dsp_format); 85062306a36Sopenharmony_ci chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci/* start_transport starts transport for a set of pipes. 85662306a36Sopenharmony_ciThe bits 1 in channel_mask specify what pipes to start. Only the bit of the 85762306a36Sopenharmony_cifirst channel must be set, regardless its interleave. 85862306a36Sopenharmony_ciSame thing for pause_ and stop_ -trasport below. */ 85962306a36Sopenharmony_cistatic int start_transport(struct echoaudio *chip, u32 channel_mask, 86062306a36Sopenharmony_ci u32 cyclic_mask) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (wait_handshake(chip)) 86462306a36Sopenharmony_ci return -EIO; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci chip->comm_page->cmd_start |= cpu_to_le32(channel_mask); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (chip->comm_page->cmd_start) { 86962306a36Sopenharmony_ci clear_handshake(chip); 87062306a36Sopenharmony_ci send_vector(chip, DSP_VC_START_TRANSFER); 87162306a36Sopenharmony_ci if (wait_handshake(chip)) 87262306a36Sopenharmony_ci return -EIO; 87362306a36Sopenharmony_ci /* Keep track of which pipes are transporting */ 87462306a36Sopenharmony_ci chip->active_mask |= channel_mask; 87562306a36Sopenharmony_ci chip->comm_page->cmd_start = 0; 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci dev_err(chip->card->dev, "start_transport: No pipes to start!\n"); 88062306a36Sopenharmony_ci return -EINVAL; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int pause_transport(struct echoaudio *chip, u32 channel_mask) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (wait_handshake(chip)) 88962306a36Sopenharmony_ci return -EIO; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); 89262306a36Sopenharmony_ci chip->comm_page->cmd_reset = 0; 89362306a36Sopenharmony_ci if (chip->comm_page->cmd_stop) { 89462306a36Sopenharmony_ci clear_handshake(chip); 89562306a36Sopenharmony_ci send_vector(chip, DSP_VC_STOP_TRANSFER); 89662306a36Sopenharmony_ci if (wait_handshake(chip)) 89762306a36Sopenharmony_ci return -EIO; 89862306a36Sopenharmony_ci /* Keep track of which pipes are transporting */ 89962306a36Sopenharmony_ci chip->active_mask &= ~channel_mask; 90062306a36Sopenharmony_ci chip->comm_page->cmd_stop = 0; 90162306a36Sopenharmony_ci chip->comm_page->cmd_reset = 0; 90262306a36Sopenharmony_ci return 0; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci dev_dbg(chip->card->dev, "pause_transport: No pipes to stop!\n"); 90662306a36Sopenharmony_ci return 0; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic int stop_transport(struct echoaudio *chip, u32 channel_mask) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (wait_handshake(chip)) 91562306a36Sopenharmony_ci return -EIO; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); 91862306a36Sopenharmony_ci chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask); 91962306a36Sopenharmony_ci if (chip->comm_page->cmd_reset) { 92062306a36Sopenharmony_ci clear_handshake(chip); 92162306a36Sopenharmony_ci send_vector(chip, DSP_VC_STOP_TRANSFER); 92262306a36Sopenharmony_ci if (wait_handshake(chip)) 92362306a36Sopenharmony_ci return -EIO; 92462306a36Sopenharmony_ci /* Keep track of which pipes are transporting */ 92562306a36Sopenharmony_ci chip->active_mask &= ~channel_mask; 92662306a36Sopenharmony_ci chip->comm_page->cmd_stop = 0; 92762306a36Sopenharmony_ci chip->comm_page->cmd_reset = 0; 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci dev_dbg(chip->card->dev, "stop_transport: No pipes to stop!\n"); 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci return (chip->pipe_alloc_mask & (1 << pipe_index)); 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci/* Stops everything and turns off the DSP. All pipes should be already 94562306a36Sopenharmony_cistopped and unallocated. */ 94662306a36Sopenharmony_cistatic int rest_in_peace(struct echoaudio *chip) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Stops all active pipes (just to be sure) */ 95062306a36Sopenharmony_ci stop_transport(chip, chip->active_mask); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci set_meters_on(chip, false); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI 95562306a36Sopenharmony_ci enable_midi_input(chip, false); 95662306a36Sopenharmony_ci#endif 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* Go to sleep */ 95962306a36Sopenharmony_ci if (chip->dsp_code) { 96062306a36Sopenharmony_ci /* Make load_firmware do a complete reload */ 96162306a36Sopenharmony_ci chip->dsp_code = NULL; 96262306a36Sopenharmony_ci /* Put the DSP to sleep */ 96362306a36Sopenharmony_ci return send_vector(chip, DSP_VC_GO_COMATOSE); 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci return 0; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci/* Fills the comm page with default values */ 97162306a36Sopenharmony_cistatic int init_dsp_comm_page(struct echoaudio *chip) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci /* Check if the compiler added extra padding inside the structure */ 97462306a36Sopenharmony_ci if (offsetof(struct comm_page, midi_output) != 0xbe0) { 97562306a36Sopenharmony_ci dev_err(chip->card->dev, 97662306a36Sopenharmony_ci "init_dsp_comm_page() - Invalid struct comm_page structure\n"); 97762306a36Sopenharmony_ci return -EPERM; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Init all the basic stuff */ 98162306a36Sopenharmony_ci chip->card_name = ECHOCARD_NAME; 98262306a36Sopenharmony_ci chip->bad_board = true; /* Set true until DSP loaded */ 98362306a36Sopenharmony_ci chip->dsp_code = NULL; /* Current DSP code not loaded */ 98462306a36Sopenharmony_ci chip->asic_loaded = false; 98562306a36Sopenharmony_ci memset(chip->comm_page, 0, sizeof(struct comm_page)); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* Init the comm page */ 98862306a36Sopenharmony_ci chip->comm_page->comm_size = 98962306a36Sopenharmony_ci cpu_to_le32(sizeof(struct comm_page)); 99062306a36Sopenharmony_ci chip->comm_page->handshake = cpu_to_le32(0xffffffff); 99162306a36Sopenharmony_ci chip->comm_page->midi_out_free_count = 99262306a36Sopenharmony_ci cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE); 99362306a36Sopenharmony_ci chip->comm_page->sample_rate = cpu_to_le32(44100); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Set line levels so we don't blast any inputs on startup */ 99662306a36Sopenharmony_ci memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE); 99762306a36Sopenharmony_ci memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/* This function initializes the chip structure with default values, ie. all 100562306a36Sopenharmony_ci * muted and internal clock source. Then it copies the settings to the DSP. 100662306a36Sopenharmony_ci * This MUST be called after the DSP is up and running ! 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_cistatic int init_line_levels(struct echoaudio *chip) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain)); 101162306a36Sopenharmony_ci memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain)); 101262306a36Sopenharmony_ci memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain)); 101362306a36Sopenharmony_ci memset(chip->vmixer_gain, ECHOGAIN_MUTED, sizeof(chip->vmixer_gain)); 101462306a36Sopenharmony_ci chip->input_clock = ECHO_CLOCK_INTERNAL; 101562306a36Sopenharmony_ci chip->output_clock = ECHO_CLOCK_WORD; 101662306a36Sopenharmony_ci chip->sample_rate = 44100; 101762306a36Sopenharmony_ci return restore_dsp_rettings(chip); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/* This is low level part of the interrupt handler. 102362306a36Sopenharmony_ciIt returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the number 102462306a36Sopenharmony_ciof midi data in the input queue. */ 102562306a36Sopenharmony_cistatic int service_irq(struct echoaudio *chip) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci int st; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* Read the DSP status register and see if this DSP generated this interrupt */ 103062306a36Sopenharmony_ci if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) { 103162306a36Sopenharmony_ci st = 0; 103262306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI 103362306a36Sopenharmony_ci /* Get and parse midi data if present */ 103462306a36Sopenharmony_ci if (chip->comm_page->midi_input[0]) /* The count is at index 0 */ 103562306a36Sopenharmony_ci st = midi_service_irq(chip); /* Returns how many midi bytes we received */ 103662306a36Sopenharmony_ci#endif 103762306a36Sopenharmony_ci /* Clear the hardware interrupt */ 103862306a36Sopenharmony_ci chip->comm_page->midi_input[0] = 0; 103962306a36Sopenharmony_ci send_vector(chip, DSP_VC_ACK_INT); 104062306a36Sopenharmony_ci return st; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci return -1; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci/****************************************************************************** 104962306a36Sopenharmony_ci Functions for opening and closing pipes 105062306a36Sopenharmony_ci ******************************************************************************/ 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci/* allocate_pipes is used to reserve audio pipes for your exclusive use. 105362306a36Sopenharmony_ciThe call will fail if some pipes are already allocated. */ 105462306a36Sopenharmony_cistatic int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, 105562306a36Sopenharmony_ci int pipe_index, int interleave) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci int i; 105862306a36Sopenharmony_ci u32 channel_mask; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci dev_dbg(chip->card->dev, 106162306a36Sopenharmony_ci "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (chip->bad_board) 106462306a36Sopenharmony_ci return -EIO; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci for (channel_mask = i = 0; i < interleave; i++) 106762306a36Sopenharmony_ci channel_mask |= 1 << (pipe_index + i); 106862306a36Sopenharmony_ci if (chip->pipe_alloc_mask & channel_mask) { 106962306a36Sopenharmony_ci dev_err(chip->card->dev, 107062306a36Sopenharmony_ci "allocate_pipes: channel already open\n"); 107162306a36Sopenharmony_ci return -EAGAIN; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci chip->comm_page->position[pipe_index] = 0; 107562306a36Sopenharmony_ci chip->pipe_alloc_mask |= channel_mask; 107662306a36Sopenharmony_ci /* This driver uses cyclic buffers only */ 107762306a36Sopenharmony_ci chip->pipe_cyclic_mask |= channel_mask; 107862306a36Sopenharmony_ci pipe->index = pipe_index; 107962306a36Sopenharmony_ci pipe->interleave = interleave; 108062306a36Sopenharmony_ci pipe->state = PIPE_STATE_STOPPED; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* The counter register is where the DSP writes the 32 bit DMA 108362306a36Sopenharmony_ci position for a pipe. The DSP is constantly updating this value as 108462306a36Sopenharmony_ci it moves data. The DMA counter is in units of bytes, not samples. */ 108562306a36Sopenharmony_ci pipe->dma_counter = (__le32 *)&chip->comm_page->position[pipe_index]; 108662306a36Sopenharmony_ci *pipe->dma_counter = 0; 108762306a36Sopenharmony_ci return pipe_index; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic int free_pipes(struct echoaudio *chip, struct audiopipe *pipe) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci u32 channel_mask; 109562306a36Sopenharmony_ci int i; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index))) 109862306a36Sopenharmony_ci return -EINVAL; 109962306a36Sopenharmony_ci if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED)) 110062306a36Sopenharmony_ci return -EINVAL; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci for (channel_mask = i = 0; i < pipe->interleave; i++) 110362306a36Sopenharmony_ci channel_mask |= 1 << (pipe->index + i); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci chip->pipe_alloc_mask &= ~channel_mask; 110662306a36Sopenharmony_ci chip->pipe_cyclic_mask &= ~channel_mask; 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/****************************************************************************** 111362306a36Sopenharmony_ci Functions for managing the scatter-gather list 111462306a36Sopenharmony_ci******************************************************************************/ 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic int sglist_init(struct echoaudio *chip, struct audiopipe *pipe) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci pipe->sglist_head = 0; 111962306a36Sopenharmony_ci memset(pipe->sgpage.area, 0, PAGE_SIZE); 112062306a36Sopenharmony_ci chip->comm_page->sglist_addr[pipe->index].addr = 112162306a36Sopenharmony_ci cpu_to_le32(pipe->sgpage.addr); 112262306a36Sopenharmony_ci return 0; 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe, 112862306a36Sopenharmony_ci dma_addr_t address, size_t length) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci int head = pipe->sglist_head; 113162306a36Sopenharmony_ci struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (head < MAX_SGLIST_ENTRIES - 1) { 113462306a36Sopenharmony_ci list[head].addr = cpu_to_le32(address); 113562306a36Sopenharmony_ci list[head].size = cpu_to_le32(length); 113662306a36Sopenharmony_ci pipe->sglist_head++; 113762306a36Sopenharmony_ci } else { 113862306a36Sopenharmony_ci dev_err(chip->card->dev, "SGlist: too many fragments\n"); 113962306a36Sopenharmony_ci return -ENOMEM; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci return sglist_add_mapping(chip, pipe, 0, 0); 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0); 115662306a36Sopenharmony_ci} 1157