162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Digigram pcxhr compatible soundcards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * main file with alsa callbacks
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2004 by Digigram <alsa@digigram.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/pci.h>
1562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <sound/core.h>
2162306a36Sopenharmony_ci#include <sound/initval.h>
2262306a36Sopenharmony_ci#include <sound/info.h>
2362306a36Sopenharmony_ci#include <sound/control.h>
2462306a36Sopenharmony_ci#include <sound/pcm.h>
2562306a36Sopenharmony_ci#include <sound/pcm_params.h>
2662306a36Sopenharmony_ci#include "pcxhr.h"
2762306a36Sopenharmony_ci#include "pcxhr_mixer.h"
2862306a36Sopenharmony_ci#include "pcxhr_hwdep.h"
2962306a36Sopenharmony_ci#include "pcxhr_core.h"
3062306a36Sopenharmony_ci#include "pcxhr_mix22.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define DRIVER_NAME "pcxhr"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciMODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>, "
3562306a36Sopenharmony_ci	      "Marc Titinger <titinger@digigram.com>");
3662306a36Sopenharmony_ciMODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);
3762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
4062306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
4162306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
4262306a36Sopenharmony_cistatic bool mono[SNDRV_CARDS];				/* capture  mono only */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
4562306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");
4662306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Digigram " DRIVER_NAME " soundcard");
4862306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
4962306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Digigram " DRIVER_NAME " soundcard");
5062306a36Sopenharmony_cimodule_param_array(mono, bool, NULL, 0444);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(mono, "Mono capture mode (default is stereo)");
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cienum {
5462306a36Sopenharmony_ci	PCI_ID_VX882HR,
5562306a36Sopenharmony_ci	PCI_ID_PCX882HR,
5662306a36Sopenharmony_ci	PCI_ID_VX881HR,
5762306a36Sopenharmony_ci	PCI_ID_PCX881HR,
5862306a36Sopenharmony_ci	PCI_ID_VX882E,
5962306a36Sopenharmony_ci	PCI_ID_PCX882E,
6062306a36Sopenharmony_ci	PCI_ID_VX881E,
6162306a36Sopenharmony_ci	PCI_ID_PCX881E,
6262306a36Sopenharmony_ci	PCI_ID_VX1222HR,
6362306a36Sopenharmony_ci	PCI_ID_PCX1222HR,
6462306a36Sopenharmony_ci	PCI_ID_VX1221HR,
6562306a36Sopenharmony_ci	PCI_ID_PCX1221HR,
6662306a36Sopenharmony_ci	PCI_ID_VX1222E,
6762306a36Sopenharmony_ci	PCI_ID_PCX1222E,
6862306a36Sopenharmony_ci	PCI_ID_VX1221E,
6962306a36Sopenharmony_ci	PCI_ID_PCX1221E,
7062306a36Sopenharmony_ci	PCI_ID_VX222HR,
7162306a36Sopenharmony_ci	PCI_ID_VX222E,
7262306a36Sopenharmony_ci	PCI_ID_PCX22HR,
7362306a36Sopenharmony_ci	PCI_ID_PCX22E,
7462306a36Sopenharmony_ci	PCI_ID_VX222HRMIC,
7562306a36Sopenharmony_ci	PCI_ID_VX222E_MIC,
7662306a36Sopenharmony_ci	PCI_ID_PCX924HR,
7762306a36Sopenharmony_ci	PCI_ID_PCX924E,
7862306a36Sopenharmony_ci	PCI_ID_PCX924HRMIC,
7962306a36Sopenharmony_ci	PCI_ID_PCX924E_MIC,
8062306a36Sopenharmony_ci	PCI_ID_VX442HR,
8162306a36Sopenharmony_ci	PCI_ID_PCX442HR,
8262306a36Sopenharmony_ci	PCI_ID_VX442E,
8362306a36Sopenharmony_ci	PCI_ID_PCX442E,
8462306a36Sopenharmony_ci	PCI_ID_VX822HR,
8562306a36Sopenharmony_ci	PCI_ID_PCX822HR,
8662306a36Sopenharmony_ci	PCI_ID_VX822E,
8762306a36Sopenharmony_ci	PCI_ID_PCX822E,
8862306a36Sopenharmony_ci	PCI_ID_LAST
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct pci_device_id pcxhr_ids[] = {
9262306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
9362306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
9462306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
9562306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },
9662306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb021, 0, 0, PCI_ID_VX882E, },
9762306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb121, 0, 0, PCI_ID_PCX882E, },
9862306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb221, 0, 0, PCI_ID_VX881E, },
9962306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb321, 0, 0, PCI_ID_PCX881E, },
10062306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb401, 0, 0, PCI_ID_VX1222HR, },
10162306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, },
10262306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb601, 0, 0, PCI_ID_VX1221HR, },
10362306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, },
10462306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb421, 0, 0, PCI_ID_VX1222E, },
10562306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb521, 0, 0, PCI_ID_PCX1222E, },
10662306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb621, 0, 0, PCI_ID_VX1221E, },
10762306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb721, 0, 0, PCI_ID_PCX1221E, },
10862306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xba01, 0, 0, PCI_ID_VX222HR, },
10962306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xba21, 0, 0, PCI_ID_VX222E, },
11062306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbd01, 0, 0, PCI_ID_PCX22HR, },
11162306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbd21, 0, 0, PCI_ID_PCX22E, },
11262306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbc01, 0, 0, PCI_ID_VX222HRMIC, },
11362306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbc21, 0, 0, PCI_ID_VX222E_MIC, },
11462306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbb01, 0, 0, PCI_ID_PCX924HR, },
11562306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
11662306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
11762306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
11862306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd001, 0, 0, PCI_ID_VX442HR, },
11962306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd101, 0, 0, PCI_ID_PCX442HR, },
12062306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd021, 0, 0, PCI_ID_VX442E, },
12162306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd121, 0, 0, PCI_ID_PCX442E, },
12262306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd201, 0, 0, PCI_ID_VX822HR, },
12362306a36Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd301, 0, 0, PCI_ID_PCX822HR, },
12462306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd221, 0, 0, PCI_ID_VX822E, },
12562306a36Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd321, 0, 0, PCI_ID_PCX822E, },
12662306a36Sopenharmony_ci	{ 0, }
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pcxhr_ids);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistruct board_parameters {
13262306a36Sopenharmony_ci	char* board_name;
13362306a36Sopenharmony_ci	short playback_chips;
13462306a36Sopenharmony_ci	short capture_chips;
13562306a36Sopenharmony_ci	short fw_file_set;
13662306a36Sopenharmony_ci	short firmware_num;
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_cistatic const struct board_parameters pcxhr_board_params[] = {
13962306a36Sopenharmony_ci[PCI_ID_VX882HR] =      { "VX882HR",      4, 4, 0, 41 },
14062306a36Sopenharmony_ci[PCI_ID_PCX882HR] =     { "PCX882HR",     4, 4, 0, 41 },
14162306a36Sopenharmony_ci[PCI_ID_VX881HR] =      { "VX881HR",      4, 4, 0, 41 },
14262306a36Sopenharmony_ci[PCI_ID_PCX881HR] =     { "PCX881HR",     4, 4, 0, 41 },
14362306a36Sopenharmony_ci[PCI_ID_VX882E] =       { "VX882e",       4, 4, 1, 41 },
14462306a36Sopenharmony_ci[PCI_ID_PCX882E] =      { "PCX882e",      4, 4, 1, 41 },
14562306a36Sopenharmony_ci[PCI_ID_VX881E] =       { "VX881e",       4, 4, 1, 41 },
14662306a36Sopenharmony_ci[PCI_ID_PCX881E] =      { "PCX881e",      4, 4, 1, 41 },
14762306a36Sopenharmony_ci[PCI_ID_VX1222HR] =     { "VX1222HR",     6, 1, 2, 42 },
14862306a36Sopenharmony_ci[PCI_ID_PCX1222HR] =    { "PCX1222HR",    6, 1, 2, 42 },
14962306a36Sopenharmony_ci[PCI_ID_VX1221HR] =     { "VX1221HR",     6, 1, 2, 42 },
15062306a36Sopenharmony_ci[PCI_ID_PCX1221HR] =    { "PCX1221HR",    6, 1, 2, 42 },
15162306a36Sopenharmony_ci[PCI_ID_VX1222E] =      { "VX1222e",      6, 1, 3, 42 },
15262306a36Sopenharmony_ci[PCI_ID_PCX1222E] =     { "PCX1222e",     6, 1, 3, 42 },
15362306a36Sopenharmony_ci[PCI_ID_VX1221E] =      { "VX1221e",      6, 1, 3, 42 },
15462306a36Sopenharmony_ci[PCI_ID_PCX1221E] =     { "PCX1221e",     6, 1, 3, 42 },
15562306a36Sopenharmony_ci[PCI_ID_VX222HR] =      { "VX222HR",      1, 1, 4, 44 },
15662306a36Sopenharmony_ci[PCI_ID_VX222E] =       { "VX222e",       1, 1, 4, 44 },
15762306a36Sopenharmony_ci[PCI_ID_PCX22HR] =      { "PCX22HR",      1, 0, 4, 44 },
15862306a36Sopenharmony_ci[PCI_ID_PCX22E] =       { "PCX22e",       1, 0, 4, 44 },
15962306a36Sopenharmony_ci[PCI_ID_VX222HRMIC] =   { "VX222HR-Mic",  1, 1, 5, 44 },
16062306a36Sopenharmony_ci[PCI_ID_VX222E_MIC] =   { "VX222e-Mic",   1, 1, 5, 44 },
16162306a36Sopenharmony_ci[PCI_ID_PCX924HR] =     { "PCX924HR",     1, 1, 5, 44 },
16262306a36Sopenharmony_ci[PCI_ID_PCX924E] =      { "PCX924e",      1, 1, 5, 44 },
16362306a36Sopenharmony_ci[PCI_ID_PCX924HRMIC] =  { "PCX924HR-Mic", 1, 1, 5, 44 },
16462306a36Sopenharmony_ci[PCI_ID_PCX924E_MIC] =  { "PCX924e-Mic",  1, 1, 5, 44 },
16562306a36Sopenharmony_ci[PCI_ID_VX442HR] =      { "VX442HR",      2, 2, 0, 41 },
16662306a36Sopenharmony_ci[PCI_ID_PCX442HR] =     { "PCX442HR",     2, 2, 0, 41 },
16762306a36Sopenharmony_ci[PCI_ID_VX442E] =       { "VX442e",       2, 2, 1, 41 },
16862306a36Sopenharmony_ci[PCI_ID_PCX442E] =      { "PCX442e",      2, 2, 1, 41 },
16962306a36Sopenharmony_ci[PCI_ID_VX822HR] =      { "VX822HR",      4, 1, 2, 42 },
17062306a36Sopenharmony_ci[PCI_ID_PCX822HR] =     { "PCX822HR",     4, 1, 2, 42 },
17162306a36Sopenharmony_ci[PCI_ID_VX822E] =       { "VX822e",       4, 1, 3, 42 },
17262306a36Sopenharmony_ci[PCI_ID_PCX822E] =      { "PCX822e",      4, 1, 3, 42 },
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
17662306a36Sopenharmony_ci/* VX222HR, VX222e, PCX22HR and PCX22e */
17762306a36Sopenharmony_ci#define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
17862306a36Sopenharmony_ci/* some boards do not support 192kHz on digital AES input plugs */
17962306a36Sopenharmony_ci#define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
18062306a36Sopenharmony_ci				      (x->fw_file_set == 0)   || \
18162306a36Sopenharmony_ci				      (x->fw_file_set == 2))
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
18462306a36Sopenharmony_ci				   unsigned int* realfreq)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	unsigned int reg;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (freq < 6900 || freq > 110000)
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci	reg = (28224000 * 2) / freq;
19162306a36Sopenharmony_ci	reg = (reg - 1) / 2;
19262306a36Sopenharmony_ci	if (reg < 0x200)
19362306a36Sopenharmony_ci		*pllreg = reg + 0x800;
19462306a36Sopenharmony_ci	else if (reg < 0x400)
19562306a36Sopenharmony_ci		*pllreg = reg & 0x1ff;
19662306a36Sopenharmony_ci	else if (reg < 0x800) {
19762306a36Sopenharmony_ci		*pllreg = ((reg >> 1) & 0x1ff) + 0x200;
19862306a36Sopenharmony_ci		reg &= ~1;
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		*pllreg = ((reg >> 2) & 0x1ff) + 0x400;
20162306a36Sopenharmony_ci		reg &= ~3;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	if (realfreq)
20462306a36Sopenharmony_ci		*realfreq = (28224000 / (reg + 1));
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci#define PCXHR_FREQ_REG_MASK		0x1f
21062306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_48000		0x00
21162306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_24000		0x01
21262306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_12000		0x09
21362306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_32000		0x08
21462306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_16000		0x04
21562306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_8000		0x0c
21662306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_44100		0x02
21762306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_22050		0x0a
21862306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_11025		0x06
21962306a36Sopenharmony_ci#define PCXHR_FREQ_PLL			0x05
22062306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_192000	0x10
22162306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_96000		0x18
22262306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_176400	0x14
22362306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_88200		0x1c
22462306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_128000	0x12
22562306a36Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_64000		0x1a
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci#define PCXHR_FREQ_WORD_CLOCK		0x0f
22862306a36Sopenharmony_ci#define PCXHR_FREQ_SYNC_AES		0x0e
22962306a36Sopenharmony_ci#define PCXHR_FREQ_AES_1		0x07
23062306a36Sopenharmony_ci#define PCXHR_FREQ_AES_2		0x0b
23162306a36Sopenharmony_ci#define PCXHR_FREQ_AES_3		0x03
23262306a36Sopenharmony_ci#define PCXHR_FREQ_AES_4		0x0d
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
23562306a36Sopenharmony_ci			       unsigned int *reg, unsigned int *freq)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	unsigned int val, realfreq, pllreg;
23862306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
23962306a36Sopenharmony_ci	int err;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	realfreq = rate;
24262306a36Sopenharmony_ci	switch (mgr->use_clock_type) {
24362306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_INTERNAL :	/* clock by quartz or pll */
24462306a36Sopenharmony_ci		switch (rate) {
24562306a36Sopenharmony_ci		case 48000 :	val = PCXHR_FREQ_QUARTZ_48000;	break;
24662306a36Sopenharmony_ci		case 24000 :	val = PCXHR_FREQ_QUARTZ_24000;	break;
24762306a36Sopenharmony_ci		case 12000 :	val = PCXHR_FREQ_QUARTZ_12000;	break;
24862306a36Sopenharmony_ci		case 32000 :	val = PCXHR_FREQ_QUARTZ_32000;	break;
24962306a36Sopenharmony_ci		case 16000 :	val = PCXHR_FREQ_QUARTZ_16000;	break;
25062306a36Sopenharmony_ci		case 8000 :	val = PCXHR_FREQ_QUARTZ_8000;	break;
25162306a36Sopenharmony_ci		case 44100 :	val = PCXHR_FREQ_QUARTZ_44100;	break;
25262306a36Sopenharmony_ci		case 22050 :	val = PCXHR_FREQ_QUARTZ_22050;	break;
25362306a36Sopenharmony_ci		case 11025 :	val = PCXHR_FREQ_QUARTZ_11025;	break;
25462306a36Sopenharmony_ci		case 192000 :	val = PCXHR_FREQ_QUARTZ_192000;	break;
25562306a36Sopenharmony_ci		case 96000 :	val = PCXHR_FREQ_QUARTZ_96000;	break;
25662306a36Sopenharmony_ci		case 176400 :	val = PCXHR_FREQ_QUARTZ_176400;	break;
25762306a36Sopenharmony_ci		case 88200 :	val = PCXHR_FREQ_QUARTZ_88200;	break;
25862306a36Sopenharmony_ci		case 128000 :	val = PCXHR_FREQ_QUARTZ_128000;	break;
25962306a36Sopenharmony_ci		case 64000 :	val = PCXHR_FREQ_QUARTZ_64000;	break;
26062306a36Sopenharmony_ci		default :
26162306a36Sopenharmony_ci			val = PCXHR_FREQ_PLL;
26262306a36Sopenharmony_ci			/* get the value for the pll register */
26362306a36Sopenharmony_ci			err = pcxhr_pll_freq_register(rate, &pllreg, &realfreq);
26462306a36Sopenharmony_ci			if (err)
26562306a36Sopenharmony_ci				return err;
26662306a36Sopenharmony_ci			pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
26762306a36Sopenharmony_ci			rmh.cmd[0] |= IO_NUM_REG_GENCLK;
26862306a36Sopenharmony_ci			rmh.cmd[1]  = pllreg & MASK_DSP_WORD;
26962306a36Sopenharmony_ci			rmh.cmd[2]  = pllreg >> 24;
27062306a36Sopenharmony_ci			rmh.cmd_len = 3;
27162306a36Sopenharmony_ci			err = pcxhr_send_msg(mgr, &rmh);
27262306a36Sopenharmony_ci			if (err < 0) {
27362306a36Sopenharmony_ci				dev_err(&mgr->pci->dev,
27462306a36Sopenharmony_ci					   "error CMD_ACCESS_IO_WRITE "
27562306a36Sopenharmony_ci					   "for PLL register : %x!\n", err);
27662306a36Sopenharmony_ci				return err;
27762306a36Sopenharmony_ci			}
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_WORD_CLOCK:
28162306a36Sopenharmony_ci		val = PCXHR_FREQ_WORD_CLOCK;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_SYNC:
28462306a36Sopenharmony_ci		val = PCXHR_FREQ_SYNC_AES;
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_1:
28762306a36Sopenharmony_ci		val = PCXHR_FREQ_AES_1;
28862306a36Sopenharmony_ci		break;
28962306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_2:
29062306a36Sopenharmony_ci		val = PCXHR_FREQ_AES_2;
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_3:
29362306a36Sopenharmony_ci		val = PCXHR_FREQ_AES_3;
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_4:
29662306a36Sopenharmony_ci		val = PCXHR_FREQ_AES_4;
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	default:
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	*reg = val;
30262306a36Sopenharmony_ci	*freq = realfreq;
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
30862306a36Sopenharmony_ci			       unsigned int rate,
30962306a36Sopenharmony_ci			       int *changed)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	unsigned int val, realfreq, speed;
31262306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
31362306a36Sopenharmony_ci	int err;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	err = pcxhr_get_clock_reg(mgr, rate, &val, &realfreq);
31662306a36Sopenharmony_ci	if (err)
31762306a36Sopenharmony_ci		return err;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* codec speed modes */
32062306a36Sopenharmony_ci	if (rate < 55000)
32162306a36Sopenharmony_ci		speed = 0;	/* single speed */
32262306a36Sopenharmony_ci	else if (rate < 100000)
32362306a36Sopenharmony_ci		speed = 1;	/* dual speed */
32462306a36Sopenharmony_ci	else
32562306a36Sopenharmony_ci		speed = 2;	/* quad speed */
32662306a36Sopenharmony_ci	if (mgr->codec_speed != speed) {
32762306a36Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* mute outputs */
32862306a36Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
32962306a36Sopenharmony_ci		if (DSP_EXT_CMD_SET(mgr)) {
33062306a36Sopenharmony_ci			rmh.cmd[1]  = 1;
33162306a36Sopenharmony_ci			rmh.cmd_len = 2;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
33462306a36Sopenharmony_ci		if (err)
33562306a36Sopenharmony_ci			return err;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set speed ratio */
33862306a36Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_SPEED_RATIO;
33962306a36Sopenharmony_ci		rmh.cmd[1] = speed;
34062306a36Sopenharmony_ci		rmh.cmd_len = 2;
34162306a36Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
34262306a36Sopenharmony_ci		if (err)
34362306a36Sopenharmony_ci			return err;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci	/* set the new frequency */
34662306a36Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "clock register : set %x\n", val);
34762306a36Sopenharmony_ci	err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK,
34862306a36Sopenharmony_ci					  val, changed);
34962306a36Sopenharmony_ci	if (err)
35062306a36Sopenharmony_ci		return err;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	mgr->sample_rate_real = realfreq;
35362306a36Sopenharmony_ci	mgr->cur_clock_type = mgr->use_clock_type;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* unmute after codec speed modes */
35662306a36Sopenharmony_ci	if (mgr->codec_speed != speed) {
35762306a36Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* unmute outputs */
35862306a36Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
35962306a36Sopenharmony_ci		if (DSP_EXT_CMD_SET(mgr)) {
36062306a36Sopenharmony_ci			rmh.cmd[1]  = 1;
36162306a36Sopenharmony_ci			rmh.cmd_len = 2;
36262306a36Sopenharmony_ci		}
36362306a36Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
36462306a36Sopenharmony_ci		if (err)
36562306a36Sopenharmony_ci			return err;
36662306a36Sopenharmony_ci		mgr->codec_speed = speed;	/* save new codec speed */
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "%s to %dHz (realfreq=%d)\n", __func__,
37062306a36Sopenharmony_ci		    rate, realfreq);
37162306a36Sopenharmony_ci	return 0;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci#define PCXHR_MODIFY_CLOCK_S_BIT	0x04
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci#define PCXHR_IRQ_TIMER_FREQ		92000
37762306a36Sopenharmony_ci#define PCXHR_IRQ_TIMER_PERIOD		48
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ciint pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
38262306a36Sopenharmony_ci	int err, changed;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (rate == 0)
38562306a36Sopenharmony_ci		return 0; /* nothing to do */
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (mgr->is_hr_stereo)
38862306a36Sopenharmony_ci		err = hr222_sub_set_clock(mgr, rate, &changed);
38962306a36Sopenharmony_ci	else
39062306a36Sopenharmony_ci		err = pcxhr_sub_set_clock(mgr, rate, &changed);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (err)
39362306a36Sopenharmony_ci		return err;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (changed) {
39662306a36Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_MODIFY_CLOCK);
39762306a36Sopenharmony_ci		rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT; /* resync fifos  */
39862306a36Sopenharmony_ci		if (rate < PCXHR_IRQ_TIMER_FREQ)
39962306a36Sopenharmony_ci			rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD;
40062306a36Sopenharmony_ci		else
40162306a36Sopenharmony_ci			rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD * 2;
40262306a36Sopenharmony_ci		rmh.cmd[2] = rate;
40362306a36Sopenharmony_ci		rmh.cmd_len = 3;
40462306a36Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
40562306a36Sopenharmony_ci		if (err)
40662306a36Sopenharmony_ci			return err;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic int pcxhr_sub_get_external_clock(struct pcxhr_mgr *mgr,
41362306a36Sopenharmony_ci					enum pcxhr_clock_type clock_type,
41462306a36Sopenharmony_ci					int *sample_rate)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
41762306a36Sopenharmony_ci	unsigned char reg;
41862306a36Sopenharmony_ci	int err, rate;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	switch (clock_type) {
42162306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_WORD_CLOCK:
42262306a36Sopenharmony_ci		reg = REG_STATUS_WORD_CLOCK;
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_SYNC:
42562306a36Sopenharmony_ci		reg = REG_STATUS_AES_SYNC;
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_1:
42862306a36Sopenharmony_ci		reg = REG_STATUS_AES_1;
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_2:
43162306a36Sopenharmony_ci		reg = REG_STATUS_AES_2;
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_3:
43462306a36Sopenharmony_ci		reg = REG_STATUS_AES_3;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_4:
43762306a36Sopenharmony_ci		reg = REG_STATUS_AES_4;
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	default:
44062306a36Sopenharmony_ci		return -EINVAL;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
44362306a36Sopenharmony_ci	rmh.cmd_len = 2;
44462306a36Sopenharmony_ci	rmh.cmd[0] |= IO_NUM_REG_STATUS;
44562306a36Sopenharmony_ci	if (mgr->last_reg_stat != reg) {
44662306a36Sopenharmony_ci		rmh.cmd[1]  = reg;
44762306a36Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
44862306a36Sopenharmony_ci		if (err)
44962306a36Sopenharmony_ci			return err;
45062306a36Sopenharmony_ci		udelay(100);	/* wait minimum 2 sample_frames at 32kHz ! */
45162306a36Sopenharmony_ci		mgr->last_reg_stat = reg;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	rmh.cmd[1]  = REG_STATUS_CURRENT;
45462306a36Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
45562306a36Sopenharmony_ci	if (err)
45662306a36Sopenharmony_ci		return err;
45762306a36Sopenharmony_ci	switch (rmh.stat[1] & 0x0f) {
45862306a36Sopenharmony_ci	case REG_STATUS_SYNC_32000 :	rate = 32000; break;
45962306a36Sopenharmony_ci	case REG_STATUS_SYNC_44100 :	rate = 44100; break;
46062306a36Sopenharmony_ci	case REG_STATUS_SYNC_48000 :	rate = 48000; break;
46162306a36Sopenharmony_ci	case REG_STATUS_SYNC_64000 :	rate = 64000; break;
46262306a36Sopenharmony_ci	case REG_STATUS_SYNC_88200 :	rate = 88200; break;
46362306a36Sopenharmony_ci	case REG_STATUS_SYNC_96000 :	rate = 96000; break;
46462306a36Sopenharmony_ci	case REG_STATUS_SYNC_128000 :	rate = 128000; break;
46562306a36Sopenharmony_ci	case REG_STATUS_SYNC_176400 :	rate = 176400; break;
46662306a36Sopenharmony_ci	case REG_STATUS_SYNC_192000 :	rate = 192000; break;
46762306a36Sopenharmony_ci	default: rate = 0;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "External clock is at %d Hz\n", rate);
47062306a36Sopenharmony_ci	*sample_rate = rate;
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciint pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
47662306a36Sopenharmony_ci			     enum pcxhr_clock_type clock_type,
47762306a36Sopenharmony_ci			     int *sample_rate)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	if (mgr->is_hr_stereo)
48062306a36Sopenharmony_ci		return hr222_get_external_clock(mgr, clock_type,
48162306a36Sopenharmony_ci						sample_rate);
48262306a36Sopenharmony_ci	else
48362306a36Sopenharmony_ci		return pcxhr_sub_get_external_clock(mgr, clock_type,
48462306a36Sopenharmony_ci						    sample_rate);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/*
48862306a36Sopenharmony_ci *  start or stop playback/capture substream
48962306a36Sopenharmony_ci */
49062306a36Sopenharmony_cistatic int pcxhr_set_stream_state(struct snd_pcxhr *chip,
49162306a36Sopenharmony_ci				  struct pcxhr_stream *stream)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int err;
49462306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
49562306a36Sopenharmony_ci	int stream_mask, start;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (stream->status == PCXHR_STREAM_STATUS_SCHEDULE_RUN)
49862306a36Sopenharmony_ci		start = 1;
49962306a36Sopenharmony_ci	else {
50062306a36Sopenharmony_ci		if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
50162306a36Sopenharmony_ci			dev_err(chip->card->dev,
50262306a36Sopenharmony_ci				"%s CANNOT be stopped\n", __func__);
50362306a36Sopenharmony_ci			return -EINVAL;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci		start = 0;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci	if (!stream->substream)
50862306a36Sopenharmony_ci		return -EINVAL;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	stream->timer_abs_periods = 0;
51162306a36Sopenharmony_ci	stream->timer_period_frag = 0;	/* reset theoretical stream pos */
51262306a36Sopenharmony_ci	stream->timer_buf_periods = 0;
51362306a36Sopenharmony_ci	stream->timer_is_synced = 0;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	stream_mask =
51662306a36Sopenharmony_ci	  stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, start ? CMD_START_STREAM : CMD_STOP_STREAM);
51962306a36Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
52062306a36Sopenharmony_ci				  stream->pipe->first_audio, 0, stream_mask);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	chip = snd_pcm_substream_chip(stream->substream);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
52562306a36Sopenharmony_ci	if (err)
52662306a36Sopenharmony_ci		dev_err(chip->card->dev,
52762306a36Sopenharmony_ci			"ERROR %s err=%x;\n", __func__, err);
52862306a36Sopenharmony_ci	stream->status =
52962306a36Sopenharmony_ci	  start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
53062306a36Sopenharmony_ci	return err;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci#define HEADER_FMT_BASE_LIN		0xfed00000
53462306a36Sopenharmony_ci#define HEADER_FMT_BASE_FLOAT		0xfad00000
53562306a36Sopenharmony_ci#define HEADER_FMT_INTEL		0x00008000
53662306a36Sopenharmony_ci#define HEADER_FMT_24BITS		0x00004000
53762306a36Sopenharmony_ci#define HEADER_FMT_16BITS		0x00002000
53862306a36Sopenharmony_ci#define HEADER_FMT_UPTO11		0x00000200
53962306a36Sopenharmony_ci#define HEADER_FMT_UPTO32		0x00000100
54062306a36Sopenharmony_ci#define HEADER_FMT_MONO			0x00000080
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic int pcxhr_set_format(struct pcxhr_stream *stream)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	int err, is_capture, sample_rate, stream_num;
54562306a36Sopenharmony_ci	struct snd_pcxhr *chip;
54662306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
54762306a36Sopenharmony_ci	unsigned int header;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	chip = snd_pcm_substream_chip(stream->substream);
55062306a36Sopenharmony_ci	switch (stream->format) {
55162306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_U8:
55262306a36Sopenharmony_ci		header = HEADER_FMT_BASE_LIN;
55362306a36Sopenharmony_ci		break;
55462306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
55562306a36Sopenharmony_ci		header = HEADER_FMT_BASE_LIN |
55662306a36Sopenharmony_ci			 HEADER_FMT_16BITS | HEADER_FMT_INTEL;
55762306a36Sopenharmony_ci		break;
55862306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
55962306a36Sopenharmony_ci		header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS;
56062306a36Sopenharmony_ci		break;
56162306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3LE:
56262306a36Sopenharmony_ci		header = HEADER_FMT_BASE_LIN |
56362306a36Sopenharmony_ci			 HEADER_FMT_24BITS | HEADER_FMT_INTEL;
56462306a36Sopenharmony_ci		break;
56562306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3BE:
56662306a36Sopenharmony_ci		header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS;
56762306a36Sopenharmony_ci		break;
56862306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_FLOAT_LE:
56962306a36Sopenharmony_ci		header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;
57062306a36Sopenharmony_ci		break;
57162306a36Sopenharmony_ci	default:
57262306a36Sopenharmony_ci		dev_err(chip->card->dev,
57362306a36Sopenharmony_ci			"error %s() : unknown format\n", __func__);
57462306a36Sopenharmony_ci		return -EINVAL;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	sample_rate = chip->mgr->sample_rate;
57862306a36Sopenharmony_ci	if (sample_rate <= 32000 && sample_rate !=0) {
57962306a36Sopenharmony_ci		if (sample_rate <= 11025)
58062306a36Sopenharmony_ci			header |= HEADER_FMT_UPTO11;
58162306a36Sopenharmony_ci		else
58262306a36Sopenharmony_ci			header |= HEADER_FMT_UPTO32;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	if (stream->channels == 1)
58562306a36Sopenharmony_ci		header |= HEADER_FMT_MONO;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	is_capture = stream->pipe->is_capture;
58862306a36Sopenharmony_ci	stream_num = is_capture ? 0 : stream->substream->number;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, is_capture ?
59162306a36Sopenharmony_ci		       CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
59262306a36Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
59362306a36Sopenharmony_ci				  stream_num, 0);
59462306a36Sopenharmony_ci	if (is_capture) {
59562306a36Sopenharmony_ci		/* bug with old dsp versions: */
59662306a36Sopenharmony_ci		/* bit 12 also sets the format of the playback stream */
59762306a36Sopenharmony_ci		if (DSP_EXT_CMD_SET(chip->mgr))
59862306a36Sopenharmony_ci			rmh.cmd[0] |= 1<<10;
59962306a36Sopenharmony_ci		else
60062306a36Sopenharmony_ci			rmh.cmd[0] |= 1<<12;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci	rmh.cmd[1] = 0;
60362306a36Sopenharmony_ci	rmh.cmd_len = 2;
60462306a36Sopenharmony_ci	if (DSP_EXT_CMD_SET(chip->mgr)) {
60562306a36Sopenharmony_ci		/* add channels and set bit 19 if channels>2 */
60662306a36Sopenharmony_ci		rmh.cmd[1] = stream->channels;
60762306a36Sopenharmony_ci		if (!is_capture) {
60862306a36Sopenharmony_ci			/* playback : add channel mask to command */
60962306a36Sopenharmony_ci			rmh.cmd[2] = (stream->channels == 1) ? 0x01 : 0x03;
61062306a36Sopenharmony_ci			rmh.cmd_len = 3;
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci	rmh.cmd[rmh.cmd_len++] = header >> 8;
61462306a36Sopenharmony_ci	rmh.cmd[rmh.cmd_len++] = (header & 0xff) << 16;
61562306a36Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
61662306a36Sopenharmony_ci	if (err)
61762306a36Sopenharmony_ci		dev_err(chip->card->dev,
61862306a36Sopenharmony_ci			"ERROR %s err=%x;\n", __func__, err);
61962306a36Sopenharmony_ci	return err;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	int err, is_capture, stream_num;
62562306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
62662306a36Sopenharmony_ci	struct snd_pcm_substream *subs = stream->substream;
62762306a36Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);
63062306a36Sopenharmony_ci	stream_num = is_capture ? 0 : subs->number;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
63362306a36Sopenharmony_ci		"%s(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", __func__,
63462306a36Sopenharmony_ci		is_capture ? 'c' : 'p',
63562306a36Sopenharmony_ci		chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
63662306a36Sopenharmony_ci		subs->runtime->dma_bytes, subs->number);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
63962306a36Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
64062306a36Sopenharmony_ci				  stream_num, 0);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	/* max buffer size is 2 MByte */
64362306a36Sopenharmony_ci	snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
64462306a36Sopenharmony_ci	/* size in bits */
64562306a36Sopenharmony_ci	rmh.cmd[1] = subs->runtime->dma_bytes * 8;
64662306a36Sopenharmony_ci	/* most significant byte */
64762306a36Sopenharmony_ci	rmh.cmd[2] = subs->runtime->dma_addr >> 24;
64862306a36Sopenharmony_ci	/* this is a circular buffer */
64962306a36Sopenharmony_ci	rmh.cmd[2] |= 1<<19;
65062306a36Sopenharmony_ci	/* least 3 significant bytes */
65162306a36Sopenharmony_ci	rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;
65262306a36Sopenharmony_ci	rmh.cmd_len = 4;
65362306a36Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
65462306a36Sopenharmony_ci	if (err)
65562306a36Sopenharmony_ci		dev_err(chip->card->dev,
65662306a36Sopenharmony_ci			   "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
65762306a36Sopenharmony_ci	return err;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci#if 0
66262306a36Sopenharmony_cistatic int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
66362306a36Sopenharmony_ci				   snd_pcm_uframes_t *sample_count)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
66662306a36Sopenharmony_ci	int err;
66762306a36Sopenharmony_ci	pcxhr_t *chip = snd_pcm_substream_chip(stream->substream);
66862306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_PIPE_SAMPLE_COUNT);
66962306a36Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture, 0, 0,
67062306a36Sopenharmony_ci				  1<<stream->pipe->first_audio);
67162306a36Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
67262306a36Sopenharmony_ci	if (err == 0) {
67362306a36Sopenharmony_ci		*sample_count = ((snd_pcm_uframes_t)rmh.stat[0]) << 24;
67462306a36Sopenharmony_ci		*sample_count += (snd_pcm_uframes_t)rmh.stat[1];
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "PIPE_SAMPLE_COUNT = %lx\n", *sample_count);
67762306a36Sopenharmony_ci	return err;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci#endif
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream,
68262306a36Sopenharmony_ci						  struct pcxhr_pipe **pipe)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	if (stream->status == PCXHR_STREAM_STATUS_SCHEDULE_RUN) {
68562306a36Sopenharmony_ci		*pipe = stream->pipe;
68662306a36Sopenharmony_ci		return 1;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci	return 0;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	int i, j, err;
69462306a36Sopenharmony_ci	struct pcxhr_pipe *pipe;
69562306a36Sopenharmony_ci	struct snd_pcxhr *chip;
69662306a36Sopenharmony_ci	int capture_mask = 0;
69762306a36Sopenharmony_ci	int playback_mask = 0;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE
70062306a36Sopenharmony_ci	ktime_t start_time, stop_time, diff_time;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	start_time = ktime_get();
70362306a36Sopenharmony_ci#endif
70462306a36Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* check the pipes concerned and build pipe_array */
70762306a36Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
70862306a36Sopenharmony_ci		chip = mgr->chip[i];
70962306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
71062306a36Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(&chip->capture_stream[j], &pipe))
71162306a36Sopenharmony_ci				capture_mask |= (1 << pipe->first_audio);
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
71462306a36Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(&chip->playback_stream[j], &pipe)) {
71562306a36Sopenharmony_ci				playback_mask |= (1 << pipe->first_audio);
71662306a36Sopenharmony_ci				break;	/* add only once, as all playback
71762306a36Sopenharmony_ci					 * streams of one chip use the same pipe
71862306a36Sopenharmony_ci					 */
71962306a36Sopenharmony_ci			}
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci	if (capture_mask == 0 && playback_mask == 0) {
72362306a36Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
72462306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "%s : no pipes\n", __func__);
72562306a36Sopenharmony_ci		return;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "%s : playback_mask=%x capture_mask=%x\n",
72962306a36Sopenharmony_ci		    __func__, playback_mask, capture_mask);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* synchronous stop of all the pipes concerned */
73262306a36Sopenharmony_ci	err = pcxhr_set_pipe_state(mgr,  playback_mask, capture_mask, 0);
73362306a36Sopenharmony_ci	if (err) {
73462306a36Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
73562306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "%s : "
73662306a36Sopenharmony_ci			   "error stop pipes (P%x C%x)\n",
73762306a36Sopenharmony_ci			   __func__, playback_mask, capture_mask);
73862306a36Sopenharmony_ci		return;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/* the dsp lost format and buffer info with the stop pipe */
74262306a36Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
74362306a36Sopenharmony_ci		struct pcxhr_stream *stream;
74462306a36Sopenharmony_ci		chip = mgr->chip[i];
74562306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
74662306a36Sopenharmony_ci			stream = &chip->capture_stream[j];
74762306a36Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) {
74862306a36Sopenharmony_ci				err = pcxhr_set_format(stream);
74962306a36Sopenharmony_ci				err = pcxhr_update_r_buffer(stream);
75062306a36Sopenharmony_ci			}
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
75362306a36Sopenharmony_ci			stream = &chip->playback_stream[j];
75462306a36Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) {
75562306a36Sopenharmony_ci				err = pcxhr_set_format(stream);
75662306a36Sopenharmony_ci				err = pcxhr_update_r_buffer(stream);
75762306a36Sopenharmony_ci			}
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci	/* start all the streams */
76162306a36Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
76262306a36Sopenharmony_ci		struct pcxhr_stream *stream;
76362306a36Sopenharmony_ci		chip = mgr->chip[i];
76462306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
76562306a36Sopenharmony_ci			stream = &chip->capture_stream[j];
76662306a36Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe))
76762306a36Sopenharmony_ci				err = pcxhr_set_stream_state(chip, stream);
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
77062306a36Sopenharmony_ci			stream = &chip->playback_stream[j];
77162306a36Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe))
77262306a36Sopenharmony_ci				err = pcxhr_set_stream_state(chip, stream);
77362306a36Sopenharmony_ci		}
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* synchronous start of all the pipes concerned */
77762306a36Sopenharmony_ci	err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
77862306a36Sopenharmony_ci	if (err) {
77962306a36Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
78062306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "%s : "
78162306a36Sopenharmony_ci			   "error start pipes (P%x C%x)\n",
78262306a36Sopenharmony_ci			   __func__, playback_mask, capture_mask);
78362306a36Sopenharmony_ci		return;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* put the streams into the running state now
78762306a36Sopenharmony_ci	 * (increment pointer by interrupt)
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	mutex_lock(&mgr->lock);
79062306a36Sopenharmony_ci	for ( i =0; i < mgr->num_cards; i++) {
79162306a36Sopenharmony_ci		struct pcxhr_stream *stream;
79262306a36Sopenharmony_ci		chip = mgr->chip[i];
79362306a36Sopenharmony_ci		for(j = 0; j < chip->nb_streams_capt; j++) {
79462306a36Sopenharmony_ci			stream = &chip->capture_stream[j];
79562306a36Sopenharmony_ci			if(stream->status == PCXHR_STREAM_STATUS_STARTED)
79662306a36Sopenharmony_ci				stream->status = PCXHR_STREAM_STATUS_RUNNING;
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
79962306a36Sopenharmony_ci			stream = &chip->playback_stream[j];
80062306a36Sopenharmony_ci			if (stream->status == PCXHR_STREAM_STATUS_STARTED) {
80162306a36Sopenharmony_ci				/* playback will already have advanced ! */
80262306a36Sopenharmony_ci				stream->timer_period_frag += mgr->granularity;
80362306a36Sopenharmony_ci				stream->status = PCXHR_STREAM_STATUS_RUNNING;
80462306a36Sopenharmony_ci			}
80562306a36Sopenharmony_ci		}
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	mutex_unlock(&mgr->lock);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE
81262306a36Sopenharmony_ci	stop_time = ktime_get();
81362306a36Sopenharmony_ci	diff_time = ktime_sub(stop_time, start_time);
81462306a36Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n",
81562306a36Sopenharmony_ci		    (long)(ktime_to_ns(diff_time)), err);
81662306a36Sopenharmony_ci#endif
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/*
82162306a36Sopenharmony_ci *  trigger callback
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_cistatic int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct pcxhr_stream *stream;
82662306a36Sopenharmony_ci	struct snd_pcm_substream *s;
82762306a36Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	switch (cmd) {
83062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
83162306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_START\n");
83262306a36Sopenharmony_ci		if (snd_pcm_stream_linked(subs)) {
83362306a36Sopenharmony_ci			snd_pcm_group_for_each_entry(s, subs) {
83462306a36Sopenharmony_ci				if (snd_pcm_substream_chip(s) != chip)
83562306a36Sopenharmony_ci					continue;
83662306a36Sopenharmony_ci				stream = s->runtime->private_data;
83762306a36Sopenharmony_ci				stream->status =
83862306a36Sopenharmony_ci					PCXHR_STREAM_STATUS_SCHEDULE_RUN;
83962306a36Sopenharmony_ci				snd_pcm_trigger_done(s, subs);
84062306a36Sopenharmony_ci			}
84162306a36Sopenharmony_ci			pcxhr_start_linked_stream(chip->mgr);
84262306a36Sopenharmony_ci		} else {
84362306a36Sopenharmony_ci			stream = subs->runtime->private_data;
84462306a36Sopenharmony_ci			dev_dbg(chip->card->dev, "Only one Substream %c %d\n",
84562306a36Sopenharmony_ci				    stream->pipe->is_capture ? 'C' : 'P',
84662306a36Sopenharmony_ci				    stream->pipe->first_audio);
84762306a36Sopenharmony_ci			if (pcxhr_set_format(stream))
84862306a36Sopenharmony_ci				return -EINVAL;
84962306a36Sopenharmony_ci			if (pcxhr_update_r_buffer(stream))
85062306a36Sopenharmony_ci				return -EINVAL;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci			stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN;
85362306a36Sopenharmony_ci			if (pcxhr_set_stream_state(chip, stream))
85462306a36Sopenharmony_ci				return -EINVAL;
85562306a36Sopenharmony_ci			stream->status = PCXHR_STREAM_STATUS_RUNNING;
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci		break;
85862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
85962306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
86062306a36Sopenharmony_ci		snd_pcm_group_for_each_entry(s, subs) {
86162306a36Sopenharmony_ci			stream = s->runtime->private_data;
86262306a36Sopenharmony_ci			stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP;
86362306a36Sopenharmony_ci			if (pcxhr_set_stream_state(chip, stream))
86462306a36Sopenharmony_ci				return -EINVAL;
86562306a36Sopenharmony_ci			snd_pcm_trigger_done(s, subs);
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci		break;
86862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
86962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
87062306a36Sopenharmony_ci		/* TODO */
87162306a36Sopenharmony_ci	default:
87262306a36Sopenharmony_ci		return -EINVAL;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci	return 0;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
88162306a36Sopenharmony_ci	int err;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_SET_TIMER_INTERRUPT);
88462306a36Sopenharmony_ci	if (start) {
88562306a36Sopenharmony_ci		/* last dsp time invalid */
88662306a36Sopenharmony_ci		mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
88762306a36Sopenharmony_ci		rmh.cmd[0] |= mgr->granularity;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
89062306a36Sopenharmony_ci	if (err < 0)
89162306a36Sopenharmony_ci		dev_err(&mgr->pci->dev, "error %s err(%x)\n", __func__,
89262306a36Sopenharmony_ci			   err);
89362306a36Sopenharmony_ci	return err;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/*
89762306a36Sopenharmony_ci *  prepare callback for all pcms
89862306a36Sopenharmony_ci */
89962306a36Sopenharmony_cistatic int pcxhr_prepare(struct snd_pcm_substream *subs)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
90262306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
90362306a36Sopenharmony_ci	int err = 0;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
90662306a36Sopenharmony_ci		"%s : period_size(%lx) periods(%x) buffer_size(%lx)\n", __func__,
90762306a36Sopenharmony_ci		    subs->runtime->period_size, subs->runtime->periods,
90862306a36Sopenharmony_ci		    subs->runtime->buffer_size);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	do {
91362306a36Sopenharmony_ci		/* only the first stream can choose the sample rate */
91462306a36Sopenharmony_ci		/* set the clock only once (first stream) */
91562306a36Sopenharmony_ci		if (mgr->sample_rate != subs->runtime->rate) {
91662306a36Sopenharmony_ci			err = pcxhr_set_clock(mgr, subs->runtime->rate);
91762306a36Sopenharmony_ci			if (err)
91862306a36Sopenharmony_ci				break;
91962306a36Sopenharmony_ci			if (mgr->sample_rate == 0)
92062306a36Sopenharmony_ci				/* start the DSP-timer */
92162306a36Sopenharmony_ci				err = pcxhr_hardware_timer(mgr, 1);
92262306a36Sopenharmony_ci			mgr->sample_rate = subs->runtime->rate;
92362306a36Sopenharmony_ci		}
92462306a36Sopenharmony_ci	} while(0);	/* do only once (so we can use break instead of goto) */
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	return err;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci/*
93362306a36Sopenharmony_ci *  HW_PARAMS callback for all pcms
93462306a36Sopenharmony_ci */
93562306a36Sopenharmony_cistatic int pcxhr_hw_params(struct snd_pcm_substream *subs,
93662306a36Sopenharmony_ci			   struct snd_pcm_hw_params *hw)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
93962306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
94062306a36Sopenharmony_ci	struct pcxhr_stream *stream = subs->runtime->private_data;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* set up channels */
94562306a36Sopenharmony_ci	stream->channels = params_channels(hw);
94662306a36Sopenharmony_ci	/* set up format for the stream */
94762306a36Sopenharmony_ci	stream->format = params_format(hw);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	return 0;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci/*
95662306a36Sopenharmony_ci *  CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
95762306a36Sopenharmony_ci */
95862306a36Sopenharmony_cistatic const struct snd_pcm_hardware pcxhr_caps =
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	.info             = (SNDRV_PCM_INFO_MMAP |
96162306a36Sopenharmony_ci			     SNDRV_PCM_INFO_INTERLEAVED |
96262306a36Sopenharmony_ci			     SNDRV_PCM_INFO_MMAP_VALID |
96362306a36Sopenharmony_ci			     SNDRV_PCM_INFO_SYNC_START),
96462306a36Sopenharmony_ci	.formats	  = (SNDRV_PCM_FMTBIT_U8 |
96562306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S16_LE |
96662306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S16_BE |
96762306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_3LE |
96862306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_3BE |
96962306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_FLOAT_LE),
97062306a36Sopenharmony_ci	.rates            = (SNDRV_PCM_RATE_CONTINUOUS |
97162306a36Sopenharmony_ci			     SNDRV_PCM_RATE_8000_192000),
97262306a36Sopenharmony_ci	.rate_min         = 8000,
97362306a36Sopenharmony_ci	.rate_max         = 192000,
97462306a36Sopenharmony_ci	.channels_min     = 1,
97562306a36Sopenharmony_ci	.channels_max     = 2,
97662306a36Sopenharmony_ci	.buffer_bytes_max = (32*1024),
97762306a36Sopenharmony_ci	/* 1 byte == 1 frame U8 mono (PCXHR_GRANULARITY is frames!) */
97862306a36Sopenharmony_ci	.period_bytes_min = (2*PCXHR_GRANULARITY),
97962306a36Sopenharmony_ci	.period_bytes_max = (16*1024),
98062306a36Sopenharmony_ci	.periods_min      = 2,
98162306a36Sopenharmony_ci	.periods_max      = (32*1024/PCXHR_GRANULARITY),
98262306a36Sopenharmony_ci};
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cistatic int pcxhr_open(struct snd_pcm_substream *subs)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct snd_pcxhr       *chip = snd_pcm_substream_chip(subs);
98862306a36Sopenharmony_ci	struct pcxhr_mgr       *mgr = chip->mgr;
98962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
99062306a36Sopenharmony_ci	struct pcxhr_stream    *stream;
99162306a36Sopenharmony_ci	int err;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/* copy the struct snd_pcm_hardware struct */
99662306a36Sopenharmony_ci	runtime->hw = pcxhr_caps;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
99962306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "%s playback chip%d subs%d\n",
100062306a36Sopenharmony_ci			    __func__, chip->chip_idx, subs->number);
100162306a36Sopenharmony_ci		stream = &chip->playback_stream[subs->number];
100262306a36Sopenharmony_ci	} else {
100362306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "%s capture chip%d subs%d\n",
100462306a36Sopenharmony_ci			    __func__, chip->chip_idx, subs->number);
100562306a36Sopenharmony_ci		if (mgr->mono_capture)
100662306a36Sopenharmony_ci			runtime->hw.channels_max = 1;
100762306a36Sopenharmony_ci		else
100862306a36Sopenharmony_ci			runtime->hw.channels_min = 2;
100962306a36Sopenharmony_ci		stream = &chip->capture_stream[subs->number];
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci	if (stream->status != PCXHR_STREAM_STATUS_FREE){
101262306a36Sopenharmony_ci		/* streams in use */
101362306a36Sopenharmony_ci		dev_err(chip->card->dev, "%s chip%d subs%d in use\n",
101462306a36Sopenharmony_ci			   __func__, chip->chip_idx, subs->number);
101562306a36Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
101662306a36Sopenharmony_ci		return -EBUSY;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* float format support is in some cases buggy on stereo cards */
102062306a36Sopenharmony_ci	if (mgr->is_hr_stereo)
102162306a36Sopenharmony_ci		runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_FLOAT_LE;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* buffer-size should better be multiple of period-size */
102462306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
102562306a36Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
102662306a36Sopenharmony_ci	if (err < 0) {
102762306a36Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
102862306a36Sopenharmony_ci		return err;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/* if a sample rate is already used or fixed by external clock,
103262306a36Sopenharmony_ci	 * the stream cannot change
103362306a36Sopenharmony_ci	 */
103462306a36Sopenharmony_ci	if (mgr->sample_rate)
103562306a36Sopenharmony_ci		runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
103662306a36Sopenharmony_ci	else {
103762306a36Sopenharmony_ci		if (mgr->use_clock_type != PCXHR_CLOCK_TYPE_INTERNAL) {
103862306a36Sopenharmony_ci			int external_rate;
103962306a36Sopenharmony_ci			if (pcxhr_get_external_clock(mgr, mgr->use_clock_type,
104062306a36Sopenharmony_ci						     &external_rate) ||
104162306a36Sopenharmony_ci			    external_rate == 0) {
104262306a36Sopenharmony_ci				/* cannot detect the external clock rate */
104362306a36Sopenharmony_ci				mutex_unlock(&mgr->setup_mutex);
104462306a36Sopenharmony_ci				return -EBUSY;
104562306a36Sopenharmony_ci			}
104662306a36Sopenharmony_ci			runtime->hw.rate_min = external_rate;
104762306a36Sopenharmony_ci			runtime->hw.rate_max = external_rate;
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	stream->status      = PCXHR_STREAM_STATUS_OPEN;
105262306a36Sopenharmony_ci	stream->substream   = subs;
105362306a36Sopenharmony_ci	stream->channels    = 0; /* not configured yet */
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	runtime->private_data = stream;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* better get a divisor of granularity values (96 or 192) */
105862306a36Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0,
105962306a36Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
106062306a36Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0,
106162306a36Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
106262306a36Sopenharmony_ci	snd_pcm_set_sync(subs);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	mgr->ref_count_rate++;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
106762306a36Sopenharmony_ci	return 0;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic int pcxhr_close(struct snd_pcm_substream *subs)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
107462306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
107562306a36Sopenharmony_ci	struct pcxhr_stream *stream = subs->runtime->private_data;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "%s chip%d subs%d\n", __func__,
108062306a36Sopenharmony_ci		    chip->chip_idx, subs->number);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	/* sample rate released */
108362306a36Sopenharmony_ci	if (--mgr->ref_count_rate == 0) {
108462306a36Sopenharmony_ci		mgr->sample_rate = 0;	/* the sample rate is no more locked */
108562306a36Sopenharmony_ci		pcxhr_hardware_timer(mgr, 0);	/* stop the DSP-timer */
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	stream->status    = PCXHR_STREAM_STATUS_FREE;
108962306a36Sopenharmony_ci	stream->substream = NULL;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return 0;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	u_int32_t timer_period_frag;
110062306a36Sopenharmony_ci	int timer_buf_periods;
110162306a36Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
110262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
110362306a36Sopenharmony_ci	struct pcxhr_stream *stream  = runtime->private_data;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	mutex_lock(&chip->mgr->lock);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* get the period fragment and the nb of periods in the buffer */
110862306a36Sopenharmony_ci	timer_period_frag = stream->timer_period_frag;
110962306a36Sopenharmony_ci	timer_buf_periods = stream->timer_buf_periods;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	mutex_unlock(&chip->mgr->lock);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) +
111462306a36Sopenharmony_ci				   timer_period_frag);
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic const struct snd_pcm_ops pcxhr_ops = {
111962306a36Sopenharmony_ci	.open      = pcxhr_open,
112062306a36Sopenharmony_ci	.close     = pcxhr_close,
112162306a36Sopenharmony_ci	.prepare   = pcxhr_prepare,
112262306a36Sopenharmony_ci	.hw_params = pcxhr_hw_params,
112362306a36Sopenharmony_ci	.trigger   = pcxhr_trigger,
112462306a36Sopenharmony_ci	.pointer   = pcxhr_stream_pointer,
112562306a36Sopenharmony_ci};
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci/*
112862306a36Sopenharmony_ci */
112962306a36Sopenharmony_ciint pcxhr_create_pcm(struct snd_pcxhr *chip)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	int err;
113262306a36Sopenharmony_ci	struct snd_pcm *pcm;
113362306a36Sopenharmony_ci	char name[32];
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	snprintf(name, sizeof(name), "pcxhr %d", chip->chip_idx);
113662306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, name, 0,
113762306a36Sopenharmony_ci			  chip->nb_streams_play,
113862306a36Sopenharmony_ci			  chip->nb_streams_capt, &pcm);
113962306a36Sopenharmony_ci	if (err < 0) {
114062306a36Sopenharmony_ci		dev_err(chip->card->dev, "cannot create pcm %s\n", name);
114162306a36Sopenharmony_ci		return err;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci	pcm->private_data = chip;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (chip->nb_streams_play)
114662306a36Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcxhr_ops);
114762306a36Sopenharmony_ci	if (chip->nb_streams_capt)
114862306a36Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	pcm->info_flags = 0;
115162306a36Sopenharmony_ci	pcm->nonatomic = true;
115262306a36Sopenharmony_ci	strcpy(pcm->name, name);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
115562306a36Sopenharmony_ci				       &chip->mgr->pci->dev,
115662306a36Sopenharmony_ci				       32*1024, 32*1024);
115762306a36Sopenharmony_ci	chip->pcm = pcm;
115862306a36Sopenharmony_ci	return 0;
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic int pcxhr_chip_free(struct snd_pcxhr *chip)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	kfree(chip);
116462306a36Sopenharmony_ci	return 0;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic int pcxhr_chip_dev_free(struct snd_device *device)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct snd_pcxhr *chip = device->device_data;
117062306a36Sopenharmony_ci	return pcxhr_chip_free(chip);
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci/*
117562306a36Sopenharmony_ci */
117662306a36Sopenharmony_cistatic int pcxhr_create(struct pcxhr_mgr *mgr,
117762306a36Sopenharmony_ci			struct snd_card *card, int idx)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	int err;
118062306a36Sopenharmony_ci	struct snd_pcxhr *chip;
118162306a36Sopenharmony_ci	static const struct snd_device_ops ops = {
118262306a36Sopenharmony_ci		.dev_free = pcxhr_chip_dev_free,
118362306a36Sopenharmony_ci	};
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
118662306a36Sopenharmony_ci	if (!chip)
118762306a36Sopenharmony_ci		return -ENOMEM;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	chip->card = card;
119062306a36Sopenharmony_ci	chip->chip_idx = idx;
119162306a36Sopenharmony_ci	chip->mgr = mgr;
119262306a36Sopenharmony_ci	card->sync_irq = mgr->irq;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (idx < mgr->playback_chips)
119562306a36Sopenharmony_ci		/* stereo or mono streams */
119662306a36Sopenharmony_ci		chip->nb_streams_play = PCXHR_PLAYBACK_STREAMS;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (idx < mgr->capture_chips) {
119962306a36Sopenharmony_ci		if (mgr->mono_capture)
120062306a36Sopenharmony_ci			chip->nb_streams_capt = 2;	/* 2 mono streams */
120162306a36Sopenharmony_ci		else
120262306a36Sopenharmony_ci			chip->nb_streams_capt = 1;	/* or 1 stereo stream */
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
120662306a36Sopenharmony_ci	if (err < 0) {
120762306a36Sopenharmony_ci		pcxhr_chip_free(chip);
120862306a36Sopenharmony_ci		return err;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	mgr->chip[idx] = chip;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return 0;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci/* proc interface */
121762306a36Sopenharmony_cistatic void pcxhr_proc_info(struct snd_info_entry *entry,
121862306a36Sopenharmony_ci			    struct snd_info_buffer *buffer)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
122162306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	snd_iprintf(buffer, "\n%s\n", mgr->name);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* stats available when embedded DSP is running */
122662306a36Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
122762306a36Sopenharmony_ci		struct pcxhr_rmh rmh;
122862306a36Sopenharmony_ci		short ver_maj = (mgr->dsp_version >> 16) & 0xff;
122962306a36Sopenharmony_ci		short ver_min = (mgr->dsp_version >> 8) & 0xff;
123062306a36Sopenharmony_ci		short ver_build = mgr->dsp_version & 0xff;
123162306a36Sopenharmony_ci		snd_iprintf(buffer, "module version %s\n",
123262306a36Sopenharmony_ci			    PCXHR_DRIVER_VERSION_STRING);
123362306a36Sopenharmony_ci		snd_iprintf(buffer, "dsp version %d.%d.%d\n",
123462306a36Sopenharmony_ci			    ver_maj, ver_min, ver_build);
123562306a36Sopenharmony_ci		if (mgr->board_has_analog)
123662306a36Sopenharmony_ci			snd_iprintf(buffer, "analog io available\n");
123762306a36Sopenharmony_ci		else
123862306a36Sopenharmony_ci			snd_iprintf(buffer, "digital only board\n");
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		/* calc cpu load of the dsp */
124162306a36Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_GET_DSP_RESOURCES);
124262306a36Sopenharmony_ci		if( ! pcxhr_send_msg(mgr, &rmh) ) {
124362306a36Sopenharmony_ci			int cur = rmh.stat[0];
124462306a36Sopenharmony_ci			int ref = rmh.stat[1];
124562306a36Sopenharmony_ci			if (ref > 0) {
124662306a36Sopenharmony_ci				if (mgr->sample_rate_real != 0 &&
124762306a36Sopenharmony_ci				    mgr->sample_rate_real != 48000) {
124862306a36Sopenharmony_ci					ref = (ref * 48000) /
124962306a36Sopenharmony_ci					  mgr->sample_rate_real;
125062306a36Sopenharmony_ci					if (mgr->sample_rate_real >=
125162306a36Sopenharmony_ci					    PCXHR_IRQ_TIMER_FREQ)
125262306a36Sopenharmony_ci						ref *= 2;
125362306a36Sopenharmony_ci				}
125462306a36Sopenharmony_ci				cur = 100 - (100 * cur) / ref;
125562306a36Sopenharmony_ci				snd_iprintf(buffer, "cpu load    %d%%\n", cur);
125662306a36Sopenharmony_ci				snd_iprintf(buffer, "buffer pool %d/%d\n",
125762306a36Sopenharmony_ci					    rmh.stat[2], rmh.stat[3]);
125862306a36Sopenharmony_ci			}
125962306a36Sopenharmony_ci		}
126062306a36Sopenharmony_ci		snd_iprintf(buffer, "dma granularity : %d\n",
126162306a36Sopenharmony_ci			    mgr->granularity);
126262306a36Sopenharmony_ci		snd_iprintf(buffer, "dsp time errors : %d\n",
126362306a36Sopenharmony_ci			    mgr->dsp_time_err);
126462306a36Sopenharmony_ci		snd_iprintf(buffer, "dsp async pipe xrun errors : %d\n",
126562306a36Sopenharmony_ci			    mgr->async_err_pipe_xrun);
126662306a36Sopenharmony_ci		snd_iprintf(buffer, "dsp async stream xrun errors : %d\n",
126762306a36Sopenharmony_ci			    mgr->async_err_stream_xrun);
126862306a36Sopenharmony_ci		snd_iprintf(buffer, "dsp async last other error : %x\n",
126962306a36Sopenharmony_ci			    mgr->async_err_other_last);
127062306a36Sopenharmony_ci		/* debug zone dsp */
127162306a36Sopenharmony_ci		rmh.cmd[0] = 0x4200 + PCXHR_SIZE_MAX_STATUS;
127262306a36Sopenharmony_ci		rmh.cmd_len = 1;
127362306a36Sopenharmony_ci		rmh.stat_len = PCXHR_SIZE_MAX_STATUS;
127462306a36Sopenharmony_ci		rmh.dsp_stat = 0;
127562306a36Sopenharmony_ci		rmh.cmd_idx = CMD_LAST_INDEX;
127662306a36Sopenharmony_ci		if( ! pcxhr_send_msg(mgr, &rmh) ) {
127762306a36Sopenharmony_ci			int i;
127862306a36Sopenharmony_ci			if (rmh.stat_len > 8)
127962306a36Sopenharmony_ci				rmh.stat_len = 8;
128062306a36Sopenharmony_ci			for (i = 0; i < rmh.stat_len; i++)
128162306a36Sopenharmony_ci				snd_iprintf(buffer, "debug[%02d] = %06x\n",
128262306a36Sopenharmony_ci					    i,  rmh.stat[i]);
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	} else
128562306a36Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
128662306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_cistatic void pcxhr_proc_sync(struct snd_info_entry *entry,
128962306a36Sopenharmony_ci			    struct snd_info_buffer *buffer)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
129262306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
129362306a36Sopenharmony_ci	static const char *textsHR22[3] = {
129462306a36Sopenharmony_ci		"Internal", "AES Sync", "AES 1"
129562306a36Sopenharmony_ci	};
129662306a36Sopenharmony_ci	static const char *textsPCXHR[7] = {
129762306a36Sopenharmony_ci		"Internal", "Word", "AES Sync",
129862306a36Sopenharmony_ci		"AES 1", "AES 2", "AES 3", "AES 4"
129962306a36Sopenharmony_ci	};
130062306a36Sopenharmony_ci	const char **texts;
130162306a36Sopenharmony_ci	int max_clock;
130262306a36Sopenharmony_ci	if (mgr->is_hr_stereo) {
130362306a36Sopenharmony_ci		texts = textsHR22;
130462306a36Sopenharmony_ci		max_clock = HR22_CLOCK_TYPE_MAX;
130562306a36Sopenharmony_ci	} else {
130662306a36Sopenharmony_ci		texts = textsPCXHR;
130762306a36Sopenharmony_ci		max_clock = PCXHR_CLOCK_TYPE_MAX;
130862306a36Sopenharmony_ci	}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	snd_iprintf(buffer, "\n%s\n", mgr->name);
131162306a36Sopenharmony_ci	snd_iprintf(buffer, "Current Sample Clock\t: %s\n",
131262306a36Sopenharmony_ci		    texts[mgr->cur_clock_type]);
131362306a36Sopenharmony_ci	snd_iprintf(buffer, "Current Sample Rate\t= %d\n",
131462306a36Sopenharmony_ci		    mgr->sample_rate_real);
131562306a36Sopenharmony_ci	/* commands available when embedded DSP is running */
131662306a36Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
131762306a36Sopenharmony_ci		int i, err, sample_rate;
131862306a36Sopenharmony_ci		for (i = 1; i <= max_clock; i++) {
131962306a36Sopenharmony_ci			err = pcxhr_get_external_clock(mgr, i, &sample_rate);
132062306a36Sopenharmony_ci			if (err)
132162306a36Sopenharmony_ci				break;
132262306a36Sopenharmony_ci			snd_iprintf(buffer, "%s Clock\t\t= %d\n",
132362306a36Sopenharmony_ci				    texts[i], sample_rate);
132462306a36Sopenharmony_ci		}
132562306a36Sopenharmony_ci	} else
132662306a36Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
132762306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic void pcxhr_proc_gpio_read(struct snd_info_entry *entry,
133162306a36Sopenharmony_ci				 struct snd_info_buffer *buffer)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
133462306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
133562306a36Sopenharmony_ci	/* commands available when embedded DSP is running */
133662306a36Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
133762306a36Sopenharmony_ci		/* gpio ports on stereo boards only available */
133862306a36Sopenharmony_ci		int value = 0;
133962306a36Sopenharmony_ci		hr222_read_gpio(mgr, 1, &value);	/* GPI */
134062306a36Sopenharmony_ci		snd_iprintf(buffer, "GPI: 0x%x\n", value);
134162306a36Sopenharmony_ci		hr222_read_gpio(mgr, 0, &value);	/* GP0 */
134262306a36Sopenharmony_ci		snd_iprintf(buffer, "GPO: 0x%x\n", value);
134362306a36Sopenharmony_ci	} else
134462306a36Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
134562306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_cistatic void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
134862306a36Sopenharmony_ci				 struct snd_info_buffer *buffer)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
135162306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
135262306a36Sopenharmony_ci	char line[64];
135362306a36Sopenharmony_ci	int value;
135462306a36Sopenharmony_ci	/* commands available when embedded DSP is running */
135562306a36Sopenharmony_ci	if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)))
135662306a36Sopenharmony_ci		return;
135762306a36Sopenharmony_ci	while (!snd_info_get_line(buffer, line, sizeof(line))) {
135862306a36Sopenharmony_ci		if (sscanf(line, "GPO: 0x%x", &value) != 1)
135962306a36Sopenharmony_ci			continue;
136062306a36Sopenharmony_ci		hr222_write_gpo(mgr, value);	/* GP0 */
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci/* Access to the results of the CMD_GET_TIME_CODE RMH */
136562306a36Sopenharmony_ci#define TIME_CODE_VALID_MASK	0x00800000
136662306a36Sopenharmony_ci#define TIME_CODE_NEW_MASK	0x00400000
136762306a36Sopenharmony_ci#define TIME_CODE_BACK_MASK	0x00200000
136862306a36Sopenharmony_ci#define TIME_CODE_WAIT_MASK	0x00100000
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci/* Values for the CMD_MANAGE_SIGNAL RMH */
137162306a36Sopenharmony_ci#define MANAGE_SIGNAL_TIME_CODE	0x01
137262306a36Sopenharmony_ci#define MANAGE_SIGNAL_MIDI	0x02
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci/* linear time code read proc*/
137562306a36Sopenharmony_cistatic void pcxhr_proc_ltc(struct snd_info_entry *entry,
137662306a36Sopenharmony_ci			   struct snd_info_buffer *buffer)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
137962306a36Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
138062306a36Sopenharmony_ci	struct pcxhr_rmh rmh;
138162306a36Sopenharmony_ci	unsigned int ltcHrs, ltcMin, ltcSec, ltcFrm;
138262306a36Sopenharmony_ci	int err;
138362306a36Sopenharmony_ci	/* commands available when embedded DSP is running */
138462306a36Sopenharmony_ci	if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))) {
138562306a36Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
138662306a36Sopenharmony_ci		return;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci	if (!mgr->capture_ltc) {
138962306a36Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_MANAGE_SIGNAL);
139062306a36Sopenharmony_ci		rmh.cmd[0] |= MANAGE_SIGNAL_TIME_CODE;
139162306a36Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
139262306a36Sopenharmony_ci		if (err) {
139362306a36Sopenharmony_ci			snd_iprintf(buffer, "ltc not activated (%d)\n", err);
139462306a36Sopenharmony_ci			return;
139562306a36Sopenharmony_ci		}
139662306a36Sopenharmony_ci		if (mgr->is_hr_stereo)
139762306a36Sopenharmony_ci			hr222_manage_timecode(mgr, 1);
139862306a36Sopenharmony_ci		else
139962306a36Sopenharmony_ci			pcxhr_write_io_num_reg_cont(mgr, REG_CONT_VALSMPTE,
140062306a36Sopenharmony_ci						    REG_CONT_VALSMPTE, NULL);
140162306a36Sopenharmony_ci		mgr->capture_ltc = 1;
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_GET_TIME_CODE);
140462306a36Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
140562306a36Sopenharmony_ci	if (err) {
140662306a36Sopenharmony_ci		snd_iprintf(buffer, "ltc read error (err=%d)\n", err);
140762306a36Sopenharmony_ci		return ;
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci	ltcHrs = 10*((rmh.stat[0] >> 8) & 0x3) + (rmh.stat[0] & 0xf);
141062306a36Sopenharmony_ci	ltcMin = 10*((rmh.stat[1] >> 16) & 0x7) + ((rmh.stat[1] >> 8) & 0xf);
141162306a36Sopenharmony_ci	ltcSec = 10*(rmh.stat[1] & 0x7) + ((rmh.stat[2] >> 16) & 0xf);
141262306a36Sopenharmony_ci	ltcFrm = 10*((rmh.stat[2] >> 8) & 0x3) + (rmh.stat[2] & 0xf);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	snd_iprintf(buffer, "timecode: %02u:%02u:%02u-%02u\n",
141562306a36Sopenharmony_ci			    ltcHrs, ltcMin, ltcSec, ltcFrm);
141662306a36Sopenharmony_ci	snd_iprintf(buffer, "raw: 0x%04x%06x%06x\n", rmh.stat[0] & 0x00ffff,
141762306a36Sopenharmony_ci			    rmh.stat[1] & 0xffffff, rmh.stat[2] & 0xffffff);
141862306a36Sopenharmony_ci	/*snd_iprintf(buffer, "dsp ref time: 0x%06x%06x\n",
141962306a36Sopenharmony_ci			    rmh.stat[3] & 0xffffff, rmh.stat[4] & 0xffffff);*/
142062306a36Sopenharmony_ci	if (!(rmh.stat[0] & TIME_CODE_VALID_MASK)) {
142162306a36Sopenharmony_ci		snd_iprintf(buffer, "warning: linear timecode not valid\n");
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic void pcxhr_proc_init(struct snd_pcxhr *chip)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	snd_card_ro_proc_new(chip->card, "info", chip, pcxhr_proc_info);
142862306a36Sopenharmony_ci	snd_card_ro_proc_new(chip->card, "sync", chip, pcxhr_proc_sync);
142962306a36Sopenharmony_ci	/* gpio available on stereo sound cards only */
143062306a36Sopenharmony_ci	if (chip->mgr->is_hr_stereo)
143162306a36Sopenharmony_ci		snd_card_rw_proc_new(chip->card, "gpio", chip,
143262306a36Sopenharmony_ci				     pcxhr_proc_gpio_read,
143362306a36Sopenharmony_ci				     pcxhr_proc_gpo_write);
143462306a36Sopenharmony_ci	snd_card_ro_proc_new(chip->card, "ltc", chip, pcxhr_proc_ltc);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci/* end of proc interface */
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci/*
143962306a36Sopenharmony_ci * release all the cards assigned to a manager instance
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_cistatic int pcxhr_free(struct pcxhr_mgr *mgr)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	unsigned int i;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
144662306a36Sopenharmony_ci		if (mgr->chip[i])
144762306a36Sopenharmony_ci			snd_card_free(mgr->chip[i]->card);
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/* reset board if some firmware was loaded */
145162306a36Sopenharmony_ci	if(mgr->dsp_loaded) {
145262306a36Sopenharmony_ci		pcxhr_reset_board(mgr);
145362306a36Sopenharmony_ci		dev_dbg(&mgr->pci->dev, "reset pcxhr !\n");
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* release irq  */
145762306a36Sopenharmony_ci	if (mgr->irq >= 0)
145862306a36Sopenharmony_ci		free_irq(mgr->irq, mgr);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	pci_release_regions(mgr->pci);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* free hostport purgebuffer */
146362306a36Sopenharmony_ci	if (mgr->hostport.area) {
146462306a36Sopenharmony_ci		snd_dma_free_pages(&mgr->hostport);
146562306a36Sopenharmony_ci		mgr->hostport.area = NULL;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	kfree(mgr->prmh);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	pci_disable_device(mgr->pci);
147162306a36Sopenharmony_ci	kfree(mgr);
147262306a36Sopenharmony_ci	return 0;
147362306a36Sopenharmony_ci}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci/*
147662306a36Sopenharmony_ci *    probe function - creates the card manager
147762306a36Sopenharmony_ci */
147862306a36Sopenharmony_cistatic int pcxhr_probe(struct pci_dev *pci,
147962306a36Sopenharmony_ci		       const struct pci_device_id *pci_id)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	static int dev;
148262306a36Sopenharmony_ci	struct pcxhr_mgr *mgr;
148362306a36Sopenharmony_ci	unsigned int i;
148462306a36Sopenharmony_ci	int err;
148562306a36Sopenharmony_ci	size_t size;
148662306a36Sopenharmony_ci	char *card_name;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
148962306a36Sopenharmony_ci		return -ENODEV;
149062306a36Sopenharmony_ci	if (! enable[dev]) {
149162306a36Sopenharmony_ci		dev++;
149262306a36Sopenharmony_ci		return -ENOENT;
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	/* enable PCI device */
149662306a36Sopenharmony_ci	err = pci_enable_device(pci);
149762306a36Sopenharmony_ci	if (err < 0)
149862306a36Sopenharmony_ci		return err;
149962306a36Sopenharmony_ci	pci_set_master(pci);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	/* check if we can restrict PCI DMA transfers to 32 bits */
150262306a36Sopenharmony_ci	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
150362306a36Sopenharmony_ci		dev_err(&pci->dev,
150462306a36Sopenharmony_ci			"architecture does not support 32bit PCI busmaster DMA\n");
150562306a36Sopenharmony_ci		pci_disable_device(pci);
150662306a36Sopenharmony_ci		return -ENXIO;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	/* alloc card manager */
151062306a36Sopenharmony_ci	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
151162306a36Sopenharmony_ci	if (! mgr) {
151262306a36Sopenharmony_ci		pci_disable_device(pci);
151362306a36Sopenharmony_ci		return -ENOMEM;
151462306a36Sopenharmony_ci	}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (snd_BUG_ON(pci_id->driver_data >= PCI_ID_LAST)) {
151762306a36Sopenharmony_ci		kfree(mgr);
151862306a36Sopenharmony_ci		pci_disable_device(pci);
151962306a36Sopenharmony_ci		return -ENODEV;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci	card_name =
152262306a36Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].board_name;
152362306a36Sopenharmony_ci	mgr->playback_chips =
152462306a36Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].playback_chips;
152562306a36Sopenharmony_ci	mgr->capture_chips  =
152662306a36Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].capture_chips;
152762306a36Sopenharmony_ci	mgr->fw_file_set =
152862306a36Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].fw_file_set;
152962306a36Sopenharmony_ci	mgr->firmware_num  =
153062306a36Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].firmware_num;
153162306a36Sopenharmony_ci	mgr->mono_capture = mono[dev];
153262306a36Sopenharmony_ci	mgr->is_hr_stereo = (mgr->playback_chips == 1);
153362306a36Sopenharmony_ci	mgr->board_has_aes1 = PCXHR_BOARD_HAS_AES1(mgr);
153462306a36Sopenharmony_ci	mgr->board_aes_in_192k = !PCXHR_BOARD_AESIN_NO_192K(mgr);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (mgr->is_hr_stereo)
153762306a36Sopenharmony_ci		mgr->granularity = PCXHR_GRANULARITY_HR22;
153862306a36Sopenharmony_ci	else
153962306a36Sopenharmony_ci		mgr->granularity = PCXHR_GRANULARITY;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* resource assignment */
154262306a36Sopenharmony_ci	err = pci_request_regions(pci, card_name);
154362306a36Sopenharmony_ci	if (err < 0) {
154462306a36Sopenharmony_ci		kfree(mgr);
154562306a36Sopenharmony_ci		pci_disable_device(pci);
154662306a36Sopenharmony_ci		return err;
154762306a36Sopenharmony_ci	}
154862306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
154962306a36Sopenharmony_ci		mgr->port[i] = pci_resource_start(pci, i);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	mgr->pci = pci;
155262306a36Sopenharmony_ci	mgr->irq = -1;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	if (request_threaded_irq(pci->irq, pcxhr_interrupt,
155562306a36Sopenharmony_ci				 pcxhr_threaded_irq, IRQF_SHARED,
155662306a36Sopenharmony_ci				 KBUILD_MODNAME, mgr)) {
155762306a36Sopenharmony_ci		dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
155862306a36Sopenharmony_ci		pcxhr_free(mgr);
155962306a36Sopenharmony_ci		return -EBUSY;
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci	mgr->irq = pci->irq;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	snprintf(mgr->name, sizeof(mgr->name),
156462306a36Sopenharmony_ci		 "Digigram at 0x%lx & 0x%lx, 0x%lx irq %i",
156562306a36Sopenharmony_ci		 mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/* ISR lock  */
156862306a36Sopenharmony_ci	mutex_init(&mgr->lock);
156962306a36Sopenharmony_ci	mutex_init(&mgr->msg_lock);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	/* init setup mutex*/
157262306a36Sopenharmony_ci	mutex_init(&mgr->setup_mutex);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
157562306a36Sopenharmony_ci			    sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
157662306a36Sopenharmony_ci					   PCXHR_SIZE_MAX_STATUS),
157762306a36Sopenharmony_ci			    GFP_KERNEL);
157862306a36Sopenharmony_ci	if (! mgr->prmh) {
157962306a36Sopenharmony_ci		pcxhr_free(mgr);
158062306a36Sopenharmony_ci		return -ENOMEM;
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	for (i=0; i < PCXHR_MAX_CARDS; i++) {
158462306a36Sopenharmony_ci		struct snd_card *card;
158562306a36Sopenharmony_ci		char tmpid[16];
158662306a36Sopenharmony_ci		int idx;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci		if (i >= max(mgr->playback_chips, mgr->capture_chips))
158962306a36Sopenharmony_ci			break;
159062306a36Sopenharmony_ci		mgr->num_cards++;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci		if (index[dev] < 0)
159362306a36Sopenharmony_ci			idx = index[dev];
159462306a36Sopenharmony_ci		else
159562306a36Sopenharmony_ci			idx = index[dev] + i;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		snprintf(tmpid, sizeof(tmpid), "%s-%d",
159862306a36Sopenharmony_ci			 id[dev] ? id[dev] : card_name, i);
159962306a36Sopenharmony_ci		err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
160062306a36Sopenharmony_ci				   0, &card);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci		if (err < 0) {
160362306a36Sopenharmony_ci			dev_err(&pci->dev, "cannot allocate the card %d\n", i);
160462306a36Sopenharmony_ci			pcxhr_free(mgr);
160562306a36Sopenharmony_ci			return err;
160662306a36Sopenharmony_ci		}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci		strcpy(card->driver, DRIVER_NAME);
160962306a36Sopenharmony_ci		snprintf(card->shortname, sizeof(card->shortname),
161062306a36Sopenharmony_ci			 "Digigram [PCM #%d]", i);
161162306a36Sopenharmony_ci		snprintf(card->longname, sizeof(card->longname),
161262306a36Sopenharmony_ci			 "%s [PCM #%d]", mgr->name, i);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci		err = pcxhr_create(mgr, card, i);
161562306a36Sopenharmony_ci		if (err < 0) {
161662306a36Sopenharmony_ci			snd_card_free(card);
161762306a36Sopenharmony_ci			pcxhr_free(mgr);
161862306a36Sopenharmony_ci			return err;
161962306a36Sopenharmony_ci		}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci		if (i == 0)
162262306a36Sopenharmony_ci			/* init proc interface only for chip0 */
162362306a36Sopenharmony_ci			pcxhr_proc_init(mgr->chip[i]);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci		err = snd_card_register(card);
162662306a36Sopenharmony_ci		if (err < 0) {
162762306a36Sopenharmony_ci			pcxhr_free(mgr);
162862306a36Sopenharmony_ci			return err;
162962306a36Sopenharmony_ci		}
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* create hostport purgebuffer */
163362306a36Sopenharmony_ci	size = PAGE_ALIGN(sizeof(struct pcxhr_hostport));
163462306a36Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
163562306a36Sopenharmony_ci				size, &mgr->hostport) < 0) {
163662306a36Sopenharmony_ci		pcxhr_free(mgr);
163762306a36Sopenharmony_ci		return -ENOMEM;
163862306a36Sopenharmony_ci	}
163962306a36Sopenharmony_ci	/* init purgebuffer */
164062306a36Sopenharmony_ci	memset(mgr->hostport.area, 0, size);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	/* create a DSP loader */
164362306a36Sopenharmony_ci	err = pcxhr_setup_firmware(mgr);
164462306a36Sopenharmony_ci	if (err < 0) {
164562306a36Sopenharmony_ci		pcxhr_free(mgr);
164662306a36Sopenharmony_ci		return err;
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	pci_set_drvdata(pci, mgr);
165062306a36Sopenharmony_ci	dev++;
165162306a36Sopenharmony_ci	return 0;
165262306a36Sopenharmony_ci}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_cistatic void pcxhr_remove(struct pci_dev *pci)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	pcxhr_free(pci_get_drvdata(pci));
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistatic struct pci_driver pcxhr_driver = {
166062306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
166162306a36Sopenharmony_ci	.id_table = pcxhr_ids,
166262306a36Sopenharmony_ci	.probe = pcxhr_probe,
166362306a36Sopenharmony_ci	.remove = pcxhr_remove,
166462306a36Sopenharmony_ci};
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cimodule_pci_driver(pcxhr_driver);
1667