xref: /kernel/linux/linux-6.6/sound/pci/ac97/ac97_pcm.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *  Universal interface for Audio Codec '97
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  For more details look to AC '97 component specification revision 2.2
762306a36Sopenharmony_ci *  by Intel Corporation (http://developer.intel.com) and to datasheets
862306a36Sopenharmony_ci *  for specific codecs.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/export.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/pcm.h>
1962306a36Sopenharmony_ci#include <sound/control.h>
2062306a36Sopenharmony_ci#include <sound/ac97_codec.h>
2162306a36Sopenharmony_ci#include <sound/asoundef.h>
2262306a36Sopenharmony_ci#include "ac97_id.h"
2362306a36Sopenharmony_ci#include "ac97_local.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci *  PCM support
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const unsigned char rate_reg_tables[2][4][9] = {
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci  /* standard rates */
3262306a36Sopenharmony_ci  {
3362306a36Sopenharmony_ci  	/* 3&4 front, 7&8 rear, 6&9 center/lfe */
3462306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */
3562306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */
3662306a36Sopenharmony_ci	0xff,				/* slot 5 */
3762306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 6 */
3862306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 7 */
3962306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 8 */
4062306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 9 */
4162306a36Sopenharmony_ci	0xff,				/* slot 10 */
4262306a36Sopenharmony_ci	0xff,				/* slot 11 */
4362306a36Sopenharmony_ci  },
4462306a36Sopenharmony_ci  {
4562306a36Sopenharmony_ci  	/* 7&8 front, 6&9 rear, 10&11 center/lfe */
4662306a36Sopenharmony_ci	0xff,				/* slot 3 */
4762306a36Sopenharmony_ci	0xff,				/* slot 4 */
4862306a36Sopenharmony_ci	0xff,				/* slot 5 */
4962306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 6 */
5062306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */
5162306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */
5262306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 9 */
5362306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 10 */
5462306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 11 */
5562306a36Sopenharmony_ci  },
5662306a36Sopenharmony_ci  {
5762306a36Sopenharmony_ci  	/* 6&9 front, 10&11 rear, 3&4 center/lfe */
5862306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 3 */
5962306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 4 */
6062306a36Sopenharmony_ci	0xff,				/* slot 5 */
6162306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 6 */
6262306a36Sopenharmony_ci	0xff,				/* slot 7 */
6362306a36Sopenharmony_ci	0xff,				/* slot 8 */
6462306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 9 */
6562306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 10 */
6662306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 11 */
6762306a36Sopenharmony_ci  },
6862306a36Sopenharmony_ci  {
6962306a36Sopenharmony_ci  	/* 10&11 front, 3&4 rear, 7&8 center/lfe */
7062306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 3 */
7162306a36Sopenharmony_ci	AC97_PCM_SURR_DAC_RATE,		/* slot 4 */
7262306a36Sopenharmony_ci	0xff,				/* slot 5 */
7362306a36Sopenharmony_ci	0xff,				/* slot 6 */
7462306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 7 */
7562306a36Sopenharmony_ci	AC97_PCM_LFE_DAC_RATE,		/* slot 8 */
7662306a36Sopenharmony_ci	0xff,				/* slot 9 */
7762306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 10 */
7862306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 11 */
7962306a36Sopenharmony_ci  },
8062306a36Sopenharmony_ci},
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci  /* double rates */
8362306a36Sopenharmony_ci  {
8462306a36Sopenharmony_ci  	/* 3&4 front, 7&8 front (t+1) */
8562306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */
8662306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */
8762306a36Sopenharmony_ci	0xff,				/* slot 5 */
8862306a36Sopenharmony_ci	0xff,				/* slot 6 */
8962306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */
9062306a36Sopenharmony_ci	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */
9162306a36Sopenharmony_ci	0xff,				/* slot 9 */
9262306a36Sopenharmony_ci	0xff,				/* slot 10 */
9362306a36Sopenharmony_ci	0xff,				/* slot 11 */
9462306a36Sopenharmony_ci  },
9562306a36Sopenharmony_ci  {
9662306a36Sopenharmony_ci	/* not specified in the specification */
9762306a36Sopenharmony_ci	0xff,				/* slot 3 */
9862306a36Sopenharmony_ci	0xff,				/* slot 4 */
9962306a36Sopenharmony_ci	0xff,				/* slot 5 */
10062306a36Sopenharmony_ci	0xff,				/* slot 6 */
10162306a36Sopenharmony_ci	0xff,				/* slot 7 */
10262306a36Sopenharmony_ci	0xff,				/* slot 8 */
10362306a36Sopenharmony_ci	0xff,				/* slot 9 */
10462306a36Sopenharmony_ci	0xff,				/* slot 10 */
10562306a36Sopenharmony_ci	0xff,				/* slot 11 */
10662306a36Sopenharmony_ci  },
10762306a36Sopenharmony_ci  {
10862306a36Sopenharmony_ci	0xff,				/* slot 3 */
10962306a36Sopenharmony_ci	0xff,				/* slot 4 */
11062306a36Sopenharmony_ci	0xff,				/* slot 5 */
11162306a36Sopenharmony_ci	0xff,				/* slot 6 */
11262306a36Sopenharmony_ci	0xff,				/* slot 7 */
11362306a36Sopenharmony_ci	0xff,				/* slot 8 */
11462306a36Sopenharmony_ci	0xff,				/* slot 9 */
11562306a36Sopenharmony_ci	0xff,				/* slot 10 */
11662306a36Sopenharmony_ci	0xff,				/* slot 11 */
11762306a36Sopenharmony_ci  },
11862306a36Sopenharmony_ci  {
11962306a36Sopenharmony_ci	0xff,				/* slot 3 */
12062306a36Sopenharmony_ci	0xff,				/* slot 4 */
12162306a36Sopenharmony_ci	0xff,				/* slot 5 */
12262306a36Sopenharmony_ci	0xff,				/* slot 6 */
12362306a36Sopenharmony_ci	0xff,				/* slot 7 */
12462306a36Sopenharmony_ci	0xff,				/* slot 8 */
12562306a36Sopenharmony_ci	0xff,				/* slot 9 */
12662306a36Sopenharmony_ci	0xff,				/* slot 10 */
12762306a36Sopenharmony_ci	0xff,				/* slot 11 */
12862306a36Sopenharmony_ci  }
12962306a36Sopenharmony_ci}};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* FIXME: more various mappings for ADC? */
13262306a36Sopenharmony_cistatic const unsigned char rate_cregs[9] = {
13362306a36Sopenharmony_ci	AC97_PCM_LR_ADC_RATE,	/* 3 */
13462306a36Sopenharmony_ci	AC97_PCM_LR_ADC_RATE,	/* 4 */
13562306a36Sopenharmony_ci	0xff,			/* 5 */
13662306a36Sopenharmony_ci	AC97_PCM_MIC_ADC_RATE,	/* 6 */
13762306a36Sopenharmony_ci	0xff,			/* 7 */
13862306a36Sopenharmony_ci	0xff,			/* 8 */
13962306a36Sopenharmony_ci	0xff,			/* 9 */
14062306a36Sopenharmony_ci	0xff,			/* 10 */
14162306a36Sopenharmony_ci	0xff,			/* 11 */
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic unsigned char get_slot_reg(struct ac97_pcm *pcm, unsigned short cidx,
14562306a36Sopenharmony_ci				  unsigned short slot, int dbl)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	if (slot < 3)
14862306a36Sopenharmony_ci		return 0xff;
14962306a36Sopenharmony_ci	if (slot > 11)
15062306a36Sopenharmony_ci		return 0xff;
15162306a36Sopenharmony_ci	if (pcm->spdif)
15262306a36Sopenharmony_ci		return AC97_SPDIF; /* pseudo register */
15362306a36Sopenharmony_ci	if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK)
15462306a36Sopenharmony_ci		return rate_reg_tables[dbl][pcm->r[dbl].rate_table[cidx]][slot - 3];
15562306a36Sopenharmony_ci	else
15662306a36Sopenharmony_ci		return rate_cregs[slot - 3];
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	unsigned short old, bits, reg, mask;
16262306a36Sopenharmony_ci	unsigned int sbits;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (! (ac97->ext_id & AC97_EI_SPDIF))
16562306a36Sopenharmony_ci		return -ENODEV;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* TODO: double rate support */
16862306a36Sopenharmony_ci	if (ac97->flags & AC97_CS_SPDIF) {
16962306a36Sopenharmony_ci		switch (rate) {
17062306a36Sopenharmony_ci		case 48000: bits = 0; break;
17162306a36Sopenharmony_ci		case 44100: bits = 1 << AC97_SC_SPSR_SHIFT; break;
17262306a36Sopenharmony_ci		default: /* invalid - disable output */
17362306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
17462306a36Sopenharmony_ci			return -EINVAL;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci		reg = AC97_CSR_SPDIF;
17762306a36Sopenharmony_ci		mask = 1 << AC97_SC_SPSR_SHIFT;
17862306a36Sopenharmony_ci	} else {
17962306a36Sopenharmony_ci		if (ac97->id == AC97_ID_CM9739 && rate != 48000) {
18062306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
18162306a36Sopenharmony_ci			return -EINVAL;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci		switch (rate) {
18462306a36Sopenharmony_ci		case 44100: bits = AC97_SC_SPSR_44K; break;
18562306a36Sopenharmony_ci		case 48000: bits = AC97_SC_SPSR_48K; break;
18662306a36Sopenharmony_ci		case 32000: bits = AC97_SC_SPSR_32K; break;
18762306a36Sopenharmony_ci		default: /* invalid - disable output */
18862306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
18962306a36Sopenharmony_ci			return -EINVAL;
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci		reg = AC97_SPDIF;
19262306a36Sopenharmony_ci		mask = AC97_SC_SPSR_MASK;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
19662306a36Sopenharmony_ci	old = snd_ac97_read(ac97, reg) & mask;
19762306a36Sopenharmony_ci	if (old != bits) {
19862306a36Sopenharmony_ci		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
19962306a36Sopenharmony_ci		snd_ac97_update_bits_nolock(ac97, reg, mask, bits);
20062306a36Sopenharmony_ci		/* update the internal spdif bits */
20162306a36Sopenharmony_ci		sbits = ac97->spdif_status;
20262306a36Sopenharmony_ci		if (sbits & IEC958_AES0_PROFESSIONAL) {
20362306a36Sopenharmony_ci			sbits &= ~IEC958_AES0_PRO_FS;
20462306a36Sopenharmony_ci			switch (rate) {
20562306a36Sopenharmony_ci			case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break;
20662306a36Sopenharmony_ci			case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break;
20762306a36Sopenharmony_ci			case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break;
20862306a36Sopenharmony_ci			}
20962306a36Sopenharmony_ci		} else {
21062306a36Sopenharmony_ci			sbits &= ~(IEC958_AES3_CON_FS << 24);
21162306a36Sopenharmony_ci			switch (rate) {
21262306a36Sopenharmony_ci			case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break;
21362306a36Sopenharmony_ci			case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break;
21462306a36Sopenharmony_ci			case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break;
21562306a36Sopenharmony_ci			}
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci		ac97->spdif_status = sbits;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);
22062306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/**
22562306a36Sopenharmony_ci * snd_ac97_set_rate - change the rate of the given input/output.
22662306a36Sopenharmony_ci * @ac97: the ac97 instance
22762306a36Sopenharmony_ci * @reg: the register to change
22862306a36Sopenharmony_ci * @rate: the sample rate to set
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Changes the rate of the given input/output on the codec.
23162306a36Sopenharmony_ci * If the codec doesn't support VAR, the rate must be 48000 (except
23262306a36Sopenharmony_ci * for SPDIF).
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * The valid registers are AC97_PCM_MIC_ADC_RATE,
23562306a36Sopenharmony_ci * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
23662306a36Sopenharmony_ci * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
23762306a36Sopenharmony_ci * if the codec supports them.
23862306a36Sopenharmony_ci * AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
23962306a36Sopenharmony_ci * status bits.
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_ciint snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int dbl;
24662306a36Sopenharmony_ci	unsigned int tmp;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	dbl = rate > 48000;
24962306a36Sopenharmony_ci	if (dbl) {
25062306a36Sopenharmony_ci		if (!(ac97->flags & AC97_DOUBLE_RATE))
25162306a36Sopenharmony_ci			return -EINVAL;
25262306a36Sopenharmony_ci		if (reg != AC97_PCM_FRONT_DAC_RATE)
25362306a36Sopenharmony_ci			return -EINVAL;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	snd_ac97_update_power(ac97, reg, 1);
25762306a36Sopenharmony_ci	switch (reg) {
25862306a36Sopenharmony_ci	case AC97_PCM_MIC_ADC_RATE:
25962306a36Sopenharmony_ci		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0)	/* MIC VRA */
26062306a36Sopenharmony_ci			if (rate != 48000)
26162306a36Sopenharmony_ci				return -EINVAL;
26262306a36Sopenharmony_ci		break;
26362306a36Sopenharmony_ci	case AC97_PCM_FRONT_DAC_RATE:
26462306a36Sopenharmony_ci	case AC97_PCM_LR_ADC_RATE:
26562306a36Sopenharmony_ci		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */
26662306a36Sopenharmony_ci			if (rate != 48000 && rate != 96000)
26762306a36Sopenharmony_ci				return -EINVAL;
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case AC97_PCM_SURR_DAC_RATE:
27062306a36Sopenharmony_ci		if (! (ac97->scaps & AC97_SCAP_SURROUND_DAC))
27162306a36Sopenharmony_ci			return -EINVAL;
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci	case AC97_PCM_LFE_DAC_RATE:
27462306a36Sopenharmony_ci		if (! (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
27562306a36Sopenharmony_ci			return -EINVAL;
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case AC97_SPDIF:
27862306a36Sopenharmony_ci		/* special case */
27962306a36Sopenharmony_ci		return set_spdif_rate(ac97, rate);
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		return -EINVAL;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	if (dbl)
28462306a36Sopenharmony_ci		rate /= 2;
28562306a36Sopenharmony_ci	tmp = (rate * ac97->bus->clock) / 48000;
28662306a36Sopenharmony_ci	if (tmp > 65535)
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)
28962306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
29062306a36Sopenharmony_ci				     AC97_EA_DRA, dbl ? AC97_EA_DRA : 0);
29162306a36Sopenharmony_ci	snd_ac97_update(ac97, reg, tmp & 0xffff);
29262306a36Sopenharmony_ci	snd_ac97_read(ac97, reg);
29362306a36Sopenharmony_ci	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) {
29462306a36Sopenharmony_ci		/* Intel controllers require double rate data to be put in
29562306a36Sopenharmony_ci		 * slots 7+8
29662306a36Sopenharmony_ci		 */
29762306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE,
29862306a36Sopenharmony_ci				     AC97_GP_DRSS_MASK,
29962306a36Sopenharmony_ci				     dbl ? AC97_GP_DRSS_78 : 0);
30062306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_GENERAL_PURPOSE);
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_set_rate);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic unsigned short get_pslots(struct snd_ac97 *ac97, unsigned char *rate_table, unsigned short *spdif_slots)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	if (!ac97_is_audio(ac97))
31062306a36Sopenharmony_ci		return 0;
31162306a36Sopenharmony_ci	if (ac97_is_rev22(ac97) || ac97_can_amap(ac97)) {
31262306a36Sopenharmony_ci		unsigned short slots = 0;
31362306a36Sopenharmony_ci		if (ac97_is_rev22(ac97)) {
31462306a36Sopenharmony_ci			/* Note: it's simply emulation of AMAP behaviour */
31562306a36Sopenharmony_ci			u16 es;
31662306a36Sopenharmony_ci			es = ac97->regs[AC97_EXTENDED_ID] &= ~AC97_EI_DACS_SLOT_MASK;
31762306a36Sopenharmony_ci			switch (ac97->addr) {
31862306a36Sopenharmony_ci			case 1:
31962306a36Sopenharmony_ci			case 2: es |= (1<<AC97_EI_DACS_SLOT_SHIFT); break;
32062306a36Sopenharmony_ci			case 3: es |= (2<<AC97_EI_DACS_SLOT_SHIFT); break;
32162306a36Sopenharmony_ci			}
32262306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, es);
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		switch (ac97->addr) {
32562306a36Sopenharmony_ci		case 0:
32662306a36Sopenharmony_ci			slots |= (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
32762306a36Sopenharmony_ci			if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
32862306a36Sopenharmony_ci				slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
32962306a36Sopenharmony_ci			if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
33062306a36Sopenharmony_ci				slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
33162306a36Sopenharmony_ci			if (ac97->ext_id & AC97_EI_SPDIF) {
33262306a36Sopenharmony_ci				if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
33362306a36Sopenharmony_ci					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);
33462306a36Sopenharmony_ci				else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
33562306a36Sopenharmony_ci					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
33662306a36Sopenharmony_ci				else
33762306a36Sopenharmony_ci					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
33862306a36Sopenharmony_ci			}
33962306a36Sopenharmony_ci			*rate_table = 0;
34062306a36Sopenharmony_ci			break;
34162306a36Sopenharmony_ci		case 1:
34262306a36Sopenharmony_ci		case 2:
34362306a36Sopenharmony_ci			slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
34462306a36Sopenharmony_ci			if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
34562306a36Sopenharmony_ci				slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
34662306a36Sopenharmony_ci			if (ac97->ext_id & AC97_EI_SPDIF) {
34762306a36Sopenharmony_ci				if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
34862306a36Sopenharmony_ci					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
34962306a36Sopenharmony_ci				else
35062306a36Sopenharmony_ci					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
35162306a36Sopenharmony_ci			}
35262306a36Sopenharmony_ci			*rate_table = 1;
35362306a36Sopenharmony_ci			break;
35462306a36Sopenharmony_ci		case 3:
35562306a36Sopenharmony_ci			slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
35662306a36Sopenharmony_ci			if (ac97->ext_id & AC97_EI_SPDIF)
35762306a36Sopenharmony_ci				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
35862306a36Sopenharmony_ci			*rate_table = 2;
35962306a36Sopenharmony_ci			break;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci		return slots;
36262306a36Sopenharmony_ci	} else {
36362306a36Sopenharmony_ci		unsigned short slots;
36462306a36Sopenharmony_ci		slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
36562306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
36662306a36Sopenharmony_ci			slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
36762306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
36862306a36Sopenharmony_ci			slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
36962306a36Sopenharmony_ci		if (ac97->ext_id & AC97_EI_SPDIF) {
37062306a36Sopenharmony_ci			if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
37162306a36Sopenharmony_ci				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);
37262306a36Sopenharmony_ci			else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
37362306a36Sopenharmony_ci				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
37462306a36Sopenharmony_ci			else
37562306a36Sopenharmony_ci				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
37662306a36Sopenharmony_ci		}
37762306a36Sopenharmony_ci		*rate_table = 0;
37862306a36Sopenharmony_ci		return slots;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic unsigned short get_cslots(struct snd_ac97 *ac97)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	unsigned short slots;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!ac97_is_audio(ac97))
38762306a36Sopenharmony_ci		return 0;
38862306a36Sopenharmony_ci	slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
38962306a36Sopenharmony_ci	slots |= (1<<AC97_SLOT_MIC);
39062306a36Sopenharmony_ci	return slots;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned short slots, int dbl)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	int i, idx;
39662306a36Sopenharmony_ci	unsigned int rates = ~0;
39762306a36Sopenharmony_ci	unsigned char reg;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	for (i = 3; i < 12; i++) {
40062306a36Sopenharmony_ci		if (!(slots & (1 << i)))
40162306a36Sopenharmony_ci			continue;
40262306a36Sopenharmony_ci		reg = get_slot_reg(pcm, cidx, i, dbl);
40362306a36Sopenharmony_ci		switch (reg) {
40462306a36Sopenharmony_ci		case AC97_PCM_FRONT_DAC_RATE:	idx = AC97_RATES_FRONT_DAC; break;
40562306a36Sopenharmony_ci		case AC97_PCM_SURR_DAC_RATE:	idx = AC97_RATES_SURR_DAC; break;
40662306a36Sopenharmony_ci		case AC97_PCM_LFE_DAC_RATE:	idx = AC97_RATES_LFE_DAC; break;
40762306a36Sopenharmony_ci		case AC97_PCM_LR_ADC_RATE:	idx = AC97_RATES_ADC; break;
40862306a36Sopenharmony_ci		case AC97_PCM_MIC_ADC_RATE:	idx = AC97_RATES_MIC_ADC; break;
40962306a36Sopenharmony_ci		default:			idx = AC97_RATES_SPDIF; break;
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci		rates &= pcm->r[dbl].codec[cidx]->rates[idx];
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci	if (!dbl)
41462306a36Sopenharmony_ci		rates &= ~(SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 |
41562306a36Sopenharmony_ci			   SNDRV_PCM_RATE_96000);
41662306a36Sopenharmony_ci	return rates;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/**
42062306a36Sopenharmony_ci * snd_ac97_pcm_assign - assign AC97 slots to given PCM streams
42162306a36Sopenharmony_ci * @bus: the ac97 bus instance
42262306a36Sopenharmony_ci * @pcms_count: count of PCMs to be assigned
42362306a36Sopenharmony_ci * @pcms: PCMs to be assigned
42462306a36Sopenharmony_ci *
42562306a36Sopenharmony_ci * It assigns available AC97 slots for given PCMs. If none or only
42662306a36Sopenharmony_ci * some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
42762306a36Sopenharmony_ci * are reduced and might be zero.
42862306a36Sopenharmony_ci *
42962306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
43062306a36Sopenharmony_ci */
43162306a36Sopenharmony_ciint snd_ac97_pcm_assign(struct snd_ac97_bus *bus,
43262306a36Sopenharmony_ci			unsigned short pcms_count,
43362306a36Sopenharmony_ci			const struct ac97_pcm *pcms)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	int i, j, k;
43662306a36Sopenharmony_ci	const struct ac97_pcm *pcm;
43762306a36Sopenharmony_ci	struct ac97_pcm *rpcms, *rpcm;
43862306a36Sopenharmony_ci	unsigned short avail_slots[2][4];
43962306a36Sopenharmony_ci	unsigned char rate_table[2][4];
44062306a36Sopenharmony_ci	unsigned short tmp, slots;
44162306a36Sopenharmony_ci	unsigned short spdif_slots[4];
44262306a36Sopenharmony_ci	unsigned int rates;
44362306a36Sopenharmony_ci	struct snd_ac97 *codec;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	rpcms = kcalloc(pcms_count, sizeof(struct ac97_pcm), GFP_KERNEL);
44662306a36Sopenharmony_ci	if (rpcms == NULL)
44762306a36Sopenharmony_ci		return -ENOMEM;
44862306a36Sopenharmony_ci	memset(avail_slots, 0, sizeof(avail_slots));
44962306a36Sopenharmony_ci	memset(rate_table, 0, sizeof(rate_table));
45062306a36Sopenharmony_ci	memset(spdif_slots, 0, sizeof(spdif_slots));
45162306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
45262306a36Sopenharmony_ci		codec = bus->codec[i];
45362306a36Sopenharmony_ci		if (!codec)
45462306a36Sopenharmony_ci			continue;
45562306a36Sopenharmony_ci		avail_slots[0][i] = get_pslots(codec, &rate_table[0][i], &spdif_slots[i]);
45662306a36Sopenharmony_ci		avail_slots[1][i] = get_cslots(codec);
45762306a36Sopenharmony_ci		if (!(codec->scaps & AC97_SCAP_INDEP_SDIN)) {
45862306a36Sopenharmony_ci			for (j = 0; j < i; j++) {
45962306a36Sopenharmony_ci				if (bus->codec[j])
46062306a36Sopenharmony_ci					avail_slots[1][i] &= ~avail_slots[1][j];
46162306a36Sopenharmony_ci			}
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci	/* first step - exclusive devices */
46562306a36Sopenharmony_ci	for (i = 0; i < pcms_count; i++) {
46662306a36Sopenharmony_ci		pcm = &pcms[i];
46762306a36Sopenharmony_ci		rpcm = &rpcms[i];
46862306a36Sopenharmony_ci		/* low-level driver thinks that it's more clever */
46962306a36Sopenharmony_ci		if (pcm->copy_flag) {
47062306a36Sopenharmony_ci			*rpcm = *pcm;
47162306a36Sopenharmony_ci			continue;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		rpcm->stream = pcm->stream;
47462306a36Sopenharmony_ci		rpcm->exclusive = pcm->exclusive;
47562306a36Sopenharmony_ci		rpcm->spdif = pcm->spdif;
47662306a36Sopenharmony_ci		rpcm->private_value = pcm->private_value;
47762306a36Sopenharmony_ci		rpcm->bus = bus;
47862306a36Sopenharmony_ci		rpcm->rates = ~0;
47962306a36Sopenharmony_ci		slots = pcm->r[0].slots;
48062306a36Sopenharmony_ci		for (j = 0; j < 4 && slots; j++) {
48162306a36Sopenharmony_ci			if (!bus->codec[j])
48262306a36Sopenharmony_ci				continue;
48362306a36Sopenharmony_ci			rates = ~0;
48462306a36Sopenharmony_ci			if (pcm->spdif && pcm->stream == 0)
48562306a36Sopenharmony_ci				tmp = spdif_slots[j];
48662306a36Sopenharmony_ci			else
48762306a36Sopenharmony_ci				tmp = avail_slots[pcm->stream][j];
48862306a36Sopenharmony_ci			if (pcm->exclusive) {
48962306a36Sopenharmony_ci				/* exclusive access */
49062306a36Sopenharmony_ci				tmp &= slots;
49162306a36Sopenharmony_ci				for (k = 0; k < i; k++) {
49262306a36Sopenharmony_ci					if (rpcm->stream == rpcms[k].stream)
49362306a36Sopenharmony_ci						tmp &= ~rpcms[k].r[0].rslots[j];
49462306a36Sopenharmony_ci				}
49562306a36Sopenharmony_ci			} else {
49662306a36Sopenharmony_ci				/* non-exclusive access */
49762306a36Sopenharmony_ci				tmp &= pcm->r[0].slots;
49862306a36Sopenharmony_ci			}
49962306a36Sopenharmony_ci			if (tmp) {
50062306a36Sopenharmony_ci				rpcm->r[0].rslots[j] = tmp;
50162306a36Sopenharmony_ci				rpcm->r[0].codec[j] = bus->codec[j];
50262306a36Sopenharmony_ci				rpcm->r[0].rate_table[j] = rate_table[pcm->stream][j];
50362306a36Sopenharmony_ci				if (bus->no_vra)
50462306a36Sopenharmony_ci					rates = SNDRV_PCM_RATE_48000;
50562306a36Sopenharmony_ci				else
50662306a36Sopenharmony_ci					rates = get_rates(rpcm, j, tmp, 0);
50762306a36Sopenharmony_ci				if (pcm->exclusive)
50862306a36Sopenharmony_ci					avail_slots[pcm->stream][j] &= ~tmp;
50962306a36Sopenharmony_ci			}
51062306a36Sopenharmony_ci			slots &= ~tmp;
51162306a36Sopenharmony_ci			rpcm->r[0].slots |= tmp;
51262306a36Sopenharmony_ci			rpcm->rates &= rates;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci		/* for double rate, we check the first codec only */
51562306a36Sopenharmony_ci		if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK &&
51662306a36Sopenharmony_ci		    bus->codec[0] && (bus->codec[0]->flags & AC97_DOUBLE_RATE) &&
51762306a36Sopenharmony_ci		    rate_table[pcm->stream][0] == 0) {
51862306a36Sopenharmony_ci			tmp = (1<<AC97_SLOT_PCM_LEFT) | (1<<AC97_SLOT_PCM_RIGHT) |
51962306a36Sopenharmony_ci			      (1<<AC97_SLOT_PCM_LEFT_0) | (1<<AC97_SLOT_PCM_RIGHT_0);
52062306a36Sopenharmony_ci			if ((tmp & pcm->r[1].slots) == tmp) {
52162306a36Sopenharmony_ci				rpcm->r[1].slots = tmp;
52262306a36Sopenharmony_ci				rpcm->r[1].rslots[0] = tmp;
52362306a36Sopenharmony_ci				rpcm->r[1].rate_table[0] = 0;
52462306a36Sopenharmony_ci				rpcm->r[1].codec[0] = bus->codec[0];
52562306a36Sopenharmony_ci				if (pcm->exclusive)
52662306a36Sopenharmony_ci					avail_slots[pcm->stream][0] &= ~tmp;
52762306a36Sopenharmony_ci				if (bus->no_vra)
52862306a36Sopenharmony_ci					rates = SNDRV_PCM_RATE_96000;
52962306a36Sopenharmony_ci				else
53062306a36Sopenharmony_ci					rates = get_rates(rpcm, 0, tmp, 1);
53162306a36Sopenharmony_ci				rpcm->rates |= rates;
53262306a36Sopenharmony_ci			}
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci		if (rpcm->rates == ~0)
53562306a36Sopenharmony_ci			rpcm->rates = 0; /* not used */
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci	bus->pcms_count = pcms_count;
53862306a36Sopenharmony_ci	bus->pcms = rpcms;
53962306a36Sopenharmony_ci	return 0;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_assign);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/**
54562306a36Sopenharmony_ci * snd_ac97_pcm_open - opens the given AC97 pcm
54662306a36Sopenharmony_ci * @pcm: the ac97 pcm instance
54762306a36Sopenharmony_ci * @rate: rate in Hz, if codec does not support VRA, this value must be 48000Hz
54862306a36Sopenharmony_ci * @cfg: output stream characteristics
54962306a36Sopenharmony_ci * @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci * It locks the specified slots and sets the given rate to AC97 registers.
55262306a36Sopenharmony_ci *
55362306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
55462306a36Sopenharmony_ci */
55562306a36Sopenharmony_ciint snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
55662306a36Sopenharmony_ci		      enum ac97_pcm_cfg cfg, unsigned short slots)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct snd_ac97_bus *bus;
55962306a36Sopenharmony_ci	int i, cidx, r, ok_flag;
56062306a36Sopenharmony_ci	unsigned int reg_ok[4] = {0,0,0,0};
56162306a36Sopenharmony_ci	unsigned char reg;
56262306a36Sopenharmony_ci	int err = 0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	r = rate > 48000;
56562306a36Sopenharmony_ci	bus = pcm->bus;
56662306a36Sopenharmony_ci	if (cfg == AC97_PCM_CFG_SPDIF) {
56762306a36Sopenharmony_ci		for (cidx = 0; cidx < 4; cidx++)
56862306a36Sopenharmony_ci			if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) {
56962306a36Sopenharmony_ci				err = set_spdif_rate(bus->codec[cidx], rate);
57062306a36Sopenharmony_ci				if (err < 0)
57162306a36Sopenharmony_ci					return err;
57262306a36Sopenharmony_ci			}
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci	spin_lock_irq(&pcm->bus->bus_lock);
57562306a36Sopenharmony_ci	for (i = 3; i < 12; i++) {
57662306a36Sopenharmony_ci		if (!(slots & (1 << i)))
57762306a36Sopenharmony_ci			continue;
57862306a36Sopenharmony_ci		ok_flag = 0;
57962306a36Sopenharmony_ci		for (cidx = 0; cidx < 4; cidx++) {
58062306a36Sopenharmony_ci			if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
58162306a36Sopenharmony_ci				spin_unlock_irq(&pcm->bus->bus_lock);
58262306a36Sopenharmony_ci				err = -EBUSY;
58362306a36Sopenharmony_ci				goto error;
58462306a36Sopenharmony_ci			}
58562306a36Sopenharmony_ci			if (pcm->r[r].rslots[cidx] & (1 << i)) {
58662306a36Sopenharmony_ci				bus->used_slots[pcm->stream][cidx] |= (1 << i);
58762306a36Sopenharmony_ci				ok_flag++;
58862306a36Sopenharmony_ci			}
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci		if (!ok_flag) {
59162306a36Sopenharmony_ci			spin_unlock_irq(&pcm->bus->bus_lock);
59262306a36Sopenharmony_ci			dev_err(bus->card->dev,
59362306a36Sopenharmony_ci				"cannot find configuration for AC97 slot %i\n",
59462306a36Sopenharmony_ci				i);
59562306a36Sopenharmony_ci			err = -EAGAIN;
59662306a36Sopenharmony_ci			goto error;
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci	pcm->cur_dbl = r;
60062306a36Sopenharmony_ci	spin_unlock_irq(&pcm->bus->bus_lock);
60162306a36Sopenharmony_ci	for (i = 3; i < 12; i++) {
60262306a36Sopenharmony_ci		if (!(slots & (1 << i)))
60362306a36Sopenharmony_ci			continue;
60462306a36Sopenharmony_ci		for (cidx = 0; cidx < 4; cidx++) {
60562306a36Sopenharmony_ci			if (pcm->r[r].rslots[cidx] & (1 << i)) {
60662306a36Sopenharmony_ci				reg = get_slot_reg(pcm, cidx, i, r);
60762306a36Sopenharmony_ci				if (reg == 0xff) {
60862306a36Sopenharmony_ci					dev_err(bus->card->dev,
60962306a36Sopenharmony_ci						"invalid AC97 slot %i?\n", i);
61062306a36Sopenharmony_ci					continue;
61162306a36Sopenharmony_ci				}
61262306a36Sopenharmony_ci				if (reg_ok[cidx] & (1 << (reg - AC97_PCM_FRONT_DAC_RATE)))
61362306a36Sopenharmony_ci					continue;
61462306a36Sopenharmony_ci				dev_dbg(bus->card->dev,
61562306a36Sopenharmony_ci					"setting ac97 reg 0x%x to rate %d\n",
61662306a36Sopenharmony_ci					reg, rate);
61762306a36Sopenharmony_ci				err = snd_ac97_set_rate(pcm->r[r].codec[cidx], reg, rate);
61862306a36Sopenharmony_ci				if (err < 0)
61962306a36Sopenharmony_ci					dev_err(bus->card->dev,
62062306a36Sopenharmony_ci						"error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n",
62162306a36Sopenharmony_ci						cidx, reg, rate, err);
62262306a36Sopenharmony_ci				else
62362306a36Sopenharmony_ci					reg_ok[cidx] |= (1 << (reg - AC97_PCM_FRONT_DAC_RATE));
62462306a36Sopenharmony_ci			}
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci	pcm->aslots = slots;
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci error:
63162306a36Sopenharmony_ci	pcm->aslots = slots;
63262306a36Sopenharmony_ci	snd_ac97_pcm_close(pcm);
63362306a36Sopenharmony_ci	return err;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_open);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * snd_ac97_pcm_close - closes the given AC97 pcm
64062306a36Sopenharmony_ci * @pcm: the ac97 pcm instance
64162306a36Sopenharmony_ci *
64262306a36Sopenharmony_ci * It frees the locked AC97 slots.
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Return: Zero.
64562306a36Sopenharmony_ci */
64662306a36Sopenharmony_ciint snd_ac97_pcm_close(struct ac97_pcm *pcm)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct snd_ac97_bus *bus;
64962306a36Sopenharmony_ci	unsigned short slots = pcm->aslots;
65062306a36Sopenharmony_ci	int i, cidx;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
65362306a36Sopenharmony_ci	int r = pcm->cur_dbl;
65462306a36Sopenharmony_ci	for (i = 3; i < 12; i++) {
65562306a36Sopenharmony_ci		if (!(slots & (1 << i)))
65662306a36Sopenharmony_ci			continue;
65762306a36Sopenharmony_ci		for (cidx = 0; cidx < 4; cidx++) {
65862306a36Sopenharmony_ci			if (pcm->r[r].rslots[cidx] & (1 << i)) {
65962306a36Sopenharmony_ci				int reg = get_slot_reg(pcm, cidx, i, r);
66062306a36Sopenharmony_ci				snd_ac97_update_power(pcm->r[r].codec[cidx],
66162306a36Sopenharmony_ci						      reg, 0);
66262306a36Sopenharmony_ci			}
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci#endif
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	bus = pcm->bus;
66862306a36Sopenharmony_ci	spin_lock_irq(&pcm->bus->bus_lock);
66962306a36Sopenharmony_ci	for (i = 3; i < 12; i++) {
67062306a36Sopenharmony_ci		if (!(slots & (1 << i)))
67162306a36Sopenharmony_ci			continue;
67262306a36Sopenharmony_ci		for (cidx = 0; cidx < 4; cidx++)
67362306a36Sopenharmony_ci			bus->used_slots[pcm->stream][cidx] &= ~(1 << i);
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci	pcm->aslots = 0;
67662306a36Sopenharmony_ci	pcm->cur_dbl = 0;
67762306a36Sopenharmony_ci	spin_unlock_irq(&pcm->bus->bus_lock);
67862306a36Sopenharmony_ci	return 0;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_close);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params,
68462306a36Sopenharmony_ci					  struct snd_pcm_hw_rule *rule)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
68762306a36Sopenharmony_ci	if (channels->min > 2) {
68862306a36Sopenharmony_ci		static const struct snd_interval single_rates = {
68962306a36Sopenharmony_ci			.min = 1,
69062306a36Sopenharmony_ci			.max = 48000,
69162306a36Sopenharmony_ci		};
69262306a36Sopenharmony_ci		struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
69362306a36Sopenharmony_ci		return snd_interval_refine(rate, &single_rates);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params,
69962306a36Sopenharmony_ci					      struct snd_pcm_hw_rule *rule)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
70262306a36Sopenharmony_ci	if (rate->min > 48000) {
70362306a36Sopenharmony_ci		static const struct snd_interval double_rate_channels = {
70462306a36Sopenharmony_ci			.min = 2,
70562306a36Sopenharmony_ci			.max = 2,
70662306a36Sopenharmony_ci		};
70762306a36Sopenharmony_ci		struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
70862306a36Sopenharmony_ci		return snd_interval_refine(channels, &double_rate_channels);
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci	return 0;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci/**
71462306a36Sopenharmony_ci * snd_ac97_pcm_double_rate_rules - set double rate constraints
71562306a36Sopenharmony_ci * @runtime: the runtime of the ac97 front playback pcm
71662306a36Sopenharmony_ci *
71762306a36Sopenharmony_ci * Installs the hardware constraint rules to prevent using double rates and
71862306a36Sopenharmony_ci * more than two channels at the same time.
71962306a36Sopenharmony_ci *
72062306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
72162306a36Sopenharmony_ci */
72262306a36Sopenharmony_ciint snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	int err;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
72762306a36Sopenharmony_ci				  double_rate_hw_constraint_rate, NULL,
72862306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
72962306a36Sopenharmony_ci	if (err < 0)
73062306a36Sopenharmony_ci		return err;
73162306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
73262306a36Sopenharmony_ci				  double_rate_hw_constraint_channels, NULL,
73362306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE, -1);
73462306a36Sopenharmony_ci	return err;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
738