18c2ecf20Sopenharmony_ci/**************************************************************************** 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci Copyright Echo Digital Audio Corporation (c) 1998 - 2004 48c2ecf20Sopenharmony_ci All rights reserved 58c2ecf20Sopenharmony_ci www.echoaudio.com 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci This file is part of Echo Digital Audio's generic driver library. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci Echo Digital Audio's generic driver library is free software; 108c2ecf20Sopenharmony_ci you can redistribute it and/or modify it under the terms of 118c2ecf20Sopenharmony_ci the GNU General Public License as published by the Free Software 128c2ecf20Sopenharmony_ci Foundation. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci This program is distributed in the hope that it will be useful, 158c2ecf20Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 168c2ecf20Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 178c2ecf20Sopenharmony_ci GNU General Public License for more details. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci You should have received a copy of the GNU General Public License 208c2ecf20Sopenharmony_ci along with this program; if not, write to the Free Software 218c2ecf20Sopenharmony_ci Foundation, Inc., 59 Temple Place - Suite 330, Boston, 228c2ecf20Sopenharmony_ci MA 02111-1307, USA. 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci ************************************************************************* 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci Translation from C++ and adaptation for use in ALSA-Driver 278c2ecf20Sopenharmony_ci were made by Giuliano Pochini <pochini@shiny.it> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci****************************************************************************/ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int read_dsp(struct echoaudio *chip, u32 *data); 338c2ecf20Sopenharmony_cistatic int set_professional_spdif(struct echoaudio *chip, char prof); 348c2ecf20Sopenharmony_cistatic int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic); 358c2ecf20Sopenharmony_cistatic int check_asic_status(struct echoaudio *chip); 368c2ecf20Sopenharmony_cistatic int update_flags(struct echoaudio *chip); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int err; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20)) 448c2ecf20Sopenharmony_ci return -ENODEV; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if ((err = init_dsp_comm_page(chip))) { 478c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 488c2ecf20Sopenharmony_ci "init_hw - could not initialize DSP comm page\n"); 498c2ecf20Sopenharmony_ci return err; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci chip->device_id = device_id; 538c2ecf20Sopenharmony_ci chip->subdevice_id = subdevice_id; 548c2ecf20Sopenharmony_ci chip->bad_board = true; 558c2ecf20Sopenharmony_ci chip->has_midi = true; 568c2ecf20Sopenharmony_ci chip->dsp_code_to_load = FW_LAYLA20_DSP; 578c2ecf20Sopenharmony_ci chip->input_clock_types = 588c2ecf20Sopenharmony_ci ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | 598c2ecf20Sopenharmony_ci ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; 608c2ecf20Sopenharmony_ci chip->output_clock_types = 618c2ecf20Sopenharmony_ci ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if ((err = load_firmware(chip)) < 0) 648c2ecf20Sopenharmony_ci return err; 658c2ecf20Sopenharmony_ci chip->bad_board = false; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return err; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int set_mixer_defaults(struct echoaudio *chip) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci chip->professional_spdif = false; 758c2ecf20Sopenharmony_ci return init_line_levels(chip); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic u32 detect_input_clocks(const struct echoaudio *chip) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u32 clocks_from_dsp, clock_bits; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Map the DSP clock detect bits to the generic driver clock detect bits */ 858c2ecf20Sopenharmony_ci clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci clock_bits = ECHO_CLOCK_BIT_INTERNAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) 908c2ecf20Sopenharmony_ci clock_bits |= ECHO_CLOCK_BIT_SPDIF; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) { 938c2ecf20Sopenharmony_ci if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER) 948c2ecf20Sopenharmony_ci clock_bits |= ECHO_CLOCK_BIT_SUPER; 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci clock_bits |= ECHO_CLOCK_BIT_WORD; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return clock_bits; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* ASIC status check - some cards have one or two ASICs that need to be 1058c2ecf20Sopenharmony_ciloaded. Once that load is complete, this function is called to see if 1068c2ecf20Sopenharmony_cithe load was successful. 1078c2ecf20Sopenharmony_ciIf this load fails, it does not necessarily mean that the hardware is 1088c2ecf20Sopenharmony_cidefective - the external box may be disconnected or turned off. 1098c2ecf20Sopenharmony_ciThis routine sometimes fails for Layla20; for Layla20, the loop runs 1108c2ecf20Sopenharmony_ci5 times and succeeds if it wins on three of the loops. */ 1118c2ecf20Sopenharmony_cistatic int check_asic_status(struct echoaudio *chip) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci u32 asic_status; 1148c2ecf20Sopenharmony_ci int goodcnt, i; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci chip->asic_loaded = false; 1178c2ecf20Sopenharmony_ci for (i = goodcnt = 0; i < 5; i++) { 1188c2ecf20Sopenharmony_ci send_vector(chip, DSP_VC_TEST_ASIC); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* The DSP will return a value to indicate whether or not 1218c2ecf20Sopenharmony_ci the ASIC is currently loaded */ 1228c2ecf20Sopenharmony_ci if (read_dsp(chip, &asic_status) < 0) { 1238c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 1248c2ecf20Sopenharmony_ci "check_asic_status: failed on read_dsp\n"); 1258c2ecf20Sopenharmony_ci return -EIO; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (asic_status == ASIC_ALREADY_LOADED) { 1298c2ecf20Sopenharmony_ci if (++goodcnt == 3) { 1308c2ecf20Sopenharmony_ci chip->asic_loaded = true; 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci return -EIO; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* Layla20 has an ASIC in the external box */ 1418c2ecf20Sopenharmony_cistatic int load_asic(struct echoaudio *chip) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (chip->asic_loaded) 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC, 1498c2ecf20Sopenharmony_ci FW_LAYLA20_ASIC); 1508c2ecf20Sopenharmony_ci if (err < 0) 1518c2ecf20Sopenharmony_ci return err; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Check if ASIC is alive and well. */ 1548c2ecf20Sopenharmony_ci return check_asic_status(chip); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int set_sample_rate(struct echoaudio *chip, u32 rate) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci if (snd_BUG_ON(rate < 8000 || rate > 50000)) 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* Only set the clock for internal mode. Do not return failure, 1658c2ecf20Sopenharmony_ci simply treat it as a non-event. */ 1668c2ecf20Sopenharmony_ci if (chip->input_clock != ECHO_CLOCK_INTERNAL) { 1678c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, 1688c2ecf20Sopenharmony_ci "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); 1698c2ecf20Sopenharmony_ci chip->comm_page->sample_rate = cpu_to_le32(rate); 1708c2ecf20Sopenharmony_ci chip->sample_rate = rate; 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (wait_handshake(chip)) 1758c2ecf20Sopenharmony_ci return -EIO; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "set_sample_rate(%d)\n", rate); 1788c2ecf20Sopenharmony_ci chip->sample_rate = rate; 1798c2ecf20Sopenharmony_ci chip->comm_page->sample_rate = cpu_to_le32(rate); 1808c2ecf20Sopenharmony_ci clear_handshake(chip); 1818c2ecf20Sopenharmony_ci return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int set_input_clock(struct echoaudio *chip, u16 clock_source) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci u16 clock; 1898c2ecf20Sopenharmony_ci u32 rate; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci rate = 0; 1928c2ecf20Sopenharmony_ci switch (clock_source) { 1938c2ecf20Sopenharmony_ci case ECHO_CLOCK_INTERNAL: 1948c2ecf20Sopenharmony_ci rate = chip->sample_rate; 1958c2ecf20Sopenharmony_ci clock = LAYLA20_CLOCK_INTERNAL; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case ECHO_CLOCK_SPDIF: 1988c2ecf20Sopenharmony_ci clock = LAYLA20_CLOCK_SPDIF; 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci case ECHO_CLOCK_WORD: 2018c2ecf20Sopenharmony_ci clock = LAYLA20_CLOCK_WORD; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case ECHO_CLOCK_SUPER: 2048c2ecf20Sopenharmony_ci clock = LAYLA20_CLOCK_SUPER; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 2088c2ecf20Sopenharmony_ci "Input clock 0x%x not supported for Layla24\n", 2098c2ecf20Sopenharmony_ci clock_source); 2108c2ecf20Sopenharmony_ci return -EINVAL; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci chip->input_clock = clock_source; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci chip->comm_page->input_clock = cpu_to_le16(clock); 2158c2ecf20Sopenharmony_ci clear_handshake(chip); 2168c2ecf20Sopenharmony_ci send_vector(chip, DSP_VC_UPDATE_CLOCKS); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (rate) 2198c2ecf20Sopenharmony_ci set_sample_rate(chip, rate); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int set_output_clock(struct echoaudio *chip, u16 clock) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci switch (clock) { 2298c2ecf20Sopenharmony_ci case ECHO_CLOCK_SUPER: 2308c2ecf20Sopenharmony_ci clock = LAYLA20_OUTPUT_CLOCK_SUPER; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case ECHO_CLOCK_WORD: 2338c2ecf20Sopenharmony_ci clock = LAYLA20_OUTPUT_CLOCK_WORD; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci default: 2368c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "set_output_clock wrong clock\n"); 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (wait_handshake(chip)) 2418c2ecf20Sopenharmony_ci return -EIO; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci chip->comm_page->output_clock = cpu_to_le16(clock); 2448c2ecf20Sopenharmony_ci chip->output_clock = clock; 2458c2ecf20Sopenharmony_ci clear_handshake(chip); 2468c2ecf20Sopenharmony_ci return send_vector(chip, DSP_VC_UPDATE_CLOCKS); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* Set input bus gain (one unit is 0.5dB !) */ 2528c2ecf20Sopenharmony_cistatic int set_input_gain(struct echoaudio *chip, u16 input, int gain) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (snd_BUG_ON(input >= num_busses_in(chip))) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (wait_handshake(chip)) 2588c2ecf20Sopenharmony_ci return -EIO; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci chip->input_gain[input] = gain; 2618c2ecf20Sopenharmony_ci gain += GL20_INPUT_GAIN_MAGIC_NUMBER; 2628c2ecf20Sopenharmony_ci chip->comm_page->line_in_level[input] = gain; 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* Tell the DSP to reread the flags from the comm page */ 2698c2ecf20Sopenharmony_cistatic int update_flags(struct echoaudio *chip) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci if (wait_handshake(chip)) 2728c2ecf20Sopenharmony_ci return -EIO; 2738c2ecf20Sopenharmony_ci clear_handshake(chip); 2748c2ecf20Sopenharmony_ci return send_vector(chip, DSP_VC_UPDATE_FLAGS); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int set_professional_spdif(struct echoaudio *chip, char prof) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci if (prof) 2828c2ecf20Sopenharmony_ci chip->comm_page->flags |= 2838c2ecf20Sopenharmony_ci cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci chip->comm_page->flags &= 2868c2ecf20Sopenharmony_ci ~cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); 2878c2ecf20Sopenharmony_ci chip->professional_spdif = prof; 2888c2ecf20Sopenharmony_ci return update_flags(chip); 2898c2ecf20Sopenharmony_ci} 290