18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Digigram VX soundcards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * IEC958 stuff 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/vx_core.h> 138c2ecf20Sopenharmony_ci#include "vx_cmd.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * vx_modify_board_clock - tell the board that its clock has been modified 188c2ecf20Sopenharmony_ci * @sync: DSP needs to resynchronize its FIFO 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_cistatic int vx_modify_board_clock(struct vx_core *chip, int sync) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct vx_rmh rmh; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci vx_init_rmh(&rmh, CMD_MODIFY_CLOCK); 258c2ecf20Sopenharmony_ci /* Ask the DSP to resynchronize its FIFO. */ 268c2ecf20Sopenharmony_ci if (sync) 278c2ecf20Sopenharmony_ci rmh.Cmd[0] |= CMD_MODIFY_CLOCK_S_BIT; 288c2ecf20Sopenharmony_ci return vx_send_msg(chip, &rmh); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * vx_modify_board_inputs - resync audio inputs 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistatic int vx_modify_board_inputs(struct vx_core *chip) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct vx_rmh rmh; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci vx_init_rmh(&rmh, CMD_RESYNC_AUDIO_INPUTS); 398c2ecf20Sopenharmony_ci rmh.Cmd[0] |= 1 << 0; /* reference: AUDIO 0 */ 408c2ecf20Sopenharmony_ci return vx_send_msg(chip, &rmh); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * vx_read_one_cbit - read one bit from UER config 458c2ecf20Sopenharmony_ci * @index: the bit index 468c2ecf20Sopenharmony_ci * returns 0 or 1. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic int vx_read_one_cbit(struct vx_core *chip, int index) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int val; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 538c2ecf20Sopenharmony_ci if (chip->type >= VX_TYPE_VXPOCKET) { 548c2ecf20Sopenharmony_ci vx_outb(chip, CSUER, 1); /* read */ 558c2ecf20Sopenharmony_ci vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); 568c2ecf20Sopenharmony_ci val = (vx_inb(chip, RUER) >> 7) & 0x01; 578c2ecf20Sopenharmony_ci } else { 588c2ecf20Sopenharmony_ci vx_outl(chip, CSUER, 1); /* read */ 598c2ecf20Sopenharmony_ci vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); 608c2ecf20Sopenharmony_ci val = (vx_inl(chip, RUER) >> 7) & 0x01; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 638c2ecf20Sopenharmony_ci return val; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * vx_write_one_cbit - write one bit to UER config 688c2ecf20Sopenharmony_ci * @index: the bit index 698c2ecf20Sopenharmony_ci * @val: bit value, 0 or 1 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_cistatic void vx_write_one_cbit(struct vx_core *chip, int index, int val) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci val = !!val; /* 0 or 1 */ 748c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 758c2ecf20Sopenharmony_ci if (vx_is_pcmcia(chip)) { 768c2ecf20Sopenharmony_ci vx_outb(chip, CSUER, 0); /* write */ 778c2ecf20Sopenharmony_ci vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci vx_outl(chip, CSUER, 0); /* write */ 808c2ecf20Sopenharmony_ci vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * vx_read_uer_status - read the current UER status 878c2ecf20Sopenharmony_ci * @mode: pointer to store the UER mode, VX_UER_MODE_XXX 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * returns the frequency of UER, or 0 if not sync, 908c2ecf20Sopenharmony_ci * or a negative error code. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic int vx_read_uer_status(struct vx_core *chip, unsigned int *mode) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int val, freq; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Default values */ 978c2ecf20Sopenharmony_ci freq = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Read UER status */ 1008c2ecf20Sopenharmony_ci if (vx_is_pcmcia(chip)) 1018c2ecf20Sopenharmony_ci val = vx_inb(chip, CSUER); 1028c2ecf20Sopenharmony_ci else 1038c2ecf20Sopenharmony_ci val = vx_inl(chip, CSUER); 1048c2ecf20Sopenharmony_ci if (val < 0) 1058c2ecf20Sopenharmony_ci return val; 1068c2ecf20Sopenharmony_ci /* If clock is present, read frequency */ 1078c2ecf20Sopenharmony_ci if (val & VX_SUER_CLOCK_PRESENT_MASK) { 1088c2ecf20Sopenharmony_ci switch (val & VX_SUER_FREQ_MASK) { 1098c2ecf20Sopenharmony_ci case VX_SUER_FREQ_32KHz_MASK: 1108c2ecf20Sopenharmony_ci freq = 32000; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci case VX_SUER_FREQ_44KHz_MASK: 1138c2ecf20Sopenharmony_ci freq = 44100; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case VX_SUER_FREQ_48KHz_MASK: 1168c2ecf20Sopenharmony_ci freq = 48000; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci if (val & VX_SUER_DATA_PRESENT_MASK) 1218c2ecf20Sopenharmony_ci /* bit 0 corresponds to consumer/professional bit */ 1228c2ecf20Sopenharmony_ci *mode = vx_read_one_cbit(chip, 0) ? 1238c2ecf20Sopenharmony_ci VX_UER_MODE_PROFESSIONAL : VX_UER_MODE_CONSUMER; 1248c2ecf20Sopenharmony_ci else 1258c2ecf20Sopenharmony_ci *mode = VX_UER_MODE_NOT_PRESENT; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return freq; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * compute the sample clock value from frequency 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * The formula is as follows: 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * HexFreq = (dword) ((double) ((double) 28224000 / (double) Frequency)) 1378c2ecf20Sopenharmony_ci * switch ( HexFreq & 0x00000F00 ) 1388c2ecf20Sopenharmony_ci * case 0x00000100: ; 1398c2ecf20Sopenharmony_ci * case 0x00000200: 1408c2ecf20Sopenharmony_ci * case 0x00000300: HexFreq -= 0x00000201 ; 1418c2ecf20Sopenharmony_ci * case 0x00000400: 1428c2ecf20Sopenharmony_ci * case 0x00000500: 1438c2ecf20Sopenharmony_ci * case 0x00000600: 1448c2ecf20Sopenharmony_ci * case 0x00000700: HexFreq = (dword) (((double) 28224000 / (double) (Frequency*2)) - 1) 1458c2ecf20Sopenharmony_ci * default : HexFreq = (dword) ((double) 28224000 / (double) (Frequency*4)) - 0x000001FF 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int vx_calc_clock_from_freq(struct vx_core *chip, int freq) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int hexfreq; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (snd_BUG_ON(freq <= 0)) 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci hexfreq = (28224000 * 10) / freq; 1568c2ecf20Sopenharmony_ci hexfreq = (hexfreq + 5) / 10; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* max freq = 55125 Hz */ 1598c2ecf20Sopenharmony_ci if (snd_BUG_ON(hexfreq <= 0x00000200)) 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (hexfreq <= 0x03ff) 1638c2ecf20Sopenharmony_ci return hexfreq - 0x00000201; 1648c2ecf20Sopenharmony_ci if (hexfreq <= 0x07ff) 1658c2ecf20Sopenharmony_ci return (hexfreq / 2) - 1; 1668c2ecf20Sopenharmony_ci if (hexfreq <= 0x0fff) 1678c2ecf20Sopenharmony_ci return (hexfreq / 4) + 0x000001ff; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0x5fe; /* min freq = 6893 Hz */ 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * vx_change_clock_source - change the clock source 1758c2ecf20Sopenharmony_ci * @source: the new source 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic void vx_change_clock_source(struct vx_core *chip, int source) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci /* we mute DAC to prevent clicks */ 1808c2ecf20Sopenharmony_ci vx_toggle_dac_mute(chip, 1); 1818c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 1828c2ecf20Sopenharmony_ci chip->ops->set_clock_source(chip, source); 1838c2ecf20Sopenharmony_ci chip->clock_source = source; 1848c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 1858c2ecf20Sopenharmony_ci /* unmute */ 1868c2ecf20Sopenharmony_ci vx_toggle_dac_mute(chip, 0); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* 1918c2ecf20Sopenharmony_ci * set the internal clock 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_civoid vx_set_internal_clock(struct vx_core *chip, unsigned int freq) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int clock; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Get real clock value */ 1988c2ecf20Sopenharmony_ci clock = vx_calc_clock_from_freq(chip, freq); 1998c2ecf20Sopenharmony_ci snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq); 2008c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 2018c2ecf20Sopenharmony_ci if (vx_is_pcmcia(chip)) { 2028c2ecf20Sopenharmony_ci vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f); 2038c2ecf20Sopenharmony_ci vx_outb(chip, LOFREQ, clock & 0xff); 2048c2ecf20Sopenharmony_ci } else { 2058c2ecf20Sopenharmony_ci vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f); 2068c2ecf20Sopenharmony_ci vx_outl(chip, LOFREQ, clock & 0xff); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * set the iec958 status bits 2148c2ecf20Sopenharmony_ci * @bits: 32-bit status bits 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_civoid vx_set_iec958_status(struct vx_core *chip, unsigned int bits) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int i; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 2218c2ecf20Sopenharmony_ci return; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) 2248c2ecf20Sopenharmony_ci vx_write_one_cbit(chip, i, bits & (1 << i)); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * vx_set_clock - change the clock and audio source if necessary 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ciint vx_set_clock(struct vx_core *chip, unsigned int freq) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int src_changed = 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* change the audio source if possible */ 2398c2ecf20Sopenharmony_ci vx_sync_audio_source(chip); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (chip->clock_mode == VX_CLOCK_MODE_EXTERNAL || 2428c2ecf20Sopenharmony_ci (chip->clock_mode == VX_CLOCK_MODE_AUTO && 2438c2ecf20Sopenharmony_ci chip->audio_source == VX_AUDIO_SRC_DIGITAL)) { 2448c2ecf20Sopenharmony_ci if (chip->clock_source != UER_SYNC) { 2458c2ecf20Sopenharmony_ci vx_change_clock_source(chip, UER_SYNC); 2468c2ecf20Sopenharmony_ci mdelay(6); 2478c2ecf20Sopenharmony_ci src_changed = 1; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } else if (chip->clock_mode == VX_CLOCK_MODE_INTERNAL || 2508c2ecf20Sopenharmony_ci (chip->clock_mode == VX_CLOCK_MODE_AUTO && 2518c2ecf20Sopenharmony_ci chip->audio_source != VX_AUDIO_SRC_DIGITAL)) { 2528c2ecf20Sopenharmony_ci if (chip->clock_source != INTERNAL_QUARTZ) { 2538c2ecf20Sopenharmony_ci vx_change_clock_source(chip, INTERNAL_QUARTZ); 2548c2ecf20Sopenharmony_ci src_changed = 1; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci if (chip->freq == freq) 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci vx_set_internal_clock(chip, freq); 2598c2ecf20Sopenharmony_ci if (src_changed) 2608c2ecf20Sopenharmony_ci vx_modify_board_inputs(chip); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci if (chip->freq == freq) 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci chip->freq = freq; 2658c2ecf20Sopenharmony_ci vx_modify_board_clock(chip, 1); 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * vx_change_frequency - called from interrupt handler 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ciint vx_change_frequency(struct vx_core *chip) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci int freq; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (chip->clock_source == INTERNAL_QUARTZ) 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * Read the real UER board frequency 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci freq = vx_read_uer_status(chip, &chip->uer_detected); 2868c2ecf20Sopenharmony_ci if (freq < 0) 2878c2ecf20Sopenharmony_ci return freq; 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * The frequency computed by the DSP is good and 2908c2ecf20Sopenharmony_ci * is different from the previous computed. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci if (freq == 48000 || freq == 44100 || freq == 32000) 2938c2ecf20Sopenharmony_ci chip->freq_detected = freq; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 297