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 33 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 34 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 35 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 36 37 module_param_array(index, int, NULL, 0444); 38 MODULE_PARM_DESC(index, "card index"); 39 module_param_array(id, charp, NULL, 0444); 40 MODULE_PARM_DESC(id, "ID string"); 41 module_param_array(enable, bool, NULL, 0444); 42 MODULE_PARM_DESC(enable, "enable card"); 43 44 static const struct pci_device_id se6x_ids[] = { 45 { OXYGEN_PCI_SUBID(0x13f6, 0x8788) }, 46 { } 47 }; 48 MODULE_DEVICE_TABLE(pci, se6x_ids); 49 50 static void se6x_init(struct oxygen *chip) 51 { 52 oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 0x005); 53 54 snd_component_add(chip->card, "PCM1792A"); 55 snd_component_add(chip->card, "PCM1804"); 56 } 57 58 static int se6x_control_filter(struct snd_kcontrol_new *template) 59 { 60 /* no DAC volume/mute */ 61 if (!strncmp(template->name, "Master Playback ", 16)) 62 return 1; 63 return 0; 64 } 65 66 static void se6x_cleanup(struct oxygen *chip) 67 { 68 } 69 70 static void set_pcm1792a_params(struct oxygen *chip, 71 struct snd_pcm_hw_params *params) 72 { 73 /* nothing to do (the microcontroller monitors DAC_LRCK) */ 74 } 75 76 static void set_pcm1804_params(struct oxygen *chip, 77 struct snd_pcm_hw_params *params) 78 { 79 } 80 81 static unsigned int se6x_adjust_dac_routing(struct oxygen *chip, 82 unsigned int play_routing) 83 { 84 /* route the same stereo pair to DAC0 and DAC1 */ 85 return ( play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | 86 ((play_routing << 2) & OXYGEN_PLAY_DAC1_SOURCE_MASK); 87 } 88 89 static const struct oxygen_model model_se6x = { 90 .shortname = "Studio Evolution SE6X", 91 .longname = "C-Media Oxygen HD Audio", 92 .chip = "CMI8787", 93 .init = se6x_init, 94 .control_filter = se6x_control_filter, 95 .cleanup = se6x_cleanup, 96 .set_dac_params = set_pcm1792a_params, 97 .set_adc_params = set_pcm1804_params, 98 .adjust_dac_routing = se6x_adjust_dac_routing, 99 .device_config = PLAYBACK_0_TO_I2S | 100 CAPTURE_0_FROM_I2S_1 | 101 CAPTURE_2_FROM_I2S_2 | 102 CAPTURE_3_FROM_I2S_3, 103 .dac_channels_pcm = 2, 104 .function_flags = OXYGEN_FUNCTION_SPI, 105 .dac_mclks = OXYGEN_MCLKS(256, 128, 128), 106 .adc_mclks = OXYGEN_MCLKS(256, 256, 128), 107 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 108 .adc_i2s_format = OXYGEN_I2S_FORMAT_I2S, 109 }; 110 111 static int se6x_get_model(struct oxygen *chip, 112 const struct pci_device_id *pci_id) 113 { 114 chip->model = model_se6x; 115 return 0; 116 } 117 118 static int se6x_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) 119 { 120 static int dev; 121 int err; 122 123 if (dev >= SNDRV_CARDS) 124 return -ENODEV; 125 if (!enable[dev]) { 126 ++dev; 127 return -ENOENT; 128 } 129 err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE, 130 se6x_ids, se6x_get_model); 131 if (err >= 0) 132 ++dev; 133 return err; 134 } 135 136 static struct pci_driver se6x_driver = { 137 .name = KBUILD_MODNAME, 138 .id_table = se6x_ids, 139 .probe = se6x_probe, 140 .remove = oxygen_pci_remove, 141 #ifdef CONFIG_PM_SLEEP 142 .driver = { 143 .pm = &oxygen_pci_pm, 144 }, 145 #endif 146 .shutdown = oxygen_pci_shutdown, 147 }; 148 149 module_pci_driver(se6x_driver); 150