162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Digigram VX soundcards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * IEC958 stuff
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <sound/core.h>
1262306a36Sopenharmony_ci#include <sound/vx_core.h>
1362306a36Sopenharmony_ci#include "vx_cmd.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * vx_modify_board_clock - tell the board that its clock has been modified
1862306a36Sopenharmony_ci * @sync: DSP needs to resynchronize its FIFO
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_cistatic int vx_modify_board_clock(struct vx_core *chip, int sync)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct vx_rmh rmh;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	vx_init_rmh(&rmh, CMD_MODIFY_CLOCK);
2562306a36Sopenharmony_ci	/* Ask the DSP to resynchronize its FIFO. */
2662306a36Sopenharmony_ci	if (sync)
2762306a36Sopenharmony_ci		rmh.Cmd[0] |= CMD_MODIFY_CLOCK_S_BIT;
2862306a36Sopenharmony_ci	return vx_send_msg(chip, &rmh);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * vx_modify_board_inputs - resync audio inputs
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_cistatic int vx_modify_board_inputs(struct vx_core *chip)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct vx_rmh rmh;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	vx_init_rmh(&rmh, CMD_RESYNC_AUDIO_INPUTS);
3962306a36Sopenharmony_ci        rmh.Cmd[0] |= 1 << 0; /* reference: AUDIO 0 */
4062306a36Sopenharmony_ci	return vx_send_msg(chip, &rmh);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * vx_read_one_cbit - read one bit from UER config
4562306a36Sopenharmony_ci * @index: the bit index
4662306a36Sopenharmony_ci * returns 0 or 1.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic int vx_read_one_cbit(struct vx_core *chip, int index)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	int val;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	mutex_lock(&chip->lock);
5362306a36Sopenharmony_ci	if (chip->type >= VX_TYPE_VXPOCKET) {
5462306a36Sopenharmony_ci		vx_outb(chip, CSUER, 1); /* read */
5562306a36Sopenharmony_ci		vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
5662306a36Sopenharmony_ci		val = (vx_inb(chip, RUER) >> 7) & 0x01;
5762306a36Sopenharmony_ci	} else {
5862306a36Sopenharmony_ci		vx_outl(chip, CSUER, 1); /* read */
5962306a36Sopenharmony_ci		vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
6062306a36Sopenharmony_ci		val = (vx_inl(chip, RUER) >> 7) & 0x01;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
6362306a36Sopenharmony_ci	return val;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * vx_write_one_cbit - write one bit to UER config
6862306a36Sopenharmony_ci * @index: the bit index
6962306a36Sopenharmony_ci * @val: bit value, 0 or 1
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic void vx_write_one_cbit(struct vx_core *chip, int index, int val)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	val = !!val;	/* 0 or 1 */
7462306a36Sopenharmony_ci	mutex_lock(&chip->lock);
7562306a36Sopenharmony_ci	if (vx_is_pcmcia(chip)) {
7662306a36Sopenharmony_ci		vx_outb(chip, CSUER, 0); /* write */
7762306a36Sopenharmony_ci		vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
7862306a36Sopenharmony_ci	} else {
7962306a36Sopenharmony_ci		vx_outl(chip, CSUER, 0); /* write */
8062306a36Sopenharmony_ci		vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * vx_read_uer_status - read the current UER status
8762306a36Sopenharmony_ci * @mode: pointer to store the UER mode, VX_UER_MODE_XXX
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * returns the frequency of UER, or 0 if not sync,
9062306a36Sopenharmony_ci * or a negative error code.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic int vx_read_uer_status(struct vx_core *chip, unsigned int *mode)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int val, freq;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Default values */
9762306a36Sopenharmony_ci	freq = 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Read UER status */
10062306a36Sopenharmony_ci	if (vx_is_pcmcia(chip))
10162306a36Sopenharmony_ci	    val = vx_inb(chip, CSUER);
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci	    val = vx_inl(chip, CSUER);
10462306a36Sopenharmony_ci	if (val < 0)
10562306a36Sopenharmony_ci		return val;
10662306a36Sopenharmony_ci	/* If clock is present, read frequency */
10762306a36Sopenharmony_ci	if (val & VX_SUER_CLOCK_PRESENT_MASK) {
10862306a36Sopenharmony_ci		switch (val & VX_SUER_FREQ_MASK) {
10962306a36Sopenharmony_ci		case VX_SUER_FREQ_32KHz_MASK:
11062306a36Sopenharmony_ci			freq = 32000;
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		case VX_SUER_FREQ_44KHz_MASK:
11362306a36Sopenharmony_ci			freq = 44100;
11462306a36Sopenharmony_ci			break;
11562306a36Sopenharmony_ci		case VX_SUER_FREQ_48KHz_MASK:
11662306a36Sopenharmony_ci			freq = 48000;
11762306a36Sopenharmony_ci			break;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci        }
12062306a36Sopenharmony_ci	if (val & VX_SUER_DATA_PRESENT_MASK)
12162306a36Sopenharmony_ci		/* bit 0 corresponds to consumer/professional bit */
12262306a36Sopenharmony_ci		*mode = vx_read_one_cbit(chip, 0) ?
12362306a36Sopenharmony_ci			VX_UER_MODE_PROFESSIONAL : VX_UER_MODE_CONSUMER;
12462306a36Sopenharmony_ci	else
12562306a36Sopenharmony_ci		*mode = VX_UER_MODE_NOT_PRESENT;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return freq;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * compute the sample clock value from frequency
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * The formula is as follows:
13562306a36Sopenharmony_ci *
13662306a36Sopenharmony_ci *    HexFreq = (dword) ((double) ((double) 28224000 / (double) Frequency))
13762306a36Sopenharmony_ci *    switch ( HexFreq & 0x00000F00 )
13862306a36Sopenharmony_ci *    case 0x00000100: ;
13962306a36Sopenharmony_ci *    case 0x00000200:
14062306a36Sopenharmony_ci *    case 0x00000300: HexFreq -= 0x00000201 ;
14162306a36Sopenharmony_ci *    case 0x00000400:
14262306a36Sopenharmony_ci *    case 0x00000500:
14362306a36Sopenharmony_ci *    case 0x00000600:
14462306a36Sopenharmony_ci *    case 0x00000700: HexFreq = (dword) (((double) 28224000 / (double) (Frequency*2)) - 1)
14562306a36Sopenharmony_ci *    default        : HexFreq = (dword) ((double) 28224000 / (double) (Frequency*4)) - 0x000001FF
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int vx_calc_clock_from_freq(struct vx_core *chip, int freq)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	int hexfreq;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (snd_BUG_ON(freq <= 0))
15362306a36Sopenharmony_ci		return 0;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	hexfreq = (28224000 * 10) / freq;
15662306a36Sopenharmony_ci	hexfreq = (hexfreq + 5) / 10;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* max freq = 55125 Hz */
15962306a36Sopenharmony_ci	if (snd_BUG_ON(hexfreq <= 0x00000200))
16062306a36Sopenharmony_ci		return 0;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (hexfreq <= 0x03ff)
16362306a36Sopenharmony_ci		return hexfreq - 0x00000201;
16462306a36Sopenharmony_ci	if (hexfreq <= 0x07ff)
16562306a36Sopenharmony_ci		return (hexfreq / 2) - 1;
16662306a36Sopenharmony_ci	if (hexfreq <= 0x0fff)
16762306a36Sopenharmony_ci		return (hexfreq / 4) + 0x000001ff;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return 0x5fe; 	/* min freq = 6893 Hz */
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/*
17462306a36Sopenharmony_ci * vx_change_clock_source - change the clock source
17562306a36Sopenharmony_ci * @source: the new source
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_cistatic void vx_change_clock_source(struct vx_core *chip, int source)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	/* we mute DAC to prevent clicks */
18062306a36Sopenharmony_ci	vx_toggle_dac_mute(chip, 1);
18162306a36Sopenharmony_ci	mutex_lock(&chip->lock);
18262306a36Sopenharmony_ci	chip->ops->set_clock_source(chip, source);
18362306a36Sopenharmony_ci	chip->clock_source = source;
18462306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
18562306a36Sopenharmony_ci	/* unmute */
18662306a36Sopenharmony_ci	vx_toggle_dac_mute(chip, 0);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * set the internal clock
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_civoid vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int clock;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Get real clock value */
19862306a36Sopenharmony_ci	clock = vx_calc_clock_from_freq(chip, freq);
19962306a36Sopenharmony_ci	snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq);
20062306a36Sopenharmony_ci	mutex_lock(&chip->lock);
20162306a36Sopenharmony_ci	if (vx_is_pcmcia(chip)) {
20262306a36Sopenharmony_ci		vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f);
20362306a36Sopenharmony_ci		vx_outb(chip, LOFREQ, clock & 0xff);
20462306a36Sopenharmony_ci	} else {
20562306a36Sopenharmony_ci		vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f);
20662306a36Sopenharmony_ci		vx_outl(chip, LOFREQ, clock & 0xff);
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/*
21362306a36Sopenharmony_ci * set the iec958 status bits
21462306a36Sopenharmony_ci * @bits: 32-bit status bits
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_civoid vx_set_iec958_status(struct vx_core *chip, unsigned int bits)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	int i;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
22162306a36Sopenharmony_ci		return;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	for (i = 0; i < 32; i++)
22462306a36Sopenharmony_ci		vx_write_one_cbit(chip, i, bits & (1 << i));
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/*
22962306a36Sopenharmony_ci * vx_set_clock - change the clock and audio source if necessary
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_ciint vx_set_clock(struct vx_core *chip, unsigned int freq)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int src_changed = 0;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* change the audio source if possible */
23962306a36Sopenharmony_ci	vx_sync_audio_source(chip);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (chip->clock_mode == VX_CLOCK_MODE_EXTERNAL ||
24262306a36Sopenharmony_ci	    (chip->clock_mode == VX_CLOCK_MODE_AUTO &&
24362306a36Sopenharmony_ci	     chip->audio_source == VX_AUDIO_SRC_DIGITAL)) {
24462306a36Sopenharmony_ci		if (chip->clock_source != UER_SYNC) {
24562306a36Sopenharmony_ci			vx_change_clock_source(chip, UER_SYNC);
24662306a36Sopenharmony_ci			mdelay(6);
24762306a36Sopenharmony_ci			src_changed = 1;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci	} else if (chip->clock_mode == VX_CLOCK_MODE_INTERNAL ||
25062306a36Sopenharmony_ci		   (chip->clock_mode == VX_CLOCK_MODE_AUTO &&
25162306a36Sopenharmony_ci		    chip->audio_source != VX_AUDIO_SRC_DIGITAL)) {
25262306a36Sopenharmony_ci		if (chip->clock_source != INTERNAL_QUARTZ) {
25362306a36Sopenharmony_ci			vx_change_clock_source(chip, INTERNAL_QUARTZ);
25462306a36Sopenharmony_ci			src_changed = 1;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci		if (chip->freq == freq)
25762306a36Sopenharmony_ci			return 0;
25862306a36Sopenharmony_ci		vx_set_internal_clock(chip, freq);
25962306a36Sopenharmony_ci		if (src_changed)
26062306a36Sopenharmony_ci			vx_modify_board_inputs(chip);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	if (chip->freq == freq)
26362306a36Sopenharmony_ci		return 0;
26462306a36Sopenharmony_ci	chip->freq = freq;
26562306a36Sopenharmony_ci	vx_modify_board_clock(chip, 1);
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/*
27162306a36Sopenharmony_ci * vx_change_frequency - called from interrupt handler
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_ciint vx_change_frequency(struct vx_core *chip)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int freq;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (chip->chip_status & VX_STAT_IS_STALE)
27862306a36Sopenharmony_ci		return 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (chip->clock_source == INTERNAL_QUARTZ)
28162306a36Sopenharmony_ci		return 0;
28262306a36Sopenharmony_ci	/*
28362306a36Sopenharmony_ci	 * Read the real UER board frequency
28462306a36Sopenharmony_ci	 */
28562306a36Sopenharmony_ci	freq = vx_read_uer_status(chip, &chip->uer_detected);
28662306a36Sopenharmony_ci	if (freq < 0)
28762306a36Sopenharmony_ci		return freq;
28862306a36Sopenharmony_ci	/*
28962306a36Sopenharmony_ci	 * The frequency computed by the DSP is good and
29062306a36Sopenharmony_ci	 * is different from the previous computed.
29162306a36Sopenharmony_ci	 */
29262306a36Sopenharmony_ci	if (freq == 48000 || freq == 44100 || freq == 32000)
29362306a36Sopenharmony_ci		chip->freq_detected = freq;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return 0;
29662306a36Sopenharmony_ci}
297