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 3262306a36Sopenharmony_cistatic int read_dsp(struct echoaudio *chip, u32 *data); 3362306a36Sopenharmony_cistatic int set_professional_spdif(struct echoaudio *chip, char prof); 3462306a36Sopenharmony_cistatic int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic); 3562306a36Sopenharmony_cistatic int check_asic_status(struct echoaudio *chip); 3662306a36Sopenharmony_cistatic int update_flags(struct echoaudio *chip); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int err; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20)) 4462306a36Sopenharmony_ci return -ENODEV; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci err = init_dsp_comm_page(chip); 4762306a36Sopenharmony_ci if (err) { 4862306a36Sopenharmony_ci dev_err(chip->card->dev, 4962306a36Sopenharmony_ci "init_hw - could not initialize DSP comm page\n"); 5062306a36Sopenharmony_ci return err; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci chip->device_id = device_id; 5462306a36Sopenharmony_ci chip->subdevice_id = subdevice_id; 5562306a36Sopenharmony_ci chip->bad_board = true; 5662306a36Sopenharmony_ci chip->has_midi = true; 5762306a36Sopenharmony_ci chip->dsp_code_to_load = FW_LAYLA20_DSP; 5862306a36Sopenharmony_ci chip->input_clock_types = 5962306a36Sopenharmony_ci ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | 6062306a36Sopenharmony_ci ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; 6162306a36Sopenharmony_ci chip->output_clock_types = 6262306a36Sopenharmony_ci ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci err = load_firmware(chip); 6562306a36Sopenharmony_ci if (err < 0) 6662306a36Sopenharmony_ci return err; 6762306a36Sopenharmony_ci chip->bad_board = false; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return err; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int set_mixer_defaults(struct echoaudio *chip) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci chip->professional_spdif = false; 7762306a36Sopenharmony_ci return init_line_levels(chip); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic u32 detect_input_clocks(const struct echoaudio *chip) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci u32 clocks_from_dsp, clock_bits; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Map the DSP clock detect bits to the generic driver clock detect bits */ 8762306a36Sopenharmony_ci clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci clock_bits = ECHO_CLOCK_BIT_INTERNAL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) 9262306a36Sopenharmony_ci clock_bits |= ECHO_CLOCK_BIT_SPDIF; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) { 9562306a36Sopenharmony_ci if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER) 9662306a36Sopenharmony_ci clock_bits |= ECHO_CLOCK_BIT_SUPER; 9762306a36Sopenharmony_ci else 9862306a36Sopenharmony_ci clock_bits |= ECHO_CLOCK_BIT_WORD; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return clock_bits; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* ASIC status check - some cards have one or two ASICs that need to be 10762306a36Sopenharmony_ciloaded. Once that load is complete, this function is called to see if 10862306a36Sopenharmony_cithe load was successful. 10962306a36Sopenharmony_ciIf this load fails, it does not necessarily mean that the hardware is 11062306a36Sopenharmony_cidefective - the external box may be disconnected or turned off. 11162306a36Sopenharmony_ciThis routine sometimes fails for Layla20; for Layla20, the loop runs 11262306a36Sopenharmony_ci5 times and succeeds if it wins on three of the loops. */ 11362306a36Sopenharmony_cistatic int check_asic_status(struct echoaudio *chip) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u32 asic_status; 11662306a36Sopenharmony_ci int goodcnt, i; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci chip->asic_loaded = false; 11962306a36Sopenharmony_ci for (i = goodcnt = 0; i < 5; i++) { 12062306a36Sopenharmony_ci send_vector(chip, DSP_VC_TEST_ASIC); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* The DSP will return a value to indicate whether or not 12362306a36Sopenharmony_ci the ASIC is currently loaded */ 12462306a36Sopenharmony_ci if (read_dsp(chip, &asic_status) < 0) { 12562306a36Sopenharmony_ci dev_err(chip->card->dev, 12662306a36Sopenharmony_ci "check_asic_status: failed on read_dsp\n"); 12762306a36Sopenharmony_ci return -EIO; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (asic_status == ASIC_ALREADY_LOADED) { 13162306a36Sopenharmony_ci if (++goodcnt == 3) { 13262306a36Sopenharmony_ci chip->asic_loaded = true; 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci return -EIO; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* Layla20 has an ASIC in the external box */ 14362306a36Sopenharmony_cistatic int load_asic(struct echoaudio *chip) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (chip->asic_loaded) 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC, 15162306a36Sopenharmony_ci FW_LAYLA20_ASIC); 15262306a36Sopenharmony_ci if (err < 0) 15362306a36Sopenharmony_ci return err; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Check if ASIC is alive and well. */ 15662306a36Sopenharmony_ci return check_asic_status(chip); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int set_sample_rate(struct echoaudio *chip, u32 rate) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci if (snd_BUG_ON(rate < 8000 || rate > 50000)) 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Only set the clock for internal mode. Do not return failure, 16762306a36Sopenharmony_ci simply treat it as a non-event. */ 16862306a36Sopenharmony_ci if (chip->input_clock != ECHO_CLOCK_INTERNAL) { 16962306a36Sopenharmony_ci dev_warn(chip->card->dev, 17062306a36Sopenharmony_ci "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); 17162306a36Sopenharmony_ci chip->comm_page->sample_rate = cpu_to_le32(rate); 17262306a36Sopenharmony_ci chip->sample_rate = rate; 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (wait_handshake(chip)) 17762306a36Sopenharmony_ci return -EIO; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dev_dbg(chip->card->dev, "set_sample_rate(%d)\n", rate); 18062306a36Sopenharmony_ci chip->sample_rate = rate; 18162306a36Sopenharmony_ci chip->comm_page->sample_rate = cpu_to_le32(rate); 18262306a36Sopenharmony_ci clear_handshake(chip); 18362306a36Sopenharmony_ci return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int set_input_clock(struct echoaudio *chip, u16 clock_source) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u16 clock; 19162306a36Sopenharmony_ci u32 rate; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci rate = 0; 19462306a36Sopenharmony_ci switch (clock_source) { 19562306a36Sopenharmony_ci case ECHO_CLOCK_INTERNAL: 19662306a36Sopenharmony_ci rate = chip->sample_rate; 19762306a36Sopenharmony_ci clock = LAYLA20_CLOCK_INTERNAL; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci case ECHO_CLOCK_SPDIF: 20062306a36Sopenharmony_ci clock = LAYLA20_CLOCK_SPDIF; 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci case ECHO_CLOCK_WORD: 20362306a36Sopenharmony_ci clock = LAYLA20_CLOCK_WORD; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci case ECHO_CLOCK_SUPER: 20662306a36Sopenharmony_ci clock = LAYLA20_CLOCK_SUPER; 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci default: 20962306a36Sopenharmony_ci dev_err(chip->card->dev, 21062306a36Sopenharmony_ci "Input clock 0x%x not supported for Layla24\n", 21162306a36Sopenharmony_ci clock_source); 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci chip->input_clock = clock_source; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci chip->comm_page->input_clock = cpu_to_le16(clock); 21762306a36Sopenharmony_ci clear_handshake(chip); 21862306a36Sopenharmony_ci send_vector(chip, DSP_VC_UPDATE_CLOCKS); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (rate) 22162306a36Sopenharmony_ci set_sample_rate(chip, rate); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int set_output_clock(struct echoaudio *chip, u16 clock) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci switch (clock) { 23162306a36Sopenharmony_ci case ECHO_CLOCK_SUPER: 23262306a36Sopenharmony_ci clock = LAYLA20_OUTPUT_CLOCK_SUPER; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case ECHO_CLOCK_WORD: 23562306a36Sopenharmony_ci clock = LAYLA20_OUTPUT_CLOCK_WORD; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci default: 23862306a36Sopenharmony_ci dev_err(chip->card->dev, "set_output_clock wrong clock\n"); 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (wait_handshake(chip)) 24362306a36Sopenharmony_ci return -EIO; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci chip->comm_page->output_clock = cpu_to_le16(clock); 24662306a36Sopenharmony_ci chip->output_clock = clock; 24762306a36Sopenharmony_ci clear_handshake(chip); 24862306a36Sopenharmony_ci return send_vector(chip, DSP_VC_UPDATE_CLOCKS); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* Set input bus gain (one unit is 0.5dB !) */ 25462306a36Sopenharmony_cistatic int set_input_gain(struct echoaudio *chip, u16 input, int gain) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci if (snd_BUG_ON(input >= num_busses_in(chip))) 25762306a36Sopenharmony_ci return -EINVAL; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (wait_handshake(chip)) 26062306a36Sopenharmony_ci return -EIO; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci chip->input_gain[input] = gain; 26362306a36Sopenharmony_ci gain += GL20_INPUT_GAIN_MAGIC_NUMBER; 26462306a36Sopenharmony_ci chip->comm_page->line_in_level[input] = gain; 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* Tell the DSP to reread the flags from the comm page */ 27162306a36Sopenharmony_cistatic int update_flags(struct echoaudio *chip) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci if (wait_handshake(chip)) 27462306a36Sopenharmony_ci return -EIO; 27562306a36Sopenharmony_ci clear_handshake(chip); 27662306a36Sopenharmony_ci return send_vector(chip, DSP_VC_UPDATE_FLAGS); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int set_professional_spdif(struct echoaudio *chip, char prof) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci if (prof) 28462306a36Sopenharmony_ci chip->comm_page->flags |= 28562306a36Sopenharmony_ci cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); 28662306a36Sopenharmony_ci else 28762306a36Sopenharmony_ci chip->comm_page->flags &= 28862306a36Sopenharmony_ci ~cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); 28962306a36Sopenharmony_ci chip->professional_spdif = prof; 29062306a36Sopenharmony_ci return update_flags(chip); 29162306a36Sopenharmony_ci} 292