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