162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * C-Media CMI8787 driver for the Studio Evolution SE6X
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * CMI8787:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *   SPI    -> microcontroller (not actually used)
1262306a36Sopenharmony_ci *   GPIO 0 -> do.
1362306a36Sopenharmony_ci *   GPIO 2 -> do.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *   DAC0   -> both PCM1792A (L+R, each in mono mode)
1662306a36Sopenharmony_ci *   ADC1  <-  1st PCM1804
1762306a36Sopenharmony_ci *   ADC2  <-  2nd PCM1804
1862306a36Sopenharmony_ci *   ADC3  <-  3rd PCM1804
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/pci.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <sound/core.h>
2462306a36Sopenharmony_ci#include <sound/control.h>
2562306a36Sopenharmony_ci#include <sound/initval.h>
2662306a36Sopenharmony_ci#include <sound/pcm.h>
2762306a36Sopenharmony_ci#include "oxygen.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
3062306a36Sopenharmony_ciMODULE_DESCRIPTION("Studio Evolution SE6X driver");
3162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
3462306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
3562306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
3862306a36Sopenharmony_ciMODULE_PARM_DESC(index, "card index");
3962306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string");
4162306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
4262306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "enable card");
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct pci_device_id se6x_ids[] = {
4562306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
4662306a36Sopenharmony_ci	{ }
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, se6x_ids);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void se6x_init(struct oxygen *chip)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 0x005);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	snd_component_add(chip->card, "PCM1792A");
5562306a36Sopenharmony_ci	snd_component_add(chip->card, "PCM1804");
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int se6x_control_filter(struct snd_kcontrol_new *template)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	/* no DAC volume/mute */
6162306a36Sopenharmony_ci	if (!strncmp(template->name, "Master Playback ", 16))
6262306a36Sopenharmony_ci		return 1;
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void se6x_cleanup(struct oxygen *chip)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void set_pcm1792a_params(struct oxygen *chip,
7162306a36Sopenharmony_ci				struct snd_pcm_hw_params *params)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	/* nothing to do (the microcontroller monitors DAC_LRCK) */
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void set_pcm1804_params(struct oxygen *chip,
7762306a36Sopenharmony_ci			       struct snd_pcm_hw_params *params)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic unsigned int se6x_adjust_dac_routing(struct oxygen *chip,
8262306a36Sopenharmony_ci					    unsigned int play_routing)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	/* route the same stereo pair to DAC0 and DAC1 */
8562306a36Sopenharmony_ci	return ( play_routing       & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
8662306a36Sopenharmony_ci	       ((play_routing << 2) & OXYGEN_PLAY_DAC1_SOURCE_MASK);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const struct oxygen_model model_se6x = {
9062306a36Sopenharmony_ci	.shortname = "Studio Evolution SE6X",
9162306a36Sopenharmony_ci	.longname = "C-Media Oxygen HD Audio",
9262306a36Sopenharmony_ci	.chip = "CMI8787",
9362306a36Sopenharmony_ci	.init = se6x_init,
9462306a36Sopenharmony_ci	.control_filter = se6x_control_filter,
9562306a36Sopenharmony_ci	.cleanup = se6x_cleanup,
9662306a36Sopenharmony_ci	.set_dac_params = set_pcm1792a_params,
9762306a36Sopenharmony_ci	.set_adc_params = set_pcm1804_params,
9862306a36Sopenharmony_ci	.adjust_dac_routing = se6x_adjust_dac_routing,
9962306a36Sopenharmony_ci	.device_config = PLAYBACK_0_TO_I2S |
10062306a36Sopenharmony_ci			 CAPTURE_0_FROM_I2S_1 |
10162306a36Sopenharmony_ci			 CAPTURE_2_FROM_I2S_2 |
10262306a36Sopenharmony_ci			 CAPTURE_3_FROM_I2S_3,
10362306a36Sopenharmony_ci	.dac_channels_pcm = 2,
10462306a36Sopenharmony_ci	.function_flags = OXYGEN_FUNCTION_SPI,
10562306a36Sopenharmony_ci	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
10662306a36Sopenharmony_ci	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
10762306a36Sopenharmony_ci	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
10862306a36Sopenharmony_ci	.adc_i2s_format = OXYGEN_I2S_FORMAT_I2S,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int se6x_get_model(struct oxygen *chip,
11262306a36Sopenharmony_ci			  const struct pci_device_id *pci_id)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	chip->model = model_se6x;
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int se6x_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	static int dev;
12162306a36Sopenharmony_ci	int err;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
12462306a36Sopenharmony_ci		return -ENODEV;
12562306a36Sopenharmony_ci	if (!enable[dev]) {
12662306a36Sopenharmony_ci		++dev;
12762306a36Sopenharmony_ci		return -ENOENT;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
13062306a36Sopenharmony_ci			       se6x_ids, se6x_get_model);
13162306a36Sopenharmony_ci	if (err >= 0)
13262306a36Sopenharmony_ci		++dev;
13362306a36Sopenharmony_ci	return err;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct pci_driver se6x_driver = {
13762306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
13862306a36Sopenharmony_ci	.id_table = se6x_ids,
13962306a36Sopenharmony_ci	.probe = se6x_probe,
14062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
14162306a36Sopenharmony_ci	.driver = {
14262306a36Sopenharmony_ci		.pm = &oxygen_pci_pm,
14362306a36Sopenharmony_ci	},
14462306a36Sopenharmony_ci#endif
14562306a36Sopenharmony_ci	.shutdown = oxygen_pci_shutdown,
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cimodule_pci_driver(se6x_driver);
149