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