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