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