1/* 2 * C-Media CMI8787 driver for the Studio Evolution SE6X 3 * 4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 5 * 6 * This driver is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2. 8 * 9 * This driver is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this driver; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18/* 19 * CMI8787: 20 * 21 * SPI -> microcontroller (not actually used) 22 * GPIO 0 -> do. 23 * GPIO 2 -> do. 24 * 25 * DAC0 -> both PCM1792A (L+R, each in mono mode) 26 * ADC1 <- 1st PCM1804 27 * ADC2 <- 2nd PCM1804 28 * ADC3 <- 3rd PCM1804 29 */ 30 31#include <linux/pci.h> 32#include <linux/module.h> 33#include <sound/core.h> 34#include <sound/control.h> 35#include <sound/initval.h> 36#include <sound/pcm.h> 37#include "oxygen.h" 38 39MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 40MODULE_DESCRIPTION("Studio Evolution SE6X driver"); 41MODULE_LICENSE("GPL v2"); 42MODULE_SUPPORTED_DEVICE("{{Studio Evolution,SE6X}}"); 43 44static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 45static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 46static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 47 48module_param_array(index, int, NULL, 0444); 49MODULE_PARM_DESC(index, "card index"); 50module_param_array(id, charp, NULL, 0444); 51MODULE_PARM_DESC(id, "ID string"); 52module_param_array(enable, bool, NULL, 0444); 53MODULE_PARM_DESC(enable, "enable card"); 54 55static const struct pci_device_id se6x_ids[] = { 56 { OXYGEN_PCI_SUBID(0x13f6, 0x8788) }, 57 { } 58}; 59MODULE_DEVICE_TABLE(pci, se6x_ids); 60 61static void se6x_init(struct oxygen *chip) 62{ 63 oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 0x005); 64 65 snd_component_add(chip->card, "PCM1792A"); 66 snd_component_add(chip->card, "PCM1804"); 67} 68 69static int se6x_control_filter(struct snd_kcontrol_new *template) 70{ 71 /* no DAC volume/mute */ 72 if (!strncmp(template->name, "Master Playback ", 16)) 73 return 1; 74 return 0; 75} 76 77static void se6x_cleanup(struct oxygen *chip) 78{ 79} 80 81static void set_pcm1792a_params(struct oxygen *chip, 82 struct snd_pcm_hw_params *params) 83{ 84 /* nothing to do (the microcontroller monitors DAC_LRCK) */ 85} 86 87static void set_pcm1804_params(struct oxygen *chip, 88 struct snd_pcm_hw_params *params) 89{ 90} 91 92static unsigned int se6x_adjust_dac_routing(struct oxygen *chip, 93 unsigned int play_routing) 94{ 95 /* route the same stereo pair to DAC0 and DAC1 */ 96 return ( play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | 97 ((play_routing << 2) & OXYGEN_PLAY_DAC1_SOURCE_MASK); 98} 99 100static const struct oxygen_model model_se6x = { 101 .shortname = "Studio Evolution SE6X", 102 .longname = "C-Media Oxygen HD Audio", 103 .chip = "CMI8787", 104 .init = se6x_init, 105 .control_filter = se6x_control_filter, 106 .cleanup = se6x_cleanup, 107 .set_dac_params = set_pcm1792a_params, 108 .set_adc_params = set_pcm1804_params, 109 .adjust_dac_routing = se6x_adjust_dac_routing, 110 .device_config = PLAYBACK_0_TO_I2S | 111 CAPTURE_0_FROM_I2S_1 | 112 CAPTURE_2_FROM_I2S_2 | 113 CAPTURE_3_FROM_I2S_3, 114 .dac_channels_pcm = 2, 115 .function_flags = OXYGEN_FUNCTION_SPI, 116 .dac_mclks = OXYGEN_MCLKS(256, 128, 128), 117 .adc_mclks = OXYGEN_MCLKS(256, 256, 128), 118 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 119 .adc_i2s_format = OXYGEN_I2S_FORMAT_I2S, 120}; 121 122static int se6x_get_model(struct oxygen *chip, 123 const struct pci_device_id *pci_id) 124{ 125 chip->model = model_se6x; 126 return 0; 127} 128 129static int se6x_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) 130{ 131 static int dev; 132 int err; 133 134 if (dev >= SNDRV_CARDS) 135 return -ENODEV; 136 if (!enable[dev]) { 137 ++dev; 138 return -ENOENT; 139 } 140 err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE, 141 se6x_ids, se6x_get_model); 142 if (err >= 0) 143 ++dev; 144 return err; 145} 146 147static struct pci_driver se6x_driver = { 148 .name = KBUILD_MODNAME, 149 .id_table = se6x_ids, 150 .probe = se6x_probe, 151 .remove = oxygen_pci_remove, 152#ifdef CONFIG_PM_SLEEP 153 .driver = { 154 .pm = &oxygen_pci_pm, 155 }, 156#endif 157 .shutdown = oxygen_pci_shutdown, 158}; 159 160module_pci_driver(se6x_driver); 161