1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si> 41da177e4SLinus Torvalds * Takashi Iwai <tiwai@suse.de> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * SB16ASP/AWE32 CSP control 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * CSP microcode loader: 91da177e4SLinus Torvalds * alsa-tools/sb16_csp/ 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/delay.h> 131da177e4SLinus Torvalds #include <linux/init.h> 141da177e4SLinus Torvalds #include <linux/slab.h> 15da155d5bSPaul Gortmaker #include <linux/module.h> 161da177e4SLinus Torvalds #include <sound/core.h> 171da177e4SLinus Torvalds #include <sound/control.h> 181da177e4SLinus Torvalds #include <sound/info.h> 191da177e4SLinus Torvalds #include <sound/sb16_csp.h> 201da177e4SLinus Torvalds #include <sound/initval.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>"); 231da177e4SLinus Torvalds MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor"); 241da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 257e0af29dSClemens Ladisch MODULE_FIRMWARE("sb16/mulaw_main.csp"); 267e0af29dSClemens Ladisch MODULE_FIRMWARE("sb16/alaw_main.csp"); 277e0af29dSClemens Ladisch MODULE_FIRMWARE("sb16/ima_adpcm_init.csp"); 287e0af29dSClemens Ladisch MODULE_FIRMWARE("sb16/ima_adpcm_playback.csp"); 297e0af29dSClemens Ladisch MODULE_FIRMWARE("sb16/ima_adpcm_capture.csp"); 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #ifdef SNDRV_LITTLE_ENDIAN 321da177e4SLinus Torvalds #define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) 331da177e4SLinus Torvalds #else 341da177e4SLinus Torvalds #define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) 351da177e4SLinus Torvalds #endif 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds #define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F') 381da177e4SLinus Torvalds #define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ') 391da177e4SLinus Torvalds #define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T') 401da177e4SLinus Torvalds #define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c') 411da177e4SLinus Torvalds #define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e') 421da177e4SLinus Torvalds #define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't') 431da177e4SLinus Torvalds #define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n') 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* 461da177e4SLinus Torvalds * RIFF data format 471da177e4SLinus Torvalds */ 48dfc866e5SAlexey Dobriyan struct riff_header { 4913e9a3edSTakashi Iwai __le32 name; 5013e9a3edSTakashi Iwai __le32 len; 51dfc866e5SAlexey Dobriyan }; 521da177e4SLinus Torvalds 53dfc866e5SAlexey Dobriyan struct desc_header { 54dfc866e5SAlexey Dobriyan struct riff_header info; 5513e9a3edSTakashi Iwai __le16 func_nr; 5613e9a3edSTakashi Iwai __le16 VOC_type; 5713e9a3edSTakashi Iwai __le16 flags_play_rec; 5813e9a3edSTakashi Iwai __le16 flags_16bit_8bit; 5913e9a3edSTakashi Iwai __le16 flags_stereo_mono; 6013e9a3edSTakashi Iwai __le16 flags_rates; 61dfc866e5SAlexey Dobriyan }; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* 641da177e4SLinus Torvalds * prototypes 651da177e4SLinus Torvalds */ 66029d64b0STakashi Iwai static void snd_sb_csp_free(struct snd_hwdep *hw); 67029d64b0STakashi Iwai static int snd_sb_csp_open(struct snd_hwdep * hw, struct file *file); 68029d64b0STakashi Iwai static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg); 69029d64b0STakashi Iwai static int snd_sb_csp_release(struct snd_hwdep * hw, struct file *file); 701da177e4SLinus Torvalds 71029d64b0STakashi Iwai static int csp_detect(struct snd_sb *chip, int *version); 72029d64b0STakashi Iwai static int set_codec_parameter(struct snd_sb *chip, unsigned char par, unsigned char val); 73029d64b0STakashi Iwai static int set_register(struct snd_sb *chip, unsigned char reg, unsigned char val); 74029d64b0STakashi Iwai static int read_register(struct snd_sb *chip, unsigned char reg); 75029d64b0STakashi Iwai static int set_mode_register(struct snd_sb *chip, unsigned char mode); 76029d64b0STakashi Iwai static int get_version(struct snd_sb *chip); 771da177e4SLinus Torvalds 78029d64b0STakashi Iwai static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 79029d64b0STakashi Iwai struct snd_sb_csp_microcode __user * code); 80029d64b0STakashi Iwai static int snd_sb_csp_unload(struct snd_sb_csp * p); 81029d64b0STakashi Iwai static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __user *buf, int size, int load_flags); 82e5d3765bSTakashi Iwai static int snd_sb_csp_autoload(struct snd_sb_csp * p, snd_pcm_format_t pcm_sfmt, int play_rec_mode); 83029d64b0STakashi Iwai static int snd_sb_csp_check_version(struct snd_sb_csp * p); 841da177e4SLinus Torvalds 85029d64b0STakashi Iwai static int snd_sb_csp_use(struct snd_sb_csp * p); 86029d64b0STakashi Iwai static int snd_sb_csp_unuse(struct snd_sb_csp * p); 87029d64b0STakashi Iwai static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channels); 88029d64b0STakashi Iwai static int snd_sb_csp_stop(struct snd_sb_csp * p); 89029d64b0STakashi Iwai static int snd_sb_csp_pause(struct snd_sb_csp * p); 90029d64b0STakashi Iwai static int snd_sb_csp_restart(struct snd_sb_csp * p); 911da177e4SLinus Torvalds 92029d64b0STakashi Iwai static int snd_sb_qsound_build(struct snd_sb_csp * p); 93029d64b0STakashi Iwai static void snd_sb_qsound_destroy(struct snd_sb_csp * p); 94029d64b0STakashi Iwai static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p); 951da177e4SLinus Torvalds 96029d64b0STakashi Iwai static int init_proc_entry(struct snd_sb_csp * p, int device); 97029d64b0STakashi Iwai static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer); 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds /* 1001da177e4SLinus Torvalds * Detect CSP chip and create a new instance 1011da177e4SLinus Torvalds */ 102029d64b0STakashi Iwai int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep) 1031da177e4SLinus Torvalds { 104029d64b0STakashi Iwai struct snd_sb_csp *p; 105cd0b4ac8STakashi Iwai int uninitialized_var(version); 106cd0b4ac8STakashi Iwai int err; 107029d64b0STakashi Iwai struct snd_hwdep *hw; 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds if (rhwdep) 1101da177e4SLinus Torvalds *rhwdep = NULL; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds if (csp_detect(chip, &version)) 1131da177e4SLinus Torvalds return -ENODEV; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) 1161da177e4SLinus Torvalds return err; 1171da177e4SLinus Torvalds 1189e76a76eSTakashi Iwai if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) { 1191da177e4SLinus Torvalds snd_device_free(chip->card, hw); 1201da177e4SLinus Torvalds return -ENOMEM; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds p->chip = chip; 1231da177e4SLinus Torvalds p->version = version; 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /* CSP operators */ 1261da177e4SLinus Torvalds p->ops.csp_use = snd_sb_csp_use; 1271da177e4SLinus Torvalds p->ops.csp_unuse = snd_sb_csp_unuse; 1281da177e4SLinus Torvalds p->ops.csp_autoload = snd_sb_csp_autoload; 1291da177e4SLinus Torvalds p->ops.csp_start = snd_sb_csp_start; 1301da177e4SLinus Torvalds p->ops.csp_stop = snd_sb_csp_stop; 1311da177e4SLinus Torvalds p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer; 1321da177e4SLinus Torvalds 1338b7547f9SIngo Molnar mutex_init(&p->access_mutex); 1341da177e4SLinus Torvalds sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f)); 1351da177e4SLinus Torvalds hw->iface = SNDRV_HWDEP_IFACE_SB16CSP; 1361da177e4SLinus Torvalds hw->private_data = p; 1371da177e4SLinus Torvalds hw->private_free = snd_sb_csp_free; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds /* operators - only write/ioctl */ 1401da177e4SLinus Torvalds hw->ops.open = snd_sb_csp_open; 1411da177e4SLinus Torvalds hw->ops.ioctl = snd_sb_csp_ioctl; 1421da177e4SLinus Torvalds hw->ops.release = snd_sb_csp_release; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds /* create a proc entry */ 1451da177e4SLinus Torvalds init_proc_entry(p, device); 1461da177e4SLinus Torvalds if (rhwdep) 1471da177e4SLinus Torvalds *rhwdep = hw; 1481da177e4SLinus Torvalds return 0; 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds /* 1521da177e4SLinus Torvalds * free_private for hwdep instance 1531da177e4SLinus Torvalds */ 154029d64b0STakashi Iwai static void snd_sb_csp_free(struct snd_hwdep *hwdep) 1551da177e4SLinus Torvalds { 156de66d53eSClemens Ladisch int i; 157029d64b0STakashi Iwai struct snd_sb_csp *p = hwdep->private_data; 1581da177e4SLinus Torvalds if (p) { 1591da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_RUNNING) 1601da177e4SLinus Torvalds snd_sb_csp_stop(p); 161de66d53eSClemens Ladisch for (i = 0; i < ARRAY_SIZE(p->csp_programs); ++i) 162de66d53eSClemens Ladisch release_firmware(p->csp_programs[i]); 1631da177e4SLinus Torvalds kfree(p); 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds /* ------------------------------ */ 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds /* 1701da177e4SLinus Torvalds * open the device exclusively 1711da177e4SLinus Torvalds */ 172029d64b0STakashi Iwai static int snd_sb_csp_open(struct snd_hwdep * hw, struct file *file) 1731da177e4SLinus Torvalds { 174029d64b0STakashi Iwai struct snd_sb_csp *p = hw->private_data; 1751da177e4SLinus Torvalds return (snd_sb_csp_use(p)); 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* 1791da177e4SLinus Torvalds * ioctl for hwdep device: 1801da177e4SLinus Torvalds */ 181029d64b0STakashi Iwai static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg) 1821da177e4SLinus Torvalds { 183029d64b0STakashi Iwai struct snd_sb_csp *p = hw->private_data; 184029d64b0STakashi Iwai struct snd_sb_csp_info info; 185029d64b0STakashi Iwai struct snd_sb_csp_start start_info; 1861da177e4SLinus Torvalds int err; 1871da177e4SLinus Torvalds 188622207dcSTakashi Iwai if (snd_BUG_ON(!p)) 189622207dcSTakashi Iwai return -EINVAL; 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds if (snd_sb_csp_check_version(p)) 1921da177e4SLinus Torvalds return -ENODEV; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds switch (cmd) { 1951da177e4SLinus Torvalds /* get information */ 1961da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_INFO: 197bffbbc0aSDan Carpenter memset(&info, 0, sizeof(info)); 1981da177e4SLinus Torvalds *info.codec_name = *p->codec_name; 1991da177e4SLinus Torvalds info.func_nr = p->func_nr; 2001da177e4SLinus Torvalds info.acc_format = p->acc_format; 2011da177e4SLinus Torvalds info.acc_channels = p->acc_channels; 2021da177e4SLinus Torvalds info.acc_width = p->acc_width; 2031da177e4SLinus Torvalds info.acc_rates = p->acc_rates; 2041da177e4SLinus Torvalds info.csp_mode = p->mode; 2051da177e4SLinus Torvalds info.run_channels = p->run_channels; 2061da177e4SLinus Torvalds info.run_width = p->run_width; 2071da177e4SLinus Torvalds info.version = p->version; 2081da177e4SLinus Torvalds info.state = p->running; 2091da177e4SLinus Torvalds if (copy_to_user((void __user *)arg, &info, sizeof(info))) 2101da177e4SLinus Torvalds err = -EFAULT; 2111da177e4SLinus Torvalds else 2121da177e4SLinus Torvalds err = 0; 2131da177e4SLinus Torvalds break; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* load CSP microcode */ 2161da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_LOAD_CODE: 2171da177e4SLinus Torvalds err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? 218029d64b0STakashi Iwai -EBUSY : snd_sb_csp_riff_load(p, (struct snd_sb_csp_microcode __user *) arg)); 2191da177e4SLinus Torvalds break; 2201da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE: 2211da177e4SLinus Torvalds err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? 2221da177e4SLinus Torvalds -EBUSY : snd_sb_csp_unload(p)); 2231da177e4SLinus Torvalds break; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds /* change CSP running state */ 2261da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_START: 2271da177e4SLinus Torvalds if (copy_from_user(&start_info, (void __user *) arg, sizeof(start_info))) 2281da177e4SLinus Torvalds err = -EFAULT; 2291da177e4SLinus Torvalds else 2301da177e4SLinus Torvalds err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels); 2311da177e4SLinus Torvalds break; 2321da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_STOP: 2331da177e4SLinus Torvalds err = snd_sb_csp_stop(p); 2341da177e4SLinus Torvalds break; 2351da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_PAUSE: 2361da177e4SLinus Torvalds err = snd_sb_csp_pause(p); 2371da177e4SLinus Torvalds break; 2381da177e4SLinus Torvalds case SNDRV_SB_CSP_IOCTL_RESTART: 2391da177e4SLinus Torvalds err = snd_sb_csp_restart(p); 2401da177e4SLinus Torvalds break; 2411da177e4SLinus Torvalds default: 2421da177e4SLinus Torvalds err = -ENOTTY; 2431da177e4SLinus Torvalds break; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds return err; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds /* 2501da177e4SLinus Torvalds * close the device 2511da177e4SLinus Torvalds */ 252029d64b0STakashi Iwai static int snd_sb_csp_release(struct snd_hwdep * hw, struct file *file) 2531da177e4SLinus Torvalds { 254029d64b0STakashi Iwai struct snd_sb_csp *p = hw->private_data; 2551da177e4SLinus Torvalds return (snd_sb_csp_unuse(p)); 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds /* ------------------------------ */ 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds /* 2611da177e4SLinus Torvalds * acquire device 2621da177e4SLinus Torvalds */ 263029d64b0STakashi Iwai static int snd_sb_csp_use(struct snd_sb_csp * p) 2641da177e4SLinus Torvalds { 2658b7547f9SIngo Molnar mutex_lock(&p->access_mutex); 2661da177e4SLinus Torvalds if (p->used) { 2678b7547f9SIngo Molnar mutex_unlock(&p->access_mutex); 2681da177e4SLinus Torvalds return -EAGAIN; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds p->used++; 2718b7547f9SIngo Molnar mutex_unlock(&p->access_mutex); 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds return 0; 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds /* 2781da177e4SLinus Torvalds * release device 2791da177e4SLinus Torvalds */ 280029d64b0STakashi Iwai static int snd_sb_csp_unuse(struct snd_sb_csp * p) 2811da177e4SLinus Torvalds { 2828b7547f9SIngo Molnar mutex_lock(&p->access_mutex); 2831da177e4SLinus Torvalds p->used--; 2848b7547f9SIngo Molnar mutex_unlock(&p->access_mutex); 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds return 0; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds /* 2901da177e4SLinus Torvalds * load microcode via ioctl: 2911da177e4SLinus Torvalds * code is user-space pointer 2921da177e4SLinus Torvalds */ 293029d64b0STakashi Iwai static int snd_sb_csp_riff_load(struct snd_sb_csp * p, 294029d64b0STakashi Iwai struct snd_sb_csp_microcode __user * mcode) 2951da177e4SLinus Torvalds { 296029d64b0STakashi Iwai struct snd_sb_csp_mc_header info; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds unsigned char __user *data_ptr; 2991da177e4SLinus Torvalds unsigned char __user *data_end; 3001da177e4SLinus Torvalds unsigned short func_nr = 0; 3011da177e4SLinus Torvalds 302dfc866e5SAlexey Dobriyan struct riff_header file_h, item_h, code_h; 30313e9a3edSTakashi Iwai __le32 item_type; 304dfc866e5SAlexey Dobriyan struct desc_header funcdesc_h; 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds unsigned long flags; 3071da177e4SLinus Torvalds int err; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds if (copy_from_user(&info, mcode, sizeof(info))) 3101da177e4SLinus Torvalds return -EFAULT; 3111da177e4SLinus Torvalds data_ptr = mcode->data; 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds if (copy_from_user(&file_h, data_ptr, sizeof(file_h))) 3141da177e4SLinus Torvalds return -EFAULT; 31513e9a3edSTakashi Iwai if ((le32_to_cpu(file_h.name) != RIFF_HEADER) || 31631bbf8f5SAlexey Dobriyan (le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { 3179bf8e7ddSHarvey Harrison snd_printd("%s: Invalid RIFF header\n", __func__); 3181da177e4SLinus Torvalds return -EINVAL; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds data_ptr += sizeof(file_h); 32131bbf8f5SAlexey Dobriyan data_end = data_ptr + le32_to_cpu(file_h.len); 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) 3241da177e4SLinus Torvalds return -EFAULT; 32513e9a3edSTakashi Iwai if (le32_to_cpu(item_type) != CSP__HEADER) { 3269bf8e7ddSHarvey Harrison snd_printd("%s: Invalid RIFF file type\n", __func__); 3271da177e4SLinus Torvalds return -EINVAL; 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds data_ptr += sizeof (item_type); 3301da177e4SLinus Torvalds 33131bbf8f5SAlexey Dobriyan for (; data_ptr < data_end; data_ptr += le32_to_cpu(item_h.len)) { 3321da177e4SLinus Torvalds if (copy_from_user(&item_h, data_ptr, sizeof(item_h))) 3331da177e4SLinus Torvalds return -EFAULT; 3341da177e4SLinus Torvalds data_ptr += sizeof(item_h); 33513e9a3edSTakashi Iwai if (le32_to_cpu(item_h.name) != LIST_HEADER) 3361da177e4SLinus Torvalds continue; 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) 3391da177e4SLinus Torvalds return -EFAULT; 34013e9a3edSTakashi Iwai switch (le32_to_cpu(item_type)) { 3411da177e4SLinus Torvalds case FUNC_HEADER: 3421da177e4SLinus Torvalds if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h))) 3431da177e4SLinus Torvalds return -EFAULT; 34431bbf8f5SAlexey Dobriyan func_nr = le16_to_cpu(funcdesc_h.func_nr); 3451da177e4SLinus Torvalds break; 3461da177e4SLinus Torvalds case CODE_HEADER: 3471da177e4SLinus Torvalds if (func_nr != info.func_req) 3481da177e4SLinus Torvalds break; /* not required function, try next */ 3491da177e4SLinus Torvalds data_ptr += sizeof(item_type); 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds /* destroy QSound mixer element */ 3521da177e4SLinus Torvalds if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { 3531da177e4SLinus Torvalds snd_sb_qsound_destroy(p); 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds /* Clear all flags */ 3561da177e4SLinus Torvalds p->running = 0; 3571da177e4SLinus Torvalds p->mode = 0; 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds /* load microcode blocks */ 3601da177e4SLinus Torvalds for (;;) { 3611da177e4SLinus Torvalds if (data_ptr >= data_end) 3621da177e4SLinus Torvalds return -EINVAL; 3631da177e4SLinus Torvalds if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) 3641da177e4SLinus Torvalds return -EFAULT; 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds /* init microcode blocks */ 36713e9a3edSTakashi Iwai if (le32_to_cpu(code_h.name) != INIT_HEADER) 3681da177e4SLinus Torvalds break; 3691da177e4SLinus Torvalds data_ptr += sizeof(code_h); 37031bbf8f5SAlexey Dobriyan err = snd_sb_csp_load_user(p, data_ptr, le32_to_cpu(code_h.len), 3711da177e4SLinus Torvalds SNDRV_SB_CSP_LOAD_INITBLOCK); 3721da177e4SLinus Torvalds if (err) 3731da177e4SLinus Torvalds return err; 37431bbf8f5SAlexey Dobriyan data_ptr += le32_to_cpu(code_h.len); 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds /* main microcode block */ 3771da177e4SLinus Torvalds if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) 3781da177e4SLinus Torvalds return -EFAULT; 3791da177e4SLinus Torvalds 38013e9a3edSTakashi Iwai if (le32_to_cpu(code_h.name) != MAIN_HEADER) { 3819bf8e7ddSHarvey Harrison snd_printd("%s: Missing 'main' microcode\n", __func__); 3821da177e4SLinus Torvalds return -EINVAL; 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds data_ptr += sizeof(code_h); 3851da177e4SLinus Torvalds err = snd_sb_csp_load_user(p, data_ptr, 38631bbf8f5SAlexey Dobriyan le32_to_cpu(code_h.len), 0); 3871da177e4SLinus Torvalds if (err) 3881da177e4SLinus Torvalds return err; 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds /* fill in codec header */ 3911da177e4SLinus Torvalds strlcpy(p->codec_name, info.codec_name, sizeof(p->codec_name)); 3921da177e4SLinus Torvalds p->func_nr = func_nr; 39331bbf8f5SAlexey Dobriyan p->mode = le16_to_cpu(funcdesc_h.flags_play_rec); 39431bbf8f5SAlexey Dobriyan switch (le16_to_cpu(funcdesc_h.VOC_type)) { 3951da177e4SLinus Torvalds case 0x0001: /* QSound decoder */ 39631bbf8f5SAlexey Dobriyan if (le16_to_cpu(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) { 3971da177e4SLinus Torvalds if (snd_sb_qsound_build(p) == 0) 3981da177e4SLinus Torvalds /* set QSound flag and clear all other mode flags */ 3991da177e4SLinus Torvalds p->mode = SNDRV_SB_CSP_MODE_QSOUND; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds p->acc_format = 0; 4021da177e4SLinus Torvalds break; 4031da177e4SLinus Torvalds case 0x0006: /* A Law codec */ 4041da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; 4051da177e4SLinus Torvalds break; 4061da177e4SLinus Torvalds case 0x0007: /* Mu Law codec */ 4071da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; 4081da177e4SLinus Torvalds break; 4091da177e4SLinus Torvalds case 0x0011: /* what Creative thinks is IMA ADPCM codec */ 4101da177e4SLinus Torvalds case 0x0200: /* Creative ADPCM codec */ 4111da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; 4121da177e4SLinus Torvalds break; 4131da177e4SLinus Torvalds case 201: /* Text 2 Speech decoder */ 4141da177e4SLinus Torvalds /* TODO: Text2Speech handling routines */ 4151da177e4SLinus Torvalds p->acc_format = 0; 4161da177e4SLinus Torvalds break; 4171da177e4SLinus Torvalds case 0x0202: /* Fast Speech 8 codec */ 4181da177e4SLinus Torvalds case 0x0203: /* Fast Speech 10 codec */ 4191da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL; 4201da177e4SLinus Torvalds break; 4211da177e4SLinus Torvalds default: /* other codecs are unsupported */ 4221da177e4SLinus Torvalds p->acc_format = p->acc_width = p->acc_rates = 0; 4231da177e4SLinus Torvalds p->mode = 0; 4241da177e4SLinus Torvalds snd_printd("%s: Unsupported CSP codec type: 0x%04x\n", 4259bf8e7ddSHarvey Harrison __func__, 42631bbf8f5SAlexey Dobriyan le16_to_cpu(funcdesc_h.VOC_type)); 4271da177e4SLinus Torvalds return -EINVAL; 4281da177e4SLinus Torvalds } 42931bbf8f5SAlexey Dobriyan p->acc_channels = le16_to_cpu(funcdesc_h.flags_stereo_mono); 43031bbf8f5SAlexey Dobriyan p->acc_width = le16_to_cpu(funcdesc_h.flags_16bit_8bit); 43131bbf8f5SAlexey Dobriyan p->acc_rates = le16_to_cpu(funcdesc_h.flags_rates); 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds /* Decouple CSP from IRQ and DMAREQ lines */ 4341da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->reg_lock, flags); 4351da177e4SLinus Torvalds set_mode_register(p->chip, 0xfc); 4361da177e4SLinus Torvalds set_mode_register(p->chip, 0x00); 4371da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->reg_lock, flags); 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds /* finished loading successfully */ 4401da177e4SLinus Torvalds p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */ 4411da177e4SLinus Torvalds return 0; 4421da177e4SLinus Torvalds } 4431da177e4SLinus Torvalds } 4449bf8e7ddSHarvey Harrison snd_printd("%s: Function #%d not found\n", __func__, info.func_req); 4451da177e4SLinus Torvalds return -EINVAL; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds /* 4491da177e4SLinus Torvalds * unload CSP microcode 4501da177e4SLinus Torvalds */ 451029d64b0STakashi Iwai static int snd_sb_csp_unload(struct snd_sb_csp * p) 4521da177e4SLinus Torvalds { 4531da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_RUNNING) 4541da177e4SLinus Torvalds return -EBUSY; 4551da177e4SLinus Torvalds if (!(p->running & SNDRV_SB_CSP_ST_LOADED)) 4561da177e4SLinus Torvalds return -ENXIO; 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds /* clear supported formats */ 4591da177e4SLinus Torvalds p->acc_format = 0; 4601da177e4SLinus Torvalds p->acc_channels = p->acc_width = p->acc_rates = 0; 4611da177e4SLinus Torvalds /* destroy QSound mixer element */ 4621da177e4SLinus Torvalds if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { 4631da177e4SLinus Torvalds snd_sb_qsound_destroy(p); 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds /* clear all flags */ 4661da177e4SLinus Torvalds p->running = 0; 4671da177e4SLinus Torvalds p->mode = 0; 4681da177e4SLinus Torvalds return 0; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds /* 4721da177e4SLinus Torvalds * send command sequence to DSP 4731da177e4SLinus Torvalds */ 474029d64b0STakashi Iwai static inline int command_seq(struct snd_sb *chip, const unsigned char *seq, int size) 4751da177e4SLinus Torvalds { 4761da177e4SLinus Torvalds int i; 4771da177e4SLinus Torvalds for (i = 0; i < size; i++) { 4781da177e4SLinus Torvalds if (!snd_sbdsp_command(chip, seq[i])) 4791da177e4SLinus Torvalds return -EIO; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds return 0; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds /* 4851da177e4SLinus Torvalds * set CSP codec parameter 4861da177e4SLinus Torvalds */ 487029d64b0STakashi Iwai static int set_codec_parameter(struct snd_sb *chip, unsigned char par, unsigned char val) 4881da177e4SLinus Torvalds { 4891da177e4SLinus Torvalds unsigned char dsp_cmd[3]; 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds dsp_cmd[0] = 0x05; /* CSP set codec parameter */ 4921da177e4SLinus Torvalds dsp_cmd[1] = val; /* Parameter value */ 4931da177e4SLinus Torvalds dsp_cmd[2] = par; /* Parameter */ 4941da177e4SLinus Torvalds command_seq(chip, dsp_cmd, 3); 4951da177e4SLinus Torvalds snd_sbdsp_command(chip, 0x03); /* DSP read? */ 4961da177e4SLinus Torvalds if (snd_sbdsp_get_byte(chip) != par) 4971da177e4SLinus Torvalds return -EIO; 4981da177e4SLinus Torvalds return 0; 4991da177e4SLinus Torvalds } 5001da177e4SLinus Torvalds 5011da177e4SLinus Torvalds /* 5021da177e4SLinus Torvalds * set CSP register 5031da177e4SLinus Torvalds */ 504029d64b0STakashi Iwai static int set_register(struct snd_sb *chip, unsigned char reg, unsigned char val) 5051da177e4SLinus Torvalds { 5061da177e4SLinus Torvalds unsigned char dsp_cmd[3]; 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds dsp_cmd[0] = 0x0e; /* CSP set register */ 5091da177e4SLinus Torvalds dsp_cmd[1] = reg; /* CSP Register */ 5101da177e4SLinus Torvalds dsp_cmd[2] = val; /* value */ 5111da177e4SLinus Torvalds return command_seq(chip, dsp_cmd, 3); 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 5141da177e4SLinus Torvalds /* 5151da177e4SLinus Torvalds * read CSP register 5161da177e4SLinus Torvalds * return < 0 -> error 5171da177e4SLinus Torvalds */ 518029d64b0STakashi Iwai static int read_register(struct snd_sb *chip, unsigned char reg) 5191da177e4SLinus Torvalds { 5201da177e4SLinus Torvalds unsigned char dsp_cmd[2]; 5211da177e4SLinus Torvalds 5221da177e4SLinus Torvalds dsp_cmd[0] = 0x0f; /* CSP read register */ 5231da177e4SLinus Torvalds dsp_cmd[1] = reg; /* CSP Register */ 5241da177e4SLinus Torvalds command_seq(chip, dsp_cmd, 2); 5251da177e4SLinus Torvalds return snd_sbdsp_get_byte(chip); /* Read DSP value */ 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds /* 5291da177e4SLinus Torvalds * set CSP mode register 5301da177e4SLinus Torvalds */ 531029d64b0STakashi Iwai static int set_mode_register(struct snd_sb *chip, unsigned char mode) 5321da177e4SLinus Torvalds { 5331da177e4SLinus Torvalds unsigned char dsp_cmd[2]; 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds dsp_cmd[0] = 0x04; /* CSP set mode register */ 5361da177e4SLinus Torvalds dsp_cmd[1] = mode; /* mode */ 5371da177e4SLinus Torvalds return command_seq(chip, dsp_cmd, 2); 5381da177e4SLinus Torvalds } 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds /* 5411da177e4SLinus Torvalds * Detect CSP 5421da177e4SLinus Torvalds * return 0 if CSP exists. 5431da177e4SLinus Torvalds */ 544029d64b0STakashi Iwai static int csp_detect(struct snd_sb *chip, int *version) 5451da177e4SLinus Torvalds { 5461da177e4SLinus Torvalds unsigned char csp_test1, csp_test2; 5471da177e4SLinus Torvalds unsigned long flags; 5481da177e4SLinus Torvalds int result = -ENODEV; 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags); 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds set_codec_parameter(chip, 0x00, 0x00); 5531da177e4SLinus Torvalds set_mode_register(chip, 0xfc); /* 0xfc = ?? */ 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds csp_test1 = read_register(chip, 0x83); 5561da177e4SLinus Torvalds set_register(chip, 0x83, ~csp_test1); 5571da177e4SLinus Torvalds csp_test2 = read_register(chip, 0x83); 5581da177e4SLinus Torvalds if (csp_test2 != (csp_test1 ^ 0xff)) 5591da177e4SLinus Torvalds goto __fail; 5601da177e4SLinus Torvalds 5611da177e4SLinus Torvalds set_register(chip, 0x83, csp_test1); 5621da177e4SLinus Torvalds csp_test2 = read_register(chip, 0x83); 5631da177e4SLinus Torvalds if (csp_test2 != csp_test1) 5641da177e4SLinus Torvalds goto __fail; 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds set_mode_register(chip, 0x00); /* 0x00 = ? */ 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds *version = get_version(chip); 5691da177e4SLinus Torvalds snd_sbdsp_reset(chip); /* reset DSP after getversion! */ 5701da177e4SLinus Torvalds if (*version >= 0x10 && *version <= 0x1f) 5711da177e4SLinus Torvalds result = 0; /* valid version id */ 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds __fail: 5741da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags); 5751da177e4SLinus Torvalds return result; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds /* 5791da177e4SLinus Torvalds * get CSP version number 5801da177e4SLinus Torvalds */ 581029d64b0STakashi Iwai static int get_version(struct snd_sb *chip) 5821da177e4SLinus Torvalds { 5831da177e4SLinus Torvalds unsigned char dsp_cmd[2]; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds dsp_cmd[0] = 0x08; /* SB_DSP_!something! */ 5861da177e4SLinus Torvalds dsp_cmd[1] = 0x03; /* get chip version id? */ 5871da177e4SLinus Torvalds command_seq(chip, dsp_cmd, 2); 5881da177e4SLinus Torvalds 5891da177e4SLinus Torvalds return (snd_sbdsp_get_byte(chip)); 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds /* 5931da177e4SLinus Torvalds * check if the CSP version is valid 5941da177e4SLinus Torvalds */ 595029d64b0STakashi Iwai static int snd_sb_csp_check_version(struct snd_sb_csp * p) 5961da177e4SLinus Torvalds { 5971da177e4SLinus Torvalds if (p->version < 0x10 || p->version > 0x1f) { 5989bf8e7ddSHarvey Harrison snd_printd("%s: Invalid CSP version: 0x%x\n", __func__, p->version); 5991da177e4SLinus Torvalds return 1; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds return 0; 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds 6041da177e4SLinus Torvalds /* 6051da177e4SLinus Torvalds * download microcode to CSP (microcode should have one "main" block). 6061da177e4SLinus Torvalds */ 607029d64b0STakashi Iwai static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int size, int load_flags) 6081da177e4SLinus Torvalds { 6091da177e4SLinus Torvalds int status, i; 6101da177e4SLinus Torvalds int err; 6111da177e4SLinus Torvalds int result = -EIO; 6121da177e4SLinus Torvalds unsigned long flags; 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->reg_lock, flags); 6151da177e4SLinus Torvalds snd_sbdsp_command(p->chip, 0x01); /* CSP download command */ 6161da177e4SLinus Torvalds if (snd_sbdsp_get_byte(p->chip)) { 6179bf8e7ddSHarvey Harrison snd_printd("%s: Download command failed\n", __func__); 6181da177e4SLinus Torvalds goto __fail; 6191da177e4SLinus Torvalds } 6201da177e4SLinus Torvalds /* Send CSP low byte (size - 1) */ 6211da177e4SLinus Torvalds snd_sbdsp_command(p->chip, (unsigned char)(size - 1)); 6221da177e4SLinus Torvalds /* Send high byte */ 6231da177e4SLinus Torvalds snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8)); 6241da177e4SLinus Torvalds /* send microcode sequence */ 6251da177e4SLinus Torvalds /* load from kernel space */ 6261da177e4SLinus Torvalds while (size--) { 6271da177e4SLinus Torvalds if (!snd_sbdsp_command(p->chip, *buf++)) 6281da177e4SLinus Torvalds goto __fail; 6291da177e4SLinus Torvalds } 6301da177e4SLinus Torvalds if (snd_sbdsp_get_byte(p->chip)) 6311da177e4SLinus Torvalds goto __fail; 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) { 6341da177e4SLinus Torvalds i = 0; 6351da177e4SLinus Torvalds /* some codecs (FastSpeech) take some time to initialize */ 6361da177e4SLinus Torvalds while (1) { 6371da177e4SLinus Torvalds snd_sbdsp_command(p->chip, 0x03); 6381da177e4SLinus Torvalds status = snd_sbdsp_get_byte(p->chip); 6391da177e4SLinus Torvalds if (status == 0x55 || ++i >= 10) 6401da177e4SLinus Torvalds break; 6411da177e4SLinus Torvalds udelay (10); 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds if (status != 0x55) { 6449bf8e7ddSHarvey Harrison snd_printd("%s: Microcode initialization failed\n", __func__); 6451da177e4SLinus Torvalds goto __fail; 6461da177e4SLinus Torvalds } 6471da177e4SLinus Torvalds } else { 6481da177e4SLinus Torvalds /* 6491da177e4SLinus Torvalds * Read mixer register SB_DSP4_DMASETUP after loading 'main' code. 6501da177e4SLinus Torvalds * Start CSP chip if no 16bit DMA channel is set - some kind 6511da177e4SLinus Torvalds * of autorun or perhaps a bugfix? 6521da177e4SLinus Torvalds */ 6531da177e4SLinus Torvalds spin_lock(&p->chip->mixer_lock); 6541da177e4SLinus Torvalds status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP); 6551da177e4SLinus Torvalds spin_unlock(&p->chip->mixer_lock); 6561da177e4SLinus Torvalds if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) { 6571da177e4SLinus Torvalds err = (set_codec_parameter(p->chip, 0xaa, 0x00) || 6581da177e4SLinus Torvalds set_codec_parameter(p->chip, 0xff, 0x00)); 6591da177e4SLinus Torvalds snd_sbdsp_reset(p->chip); /* really! */ 6601da177e4SLinus Torvalds if (err) 6611da177e4SLinus Torvalds goto __fail; 6621da177e4SLinus Torvalds set_mode_register(p->chip, 0xc0); /* c0 = STOP */ 6631da177e4SLinus Torvalds set_mode_register(p->chip, 0x70); /* 70 = RUN */ 6641da177e4SLinus Torvalds } 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds result = 0; 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds __fail: 6691da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->reg_lock, flags); 6701da177e4SLinus Torvalds return result; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds 673029d64b0STakashi Iwai static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __user *buf, int size, int load_flags) 6741da177e4SLinus Torvalds { 67568425adcSLi Zefan int err; 67668425adcSLi Zefan unsigned char *kbuf; 67768425adcSLi Zefan 67868425adcSLi Zefan kbuf = memdup_user(buf, size); 67968425adcSLi Zefan if (IS_ERR(kbuf)) 68068425adcSLi Zefan return PTR_ERR(kbuf); 68168425adcSLi Zefan 6821da177e4SLinus Torvalds err = snd_sb_csp_load(p, kbuf, size, load_flags); 68368425adcSLi Zefan 6841da177e4SLinus Torvalds kfree(kbuf); 6851da177e4SLinus Torvalds return err; 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds 688de66d53eSClemens Ladisch static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags) 689de66d53eSClemens Ladisch { 690de66d53eSClemens Ladisch static const char *const names[] = { 691de66d53eSClemens Ladisch "sb16/mulaw_main.csp", 692de66d53eSClemens Ladisch "sb16/alaw_main.csp", 693de66d53eSClemens Ladisch "sb16/ima_adpcm_init.csp", 694de66d53eSClemens Ladisch "sb16/ima_adpcm_playback.csp", 695de66d53eSClemens Ladisch "sb16/ima_adpcm_capture.csp", 696de66d53eSClemens Ladisch }; 697de66d53eSClemens Ladisch const struct firmware *program; 698de66d53eSClemens Ladisch 699de66d53eSClemens Ladisch BUILD_BUG_ON(ARRAY_SIZE(names) != CSP_PROGRAM_COUNT); 700de66d53eSClemens Ladisch program = p->csp_programs[index]; 701de66d53eSClemens Ladisch if (!program) { 702b7dd2b34STakashi Iwai int err = request_firmware(&program, names[index], 703b7dd2b34STakashi Iwai p->chip->card->dev); 704b7dd2b34STakashi Iwai if (err < 0) 705de66d53eSClemens Ladisch return err; 706b7dd2b34STakashi Iwai p->csp_programs[index] = program; 707de66d53eSClemens Ladisch } 708de66d53eSClemens Ladisch return snd_sb_csp_load(p, program->data, program->size, flags); 709de66d53eSClemens Ladisch } 710de66d53eSClemens Ladisch 7111da177e4SLinus Torvalds /* 7121da177e4SLinus Torvalds * autoload hardware codec if necessary 7131da177e4SLinus Torvalds * return 0 if CSP is loaded and ready to run (p->running != 0) 7141da177e4SLinus Torvalds */ 715e5d3765bSTakashi Iwai static int snd_sb_csp_autoload(struct snd_sb_csp * p, snd_pcm_format_t pcm_sfmt, int play_rec_mode) 7161da177e4SLinus Torvalds { 7171da177e4SLinus Torvalds unsigned long flags; 7181da177e4SLinus Torvalds int err = 0; 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds /* if CSP is running or manually loaded then exit */ 7211da177e4SLinus Torvalds if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED)) 7221da177e4SLinus Torvalds return -EBUSY; 7231da177e4SLinus Torvalds 7241da177e4SLinus Torvalds /* autoload microcode only if requested hardware codec is not already loaded */ 725e5d3765bSTakashi Iwai if (((1U << (__force int)pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) { 7261da177e4SLinus Torvalds p->running = SNDRV_SB_CSP_ST_AUTO; 7271da177e4SLinus Torvalds } else { 7281da177e4SLinus Torvalds switch (pcm_sfmt) { 7291da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_MU_LAW: 730de66d53eSClemens Ladisch err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_MULAW, 0); 7311da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; 7321da177e4SLinus Torvalds p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; 7331da177e4SLinus Torvalds break; 7341da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_A_LAW: 735de66d53eSClemens Ladisch err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_ALAW, 0); 7361da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; 7371da177e4SLinus Torvalds p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; 7381da177e4SLinus Torvalds break; 7391da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_IMA_ADPCM: 740de66d53eSClemens Ladisch err = snd_sb_csp_firmware_load(p, CSP_PROGRAM_ADPCM_INIT, 7411da177e4SLinus Torvalds SNDRV_SB_CSP_LOAD_INITBLOCK); 7421da177e4SLinus Torvalds if (err) 7431da177e4SLinus Torvalds break; 7441da177e4SLinus Torvalds if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) { 745de66d53eSClemens Ladisch err = snd_sb_csp_firmware_load 746de66d53eSClemens Ladisch (p, CSP_PROGRAM_ADPCM_PLAYBACK, 0); 7471da177e4SLinus Torvalds p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE; 7481da177e4SLinus Torvalds } else { 749de66d53eSClemens Ladisch err = snd_sb_csp_firmware_load 750de66d53eSClemens Ladisch (p, CSP_PROGRAM_ADPCM_CAPTURE, 0); 7511da177e4SLinus Torvalds p->mode = SNDRV_SB_CSP_MODE_DSP_READ; 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; 7541da177e4SLinus Torvalds break; 7551da177e4SLinus Torvalds default: 7561da177e4SLinus Torvalds /* Decouple CSP from IRQ and DMAREQ lines */ 7571da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_AUTO) { 7581da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->reg_lock, flags); 7591da177e4SLinus Torvalds set_mode_register(p->chip, 0xfc); 7601da177e4SLinus Torvalds set_mode_register(p->chip, 0x00); 7611da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->reg_lock, flags); 7621da177e4SLinus Torvalds p->running = 0; /* clear autoloaded flag */ 7631da177e4SLinus Torvalds } 7641da177e4SLinus Torvalds return -EINVAL; 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds if (err) { 7671da177e4SLinus Torvalds p->acc_format = 0; 7681da177e4SLinus Torvalds p->acc_channels = p->acc_width = p->acc_rates = 0; 7691da177e4SLinus Torvalds 7701da177e4SLinus Torvalds p->running = 0; /* clear autoloaded flag */ 7711da177e4SLinus Torvalds p->mode = 0; 7721da177e4SLinus Torvalds return (err); 7731da177e4SLinus Torvalds } else { 7741da177e4SLinus Torvalds p->running = SNDRV_SB_CSP_ST_AUTO; /* set autoloaded flag */ 7751da177e4SLinus Torvalds p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT; /* only 16 bit data */ 7761da177e4SLinus Torvalds p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO; 7771da177e4SLinus Torvalds p->acc_rates = SNDRV_SB_CSP_RATE_ALL; /* HW codecs accept all rates */ 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds 7801da177e4SLinus Torvalds } 7811da177e4SLinus Torvalds return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds /* 7851da177e4SLinus Torvalds * start CSP 7861da177e4SLinus Torvalds */ 787029d64b0STakashi Iwai static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channels) 7881da177e4SLinus Torvalds { 7891da177e4SLinus Torvalds unsigned char s_type; /* sample type */ 7901da177e4SLinus Torvalds unsigned char mixL, mixR; 7911da177e4SLinus Torvalds int result = -EIO; 7921da177e4SLinus Torvalds unsigned long flags; 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) { 7959bf8e7ddSHarvey Harrison snd_printd("%s: Microcode not loaded\n", __func__); 7961da177e4SLinus Torvalds return -ENXIO; 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_RUNNING) { 7999bf8e7ddSHarvey Harrison snd_printd("%s: CSP already running\n", __func__); 8001da177e4SLinus Torvalds return -EBUSY; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds if (!(sample_width & p->acc_width)) { 8039bf8e7ddSHarvey Harrison snd_printd("%s: Unsupported PCM sample width\n", __func__); 8041da177e4SLinus Torvalds return -EINVAL; 8051da177e4SLinus Torvalds } 8061da177e4SLinus Torvalds if (!(channels & p->acc_channels)) { 8079bf8e7ddSHarvey Harrison snd_printd("%s: Invalid number of channels\n", __func__); 8081da177e4SLinus Torvalds return -EINVAL; 8091da177e4SLinus Torvalds } 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds /* Mute PCM volume */ 8121da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->mixer_lock, flags); 8131da177e4SLinus Torvalds mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); 8141da177e4SLinus Torvalds mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); 8151da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); 8161da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds spin_lock(&p->chip->reg_lock); 8191da177e4SLinus Torvalds set_mode_register(p->chip, 0xc0); /* c0 = STOP */ 8201da177e4SLinus Torvalds set_mode_register(p->chip, 0x70); /* 70 = RUN */ 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds s_type = 0x00; 8231da177e4SLinus Torvalds if (channels == SNDRV_SB_CSP_MONO) 8241da177e4SLinus Torvalds s_type = 0x11; /* 000n 000n (n = 1 if mono) */ 8251da177e4SLinus Torvalds if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT) 8261da177e4SLinus Torvalds s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */ 8271da177e4SLinus Torvalds 8281da177e4SLinus Torvalds if (set_codec_parameter(p->chip, 0x81, s_type)) { 8299bf8e7ddSHarvey Harrison snd_printd("%s: Set sample type command failed\n", __func__); 8301da177e4SLinus Torvalds goto __fail; 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds if (set_codec_parameter(p->chip, 0x80, 0x00)) { 8339bf8e7ddSHarvey Harrison snd_printd("%s: Codec start command failed\n", __func__); 8341da177e4SLinus Torvalds goto __fail; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds p->run_width = sample_width; 8371da177e4SLinus Torvalds p->run_channels = channels; 8381da177e4SLinus Torvalds 8391da177e4SLinus Torvalds p->running |= SNDRV_SB_CSP_ST_RUNNING; 8401da177e4SLinus Torvalds 8411da177e4SLinus Torvalds if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) { 8421da177e4SLinus Torvalds set_codec_parameter(p->chip, 0xe0, 0x01); 8431da177e4SLinus Torvalds /* enable QSound decoder */ 8441da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x00, 0xff); 8451da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x01, 0xff); 8461da177e4SLinus Torvalds p->running |= SNDRV_SB_CSP_ST_QSOUND; 8471da177e4SLinus Torvalds /* set QSound startup value */ 8481da177e4SLinus Torvalds snd_sb_csp_qsound_transfer(p); 8491da177e4SLinus Torvalds } 8501da177e4SLinus Torvalds result = 0; 8511da177e4SLinus Torvalds 8521da177e4SLinus Torvalds __fail: 8531da177e4SLinus Torvalds spin_unlock(&p->chip->reg_lock); 8541da177e4SLinus Torvalds 8551da177e4SLinus Torvalds /* restore PCM volume */ 8561da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); 8571da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); 8581da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->mixer_lock, flags); 8591da177e4SLinus Torvalds 8601da177e4SLinus Torvalds return result; 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds 8631da177e4SLinus Torvalds /* 8641da177e4SLinus Torvalds * stop CSP 8651da177e4SLinus Torvalds */ 866029d64b0STakashi Iwai static int snd_sb_csp_stop(struct snd_sb_csp * p) 8671da177e4SLinus Torvalds { 8681da177e4SLinus Torvalds int result; 8691da177e4SLinus Torvalds unsigned char mixL, mixR; 8701da177e4SLinus Torvalds unsigned long flags; 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) 8731da177e4SLinus Torvalds return 0; 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds /* Mute PCM volume */ 8761da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->mixer_lock, flags); 8771da177e4SLinus Torvalds mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); 8781da177e4SLinus Torvalds mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); 8791da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); 8801da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds spin_lock(&p->chip->reg_lock); 8831da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_QSOUND) { 8841da177e4SLinus Torvalds set_codec_parameter(p->chip, 0xe0, 0x01); 8851da177e4SLinus Torvalds /* disable QSound decoder */ 8861da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x00, 0x00); 8871da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x01, 0x00); 8881da177e4SLinus Torvalds 8891da177e4SLinus Torvalds p->running &= ~SNDRV_SB_CSP_ST_QSOUND; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */ 8921da177e4SLinus Torvalds spin_unlock(&p->chip->reg_lock); 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds /* restore PCM volume */ 8951da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); 8961da177e4SLinus Torvalds snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); 8971da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->mixer_lock, flags); 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds if (!(result)) 9001da177e4SLinus Torvalds p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING); 9011da177e4SLinus Torvalds return result; 9021da177e4SLinus Torvalds } 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds /* 9051da177e4SLinus Torvalds * pause CSP codec and hold DMA transfer 9061da177e4SLinus Torvalds */ 907029d64b0STakashi Iwai static int snd_sb_csp_pause(struct snd_sb_csp * p) 9081da177e4SLinus Torvalds { 9091da177e4SLinus Torvalds int result; 9101da177e4SLinus Torvalds unsigned long flags; 9111da177e4SLinus Torvalds 9121da177e4SLinus Torvalds if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) 9131da177e4SLinus Torvalds return -EBUSY; 9141da177e4SLinus Torvalds 9151da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->reg_lock, flags); 9161da177e4SLinus Torvalds result = set_codec_parameter(p->chip, 0x80, 0xff); 9171da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->reg_lock, flags); 9181da177e4SLinus Torvalds if (!(result)) 9191da177e4SLinus Torvalds p->running |= SNDRV_SB_CSP_ST_PAUSED; 9201da177e4SLinus Torvalds 9211da177e4SLinus Torvalds return result; 9221da177e4SLinus Torvalds } 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds /* 9251da177e4SLinus Torvalds * restart CSP codec and resume DMA transfer 9261da177e4SLinus Torvalds */ 927029d64b0STakashi Iwai static int snd_sb_csp_restart(struct snd_sb_csp * p) 9281da177e4SLinus Torvalds { 9291da177e4SLinus Torvalds int result; 9301da177e4SLinus Torvalds unsigned long flags; 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds if (!(p->running & SNDRV_SB_CSP_ST_PAUSED)) 9331da177e4SLinus Torvalds return -EBUSY; 9341da177e4SLinus Torvalds 9351da177e4SLinus Torvalds spin_lock_irqsave(&p->chip->reg_lock, flags); 9361da177e4SLinus Torvalds result = set_codec_parameter(p->chip, 0x80, 0x00); 9371da177e4SLinus Torvalds spin_unlock_irqrestore(&p->chip->reg_lock, flags); 9381da177e4SLinus Torvalds if (!(result)) 9391da177e4SLinus Torvalds p->running &= ~SNDRV_SB_CSP_ST_PAUSED; 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds return result; 9421da177e4SLinus Torvalds } 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds /* ------------------------------ */ 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds /* 9471da177e4SLinus Torvalds * QSound mixer control for PCM 9481da177e4SLinus Torvalds */ 9491da177e4SLinus Torvalds 950a5ce8890STakashi Iwai #define snd_sb_qsound_switch_info snd_ctl_boolean_mono_info 9511da177e4SLinus Torvalds 952029d64b0STakashi Iwai static int snd_sb_qsound_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9531da177e4SLinus Torvalds { 954029d64b0STakashi Iwai struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol); 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0; 9571da177e4SLinus Torvalds return 0; 9581da177e4SLinus Torvalds } 9591da177e4SLinus Torvalds 960029d64b0STakashi Iwai static int snd_sb_qsound_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9611da177e4SLinus Torvalds { 962029d64b0STakashi Iwai struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol); 9631da177e4SLinus Torvalds unsigned long flags; 9641da177e4SLinus Torvalds int change; 9651da177e4SLinus Torvalds unsigned char nval; 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds nval = ucontrol->value.integer.value[0] & 0x01; 9681da177e4SLinus Torvalds spin_lock_irqsave(&p->q_lock, flags); 9691da177e4SLinus Torvalds change = p->q_enabled != nval; 9701da177e4SLinus Torvalds p->q_enabled = nval; 9711da177e4SLinus Torvalds spin_unlock_irqrestore(&p->q_lock, flags); 9721da177e4SLinus Torvalds return change; 9731da177e4SLinus Torvalds } 9741da177e4SLinus Torvalds 975029d64b0STakashi Iwai static int snd_sb_qsound_space_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 9761da177e4SLinus Torvalds { 9771da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 9781da177e4SLinus Torvalds uinfo->count = 2; 9791da177e4SLinus Torvalds uinfo->value.integer.min = 0; 9801da177e4SLinus Torvalds uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; 9811da177e4SLinus Torvalds return 0; 9821da177e4SLinus Torvalds } 9831da177e4SLinus Torvalds 984029d64b0STakashi Iwai static int snd_sb_qsound_space_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9851da177e4SLinus Torvalds { 986029d64b0STakashi Iwai struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol); 9871da177e4SLinus Torvalds unsigned long flags; 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds spin_lock_irqsave(&p->q_lock, flags); 9901da177e4SLinus Torvalds ucontrol->value.integer.value[0] = p->qpos_left; 9911da177e4SLinus Torvalds ucontrol->value.integer.value[1] = p->qpos_right; 9921da177e4SLinus Torvalds spin_unlock_irqrestore(&p->q_lock, flags); 9931da177e4SLinus Torvalds return 0; 9941da177e4SLinus Torvalds } 9951da177e4SLinus Torvalds 996029d64b0STakashi Iwai static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9971da177e4SLinus Torvalds { 998029d64b0STakashi Iwai struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol); 9991da177e4SLinus Torvalds unsigned long flags; 10001da177e4SLinus Torvalds int change; 10011da177e4SLinus Torvalds unsigned char nval1, nval2; 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds nval1 = ucontrol->value.integer.value[0]; 10041da177e4SLinus Torvalds if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) 10051da177e4SLinus Torvalds nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; 10061da177e4SLinus Torvalds nval2 = ucontrol->value.integer.value[1]; 10071da177e4SLinus Torvalds if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) 10081da177e4SLinus Torvalds nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; 10091da177e4SLinus Torvalds spin_lock_irqsave(&p->q_lock, flags); 10101da177e4SLinus Torvalds change = p->qpos_left != nval1 || p->qpos_right != nval2; 10111da177e4SLinus Torvalds p->qpos_left = nval1; 10121da177e4SLinus Torvalds p->qpos_right = nval2; 10131da177e4SLinus Torvalds p->qpos_changed = change; 10141da177e4SLinus Torvalds spin_unlock_irqrestore(&p->q_lock, flags); 10151da177e4SLinus Torvalds return change; 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds 10183a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_sb_qsound_switch = { 10191da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10201da177e4SLinus Torvalds .name = "3D Control - Switch", 10211da177e4SLinus Torvalds .info = snd_sb_qsound_switch_info, 10221da177e4SLinus Torvalds .get = snd_sb_qsound_switch_get, 10231da177e4SLinus Torvalds .put = snd_sb_qsound_switch_put 10241da177e4SLinus Torvalds }; 10251da177e4SLinus Torvalds 10263a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_sb_qsound_space = { 10271da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10281da177e4SLinus Torvalds .name = "3D Control - Space", 10291da177e4SLinus Torvalds .info = snd_sb_qsound_space_info, 10301da177e4SLinus Torvalds .get = snd_sb_qsound_space_get, 10311da177e4SLinus Torvalds .put = snd_sb_qsound_space_put 10321da177e4SLinus Torvalds }; 10331da177e4SLinus Torvalds 1034029d64b0STakashi Iwai static int snd_sb_qsound_build(struct snd_sb_csp * p) 10351da177e4SLinus Torvalds { 1036029d64b0STakashi Iwai struct snd_card *card; 10371da177e4SLinus Torvalds int err; 10381da177e4SLinus Torvalds 1039622207dcSTakashi Iwai if (snd_BUG_ON(!p)) 1040622207dcSTakashi Iwai return -EINVAL; 10411da177e4SLinus Torvalds 10421da177e4SLinus Torvalds card = p->chip->card; 10431da177e4SLinus Torvalds p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2; 10441da177e4SLinus Torvalds p->qpos_changed = 0; 10451da177e4SLinus Torvalds 10461da177e4SLinus Torvalds spin_lock_init(&p->q_lock); 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0) 10491da177e4SLinus Torvalds goto __error; 10501da177e4SLinus Torvalds if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0) 10511da177e4SLinus Torvalds goto __error; 10521da177e4SLinus Torvalds 10531da177e4SLinus Torvalds return 0; 10541da177e4SLinus Torvalds 10551da177e4SLinus Torvalds __error: 10561da177e4SLinus Torvalds snd_sb_qsound_destroy(p); 10571da177e4SLinus Torvalds return err; 10581da177e4SLinus Torvalds } 10591da177e4SLinus Torvalds 1060029d64b0STakashi Iwai static void snd_sb_qsound_destroy(struct snd_sb_csp * p) 10611da177e4SLinus Torvalds { 1062029d64b0STakashi Iwai struct snd_card *card; 10631da177e4SLinus Torvalds unsigned long flags; 10641da177e4SLinus Torvalds 1065622207dcSTakashi Iwai if (snd_BUG_ON(!p)) 1066622207dcSTakashi Iwai return; 10671da177e4SLinus Torvalds 10681da177e4SLinus Torvalds card = p->chip->card; 10691da177e4SLinus Torvalds 10701da177e4SLinus Torvalds down_write(&card->controls_rwsem); 10711da177e4SLinus Torvalds if (p->qsound_switch) 10721da177e4SLinus Torvalds snd_ctl_remove(card, p->qsound_switch); 10731da177e4SLinus Torvalds if (p->qsound_space) 10741da177e4SLinus Torvalds snd_ctl_remove(card, p->qsound_space); 10751da177e4SLinus Torvalds up_write(&card->controls_rwsem); 10761da177e4SLinus Torvalds 10771da177e4SLinus Torvalds /* cancel pending transfer of QSound parameters */ 10781da177e4SLinus Torvalds spin_lock_irqsave (&p->q_lock, flags); 10791da177e4SLinus Torvalds p->qpos_changed = 0; 10801da177e4SLinus Torvalds spin_unlock_irqrestore (&p->q_lock, flags); 10811da177e4SLinus Torvalds } 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds /* 10841da177e4SLinus Torvalds * Transfer qsound parameters to CSP, 10851da177e4SLinus Torvalds * function should be called from interrupt routine 10861da177e4SLinus Torvalds */ 1087029d64b0STakashi Iwai static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p) 10881da177e4SLinus Torvalds { 10891da177e4SLinus Torvalds int err = -ENXIO; 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds spin_lock(&p->q_lock); 10921da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_QSOUND) { 10931da177e4SLinus Torvalds set_codec_parameter(p->chip, 0xe0, 0x01); 10941da177e4SLinus Torvalds /* left channel */ 10951da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x00, p->qpos_left); 10961da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x02, 0x00); 10971da177e4SLinus Torvalds /* right channel */ 10981da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x00, p->qpos_right); 10991da177e4SLinus Torvalds set_codec_parameter(p->chip, 0x03, 0x00); 11001da177e4SLinus Torvalds err = 0; 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds p->qpos_changed = 0; 11031da177e4SLinus Torvalds spin_unlock(&p->q_lock); 11041da177e4SLinus Torvalds return err; 11051da177e4SLinus Torvalds } 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds /* ------------------------------ */ 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds /* 11101da177e4SLinus Torvalds * proc interface 11111da177e4SLinus Torvalds */ 1112029d64b0STakashi Iwai static int init_proc_entry(struct snd_sb_csp * p, int device) 11131da177e4SLinus Torvalds { 11141da177e4SLinus Torvalds char name[16]; 11151bac5e1cSTakashi Iwai 11161da177e4SLinus Torvalds sprintf(name, "cspD%d", device); 11171bac5e1cSTakashi Iwai snd_card_ro_proc_new(p->chip->card, name, p, info_read); 11181da177e4SLinus Torvalds return 0; 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds 1121029d64b0STakashi Iwai static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 11221da177e4SLinus Torvalds { 1123029d64b0STakashi Iwai struct snd_sb_csp *p = entry->private_data; 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f)); 11261da177e4SLinus Torvalds snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'), 11271da177e4SLinus Torvalds ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'), 11281da177e4SLinus Torvalds ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'), 11291da177e4SLinus Torvalds ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-')); 11301da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_LOADED) { 11311da177e4SLinus Torvalds snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr); 11321da177e4SLinus Torvalds snd_iprintf(buffer, "Sample rates: "); 11331da177e4SLinus Torvalds if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) { 11341da177e4SLinus Torvalds snd_iprintf(buffer, "All\n"); 11351da177e4SLinus Torvalds } else { 11361da177e4SLinus Torvalds snd_iprintf(buffer, "%s%s%s%s\n", 11371da177e4SLinus Torvalds ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""), 11381da177e4SLinus Torvalds ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""), 11391da177e4SLinus Torvalds ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""), 11401da177e4SLinus Torvalds ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : "")); 11411da177e4SLinus Torvalds } 11421da177e4SLinus Torvalds if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { 11431da177e4SLinus Torvalds snd_iprintf(buffer, "QSound decoder %sabled\n", 11441da177e4SLinus Torvalds p->q_enabled ? "en" : "dis"); 11451da177e4SLinus Torvalds } else { 11461da177e4SLinus Torvalds snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n", 11471da177e4SLinus Torvalds p->acc_format, 11481da177e4SLinus Torvalds ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"), 11491da177e4SLinus Torvalds ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"), 11501da177e4SLinus Torvalds ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"), 11511da177e4SLinus Torvalds ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"), 11521da177e4SLinus Torvalds ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"), 11531da177e4SLinus Torvalds ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-")); 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_AUTO) { 11571da177e4SLinus Torvalds snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n"); 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_RUNNING) { 11601da177e4SLinus Torvalds snd_iprintf(buffer, "Processing %dbit %s PCM samples\n", 11611da177e4SLinus Torvalds ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8), 11621da177e4SLinus Torvalds ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo")); 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds if (p->running & SNDRV_SB_CSP_ST_QSOUND) { 11651da177e4SLinus Torvalds snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n", 11661da177e4SLinus Torvalds p->qpos_left, p->qpos_right); 11671da177e4SLinus Torvalds } 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds /* */ 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds EXPORT_SYMBOL(snd_sb_csp_new); 1173