xref: /kernel/linux/linux-5.10/sound/pci/pcxhr/pcxhr.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Digigram pcxhr compatible soundcards
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * main file with alsa callbacks
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2004 by Digigram <alsa@digigram.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/mutex.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <sound/core.h>
218c2ecf20Sopenharmony_ci#include <sound/initval.h>
228c2ecf20Sopenharmony_ci#include <sound/info.h>
238c2ecf20Sopenharmony_ci#include <sound/control.h>
248c2ecf20Sopenharmony_ci#include <sound/pcm.h>
258c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
268c2ecf20Sopenharmony_ci#include "pcxhr.h"
278c2ecf20Sopenharmony_ci#include "pcxhr_mixer.h"
288c2ecf20Sopenharmony_ci#include "pcxhr_hwdep.h"
298c2ecf20Sopenharmony_ci#include "pcxhr_core.h"
308c2ecf20Sopenharmony_ci#include "pcxhr_mix22.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define DRIVER_NAME "pcxhr"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>, "
358c2ecf20Sopenharmony_ci	      "Marc Titinger <titinger@digigram.com>");
368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);
378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
388c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Digigram," DRIVER_NAME "}}");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
418c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
428c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
438c2ecf20Sopenharmony_cistatic bool mono[SNDRV_CARDS];				/* capture  mono only */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");
478c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Digigram " DRIVER_NAME " soundcard");
498c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Digigram " DRIVER_NAME " soundcard");
518c2ecf20Sopenharmony_cimodule_param_array(mono, bool, NULL, 0444);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mono, "Mono capture mode (default is stereo)");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cienum {
558c2ecf20Sopenharmony_ci	PCI_ID_VX882HR,
568c2ecf20Sopenharmony_ci	PCI_ID_PCX882HR,
578c2ecf20Sopenharmony_ci	PCI_ID_VX881HR,
588c2ecf20Sopenharmony_ci	PCI_ID_PCX881HR,
598c2ecf20Sopenharmony_ci	PCI_ID_VX882E,
608c2ecf20Sopenharmony_ci	PCI_ID_PCX882E,
618c2ecf20Sopenharmony_ci	PCI_ID_VX881E,
628c2ecf20Sopenharmony_ci	PCI_ID_PCX881E,
638c2ecf20Sopenharmony_ci	PCI_ID_VX1222HR,
648c2ecf20Sopenharmony_ci	PCI_ID_PCX1222HR,
658c2ecf20Sopenharmony_ci	PCI_ID_VX1221HR,
668c2ecf20Sopenharmony_ci	PCI_ID_PCX1221HR,
678c2ecf20Sopenharmony_ci	PCI_ID_VX1222E,
688c2ecf20Sopenharmony_ci	PCI_ID_PCX1222E,
698c2ecf20Sopenharmony_ci	PCI_ID_VX1221E,
708c2ecf20Sopenharmony_ci	PCI_ID_PCX1221E,
718c2ecf20Sopenharmony_ci	PCI_ID_VX222HR,
728c2ecf20Sopenharmony_ci	PCI_ID_VX222E,
738c2ecf20Sopenharmony_ci	PCI_ID_PCX22HR,
748c2ecf20Sopenharmony_ci	PCI_ID_PCX22E,
758c2ecf20Sopenharmony_ci	PCI_ID_VX222HRMIC,
768c2ecf20Sopenharmony_ci	PCI_ID_VX222E_MIC,
778c2ecf20Sopenharmony_ci	PCI_ID_PCX924HR,
788c2ecf20Sopenharmony_ci	PCI_ID_PCX924E,
798c2ecf20Sopenharmony_ci	PCI_ID_PCX924HRMIC,
808c2ecf20Sopenharmony_ci	PCI_ID_PCX924E_MIC,
818c2ecf20Sopenharmony_ci	PCI_ID_VX442HR,
828c2ecf20Sopenharmony_ci	PCI_ID_PCX442HR,
838c2ecf20Sopenharmony_ci	PCI_ID_VX442E,
848c2ecf20Sopenharmony_ci	PCI_ID_PCX442E,
858c2ecf20Sopenharmony_ci	PCI_ID_VX822HR,
868c2ecf20Sopenharmony_ci	PCI_ID_PCX822HR,
878c2ecf20Sopenharmony_ci	PCI_ID_VX822E,
888c2ecf20Sopenharmony_ci	PCI_ID_PCX822E,
898c2ecf20Sopenharmony_ci	PCI_ID_LAST
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic const struct pci_device_id pcxhr_ids[] = {
938c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
948c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
958c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
968c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },
978c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb021, 0, 0, PCI_ID_VX882E, },
988c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb121, 0, 0, PCI_ID_PCX882E, },
998c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb221, 0, 0, PCI_ID_VX881E, },
1008c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb321, 0, 0, PCI_ID_PCX881E, },
1018c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb401, 0, 0, PCI_ID_VX1222HR, },
1028c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, },
1038c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb601, 0, 0, PCI_ID_VX1221HR, },
1048c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, },
1058c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb421, 0, 0, PCI_ID_VX1222E, },
1068c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb521, 0, 0, PCI_ID_PCX1222E, },
1078c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb621, 0, 0, PCI_ID_VX1221E, },
1088c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xb721, 0, 0, PCI_ID_PCX1221E, },
1098c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xba01, 0, 0, PCI_ID_VX222HR, },
1108c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xba21, 0, 0, PCI_ID_VX222E, },
1118c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbd01, 0, 0, PCI_ID_PCX22HR, },
1128c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbd21, 0, 0, PCI_ID_PCX22E, },
1138c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbc01, 0, 0, PCI_ID_VX222HRMIC, },
1148c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbc21, 0, 0, PCI_ID_VX222E_MIC, },
1158c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbb01, 0, 0, PCI_ID_PCX924HR, },
1168c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
1178c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
1188c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
1198c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd001, 0, 0, PCI_ID_VX442HR, },
1208c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd101, 0, 0, PCI_ID_PCX442HR, },
1218c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd021, 0, 0, PCI_ID_VX442E, },
1228c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd121, 0, 0, PCI_ID_PCX442E, },
1238c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd201, 0, 0, PCI_ID_VX822HR, },
1248c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9656, 0x1369, 0xd301, 0, 0, PCI_ID_PCX822HR, },
1258c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd221, 0, 0, PCI_ID_VX822E, },
1268c2ecf20Sopenharmony_ci	{ 0x10b5, 0x9056, 0x1369, 0xd321, 0, 0, PCI_ID_PCX822E, },
1278c2ecf20Sopenharmony_ci	{ 0, }
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pcxhr_ids);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistruct board_parameters {
1338c2ecf20Sopenharmony_ci	char* board_name;
1348c2ecf20Sopenharmony_ci	short playback_chips;
1358c2ecf20Sopenharmony_ci	short capture_chips;
1368c2ecf20Sopenharmony_ci	short fw_file_set;
1378c2ecf20Sopenharmony_ci	short firmware_num;
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_cistatic const struct board_parameters pcxhr_board_params[] = {
1408c2ecf20Sopenharmony_ci[PCI_ID_VX882HR] =      { "VX882HR",      4, 4, 0, 41 },
1418c2ecf20Sopenharmony_ci[PCI_ID_PCX882HR] =     { "PCX882HR",     4, 4, 0, 41 },
1428c2ecf20Sopenharmony_ci[PCI_ID_VX881HR] =      { "VX881HR",      4, 4, 0, 41 },
1438c2ecf20Sopenharmony_ci[PCI_ID_PCX881HR] =     { "PCX881HR",     4, 4, 0, 41 },
1448c2ecf20Sopenharmony_ci[PCI_ID_VX882E] =       { "VX882e",       4, 4, 1, 41 },
1458c2ecf20Sopenharmony_ci[PCI_ID_PCX882E] =      { "PCX882e",      4, 4, 1, 41 },
1468c2ecf20Sopenharmony_ci[PCI_ID_VX881E] =       { "VX881e",       4, 4, 1, 41 },
1478c2ecf20Sopenharmony_ci[PCI_ID_PCX881E] =      { "PCX881e",      4, 4, 1, 41 },
1488c2ecf20Sopenharmony_ci[PCI_ID_VX1222HR] =     { "VX1222HR",     6, 1, 2, 42 },
1498c2ecf20Sopenharmony_ci[PCI_ID_PCX1222HR] =    { "PCX1222HR",    6, 1, 2, 42 },
1508c2ecf20Sopenharmony_ci[PCI_ID_VX1221HR] =     { "VX1221HR",     6, 1, 2, 42 },
1518c2ecf20Sopenharmony_ci[PCI_ID_PCX1221HR] =    { "PCX1221HR",    6, 1, 2, 42 },
1528c2ecf20Sopenharmony_ci[PCI_ID_VX1222E] =      { "VX1222e",      6, 1, 3, 42 },
1538c2ecf20Sopenharmony_ci[PCI_ID_PCX1222E] =     { "PCX1222e",     6, 1, 3, 42 },
1548c2ecf20Sopenharmony_ci[PCI_ID_VX1221E] =      { "VX1221e",      6, 1, 3, 42 },
1558c2ecf20Sopenharmony_ci[PCI_ID_PCX1221E] =     { "PCX1221e",     6, 1, 3, 42 },
1568c2ecf20Sopenharmony_ci[PCI_ID_VX222HR] =      { "VX222HR",      1, 1, 4, 44 },
1578c2ecf20Sopenharmony_ci[PCI_ID_VX222E] =       { "VX222e",       1, 1, 4, 44 },
1588c2ecf20Sopenharmony_ci[PCI_ID_PCX22HR] =      { "PCX22HR",      1, 0, 4, 44 },
1598c2ecf20Sopenharmony_ci[PCI_ID_PCX22E] =       { "PCX22e",       1, 0, 4, 44 },
1608c2ecf20Sopenharmony_ci[PCI_ID_VX222HRMIC] =   { "VX222HR-Mic",  1, 1, 5, 44 },
1618c2ecf20Sopenharmony_ci[PCI_ID_VX222E_MIC] =   { "VX222e-Mic",   1, 1, 5, 44 },
1628c2ecf20Sopenharmony_ci[PCI_ID_PCX924HR] =     { "PCX924HR",     1, 1, 5, 44 },
1638c2ecf20Sopenharmony_ci[PCI_ID_PCX924E] =      { "PCX924e",      1, 1, 5, 44 },
1648c2ecf20Sopenharmony_ci[PCI_ID_PCX924HRMIC] =  { "PCX924HR-Mic", 1, 1, 5, 44 },
1658c2ecf20Sopenharmony_ci[PCI_ID_PCX924E_MIC] =  { "PCX924e-Mic",  1, 1, 5, 44 },
1668c2ecf20Sopenharmony_ci[PCI_ID_VX442HR] =      { "VX442HR",      2, 2, 0, 41 },
1678c2ecf20Sopenharmony_ci[PCI_ID_PCX442HR] =     { "PCX442HR",     2, 2, 0, 41 },
1688c2ecf20Sopenharmony_ci[PCI_ID_VX442E] =       { "VX442e",       2, 2, 1, 41 },
1698c2ecf20Sopenharmony_ci[PCI_ID_PCX442E] =      { "PCX442e",      2, 2, 1, 41 },
1708c2ecf20Sopenharmony_ci[PCI_ID_VX822HR] =      { "VX822HR",      4, 1, 2, 42 },
1718c2ecf20Sopenharmony_ci[PCI_ID_PCX822HR] =     { "PCX822HR",     4, 1, 2, 42 },
1728c2ecf20Sopenharmony_ci[PCI_ID_VX822E] =       { "VX822e",       4, 1, 3, 42 },
1738c2ecf20Sopenharmony_ci[PCI_ID_PCX822E] =      { "PCX822e",      4, 1, 3, 42 },
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
1778c2ecf20Sopenharmony_ci/* VX222HR, VX222e, PCX22HR and PCX22e */
1788c2ecf20Sopenharmony_ci#define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
1798c2ecf20Sopenharmony_ci/* some boards do not support 192kHz on digital AES input plugs */
1808c2ecf20Sopenharmony_ci#define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
1818c2ecf20Sopenharmony_ci				      (x->fw_file_set == 0)   || \
1828c2ecf20Sopenharmony_ci				      (x->fw_file_set == 2))
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
1858c2ecf20Sopenharmony_ci				   unsigned int* realfreq)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	unsigned int reg;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (freq < 6900 || freq > 110000)
1908c2ecf20Sopenharmony_ci		return -EINVAL;
1918c2ecf20Sopenharmony_ci	reg = (28224000 * 2) / freq;
1928c2ecf20Sopenharmony_ci	reg = (reg - 1) / 2;
1938c2ecf20Sopenharmony_ci	if (reg < 0x200)
1948c2ecf20Sopenharmony_ci		*pllreg = reg + 0x800;
1958c2ecf20Sopenharmony_ci	else if (reg < 0x400)
1968c2ecf20Sopenharmony_ci		*pllreg = reg & 0x1ff;
1978c2ecf20Sopenharmony_ci	else if (reg < 0x800) {
1988c2ecf20Sopenharmony_ci		*pllreg = ((reg >> 1) & 0x1ff) + 0x200;
1998c2ecf20Sopenharmony_ci		reg &= ~1;
2008c2ecf20Sopenharmony_ci	} else {
2018c2ecf20Sopenharmony_ci		*pllreg = ((reg >> 2) & 0x1ff) + 0x400;
2028c2ecf20Sopenharmony_ci		reg &= ~3;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci	if (realfreq)
2058c2ecf20Sopenharmony_ci		*realfreq = (28224000 / (reg + 1));
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci#define PCXHR_FREQ_REG_MASK		0x1f
2118c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_48000		0x00
2128c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_24000		0x01
2138c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_12000		0x09
2148c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_32000		0x08
2158c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_16000		0x04
2168c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_8000		0x0c
2178c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_44100		0x02
2188c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_22050		0x0a
2198c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_11025		0x06
2208c2ecf20Sopenharmony_ci#define PCXHR_FREQ_PLL			0x05
2218c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_192000	0x10
2228c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_96000		0x18
2238c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_176400	0x14
2248c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_88200		0x1c
2258c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_128000	0x12
2268c2ecf20Sopenharmony_ci#define PCXHR_FREQ_QUARTZ_64000		0x1a
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci#define PCXHR_FREQ_WORD_CLOCK		0x0f
2298c2ecf20Sopenharmony_ci#define PCXHR_FREQ_SYNC_AES		0x0e
2308c2ecf20Sopenharmony_ci#define PCXHR_FREQ_AES_1		0x07
2318c2ecf20Sopenharmony_ci#define PCXHR_FREQ_AES_2		0x0b
2328c2ecf20Sopenharmony_ci#define PCXHR_FREQ_AES_3		0x03
2338c2ecf20Sopenharmony_ci#define PCXHR_FREQ_AES_4		0x0d
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
2368c2ecf20Sopenharmony_ci			       unsigned int *reg, unsigned int *freq)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	unsigned int val, realfreq, pllreg;
2398c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
2408c2ecf20Sopenharmony_ci	int err;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	realfreq = rate;
2438c2ecf20Sopenharmony_ci	switch (mgr->use_clock_type) {
2448c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_INTERNAL :	/* clock by quartz or pll */
2458c2ecf20Sopenharmony_ci		switch (rate) {
2468c2ecf20Sopenharmony_ci		case 48000 :	val = PCXHR_FREQ_QUARTZ_48000;	break;
2478c2ecf20Sopenharmony_ci		case 24000 :	val = PCXHR_FREQ_QUARTZ_24000;	break;
2488c2ecf20Sopenharmony_ci		case 12000 :	val = PCXHR_FREQ_QUARTZ_12000;	break;
2498c2ecf20Sopenharmony_ci		case 32000 :	val = PCXHR_FREQ_QUARTZ_32000;	break;
2508c2ecf20Sopenharmony_ci		case 16000 :	val = PCXHR_FREQ_QUARTZ_16000;	break;
2518c2ecf20Sopenharmony_ci		case 8000 :	val = PCXHR_FREQ_QUARTZ_8000;	break;
2528c2ecf20Sopenharmony_ci		case 44100 :	val = PCXHR_FREQ_QUARTZ_44100;	break;
2538c2ecf20Sopenharmony_ci		case 22050 :	val = PCXHR_FREQ_QUARTZ_22050;	break;
2548c2ecf20Sopenharmony_ci		case 11025 :	val = PCXHR_FREQ_QUARTZ_11025;	break;
2558c2ecf20Sopenharmony_ci		case 192000 :	val = PCXHR_FREQ_QUARTZ_192000;	break;
2568c2ecf20Sopenharmony_ci		case 96000 :	val = PCXHR_FREQ_QUARTZ_96000;	break;
2578c2ecf20Sopenharmony_ci		case 176400 :	val = PCXHR_FREQ_QUARTZ_176400;	break;
2588c2ecf20Sopenharmony_ci		case 88200 :	val = PCXHR_FREQ_QUARTZ_88200;	break;
2598c2ecf20Sopenharmony_ci		case 128000 :	val = PCXHR_FREQ_QUARTZ_128000;	break;
2608c2ecf20Sopenharmony_ci		case 64000 :	val = PCXHR_FREQ_QUARTZ_64000;	break;
2618c2ecf20Sopenharmony_ci		default :
2628c2ecf20Sopenharmony_ci			val = PCXHR_FREQ_PLL;
2638c2ecf20Sopenharmony_ci			/* get the value for the pll register */
2648c2ecf20Sopenharmony_ci			err = pcxhr_pll_freq_register(rate, &pllreg, &realfreq);
2658c2ecf20Sopenharmony_ci			if (err)
2668c2ecf20Sopenharmony_ci				return err;
2678c2ecf20Sopenharmony_ci			pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
2688c2ecf20Sopenharmony_ci			rmh.cmd[0] |= IO_NUM_REG_GENCLK;
2698c2ecf20Sopenharmony_ci			rmh.cmd[1]  = pllreg & MASK_DSP_WORD;
2708c2ecf20Sopenharmony_ci			rmh.cmd[2]  = pllreg >> 24;
2718c2ecf20Sopenharmony_ci			rmh.cmd_len = 3;
2728c2ecf20Sopenharmony_ci			err = pcxhr_send_msg(mgr, &rmh);
2738c2ecf20Sopenharmony_ci			if (err < 0) {
2748c2ecf20Sopenharmony_ci				dev_err(&mgr->pci->dev,
2758c2ecf20Sopenharmony_ci					   "error CMD_ACCESS_IO_WRITE "
2768c2ecf20Sopenharmony_ci					   "for PLL register : %x!\n", err);
2778c2ecf20Sopenharmony_ci				return err;
2788c2ecf20Sopenharmony_ci			}
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci		break;
2818c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_WORD_CLOCK:
2828c2ecf20Sopenharmony_ci		val = PCXHR_FREQ_WORD_CLOCK;
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_SYNC:
2858c2ecf20Sopenharmony_ci		val = PCXHR_FREQ_SYNC_AES;
2868c2ecf20Sopenharmony_ci		break;
2878c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_1:
2888c2ecf20Sopenharmony_ci		val = PCXHR_FREQ_AES_1;
2898c2ecf20Sopenharmony_ci		break;
2908c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_2:
2918c2ecf20Sopenharmony_ci		val = PCXHR_FREQ_AES_2;
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_3:
2948c2ecf20Sopenharmony_ci		val = PCXHR_FREQ_AES_3;
2958c2ecf20Sopenharmony_ci		break;
2968c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_4:
2978c2ecf20Sopenharmony_ci		val = PCXHR_FREQ_AES_4;
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	default:
3008c2ecf20Sopenharmony_ci		return -EINVAL;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	*reg = val;
3038c2ecf20Sopenharmony_ci	*freq = realfreq;
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
3098c2ecf20Sopenharmony_ci			       unsigned int rate,
3108c2ecf20Sopenharmony_ci			       int *changed)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	unsigned int val, realfreq, speed;
3138c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
3148c2ecf20Sopenharmony_ci	int err;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	err = pcxhr_get_clock_reg(mgr, rate, &val, &realfreq);
3178c2ecf20Sopenharmony_ci	if (err)
3188c2ecf20Sopenharmony_ci		return err;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* codec speed modes */
3218c2ecf20Sopenharmony_ci	if (rate < 55000)
3228c2ecf20Sopenharmony_ci		speed = 0;	/* single speed */
3238c2ecf20Sopenharmony_ci	else if (rate < 100000)
3248c2ecf20Sopenharmony_ci		speed = 1;	/* dual speed */
3258c2ecf20Sopenharmony_ci	else
3268c2ecf20Sopenharmony_ci		speed = 2;	/* quad speed */
3278c2ecf20Sopenharmony_ci	if (mgr->codec_speed != speed) {
3288c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* mute outputs */
3298c2ecf20Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
3308c2ecf20Sopenharmony_ci		if (DSP_EXT_CMD_SET(mgr)) {
3318c2ecf20Sopenharmony_ci			rmh.cmd[1]  = 1;
3328c2ecf20Sopenharmony_ci			rmh.cmd_len = 2;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
3358c2ecf20Sopenharmony_ci		if (err)
3368c2ecf20Sopenharmony_ci			return err;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set speed ratio */
3398c2ecf20Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_SPEED_RATIO;
3408c2ecf20Sopenharmony_ci		rmh.cmd[1] = speed;
3418c2ecf20Sopenharmony_ci		rmh.cmd_len = 2;
3428c2ecf20Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
3438c2ecf20Sopenharmony_ci		if (err)
3448c2ecf20Sopenharmony_ci			return err;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci	/* set the new frequency */
3478c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "clock register : set %x\n", val);
3488c2ecf20Sopenharmony_ci	err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK,
3498c2ecf20Sopenharmony_ci					  val, changed);
3508c2ecf20Sopenharmony_ci	if (err)
3518c2ecf20Sopenharmony_ci		return err;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	mgr->sample_rate_real = realfreq;
3548c2ecf20Sopenharmony_ci	mgr->cur_clock_type = mgr->use_clock_type;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* unmute after codec speed modes */
3578c2ecf20Sopenharmony_ci	if (mgr->codec_speed != speed) {
3588c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* unmute outputs */
3598c2ecf20Sopenharmony_ci		rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
3608c2ecf20Sopenharmony_ci		if (DSP_EXT_CMD_SET(mgr)) {
3618c2ecf20Sopenharmony_ci			rmh.cmd[1]  = 1;
3628c2ecf20Sopenharmony_ci			rmh.cmd_len = 2;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
3658c2ecf20Sopenharmony_ci		if (err)
3668c2ecf20Sopenharmony_ci			return err;
3678c2ecf20Sopenharmony_ci		mgr->codec_speed = speed;	/* save new codec speed */
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "pcxhr_sub_set_clock to %dHz (realfreq=%d)\n",
3718c2ecf20Sopenharmony_ci		    rate, realfreq);
3728c2ecf20Sopenharmony_ci	return 0;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci#define PCXHR_MODIFY_CLOCK_S_BIT	0x04
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci#define PCXHR_IRQ_TIMER_FREQ		92000
3788c2ecf20Sopenharmony_ci#define PCXHR_IRQ_TIMER_PERIOD		48
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ciint pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
3838c2ecf20Sopenharmony_ci	int err, changed;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (rate == 0)
3868c2ecf20Sopenharmony_ci		return 0; /* nothing to do */
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (mgr->is_hr_stereo)
3898c2ecf20Sopenharmony_ci		err = hr222_sub_set_clock(mgr, rate, &changed);
3908c2ecf20Sopenharmony_ci	else
3918c2ecf20Sopenharmony_ci		err = pcxhr_sub_set_clock(mgr, rate, &changed);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (err)
3948c2ecf20Sopenharmony_ci		return err;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (changed) {
3978c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_MODIFY_CLOCK);
3988c2ecf20Sopenharmony_ci		rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT; /* resync fifos  */
3998c2ecf20Sopenharmony_ci		if (rate < PCXHR_IRQ_TIMER_FREQ)
4008c2ecf20Sopenharmony_ci			rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD;
4018c2ecf20Sopenharmony_ci		else
4028c2ecf20Sopenharmony_ci			rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD * 2;
4038c2ecf20Sopenharmony_ci		rmh.cmd[2] = rate;
4048c2ecf20Sopenharmony_ci		rmh.cmd_len = 3;
4058c2ecf20Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
4068c2ecf20Sopenharmony_ci		if (err)
4078c2ecf20Sopenharmony_ci			return err;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int pcxhr_sub_get_external_clock(struct pcxhr_mgr *mgr,
4148c2ecf20Sopenharmony_ci					enum pcxhr_clock_type clock_type,
4158c2ecf20Sopenharmony_ci					int *sample_rate)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
4188c2ecf20Sopenharmony_ci	unsigned char reg;
4198c2ecf20Sopenharmony_ci	int err, rate;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	switch (clock_type) {
4228c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_WORD_CLOCK:
4238c2ecf20Sopenharmony_ci		reg = REG_STATUS_WORD_CLOCK;
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_SYNC:
4268c2ecf20Sopenharmony_ci		reg = REG_STATUS_AES_SYNC;
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_1:
4298c2ecf20Sopenharmony_ci		reg = REG_STATUS_AES_1;
4308c2ecf20Sopenharmony_ci		break;
4318c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_2:
4328c2ecf20Sopenharmony_ci		reg = REG_STATUS_AES_2;
4338c2ecf20Sopenharmony_ci		break;
4348c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_3:
4358c2ecf20Sopenharmony_ci		reg = REG_STATUS_AES_3;
4368c2ecf20Sopenharmony_ci		break;
4378c2ecf20Sopenharmony_ci	case PCXHR_CLOCK_TYPE_AES_4:
4388c2ecf20Sopenharmony_ci		reg = REG_STATUS_AES_4;
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	default:
4418c2ecf20Sopenharmony_ci		return -EINVAL;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
4448c2ecf20Sopenharmony_ci	rmh.cmd_len = 2;
4458c2ecf20Sopenharmony_ci	rmh.cmd[0] |= IO_NUM_REG_STATUS;
4468c2ecf20Sopenharmony_ci	if (mgr->last_reg_stat != reg) {
4478c2ecf20Sopenharmony_ci		rmh.cmd[1]  = reg;
4488c2ecf20Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
4498c2ecf20Sopenharmony_ci		if (err)
4508c2ecf20Sopenharmony_ci			return err;
4518c2ecf20Sopenharmony_ci		udelay(100);	/* wait minimum 2 sample_frames at 32kHz ! */
4528c2ecf20Sopenharmony_ci		mgr->last_reg_stat = reg;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci	rmh.cmd[1]  = REG_STATUS_CURRENT;
4558c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
4568c2ecf20Sopenharmony_ci	if (err)
4578c2ecf20Sopenharmony_ci		return err;
4588c2ecf20Sopenharmony_ci	switch (rmh.stat[1] & 0x0f) {
4598c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_32000 :	rate = 32000; break;
4608c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_44100 :	rate = 44100; break;
4618c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_48000 :	rate = 48000; break;
4628c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_64000 :	rate = 64000; break;
4638c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_88200 :	rate = 88200; break;
4648c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_96000 :	rate = 96000; break;
4658c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_128000 :	rate = 128000; break;
4668c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_176400 :	rate = 176400; break;
4678c2ecf20Sopenharmony_ci	case REG_STATUS_SYNC_192000 :	rate = 192000; break;
4688c2ecf20Sopenharmony_ci	default: rate = 0;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "External clock is at %d Hz\n", rate);
4718c2ecf20Sopenharmony_ci	*sample_rate = rate;
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciint pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
4778c2ecf20Sopenharmony_ci			     enum pcxhr_clock_type clock_type,
4788c2ecf20Sopenharmony_ci			     int *sample_rate)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	if (mgr->is_hr_stereo)
4818c2ecf20Sopenharmony_ci		return hr222_get_external_clock(mgr, clock_type,
4828c2ecf20Sopenharmony_ci						sample_rate);
4838c2ecf20Sopenharmony_ci	else
4848c2ecf20Sopenharmony_ci		return pcxhr_sub_get_external_clock(mgr, clock_type,
4858c2ecf20Sopenharmony_ci						    sample_rate);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/*
4898c2ecf20Sopenharmony_ci *  start or stop playback/capture substream
4908c2ecf20Sopenharmony_ci */
4918c2ecf20Sopenharmony_cistatic int pcxhr_set_stream_state(struct snd_pcxhr *chip,
4928c2ecf20Sopenharmony_ci				  struct pcxhr_stream *stream)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	int err;
4958c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
4968c2ecf20Sopenharmony_ci	int stream_mask, start;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (stream->status == PCXHR_STREAM_STATUS_SCHEDULE_RUN)
4998c2ecf20Sopenharmony_ci		start = 1;
5008c2ecf20Sopenharmony_ci	else {
5018c2ecf20Sopenharmony_ci		if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
5028c2ecf20Sopenharmony_ci			dev_err(chip->card->dev,
5038c2ecf20Sopenharmony_ci				"pcxhr_set_stream_state CANNOT be stopped\n");
5048c2ecf20Sopenharmony_ci			return -EINVAL;
5058c2ecf20Sopenharmony_ci		}
5068c2ecf20Sopenharmony_ci		start = 0;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	if (!stream->substream)
5098c2ecf20Sopenharmony_ci		return -EINVAL;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	stream->timer_abs_periods = 0;
5128c2ecf20Sopenharmony_ci	stream->timer_period_frag = 0;	/* reset theoretical stream pos */
5138c2ecf20Sopenharmony_ci	stream->timer_buf_periods = 0;
5148c2ecf20Sopenharmony_ci	stream->timer_is_synced = 0;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	stream_mask =
5178c2ecf20Sopenharmony_ci	  stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, start ? CMD_START_STREAM : CMD_STOP_STREAM);
5208c2ecf20Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
5218c2ecf20Sopenharmony_ci				  stream->pipe->first_audio, 0, stream_mask);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	chip = snd_pcm_substream_chip(stream->substream);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
5268c2ecf20Sopenharmony_ci	if (err)
5278c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
5288c2ecf20Sopenharmony_ci			"ERROR pcxhr_set_stream_state err=%x;\n", err);
5298c2ecf20Sopenharmony_ci	stream->status =
5308c2ecf20Sopenharmony_ci	  start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
5318c2ecf20Sopenharmony_ci	return err;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci#define HEADER_FMT_BASE_LIN		0xfed00000
5358c2ecf20Sopenharmony_ci#define HEADER_FMT_BASE_FLOAT		0xfad00000
5368c2ecf20Sopenharmony_ci#define HEADER_FMT_INTEL		0x00008000
5378c2ecf20Sopenharmony_ci#define HEADER_FMT_24BITS		0x00004000
5388c2ecf20Sopenharmony_ci#define HEADER_FMT_16BITS		0x00002000
5398c2ecf20Sopenharmony_ci#define HEADER_FMT_UPTO11		0x00000200
5408c2ecf20Sopenharmony_ci#define HEADER_FMT_UPTO32		0x00000100
5418c2ecf20Sopenharmony_ci#define HEADER_FMT_MONO			0x00000080
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int pcxhr_set_format(struct pcxhr_stream *stream)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	int err, is_capture, sample_rate, stream_num;
5468c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip;
5478c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
5488c2ecf20Sopenharmony_ci	unsigned int header;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	chip = snd_pcm_substream_chip(stream->substream);
5518c2ecf20Sopenharmony_ci	switch (stream->format) {
5528c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_U8:
5538c2ecf20Sopenharmony_ci		header = HEADER_FMT_BASE_LIN;
5548c2ecf20Sopenharmony_ci		break;
5558c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
5568c2ecf20Sopenharmony_ci		header = HEADER_FMT_BASE_LIN |
5578c2ecf20Sopenharmony_ci			 HEADER_FMT_16BITS | HEADER_FMT_INTEL;
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
5608c2ecf20Sopenharmony_ci		header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS;
5618c2ecf20Sopenharmony_ci		break;
5628c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3LE:
5638c2ecf20Sopenharmony_ci		header = HEADER_FMT_BASE_LIN |
5648c2ecf20Sopenharmony_ci			 HEADER_FMT_24BITS | HEADER_FMT_INTEL;
5658c2ecf20Sopenharmony_ci		break;
5668c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3BE:
5678c2ecf20Sopenharmony_ci		header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS;
5688c2ecf20Sopenharmony_ci		break;
5698c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_FLOAT_LE:
5708c2ecf20Sopenharmony_ci		header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;
5718c2ecf20Sopenharmony_ci		break;
5728c2ecf20Sopenharmony_ci	default:
5738c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
5748c2ecf20Sopenharmony_ci			"error pcxhr_set_format() : unknown format\n");
5758c2ecf20Sopenharmony_ci		return -EINVAL;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	sample_rate = chip->mgr->sample_rate;
5798c2ecf20Sopenharmony_ci	if (sample_rate <= 32000 && sample_rate !=0) {
5808c2ecf20Sopenharmony_ci		if (sample_rate <= 11025)
5818c2ecf20Sopenharmony_ci			header |= HEADER_FMT_UPTO11;
5828c2ecf20Sopenharmony_ci		else
5838c2ecf20Sopenharmony_ci			header |= HEADER_FMT_UPTO32;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci	if (stream->channels == 1)
5868c2ecf20Sopenharmony_ci		header |= HEADER_FMT_MONO;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	is_capture = stream->pipe->is_capture;
5898c2ecf20Sopenharmony_ci	stream_num = is_capture ? 0 : stream->substream->number;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, is_capture ?
5928c2ecf20Sopenharmony_ci		       CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
5938c2ecf20Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
5948c2ecf20Sopenharmony_ci				  stream_num, 0);
5958c2ecf20Sopenharmony_ci	if (is_capture) {
5968c2ecf20Sopenharmony_ci		/* bug with old dsp versions: */
5978c2ecf20Sopenharmony_ci		/* bit 12 also sets the format of the playback stream */
5988c2ecf20Sopenharmony_ci		if (DSP_EXT_CMD_SET(chip->mgr))
5998c2ecf20Sopenharmony_ci			rmh.cmd[0] |= 1<<10;
6008c2ecf20Sopenharmony_ci		else
6018c2ecf20Sopenharmony_ci			rmh.cmd[0] |= 1<<12;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci	rmh.cmd[1] = 0;
6048c2ecf20Sopenharmony_ci	rmh.cmd_len = 2;
6058c2ecf20Sopenharmony_ci	if (DSP_EXT_CMD_SET(chip->mgr)) {
6068c2ecf20Sopenharmony_ci		/* add channels and set bit 19 if channels>2 */
6078c2ecf20Sopenharmony_ci		rmh.cmd[1] = stream->channels;
6088c2ecf20Sopenharmony_ci		if (!is_capture) {
6098c2ecf20Sopenharmony_ci			/* playback : add channel mask to command */
6108c2ecf20Sopenharmony_ci			rmh.cmd[2] = (stream->channels == 1) ? 0x01 : 0x03;
6118c2ecf20Sopenharmony_ci			rmh.cmd_len = 3;
6128c2ecf20Sopenharmony_ci		}
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci	rmh.cmd[rmh.cmd_len++] = header >> 8;
6158c2ecf20Sopenharmony_ci	rmh.cmd[rmh.cmd_len++] = (header & 0xff) << 16;
6168c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
6178c2ecf20Sopenharmony_ci	if (err)
6188c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
6198c2ecf20Sopenharmony_ci			"ERROR pcxhr_set_format err=%x;\n", err);
6208c2ecf20Sopenharmony_ci	return err;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	int err, is_capture, stream_num;
6268c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
6278c2ecf20Sopenharmony_ci	struct snd_pcm_substream *subs = stream->substream;
6288c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);
6318c2ecf20Sopenharmony_ci	stream_num = is_capture ? 0 : subs->number;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev,
6348c2ecf20Sopenharmony_ci		"pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n",
6358c2ecf20Sopenharmony_ci		is_capture ? 'c' : 'p',
6368c2ecf20Sopenharmony_ci		chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
6378c2ecf20Sopenharmony_ci		subs->runtime->dma_bytes, subs->number);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
6408c2ecf20Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
6418c2ecf20Sopenharmony_ci				  stream_num, 0);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* max buffer size is 2 MByte */
6448c2ecf20Sopenharmony_ci	snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
6458c2ecf20Sopenharmony_ci	/* size in bits */
6468c2ecf20Sopenharmony_ci	rmh.cmd[1] = subs->runtime->dma_bytes * 8;
6478c2ecf20Sopenharmony_ci	/* most significant byte */
6488c2ecf20Sopenharmony_ci	rmh.cmd[2] = subs->runtime->dma_addr >> 24;
6498c2ecf20Sopenharmony_ci	/* this is a circular buffer */
6508c2ecf20Sopenharmony_ci	rmh.cmd[2] |= 1<<19;
6518c2ecf20Sopenharmony_ci	/* least 3 significant bytes */
6528c2ecf20Sopenharmony_ci	rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;
6538c2ecf20Sopenharmony_ci	rmh.cmd_len = 4;
6548c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
6558c2ecf20Sopenharmony_ci	if (err)
6568c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
6578c2ecf20Sopenharmony_ci			   "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
6588c2ecf20Sopenharmony_ci	return err;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci#if 0
6638c2ecf20Sopenharmony_cistatic int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
6648c2ecf20Sopenharmony_ci				   snd_pcm_uframes_t *sample_count)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
6678c2ecf20Sopenharmony_ci	int err;
6688c2ecf20Sopenharmony_ci	pcxhr_t *chip = snd_pcm_substream_chip(stream->substream);
6698c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_PIPE_SAMPLE_COUNT);
6708c2ecf20Sopenharmony_ci	pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture, 0, 0,
6718c2ecf20Sopenharmony_ci				  1<<stream->pipe->first_audio);
6728c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(chip->mgr, &rmh);
6738c2ecf20Sopenharmony_ci	if (err == 0) {
6748c2ecf20Sopenharmony_ci		*sample_count = ((snd_pcm_uframes_t)rmh.stat[0]) << 24;
6758c2ecf20Sopenharmony_ci		*sample_count += (snd_pcm_uframes_t)rmh.stat[1];
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "PIPE_SAMPLE_COUNT = %lx\n", *sample_count);
6788c2ecf20Sopenharmony_ci	return err;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci#endif
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic inline int pcxhr_stream_scheduled_get_pipe(struct pcxhr_stream *stream,
6838c2ecf20Sopenharmony_ci						  struct pcxhr_pipe **pipe)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	if (stream->status == PCXHR_STREAM_STATUS_SCHEDULE_RUN) {
6868c2ecf20Sopenharmony_ci		*pipe = stream->pipe;
6878c2ecf20Sopenharmony_ci		return 1;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci	return 0;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	int i, j, err;
6958c2ecf20Sopenharmony_ci	struct pcxhr_pipe *pipe;
6968c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip;
6978c2ecf20Sopenharmony_ci	int capture_mask = 0;
6988c2ecf20Sopenharmony_ci	int playback_mask = 0;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE
7018c2ecf20Sopenharmony_ci	ktime_t start_time, stop_time, diff_time;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	start_time = ktime_get();
7048c2ecf20Sopenharmony_ci#endif
7058c2ecf20Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* check the pipes concerned and build pipe_array */
7088c2ecf20Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
7098c2ecf20Sopenharmony_ci		chip = mgr->chip[i];
7108c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
7118c2ecf20Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(&chip->capture_stream[j], &pipe))
7128c2ecf20Sopenharmony_ci				capture_mask |= (1 << pipe->first_audio);
7138c2ecf20Sopenharmony_ci		}
7148c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
7158c2ecf20Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(&chip->playback_stream[j], &pipe)) {
7168c2ecf20Sopenharmony_ci				playback_mask |= (1 << pipe->first_audio);
7178c2ecf20Sopenharmony_ci				break;	/* add only once, as all playback
7188c2ecf20Sopenharmony_ci					 * streams of one chip use the same pipe
7198c2ecf20Sopenharmony_ci					 */
7208c2ecf20Sopenharmony_ci			}
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci	if (capture_mask == 0 && playback_mask == 0) {
7248c2ecf20Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
7258c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : no pipes\n");
7268c2ecf20Sopenharmony_ci		return;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "pcxhr_start_linked_stream : "
7308c2ecf20Sopenharmony_ci		    "playback_mask=%x capture_mask=%x\n",
7318c2ecf20Sopenharmony_ci		    playback_mask, capture_mask);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* synchronous stop of all the pipes concerned */
7348c2ecf20Sopenharmony_ci	err = pcxhr_set_pipe_state(mgr,  playback_mask, capture_mask, 0);
7358c2ecf20Sopenharmony_ci	if (err) {
7368c2ecf20Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
7378c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
7388c2ecf20Sopenharmony_ci			   "error stop pipes (P%x C%x)\n",
7398c2ecf20Sopenharmony_ci			   playback_mask, capture_mask);
7408c2ecf20Sopenharmony_ci		return;
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	/* the dsp lost format and buffer info with the stop pipe */
7448c2ecf20Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
7458c2ecf20Sopenharmony_ci		struct pcxhr_stream *stream;
7468c2ecf20Sopenharmony_ci		chip = mgr->chip[i];
7478c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
7488c2ecf20Sopenharmony_ci			stream = &chip->capture_stream[j];
7498c2ecf20Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) {
7508c2ecf20Sopenharmony_ci				err = pcxhr_set_format(stream);
7518c2ecf20Sopenharmony_ci				err = pcxhr_update_r_buffer(stream);
7528c2ecf20Sopenharmony_ci			}
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
7558c2ecf20Sopenharmony_ci			stream = &chip->playback_stream[j];
7568c2ecf20Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) {
7578c2ecf20Sopenharmony_ci				err = pcxhr_set_format(stream);
7588c2ecf20Sopenharmony_ci				err = pcxhr_update_r_buffer(stream);
7598c2ecf20Sopenharmony_ci			}
7608c2ecf20Sopenharmony_ci		}
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci	/* start all the streams */
7638c2ecf20Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
7648c2ecf20Sopenharmony_ci		struct pcxhr_stream *stream;
7658c2ecf20Sopenharmony_ci		chip = mgr->chip[i];
7668c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_capt; j++) {
7678c2ecf20Sopenharmony_ci			stream = &chip->capture_stream[j];
7688c2ecf20Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe))
7698c2ecf20Sopenharmony_ci				err = pcxhr_set_stream_state(chip, stream);
7708c2ecf20Sopenharmony_ci		}
7718c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
7728c2ecf20Sopenharmony_ci			stream = &chip->playback_stream[j];
7738c2ecf20Sopenharmony_ci			if (pcxhr_stream_scheduled_get_pipe(stream, &pipe))
7748c2ecf20Sopenharmony_ci				err = pcxhr_set_stream_state(chip, stream);
7758c2ecf20Sopenharmony_ci		}
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	/* synchronous start of all the pipes concerned */
7798c2ecf20Sopenharmony_ci	err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
7808c2ecf20Sopenharmony_ci	if (err) {
7818c2ecf20Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
7828c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "pcxhr_start_linked_stream : "
7838c2ecf20Sopenharmony_ci			   "error start pipes (P%x C%x)\n",
7848c2ecf20Sopenharmony_ci			   playback_mask, capture_mask);
7858c2ecf20Sopenharmony_ci		return;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* put the streams into the running state now
7898c2ecf20Sopenharmony_ci	 * (increment pointer by interrupt)
7908c2ecf20Sopenharmony_ci	 */
7918c2ecf20Sopenharmony_ci	mutex_lock(&mgr->lock);
7928c2ecf20Sopenharmony_ci	for ( i =0; i < mgr->num_cards; i++) {
7938c2ecf20Sopenharmony_ci		struct pcxhr_stream *stream;
7948c2ecf20Sopenharmony_ci		chip = mgr->chip[i];
7958c2ecf20Sopenharmony_ci		for(j = 0; j < chip->nb_streams_capt; j++) {
7968c2ecf20Sopenharmony_ci			stream = &chip->capture_stream[j];
7978c2ecf20Sopenharmony_ci			if(stream->status == PCXHR_STREAM_STATUS_STARTED)
7988c2ecf20Sopenharmony_ci				stream->status = PCXHR_STREAM_STATUS_RUNNING;
7998c2ecf20Sopenharmony_ci		}
8008c2ecf20Sopenharmony_ci		for (j = 0; j < chip->nb_streams_play; j++) {
8018c2ecf20Sopenharmony_ci			stream = &chip->playback_stream[j];
8028c2ecf20Sopenharmony_ci			if (stream->status == PCXHR_STREAM_STATUS_STARTED) {
8038c2ecf20Sopenharmony_ci				/* playback will already have advanced ! */
8048c2ecf20Sopenharmony_ci				stream->timer_period_frag += mgr->granularity;
8058c2ecf20Sopenharmony_ci				stream->status = PCXHR_STREAM_STATUS_RUNNING;
8068c2ecf20Sopenharmony_ci			}
8078c2ecf20Sopenharmony_ci		}
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci	mutex_unlock(&mgr->lock);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG_VERBOSE
8148c2ecf20Sopenharmony_ci	stop_time = ktime_get();
8158c2ecf20Sopenharmony_ci	diff_time = ktime_sub(stop_time, start_time);
8168c2ecf20Sopenharmony_ci	dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n",
8178c2ecf20Sopenharmony_ci		    (long)(ktime_to_ns(diff_time)), err);
8188c2ecf20Sopenharmony_ci#endif
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/*
8238c2ecf20Sopenharmony_ci *  trigger callback
8248c2ecf20Sopenharmony_ci */
8258c2ecf20Sopenharmony_cistatic int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	struct pcxhr_stream *stream;
8288c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
8298c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	switch (cmd) {
8328c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
8338c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_START\n");
8348c2ecf20Sopenharmony_ci		if (snd_pcm_stream_linked(subs)) {
8358c2ecf20Sopenharmony_ci			snd_pcm_group_for_each_entry(s, subs) {
8368c2ecf20Sopenharmony_ci				if (snd_pcm_substream_chip(s) != chip)
8378c2ecf20Sopenharmony_ci					continue;
8388c2ecf20Sopenharmony_ci				stream = s->runtime->private_data;
8398c2ecf20Sopenharmony_ci				stream->status =
8408c2ecf20Sopenharmony_ci					PCXHR_STREAM_STATUS_SCHEDULE_RUN;
8418c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, subs);
8428c2ecf20Sopenharmony_ci			}
8438c2ecf20Sopenharmony_ci			pcxhr_start_linked_stream(chip->mgr);
8448c2ecf20Sopenharmony_ci		} else {
8458c2ecf20Sopenharmony_ci			stream = subs->runtime->private_data;
8468c2ecf20Sopenharmony_ci			dev_dbg(chip->card->dev, "Only one Substream %c %d\n",
8478c2ecf20Sopenharmony_ci				    stream->pipe->is_capture ? 'C' : 'P',
8488c2ecf20Sopenharmony_ci				    stream->pipe->first_audio);
8498c2ecf20Sopenharmony_ci			if (pcxhr_set_format(stream))
8508c2ecf20Sopenharmony_ci				return -EINVAL;
8518c2ecf20Sopenharmony_ci			if (pcxhr_update_r_buffer(stream))
8528c2ecf20Sopenharmony_ci				return -EINVAL;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci			stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN;
8558c2ecf20Sopenharmony_ci			if (pcxhr_set_stream_state(chip, stream))
8568c2ecf20Sopenharmony_ci				return -EINVAL;
8578c2ecf20Sopenharmony_ci			stream->status = PCXHR_STREAM_STATUS_RUNNING;
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci		break;
8608c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
8618c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_STOP\n");
8628c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, subs) {
8638c2ecf20Sopenharmony_ci			stream = s->runtime->private_data;
8648c2ecf20Sopenharmony_ci			stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP;
8658c2ecf20Sopenharmony_ci			if (pcxhr_set_stream_state(chip, stream))
8668c2ecf20Sopenharmony_ci				return -EINVAL;
8678c2ecf20Sopenharmony_ci			snd_pcm_trigger_done(s, subs);
8688c2ecf20Sopenharmony_ci		}
8698c2ecf20Sopenharmony_ci		break;
8708c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
8718c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
8728c2ecf20Sopenharmony_ci		/* TODO */
8738c2ecf20Sopenharmony_ci	default:
8748c2ecf20Sopenharmony_ci		return -EINVAL;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistatic int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
8838c2ecf20Sopenharmony_ci	int err;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_SET_TIMER_INTERRUPT);
8868c2ecf20Sopenharmony_ci	if (start) {
8878c2ecf20Sopenharmony_ci		/* last dsp time invalid */
8888c2ecf20Sopenharmony_ci		mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
8898c2ecf20Sopenharmony_ci		rmh.cmd[0] |= mgr->granularity;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
8928c2ecf20Sopenharmony_ci	if (err < 0)
8938c2ecf20Sopenharmony_ci		dev_err(&mgr->pci->dev, "error pcxhr_hardware_timer err(%x)\n",
8948c2ecf20Sopenharmony_ci			   err);
8958c2ecf20Sopenharmony_ci	return err;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/*
8998c2ecf20Sopenharmony_ci *  prepare callback for all pcms
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic int pcxhr_prepare(struct snd_pcm_substream *subs)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
9048c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
9058c2ecf20Sopenharmony_ci	int err = 0;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev,
9088c2ecf20Sopenharmony_ci		"pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n",
9098c2ecf20Sopenharmony_ci		    subs->runtime->period_size, subs->runtime->periods,
9108c2ecf20Sopenharmony_ci		    subs->runtime->buffer_size);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	do {
9158c2ecf20Sopenharmony_ci		/* only the first stream can choose the sample rate */
9168c2ecf20Sopenharmony_ci		/* set the clock only once (first stream) */
9178c2ecf20Sopenharmony_ci		if (mgr->sample_rate != subs->runtime->rate) {
9188c2ecf20Sopenharmony_ci			err = pcxhr_set_clock(mgr, subs->runtime->rate);
9198c2ecf20Sopenharmony_ci			if (err)
9208c2ecf20Sopenharmony_ci				break;
9218c2ecf20Sopenharmony_ci			if (mgr->sample_rate == 0)
9228c2ecf20Sopenharmony_ci				/* start the DSP-timer */
9238c2ecf20Sopenharmony_ci				err = pcxhr_hardware_timer(mgr, 1);
9248c2ecf20Sopenharmony_ci			mgr->sample_rate = subs->runtime->rate;
9258c2ecf20Sopenharmony_ci		}
9268c2ecf20Sopenharmony_ci	} while(0);	/* do only once (so we can use break instead of goto) */
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	return err;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci/*
9358c2ecf20Sopenharmony_ci *  HW_PARAMS callback for all pcms
9368c2ecf20Sopenharmony_ci */
9378c2ecf20Sopenharmony_cistatic int pcxhr_hw_params(struct snd_pcm_substream *subs,
9388c2ecf20Sopenharmony_ci			   struct snd_pcm_hw_params *hw)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
9418c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
9428c2ecf20Sopenharmony_ci	struct pcxhr_stream *stream = subs->runtime->private_data;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* set up channels */
9478c2ecf20Sopenharmony_ci	stream->channels = params_channels(hw);
9488c2ecf20Sopenharmony_ci	/* set up format for the stream */
9498c2ecf20Sopenharmony_ci	stream->format = params_format(hw);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	return 0;
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci/*
9588c2ecf20Sopenharmony_ci *  CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
9598c2ecf20Sopenharmony_ci */
9608c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware pcxhr_caps =
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	.info             = (SNDRV_PCM_INFO_MMAP |
9638c2ecf20Sopenharmony_ci			     SNDRV_PCM_INFO_INTERLEAVED |
9648c2ecf20Sopenharmony_ci			     SNDRV_PCM_INFO_MMAP_VALID |
9658c2ecf20Sopenharmony_ci			     SNDRV_PCM_INFO_SYNC_START),
9668c2ecf20Sopenharmony_ci	.formats	  = (SNDRV_PCM_FMTBIT_U8 |
9678c2ecf20Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S16_LE |
9688c2ecf20Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S16_BE |
9698c2ecf20Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_3LE |
9708c2ecf20Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_3BE |
9718c2ecf20Sopenharmony_ci			     SNDRV_PCM_FMTBIT_FLOAT_LE),
9728c2ecf20Sopenharmony_ci	.rates            = (SNDRV_PCM_RATE_CONTINUOUS |
9738c2ecf20Sopenharmony_ci			     SNDRV_PCM_RATE_8000_192000),
9748c2ecf20Sopenharmony_ci	.rate_min         = 8000,
9758c2ecf20Sopenharmony_ci	.rate_max         = 192000,
9768c2ecf20Sopenharmony_ci	.channels_min     = 1,
9778c2ecf20Sopenharmony_ci	.channels_max     = 2,
9788c2ecf20Sopenharmony_ci	.buffer_bytes_max = (32*1024),
9798c2ecf20Sopenharmony_ci	/* 1 byte == 1 frame U8 mono (PCXHR_GRANULARITY is frames!) */
9808c2ecf20Sopenharmony_ci	.period_bytes_min = (2*PCXHR_GRANULARITY),
9818c2ecf20Sopenharmony_ci	.period_bytes_max = (16*1024),
9828c2ecf20Sopenharmony_ci	.periods_min      = 2,
9838c2ecf20Sopenharmony_ci	.periods_max      = (32*1024/PCXHR_GRANULARITY),
9848c2ecf20Sopenharmony_ci};
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic int pcxhr_open(struct snd_pcm_substream *subs)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	struct snd_pcxhr       *chip = snd_pcm_substream_chip(subs);
9908c2ecf20Sopenharmony_ci	struct pcxhr_mgr       *mgr = chip->mgr;
9918c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
9928c2ecf20Sopenharmony_ci	struct pcxhr_stream    *stream;
9938c2ecf20Sopenharmony_ci	int err;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* copy the struct snd_pcm_hardware struct */
9988c2ecf20Sopenharmony_ci	runtime->hw = pcxhr_caps;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
10018c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "pcxhr_open playback chip%d subs%d\n",
10028c2ecf20Sopenharmony_ci			    chip->chip_idx, subs->number);
10038c2ecf20Sopenharmony_ci		stream = &chip->playback_stream[subs->number];
10048c2ecf20Sopenharmony_ci	} else {
10058c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "pcxhr_open capture chip%d subs%d\n",
10068c2ecf20Sopenharmony_ci			    chip->chip_idx, subs->number);
10078c2ecf20Sopenharmony_ci		if (mgr->mono_capture)
10088c2ecf20Sopenharmony_ci			runtime->hw.channels_max = 1;
10098c2ecf20Sopenharmony_ci		else
10108c2ecf20Sopenharmony_ci			runtime->hw.channels_min = 2;
10118c2ecf20Sopenharmony_ci		stream = &chip->capture_stream[subs->number];
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci	if (stream->status != PCXHR_STREAM_STATUS_FREE){
10148c2ecf20Sopenharmony_ci		/* streams in use */
10158c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "pcxhr_open chip%d subs%d in use\n",
10168c2ecf20Sopenharmony_ci			   chip->chip_idx, subs->number);
10178c2ecf20Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
10188c2ecf20Sopenharmony_ci		return -EBUSY;
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	/* float format support is in some cases buggy on stereo cards */
10228c2ecf20Sopenharmony_ci	if (mgr->is_hr_stereo)
10238c2ecf20Sopenharmony_ci		runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_FLOAT_LE;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	/* buffer-size should better be multiple of period-size */
10268c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
10278c2ecf20Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
10288c2ecf20Sopenharmony_ci	if (err < 0) {
10298c2ecf20Sopenharmony_ci		mutex_unlock(&mgr->setup_mutex);
10308c2ecf20Sopenharmony_ci		return err;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* if a sample rate is already used or fixed by external clock,
10348c2ecf20Sopenharmony_ci	 * the stream cannot change
10358c2ecf20Sopenharmony_ci	 */
10368c2ecf20Sopenharmony_ci	if (mgr->sample_rate)
10378c2ecf20Sopenharmony_ci		runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
10388c2ecf20Sopenharmony_ci	else {
10398c2ecf20Sopenharmony_ci		if (mgr->use_clock_type != PCXHR_CLOCK_TYPE_INTERNAL) {
10408c2ecf20Sopenharmony_ci			int external_rate;
10418c2ecf20Sopenharmony_ci			if (pcxhr_get_external_clock(mgr, mgr->use_clock_type,
10428c2ecf20Sopenharmony_ci						     &external_rate) ||
10438c2ecf20Sopenharmony_ci			    external_rate == 0) {
10448c2ecf20Sopenharmony_ci				/* cannot detect the external clock rate */
10458c2ecf20Sopenharmony_ci				mutex_unlock(&mgr->setup_mutex);
10468c2ecf20Sopenharmony_ci				return -EBUSY;
10478c2ecf20Sopenharmony_ci			}
10488c2ecf20Sopenharmony_ci			runtime->hw.rate_min = external_rate;
10498c2ecf20Sopenharmony_ci			runtime->hw.rate_max = external_rate;
10508c2ecf20Sopenharmony_ci		}
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	stream->status      = PCXHR_STREAM_STATUS_OPEN;
10548c2ecf20Sopenharmony_ci	stream->substream   = subs;
10558c2ecf20Sopenharmony_ci	stream->channels    = 0; /* not configured yet */
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	runtime->private_data = stream;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	/* better get a divisor of granularity values (96 or 192) */
10608c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0,
10618c2ecf20Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
10628c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0,
10638c2ecf20Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
10648c2ecf20Sopenharmony_ci	snd_pcm_set_sync(subs);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	mgr->ref_count_rate++;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
10698c2ecf20Sopenharmony_ci	return 0;
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_cistatic int pcxhr_close(struct snd_pcm_substream *subs)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
10768c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
10778c2ecf20Sopenharmony_ci	struct pcxhr_stream *stream = subs->runtime->private_data;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	mutex_lock(&mgr->setup_mutex);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "pcxhr_close chip%d subs%d\n",
10828c2ecf20Sopenharmony_ci		    chip->chip_idx, subs->number);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	/* sample rate released */
10858c2ecf20Sopenharmony_ci	if (--mgr->ref_count_rate == 0) {
10868c2ecf20Sopenharmony_ci		mgr->sample_rate = 0;	/* the sample rate is no more locked */
10878c2ecf20Sopenharmony_ci		pcxhr_hardware_timer(mgr, 0);	/* stop the DSP-timer */
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	stream->status    = PCXHR_STREAM_STATUS_FREE;
10918c2ecf20Sopenharmony_ci	stream->substream = NULL;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	mutex_unlock(&mgr->setup_mutex);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	return 0;
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	u_int32_t timer_period_frag;
11028c2ecf20Sopenharmony_ci	int timer_buf_periods;
11038c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
11048c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->runtime;
11058c2ecf20Sopenharmony_ci	struct pcxhr_stream *stream  = runtime->private_data;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	mutex_lock(&chip->mgr->lock);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	/* get the period fragment and the nb of periods in the buffer */
11108c2ecf20Sopenharmony_ci	timer_period_frag = stream->timer_period_frag;
11118c2ecf20Sopenharmony_ci	timer_buf_periods = stream->timer_buf_periods;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mgr->lock);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	return (snd_pcm_uframes_t)((timer_buf_periods * runtime->period_size) +
11168c2ecf20Sopenharmony_ci				   timer_period_frag);
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops pcxhr_ops = {
11218c2ecf20Sopenharmony_ci	.open      = pcxhr_open,
11228c2ecf20Sopenharmony_ci	.close     = pcxhr_close,
11238c2ecf20Sopenharmony_ci	.prepare   = pcxhr_prepare,
11248c2ecf20Sopenharmony_ci	.hw_params = pcxhr_hw_params,
11258c2ecf20Sopenharmony_ci	.trigger   = pcxhr_trigger,
11268c2ecf20Sopenharmony_ci	.pointer   = pcxhr_stream_pointer,
11278c2ecf20Sopenharmony_ci};
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci/*
11308c2ecf20Sopenharmony_ci */
11318c2ecf20Sopenharmony_ciint pcxhr_create_pcm(struct snd_pcxhr *chip)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	int err;
11348c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
11358c2ecf20Sopenharmony_ci	char name[32];
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "pcxhr %d", chip->chip_idx);
11388c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(chip->card, name, 0,
11398c2ecf20Sopenharmony_ci			       chip->nb_streams_play,
11408c2ecf20Sopenharmony_ci			       chip->nb_streams_capt, &pcm)) < 0) {
11418c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "cannot create pcm %s\n", name);
11428c2ecf20Sopenharmony_ci		return err;
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci	pcm->private_data = chip;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	if (chip->nb_streams_play)
11478c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcxhr_ops);
11488c2ecf20Sopenharmony_ci	if (chip->nb_streams_capt)
11498c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcxhr_ops);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
11528c2ecf20Sopenharmony_ci	pcm->nonatomic = true;
11538c2ecf20Sopenharmony_ci	strcpy(pcm->name, name);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
11568c2ecf20Sopenharmony_ci				       &chip->mgr->pci->dev,
11578c2ecf20Sopenharmony_ci				       32*1024, 32*1024);
11588c2ecf20Sopenharmony_ci	chip->pcm = pcm;
11598c2ecf20Sopenharmony_ci	return 0;
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic int pcxhr_chip_free(struct snd_pcxhr *chip)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	kfree(chip);
11658c2ecf20Sopenharmony_ci	return 0;
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_cistatic int pcxhr_chip_dev_free(struct snd_device *device)
11698c2ecf20Sopenharmony_ci{
11708c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = device->device_data;
11718c2ecf20Sopenharmony_ci	return pcxhr_chip_free(chip);
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci/*
11768c2ecf20Sopenharmony_ci */
11778c2ecf20Sopenharmony_cistatic int pcxhr_create(struct pcxhr_mgr *mgr,
11788c2ecf20Sopenharmony_ci			struct snd_card *card, int idx)
11798c2ecf20Sopenharmony_ci{
11808c2ecf20Sopenharmony_ci	int err;
11818c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip;
11828c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
11838c2ecf20Sopenharmony_ci		.dev_free = pcxhr_chip_dev_free,
11848c2ecf20Sopenharmony_ci	};
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
11878c2ecf20Sopenharmony_ci	if (!chip)
11888c2ecf20Sopenharmony_ci		return -ENOMEM;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	chip->card = card;
11918c2ecf20Sopenharmony_ci	chip->chip_idx = idx;
11928c2ecf20Sopenharmony_ci	chip->mgr = mgr;
11938c2ecf20Sopenharmony_ci	card->sync_irq = mgr->irq;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	if (idx < mgr->playback_chips)
11968c2ecf20Sopenharmony_ci		/* stereo or mono streams */
11978c2ecf20Sopenharmony_ci		chip->nb_streams_play = PCXHR_PLAYBACK_STREAMS;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (idx < mgr->capture_chips) {
12008c2ecf20Sopenharmony_ci		if (mgr->mono_capture)
12018c2ecf20Sopenharmony_ci			chip->nb_streams_capt = 2;	/* 2 mono streams */
12028c2ecf20Sopenharmony_ci		else
12038c2ecf20Sopenharmony_ci			chip->nb_streams_capt = 1;	/* or 1 stereo stream */
12048c2ecf20Sopenharmony_ci	}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
12078c2ecf20Sopenharmony_ci		pcxhr_chip_free(chip);
12088c2ecf20Sopenharmony_ci		return err;
12098c2ecf20Sopenharmony_ci	}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	mgr->chip[idx] = chip;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	return 0;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci/* proc interface */
12178c2ecf20Sopenharmony_cistatic void pcxhr_proc_info(struct snd_info_entry *entry,
12188c2ecf20Sopenharmony_ci			    struct snd_info_buffer *buffer)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
12218c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\n%s\n", mgr->name);
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	/* stats available when embedded DSP is running */
12268c2ecf20Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
12278c2ecf20Sopenharmony_ci		struct pcxhr_rmh rmh;
12288c2ecf20Sopenharmony_ci		short ver_maj = (mgr->dsp_version >> 16) & 0xff;
12298c2ecf20Sopenharmony_ci		short ver_min = (mgr->dsp_version >> 8) & 0xff;
12308c2ecf20Sopenharmony_ci		short ver_build = mgr->dsp_version & 0xff;
12318c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "module version %s\n",
12328c2ecf20Sopenharmony_ci			    PCXHR_DRIVER_VERSION_STRING);
12338c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "dsp version %d.%d.%d\n",
12348c2ecf20Sopenharmony_ci			    ver_maj, ver_min, ver_build);
12358c2ecf20Sopenharmony_ci		if (mgr->board_has_analog)
12368c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "analog io available\n");
12378c2ecf20Sopenharmony_ci		else
12388c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "digital only board\n");
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		/* calc cpu load of the dsp */
12418c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_GET_DSP_RESOURCES);
12428c2ecf20Sopenharmony_ci		if( ! pcxhr_send_msg(mgr, &rmh) ) {
12438c2ecf20Sopenharmony_ci			int cur = rmh.stat[0];
12448c2ecf20Sopenharmony_ci			int ref = rmh.stat[1];
12458c2ecf20Sopenharmony_ci			if (ref > 0) {
12468c2ecf20Sopenharmony_ci				if (mgr->sample_rate_real != 0 &&
12478c2ecf20Sopenharmony_ci				    mgr->sample_rate_real != 48000) {
12488c2ecf20Sopenharmony_ci					ref = (ref * 48000) /
12498c2ecf20Sopenharmony_ci					  mgr->sample_rate_real;
12508c2ecf20Sopenharmony_ci					if (mgr->sample_rate_real >=
12518c2ecf20Sopenharmony_ci					    PCXHR_IRQ_TIMER_FREQ)
12528c2ecf20Sopenharmony_ci						ref *= 2;
12538c2ecf20Sopenharmony_ci				}
12548c2ecf20Sopenharmony_ci				cur = 100 - (100 * cur) / ref;
12558c2ecf20Sopenharmony_ci				snd_iprintf(buffer, "cpu load    %d%%\n", cur);
12568c2ecf20Sopenharmony_ci				snd_iprintf(buffer, "buffer pool %d/%d\n",
12578c2ecf20Sopenharmony_ci					    rmh.stat[2], rmh.stat[3]);
12588c2ecf20Sopenharmony_ci			}
12598c2ecf20Sopenharmony_ci		}
12608c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "dma granularity : %d\n",
12618c2ecf20Sopenharmony_ci			    mgr->granularity);
12628c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "dsp time errors : %d\n",
12638c2ecf20Sopenharmony_ci			    mgr->dsp_time_err);
12648c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "dsp async pipe xrun errors : %d\n",
12658c2ecf20Sopenharmony_ci			    mgr->async_err_pipe_xrun);
12668c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "dsp async stream xrun errors : %d\n",
12678c2ecf20Sopenharmony_ci			    mgr->async_err_stream_xrun);
12688c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "dsp async last other error : %x\n",
12698c2ecf20Sopenharmony_ci			    mgr->async_err_other_last);
12708c2ecf20Sopenharmony_ci		/* debug zone dsp */
12718c2ecf20Sopenharmony_ci		rmh.cmd[0] = 0x4200 + PCXHR_SIZE_MAX_STATUS;
12728c2ecf20Sopenharmony_ci		rmh.cmd_len = 1;
12738c2ecf20Sopenharmony_ci		rmh.stat_len = PCXHR_SIZE_MAX_STATUS;
12748c2ecf20Sopenharmony_ci		rmh.dsp_stat = 0;
12758c2ecf20Sopenharmony_ci		rmh.cmd_idx = CMD_LAST_INDEX;
12768c2ecf20Sopenharmony_ci		if( ! pcxhr_send_msg(mgr, &rmh) ) {
12778c2ecf20Sopenharmony_ci			int i;
12788c2ecf20Sopenharmony_ci			if (rmh.stat_len > 8)
12798c2ecf20Sopenharmony_ci				rmh.stat_len = 8;
12808c2ecf20Sopenharmony_ci			for (i = 0; i < rmh.stat_len; i++)
12818c2ecf20Sopenharmony_ci				snd_iprintf(buffer, "debug[%02d] = %06x\n",
12828c2ecf20Sopenharmony_ci					    i,  rmh.stat[i]);
12838c2ecf20Sopenharmony_ci		}
12848c2ecf20Sopenharmony_ci	} else
12858c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
12868c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\n");
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_cistatic void pcxhr_proc_sync(struct snd_info_entry *entry,
12898c2ecf20Sopenharmony_ci			    struct snd_info_buffer *buffer)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
12928c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
12938c2ecf20Sopenharmony_ci	static const char *textsHR22[3] = {
12948c2ecf20Sopenharmony_ci		"Internal", "AES Sync", "AES 1"
12958c2ecf20Sopenharmony_ci	};
12968c2ecf20Sopenharmony_ci	static const char *textsPCXHR[7] = {
12978c2ecf20Sopenharmony_ci		"Internal", "Word", "AES Sync",
12988c2ecf20Sopenharmony_ci		"AES 1", "AES 2", "AES 3", "AES 4"
12998c2ecf20Sopenharmony_ci	};
13008c2ecf20Sopenharmony_ci	const char **texts;
13018c2ecf20Sopenharmony_ci	int max_clock;
13028c2ecf20Sopenharmony_ci	if (mgr->is_hr_stereo) {
13038c2ecf20Sopenharmony_ci		texts = textsHR22;
13048c2ecf20Sopenharmony_ci		max_clock = HR22_CLOCK_TYPE_MAX;
13058c2ecf20Sopenharmony_ci	} else {
13068c2ecf20Sopenharmony_ci		texts = textsPCXHR;
13078c2ecf20Sopenharmony_ci		max_clock = PCXHR_CLOCK_TYPE_MAX;
13088c2ecf20Sopenharmony_ci	}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\n%s\n", mgr->name);
13118c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "Current Sample Clock\t: %s\n",
13128c2ecf20Sopenharmony_ci		    texts[mgr->cur_clock_type]);
13138c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "Current Sample Rate\t= %d\n",
13148c2ecf20Sopenharmony_ci		    mgr->sample_rate_real);
13158c2ecf20Sopenharmony_ci	/* commands available when embedded DSP is running */
13168c2ecf20Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
13178c2ecf20Sopenharmony_ci		int i, err, sample_rate;
13188c2ecf20Sopenharmony_ci		for (i = 1; i <= max_clock; i++) {
13198c2ecf20Sopenharmony_ci			err = pcxhr_get_external_clock(mgr, i, &sample_rate);
13208c2ecf20Sopenharmony_ci			if (err)
13218c2ecf20Sopenharmony_ci				break;
13228c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "%s Clock\t\t= %d\n",
13238c2ecf20Sopenharmony_ci				    texts[i], sample_rate);
13248c2ecf20Sopenharmony_ci		}
13258c2ecf20Sopenharmony_ci	} else
13268c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
13278c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\n");
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_cistatic void pcxhr_proc_gpio_read(struct snd_info_entry *entry,
13318c2ecf20Sopenharmony_ci				 struct snd_info_buffer *buffer)
13328c2ecf20Sopenharmony_ci{
13338c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
13348c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
13358c2ecf20Sopenharmony_ci	/* commands available when embedded DSP is running */
13368c2ecf20Sopenharmony_ci	if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
13378c2ecf20Sopenharmony_ci		/* gpio ports on stereo boards only available */
13388c2ecf20Sopenharmony_ci		int value = 0;
13398c2ecf20Sopenharmony_ci		hr222_read_gpio(mgr, 1, &value);	/* GPI */
13408c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "GPI: 0x%x\n", value);
13418c2ecf20Sopenharmony_ci		hr222_read_gpio(mgr, 0, &value);	/* GP0 */
13428c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "GPO: 0x%x\n", value);
13438c2ecf20Sopenharmony_ci	} else
13448c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
13458c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\n");
13468c2ecf20Sopenharmony_ci}
13478c2ecf20Sopenharmony_cistatic void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
13488c2ecf20Sopenharmony_ci				 struct snd_info_buffer *buffer)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
13518c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
13528c2ecf20Sopenharmony_ci	char line[64];
13538c2ecf20Sopenharmony_ci	int value;
13548c2ecf20Sopenharmony_ci	/* commands available when embedded DSP is running */
13558c2ecf20Sopenharmony_ci	if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)))
13568c2ecf20Sopenharmony_ci		return;
13578c2ecf20Sopenharmony_ci	while (!snd_info_get_line(buffer, line, sizeof(line))) {
13588c2ecf20Sopenharmony_ci		if (sscanf(line, "GPO: 0x%x", &value) != 1)
13598c2ecf20Sopenharmony_ci			continue;
13608c2ecf20Sopenharmony_ci		hr222_write_gpo(mgr, value);	/* GP0 */
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci}
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci/* Access to the results of the CMD_GET_TIME_CODE RMH */
13658c2ecf20Sopenharmony_ci#define TIME_CODE_VALID_MASK	0x00800000
13668c2ecf20Sopenharmony_ci#define TIME_CODE_NEW_MASK	0x00400000
13678c2ecf20Sopenharmony_ci#define TIME_CODE_BACK_MASK	0x00200000
13688c2ecf20Sopenharmony_ci#define TIME_CODE_WAIT_MASK	0x00100000
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci/* Values for the CMD_MANAGE_SIGNAL RMH */
13718c2ecf20Sopenharmony_ci#define MANAGE_SIGNAL_TIME_CODE	0x01
13728c2ecf20Sopenharmony_ci#define MANAGE_SIGNAL_MIDI	0x02
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci/* linear time code read proc*/
13758c2ecf20Sopenharmony_cistatic void pcxhr_proc_ltc(struct snd_info_entry *entry,
13768c2ecf20Sopenharmony_ci			   struct snd_info_buffer *buffer)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	struct snd_pcxhr *chip = entry->private_data;
13798c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr = chip->mgr;
13808c2ecf20Sopenharmony_ci	struct pcxhr_rmh rmh;
13818c2ecf20Sopenharmony_ci	unsigned int ltcHrs, ltcMin, ltcSec, ltcFrm;
13828c2ecf20Sopenharmony_ci	int err;
13838c2ecf20Sopenharmony_ci	/* commands available when embedded DSP is running */
13848c2ecf20Sopenharmony_ci	if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))) {
13858c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "no firmware loaded\n");
13868c2ecf20Sopenharmony_ci		return;
13878c2ecf20Sopenharmony_ci	}
13888c2ecf20Sopenharmony_ci	if (!mgr->capture_ltc) {
13898c2ecf20Sopenharmony_ci		pcxhr_init_rmh(&rmh, CMD_MANAGE_SIGNAL);
13908c2ecf20Sopenharmony_ci		rmh.cmd[0] |= MANAGE_SIGNAL_TIME_CODE;
13918c2ecf20Sopenharmony_ci		err = pcxhr_send_msg(mgr, &rmh);
13928c2ecf20Sopenharmony_ci		if (err) {
13938c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "ltc not activated (%d)\n", err);
13948c2ecf20Sopenharmony_ci			return;
13958c2ecf20Sopenharmony_ci		}
13968c2ecf20Sopenharmony_ci		if (mgr->is_hr_stereo)
13978c2ecf20Sopenharmony_ci			hr222_manage_timecode(mgr, 1);
13988c2ecf20Sopenharmony_ci		else
13998c2ecf20Sopenharmony_ci			pcxhr_write_io_num_reg_cont(mgr, REG_CONT_VALSMPTE,
14008c2ecf20Sopenharmony_ci						    REG_CONT_VALSMPTE, NULL);
14018c2ecf20Sopenharmony_ci		mgr->capture_ltc = 1;
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci	pcxhr_init_rmh(&rmh, CMD_GET_TIME_CODE);
14048c2ecf20Sopenharmony_ci	err = pcxhr_send_msg(mgr, &rmh);
14058c2ecf20Sopenharmony_ci	if (err) {
14068c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "ltc read error (err=%d)\n", err);
14078c2ecf20Sopenharmony_ci		return ;
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci	ltcHrs = 10*((rmh.stat[0] >> 8) & 0x3) + (rmh.stat[0] & 0xf);
14108c2ecf20Sopenharmony_ci	ltcMin = 10*((rmh.stat[1] >> 16) & 0x7) + ((rmh.stat[1] >> 8) & 0xf);
14118c2ecf20Sopenharmony_ci	ltcSec = 10*(rmh.stat[1] & 0x7) + ((rmh.stat[2] >> 16) & 0xf);
14128c2ecf20Sopenharmony_ci	ltcFrm = 10*((rmh.stat[2] >> 8) & 0x3) + (rmh.stat[2] & 0xf);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "timecode: %02u:%02u:%02u-%02u\n",
14158c2ecf20Sopenharmony_ci			    ltcHrs, ltcMin, ltcSec, ltcFrm);
14168c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "raw: 0x%04x%06x%06x\n", rmh.stat[0] & 0x00ffff,
14178c2ecf20Sopenharmony_ci			    rmh.stat[1] & 0xffffff, rmh.stat[2] & 0xffffff);
14188c2ecf20Sopenharmony_ci	/*snd_iprintf(buffer, "dsp ref time: 0x%06x%06x\n",
14198c2ecf20Sopenharmony_ci			    rmh.stat[3] & 0xffffff, rmh.stat[4] & 0xffffff);*/
14208c2ecf20Sopenharmony_ci	if (!(rmh.stat[0] & TIME_CODE_VALID_MASK)) {
14218c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "warning: linear timecode not valid\n");
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic void pcxhr_proc_init(struct snd_pcxhr *chip)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(chip->card, "info", chip, pcxhr_proc_info);
14288c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(chip->card, "sync", chip, pcxhr_proc_sync);
14298c2ecf20Sopenharmony_ci	/* gpio available on stereo sound cards only */
14308c2ecf20Sopenharmony_ci	if (chip->mgr->is_hr_stereo)
14318c2ecf20Sopenharmony_ci		snd_card_rw_proc_new(chip->card, "gpio", chip,
14328c2ecf20Sopenharmony_ci				     pcxhr_proc_gpio_read,
14338c2ecf20Sopenharmony_ci				     pcxhr_proc_gpo_write);
14348c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(chip->card, "ltc", chip, pcxhr_proc_ltc);
14358c2ecf20Sopenharmony_ci}
14368c2ecf20Sopenharmony_ci/* end of proc interface */
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci/*
14398c2ecf20Sopenharmony_ci * release all the cards assigned to a manager instance
14408c2ecf20Sopenharmony_ci */
14418c2ecf20Sopenharmony_cistatic int pcxhr_free(struct pcxhr_mgr *mgr)
14428c2ecf20Sopenharmony_ci{
14438c2ecf20Sopenharmony_ci	unsigned int i;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	for (i = 0; i < mgr->num_cards; i++) {
14468c2ecf20Sopenharmony_ci		if (mgr->chip[i])
14478c2ecf20Sopenharmony_ci			snd_card_free(mgr->chip[i]->card);
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	/* reset board if some firmware was loaded */
14518c2ecf20Sopenharmony_ci	if(mgr->dsp_loaded) {
14528c2ecf20Sopenharmony_ci		pcxhr_reset_board(mgr);
14538c2ecf20Sopenharmony_ci		dev_dbg(&mgr->pci->dev, "reset pcxhr !\n");
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* release irq  */
14578c2ecf20Sopenharmony_ci	if (mgr->irq >= 0)
14588c2ecf20Sopenharmony_ci		free_irq(mgr->irq, mgr);
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	pci_release_regions(mgr->pci);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	/* free hostport purgebuffer */
14638c2ecf20Sopenharmony_ci	if (mgr->hostport.area) {
14648c2ecf20Sopenharmony_ci		snd_dma_free_pages(&mgr->hostport);
14658c2ecf20Sopenharmony_ci		mgr->hostport.area = NULL;
14668c2ecf20Sopenharmony_ci	}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	kfree(mgr->prmh);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	pci_disable_device(mgr->pci);
14718c2ecf20Sopenharmony_ci	kfree(mgr);
14728c2ecf20Sopenharmony_ci	return 0;
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci/*
14768c2ecf20Sopenharmony_ci *    probe function - creates the card manager
14778c2ecf20Sopenharmony_ci */
14788c2ecf20Sopenharmony_cistatic int pcxhr_probe(struct pci_dev *pci,
14798c2ecf20Sopenharmony_ci		       const struct pci_device_id *pci_id)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	static int dev;
14828c2ecf20Sopenharmony_ci	struct pcxhr_mgr *mgr;
14838c2ecf20Sopenharmony_ci	unsigned int i;
14848c2ecf20Sopenharmony_ci	int err;
14858c2ecf20Sopenharmony_ci	size_t size;
14868c2ecf20Sopenharmony_ci	char *card_name;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
14898c2ecf20Sopenharmony_ci		return -ENODEV;
14908c2ecf20Sopenharmony_ci	if (! enable[dev]) {
14918c2ecf20Sopenharmony_ci		dev++;
14928c2ecf20Sopenharmony_ci		return -ENOENT;
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* enable PCI device */
14968c2ecf20Sopenharmony_ci	if ((err = pci_enable_device(pci)) < 0)
14978c2ecf20Sopenharmony_ci		return err;
14988c2ecf20Sopenharmony_ci	pci_set_master(pci);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	/* check if we can restrict PCI DMA transfers to 32 bits */
15018c2ecf20Sopenharmony_ci	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
15028c2ecf20Sopenharmony_ci		dev_err(&pci->dev,
15038c2ecf20Sopenharmony_ci			"architecture does not support 32bit PCI busmaster DMA\n");
15048c2ecf20Sopenharmony_ci		pci_disable_device(pci);
15058c2ecf20Sopenharmony_ci		return -ENXIO;
15068c2ecf20Sopenharmony_ci	}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	/* alloc card manager */
15098c2ecf20Sopenharmony_ci	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
15108c2ecf20Sopenharmony_ci	if (! mgr) {
15118c2ecf20Sopenharmony_ci		pci_disable_device(pci);
15128c2ecf20Sopenharmony_ci		return -ENOMEM;
15138c2ecf20Sopenharmony_ci	}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (snd_BUG_ON(pci_id->driver_data >= PCI_ID_LAST)) {
15168c2ecf20Sopenharmony_ci		kfree(mgr);
15178c2ecf20Sopenharmony_ci		pci_disable_device(pci);
15188c2ecf20Sopenharmony_ci		return -ENODEV;
15198c2ecf20Sopenharmony_ci	}
15208c2ecf20Sopenharmony_ci	card_name =
15218c2ecf20Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].board_name;
15228c2ecf20Sopenharmony_ci	mgr->playback_chips =
15238c2ecf20Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].playback_chips;
15248c2ecf20Sopenharmony_ci	mgr->capture_chips  =
15258c2ecf20Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].capture_chips;
15268c2ecf20Sopenharmony_ci	mgr->fw_file_set =
15278c2ecf20Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].fw_file_set;
15288c2ecf20Sopenharmony_ci	mgr->firmware_num  =
15298c2ecf20Sopenharmony_ci		pcxhr_board_params[pci_id->driver_data].firmware_num;
15308c2ecf20Sopenharmony_ci	mgr->mono_capture = mono[dev];
15318c2ecf20Sopenharmony_ci	mgr->is_hr_stereo = (mgr->playback_chips == 1);
15328c2ecf20Sopenharmony_ci	mgr->board_has_aes1 = PCXHR_BOARD_HAS_AES1(mgr);
15338c2ecf20Sopenharmony_ci	mgr->board_aes_in_192k = !PCXHR_BOARD_AESIN_NO_192K(mgr);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	if (mgr->is_hr_stereo)
15368c2ecf20Sopenharmony_ci		mgr->granularity = PCXHR_GRANULARITY_HR22;
15378c2ecf20Sopenharmony_ci	else
15388c2ecf20Sopenharmony_ci		mgr->granularity = PCXHR_GRANULARITY;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	/* resource assignment */
15418c2ecf20Sopenharmony_ci	if ((err = pci_request_regions(pci, card_name)) < 0) {
15428c2ecf20Sopenharmony_ci		kfree(mgr);
15438c2ecf20Sopenharmony_ci		pci_disable_device(pci);
15448c2ecf20Sopenharmony_ci		return err;
15458c2ecf20Sopenharmony_ci	}
15468c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
15478c2ecf20Sopenharmony_ci		mgr->port[i] = pci_resource_start(pci, i);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	mgr->pci = pci;
15508c2ecf20Sopenharmony_ci	mgr->irq = -1;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (request_threaded_irq(pci->irq, pcxhr_interrupt,
15538c2ecf20Sopenharmony_ci				 pcxhr_threaded_irq, IRQF_SHARED,
15548c2ecf20Sopenharmony_ci				 KBUILD_MODNAME, mgr)) {
15558c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "unable to grab IRQ %d\n", pci->irq);
15568c2ecf20Sopenharmony_ci		pcxhr_free(mgr);
15578c2ecf20Sopenharmony_ci		return -EBUSY;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci	mgr->irq = pci->irq;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	snprintf(mgr->name, sizeof(mgr->name),
15628c2ecf20Sopenharmony_ci		 "Digigram at 0x%lx & 0x%lx, 0x%lx irq %i",
15638c2ecf20Sopenharmony_ci		 mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	/* ISR lock  */
15668c2ecf20Sopenharmony_ci	mutex_init(&mgr->lock);
15678c2ecf20Sopenharmony_ci	mutex_init(&mgr->msg_lock);
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	/* init setup mutex*/
15708c2ecf20Sopenharmony_ci	mutex_init(&mgr->setup_mutex);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
15738c2ecf20Sopenharmony_ci			    sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
15748c2ecf20Sopenharmony_ci					   PCXHR_SIZE_MAX_STATUS),
15758c2ecf20Sopenharmony_ci			    GFP_KERNEL);
15768c2ecf20Sopenharmony_ci	if (! mgr->prmh) {
15778c2ecf20Sopenharmony_ci		pcxhr_free(mgr);
15788c2ecf20Sopenharmony_ci		return -ENOMEM;
15798c2ecf20Sopenharmony_ci	}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	for (i=0; i < PCXHR_MAX_CARDS; i++) {
15828c2ecf20Sopenharmony_ci		struct snd_card *card;
15838c2ecf20Sopenharmony_ci		char tmpid[16];
15848c2ecf20Sopenharmony_ci		int idx;
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci		if (i >= max(mgr->playback_chips, mgr->capture_chips))
15878c2ecf20Sopenharmony_ci			break;
15888c2ecf20Sopenharmony_ci		mgr->num_cards++;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci		if (index[dev] < 0)
15918c2ecf20Sopenharmony_ci			idx = index[dev];
15928c2ecf20Sopenharmony_ci		else
15938c2ecf20Sopenharmony_ci			idx = index[dev] + i;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci		snprintf(tmpid, sizeof(tmpid), "%s-%d",
15968c2ecf20Sopenharmony_ci			 id[dev] ? id[dev] : card_name, i);
15978c2ecf20Sopenharmony_ci		err = snd_card_new(&pci->dev, idx, tmpid, THIS_MODULE,
15988c2ecf20Sopenharmony_ci				   0, &card);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci		if (err < 0) {
16018c2ecf20Sopenharmony_ci			dev_err(&pci->dev, "cannot allocate the card %d\n", i);
16028c2ecf20Sopenharmony_ci			pcxhr_free(mgr);
16038c2ecf20Sopenharmony_ci			return err;
16048c2ecf20Sopenharmony_ci		}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci		strcpy(card->driver, DRIVER_NAME);
16078c2ecf20Sopenharmony_ci		snprintf(card->shortname, sizeof(card->shortname),
16088c2ecf20Sopenharmony_ci			 "Digigram [PCM #%d]", i);
16098c2ecf20Sopenharmony_ci		snprintf(card->longname, sizeof(card->longname),
16108c2ecf20Sopenharmony_ci			 "%s [PCM #%d]", mgr->name, i);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci		if ((err = pcxhr_create(mgr, card, i)) < 0) {
16138c2ecf20Sopenharmony_ci			snd_card_free(card);
16148c2ecf20Sopenharmony_ci			pcxhr_free(mgr);
16158c2ecf20Sopenharmony_ci			return err;
16168c2ecf20Sopenharmony_ci		}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci		if (i == 0)
16198c2ecf20Sopenharmony_ci			/* init proc interface only for chip0 */
16208c2ecf20Sopenharmony_ci			pcxhr_proc_init(mgr->chip[i]);
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci		if ((err = snd_card_register(card)) < 0) {
16238c2ecf20Sopenharmony_ci			pcxhr_free(mgr);
16248c2ecf20Sopenharmony_ci			return err;
16258c2ecf20Sopenharmony_ci		}
16268c2ecf20Sopenharmony_ci	}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	/* create hostport purgebuffer */
16298c2ecf20Sopenharmony_ci	size = PAGE_ALIGN(sizeof(struct pcxhr_hostport));
16308c2ecf20Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev,
16318c2ecf20Sopenharmony_ci				size, &mgr->hostport) < 0) {
16328c2ecf20Sopenharmony_ci		pcxhr_free(mgr);
16338c2ecf20Sopenharmony_ci		return -ENOMEM;
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci	/* init purgebuffer */
16368c2ecf20Sopenharmony_ci	memset(mgr->hostport.area, 0, size);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	/* create a DSP loader */
16398c2ecf20Sopenharmony_ci	err = pcxhr_setup_firmware(mgr);
16408c2ecf20Sopenharmony_ci	if (err < 0) {
16418c2ecf20Sopenharmony_ci		pcxhr_free(mgr);
16428c2ecf20Sopenharmony_ci		return err;
16438c2ecf20Sopenharmony_ci	}
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, mgr);
16468c2ecf20Sopenharmony_ci	dev++;
16478c2ecf20Sopenharmony_ci	return 0;
16488c2ecf20Sopenharmony_ci}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_cistatic void pcxhr_remove(struct pci_dev *pci)
16518c2ecf20Sopenharmony_ci{
16528c2ecf20Sopenharmony_ci	pcxhr_free(pci_get_drvdata(pci));
16538c2ecf20Sopenharmony_ci}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_cistatic struct pci_driver pcxhr_driver = {
16568c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
16578c2ecf20Sopenharmony_ci	.id_table = pcxhr_ids,
16588c2ecf20Sopenharmony_ci	.probe = pcxhr_probe,
16598c2ecf20Sopenharmony_ci	.remove = pcxhr_remove,
16608c2ecf20Sopenharmony_ci};
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_cimodule_pci_driver(pcxhr_driver);
1663