xref: /kernel/linux/linux-6.6/sound/pci/oxygen/oxygen.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * C-Media CMI8788 driver for C-Media's reference design and similar models
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * CMI8788:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *   SPI 0 -> 1st AK4396 (front)
1262306a36Sopenharmony_ci *   SPI 1 -> 2nd AK4396 (surround)
1362306a36Sopenharmony_ci *   SPI 2 -> 3rd AK4396 (center/LFE)
1462306a36Sopenharmony_ci *   SPI 3 -> WM8785
1562306a36Sopenharmony_ci *   SPI 4 -> 4th AK4396 (back)
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *   GPIO 0 -> DFS0 of AK5385
1862306a36Sopenharmony_ci *   GPIO 1 -> DFS1 of AK5385
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * X-Meridian models:
2162306a36Sopenharmony_ci *   GPIO 4 -> enable extension S/PDIF input
2262306a36Sopenharmony_ci *   GPIO 6 -> enable on-board S/PDIF input
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Claro models:
2562306a36Sopenharmony_ci *   GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input
2662306a36Sopenharmony_ci *   GPIO 8 -> enable headphone amplifier
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * CM9780:
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci *   LINE_OUT -> input of ADC
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci *   AUX_IN <- aux
3362306a36Sopenharmony_ci *   CD_IN  <- CD
3462306a36Sopenharmony_ci *   MIC_IN <- mic
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci *   GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include <linux/delay.h>
4062306a36Sopenharmony_ci#include <linux/mutex.h>
4162306a36Sopenharmony_ci#include <linux/pci.h>
4262306a36Sopenharmony_ci#include <linux/module.h>
4362306a36Sopenharmony_ci#include <sound/ac97_codec.h>
4462306a36Sopenharmony_ci#include <sound/control.h>
4562306a36Sopenharmony_ci#include <sound/core.h>
4662306a36Sopenharmony_ci#include <sound/info.h>
4762306a36Sopenharmony_ci#include <sound/initval.h>
4862306a36Sopenharmony_ci#include <sound/pcm.h>
4962306a36Sopenharmony_ci#include <sound/pcm_params.h>
5062306a36Sopenharmony_ci#include <sound/tlv.h>
5162306a36Sopenharmony_ci#include "oxygen.h"
5262306a36Sopenharmony_ci#include "xonar_dg.h"
5362306a36Sopenharmony_ci#include "ak4396.h"
5462306a36Sopenharmony_ci#include "wm8785.h"
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
5762306a36Sopenharmony_ciMODULE_DESCRIPTION("C-Media CMI8788 driver");
5862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
6162306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
6262306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(index, "card index");
6662306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
6762306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string");
6862306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "enable card");
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cienum {
7262306a36Sopenharmony_ci	MODEL_CMEDIA_REF,
7362306a36Sopenharmony_ci	MODEL_MERIDIAN,
7462306a36Sopenharmony_ci	MODEL_MERIDIAN_2G,
7562306a36Sopenharmony_ci	MODEL_CLARO,
7662306a36Sopenharmony_ci	MODEL_CLARO_HALO,
7762306a36Sopenharmony_ci	MODEL_FANTASIA,
7862306a36Sopenharmony_ci	MODEL_SERENADE,
7962306a36Sopenharmony_ci	MODEL_2CH_OUTPUT,
8062306a36Sopenharmony_ci	MODEL_HG2PCI,
8162306a36Sopenharmony_ci	MODEL_XONAR_DG,
8262306a36Sopenharmony_ci	MODEL_XONAR_DGX,
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic const struct pci_device_id oxygen_ids[] = {
8662306a36Sopenharmony_ci	/* C-Media's reference design */
8762306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
8862306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF },
8962306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
9062306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
9162306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
9262306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
9362306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
9462306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
9562306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
9662306a36Sopenharmony_ci	/* Asus Xonar DG */
9762306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
9862306a36Sopenharmony_ci	/* Asus Xonar DGX */
9962306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX },
10062306a36Sopenharmony_ci	/* PCI 2.0 HD Audio */
10162306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
10262306a36Sopenharmony_ci	/* Kuroutoshikou CMI8787-HG2PCI */
10362306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI },
10462306a36Sopenharmony_ci	/* TempoTec HiFier Fantasia */
10562306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA },
10662306a36Sopenharmony_ci	/* TempoTec HiFier Serenade */
10762306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE },
10862306a36Sopenharmony_ci	/* AuzenTech X-Meridian */
10962306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
11062306a36Sopenharmony_ci	/* AuzenTech X-Meridian 2G */
11162306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G },
11262306a36Sopenharmony_ci	/* HT-Omega Claro */
11362306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
11462306a36Sopenharmony_ci	/* HT-Omega Claro halo */
11562306a36Sopenharmony_ci	{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
11662306a36Sopenharmony_ci	{ }
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, oxygen_ids);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define GPIO_AK5385_DFS_MASK	0x0003
12262306a36Sopenharmony_ci#define GPIO_AK5385_DFS_NORMAL	0x0000
12362306a36Sopenharmony_ci#define GPIO_AK5385_DFS_DOUBLE	0x0001
12462306a36Sopenharmony_ci#define GPIO_AK5385_DFS_QUAD	0x0002
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#define GPIO_MERIDIAN_DIG_MASK	0x0050
12762306a36Sopenharmony_ci#define GPIO_MERIDIAN_DIG_EXT	0x0010
12862306a36Sopenharmony_ci#define GPIO_MERIDIAN_DIG_BOARD	0x0040
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define GPIO_CLARO_DIG_COAX	0x0040
13162306a36Sopenharmony_ci#define GPIO_CLARO_HP		0x0100
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistruct generic_data {
13462306a36Sopenharmony_ci	unsigned int dacs;
13562306a36Sopenharmony_ci	u8 ak4396_regs[4][5];
13662306a36Sopenharmony_ci	u16 wm8785_regs[3];
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void ak4396_write(struct oxygen *chip, unsigned int codec,
14062306a36Sopenharmony_ci			 u8 reg, u8 value)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	/* maps ALSA channel pair number to SPI output */
14362306a36Sopenharmony_ci	static const u8 codec_spi_map[4] = {
14462306a36Sopenharmony_ci		0, 1, 2, 4
14562306a36Sopenharmony_ci	};
14662306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
14962306a36Sopenharmony_ci			 OXYGEN_SPI_DATA_LENGTH_2 |
15062306a36Sopenharmony_ci			 OXYGEN_SPI_CLOCK_160 |
15162306a36Sopenharmony_ci			 (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
15262306a36Sopenharmony_ci			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
15362306a36Sopenharmony_ci			 AK4396_WRITE | (reg << 8) | value);
15462306a36Sopenharmony_ci	data->ak4396_regs[codec][reg] = value;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void ak4396_write_cached(struct oxygen *chip, unsigned int codec,
15862306a36Sopenharmony_ci				u8 reg, u8 value)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (value != data->ak4396_regs[codec][reg])
16362306a36Sopenharmony_ci		ak4396_write(chip, codec, reg, value);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
17162306a36Sopenharmony_ci			 OXYGEN_SPI_DATA_LENGTH_2 |
17262306a36Sopenharmony_ci			 OXYGEN_SPI_CLOCK_160 |
17362306a36Sopenharmony_ci			 (3 << OXYGEN_SPI_CODEC_SHIFT) |
17462306a36Sopenharmony_ci			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
17562306a36Sopenharmony_ci			 (reg << 9) | value);
17662306a36Sopenharmony_ci	if (reg < ARRAY_SIZE(data->wm8785_regs))
17762306a36Sopenharmony_ci		data->wm8785_regs[reg] = value;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void ak4396_registers_init(struct oxygen *chip)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
18362306a36Sopenharmony_ci	unsigned int i;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	for (i = 0; i < data->dacs; ++i) {
18662306a36Sopenharmony_ci		ak4396_write(chip, i, AK4396_CONTROL_1,
18762306a36Sopenharmony_ci			     AK4396_DIF_24_MSB | AK4396_RSTN);
18862306a36Sopenharmony_ci		ak4396_write(chip, i, AK4396_CONTROL_2,
18962306a36Sopenharmony_ci			     data->ak4396_regs[0][AK4396_CONTROL_2]);
19062306a36Sopenharmony_ci		ak4396_write(chip, i, AK4396_CONTROL_3,
19162306a36Sopenharmony_ci			     AK4396_PCM);
19262306a36Sopenharmony_ci		ak4396_write(chip, i, AK4396_LCH_ATT,
19362306a36Sopenharmony_ci			     chip->dac_volume[i * 2]);
19462306a36Sopenharmony_ci		ak4396_write(chip, i, AK4396_RCH_ATT,
19562306a36Sopenharmony_ci			     chip->dac_volume[i * 2 + 1]);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void ak4396_init(struct oxygen *chip)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	data->dacs = chip->model.dac_channels_pcm / 2;
20462306a36Sopenharmony_ci	data->ak4396_regs[0][AK4396_CONTROL_2] =
20562306a36Sopenharmony_ci		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
20662306a36Sopenharmony_ci	ak4396_registers_init(chip);
20762306a36Sopenharmony_ci	snd_component_add(chip->card, "AK4396");
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void ak5385_init(struct oxygen *chip)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK);
21362306a36Sopenharmony_ci	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK);
21462306a36Sopenharmony_ci	snd_component_add(chip->card, "AK5385");
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void wm8785_registers_init(struct oxygen *chip)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	wm8785_write(chip, WM8785_R7, 0);
22262306a36Sopenharmony_ci	wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]);
22362306a36Sopenharmony_ci	wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void wm8785_init(struct oxygen *chip)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	data->wm8785_regs[0] =
23162306a36Sopenharmony_ci		WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
23262306a36Sopenharmony_ci	data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL;
23362306a36Sopenharmony_ci	wm8785_registers_init(chip);
23462306a36Sopenharmony_ci	snd_component_add(chip->card, "WM8785");
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void generic_init(struct oxygen *chip)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	ak4396_init(chip);
24062306a36Sopenharmony_ci	wm8785_init(chip);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void meridian_init(struct oxygen *chip)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
24662306a36Sopenharmony_ci			  GPIO_MERIDIAN_DIG_MASK);
24762306a36Sopenharmony_ci	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
24862306a36Sopenharmony_ci			      GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);
24962306a36Sopenharmony_ci	ak4396_init(chip);
25062306a36Sopenharmony_ci	ak5385_init(chip);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void claro_enable_hp(struct oxygen *chip)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	msleep(300);
25662306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_HP);
25762306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic void claro_init(struct oxygen *chip)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
26362306a36Sopenharmony_ci	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
26462306a36Sopenharmony_ci	ak4396_init(chip);
26562306a36Sopenharmony_ci	wm8785_init(chip);
26662306a36Sopenharmony_ci	claro_enable_hp(chip);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void claro_halo_init(struct oxygen *chip)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
27262306a36Sopenharmony_ci	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
27362306a36Sopenharmony_ci	ak4396_init(chip);
27462306a36Sopenharmony_ci	ak5385_init(chip);
27562306a36Sopenharmony_ci	claro_enable_hp(chip);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void fantasia_init(struct oxygen *chip)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	ak4396_init(chip);
28162306a36Sopenharmony_ci	snd_component_add(chip->card, "CS5340");
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void stereo_output_init(struct oxygen *chip)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	ak4396_init(chip);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void generic_cleanup(struct oxygen *chip)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void claro_disable_hp(struct oxygen *chip)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void claro_cleanup(struct oxygen *chip)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	claro_disable_hp(chip);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void claro_suspend(struct oxygen *chip)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	claro_disable_hp(chip);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void generic_resume(struct oxygen *chip)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	ak4396_registers_init(chip);
31162306a36Sopenharmony_ci	wm8785_registers_init(chip);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void meridian_resume(struct oxygen *chip)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	ak4396_registers_init(chip);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void claro_resume(struct oxygen *chip)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	ak4396_registers_init(chip);
32262306a36Sopenharmony_ci	claro_enable_hp(chip);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void stereo_resume(struct oxygen *chip)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	ak4396_registers_init(chip);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void set_ak4396_params(struct oxygen *chip,
33162306a36Sopenharmony_ci			      struct snd_pcm_hw_params *params)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
33462306a36Sopenharmony_ci	unsigned int i;
33562306a36Sopenharmony_ci	u8 value;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
33862306a36Sopenharmony_ci	if (params_rate(params) <= 54000)
33962306a36Sopenharmony_ci		value |= AK4396_DFS_NORMAL;
34062306a36Sopenharmony_ci	else if (params_rate(params) <= 108000)
34162306a36Sopenharmony_ci		value |= AK4396_DFS_DOUBLE;
34262306a36Sopenharmony_ci	else
34362306a36Sopenharmony_ci		value |= AK4396_DFS_QUAD;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	msleep(1); /* wait for the new MCLK to become stable */
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
34862306a36Sopenharmony_ci		for (i = 0; i < data->dacs; ++i) {
34962306a36Sopenharmony_ci			ak4396_write(chip, i, AK4396_CONTROL_1,
35062306a36Sopenharmony_ci				     AK4396_DIF_24_MSB);
35162306a36Sopenharmony_ci			ak4396_write(chip, i, AK4396_CONTROL_2, value);
35262306a36Sopenharmony_ci			ak4396_write(chip, i, AK4396_CONTROL_1,
35362306a36Sopenharmony_ci				     AK4396_DIF_24_MSB | AK4396_RSTN);
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void update_ak4396_volume(struct oxygen *chip)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
36162306a36Sopenharmony_ci	unsigned int i;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	for (i = 0; i < data->dacs; ++i) {
36462306a36Sopenharmony_ci		ak4396_write_cached(chip, i, AK4396_LCH_ATT,
36562306a36Sopenharmony_ci				    chip->dac_volume[i * 2]);
36662306a36Sopenharmony_ci		ak4396_write_cached(chip, i, AK4396_RCH_ATT,
36762306a36Sopenharmony_ci				    chip->dac_volume[i * 2 + 1]);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void update_ak4396_mute(struct oxygen *chip)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
37462306a36Sopenharmony_ci	unsigned int i;
37562306a36Sopenharmony_ci	u8 value;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
37862306a36Sopenharmony_ci	if (chip->dac_mute)
37962306a36Sopenharmony_ci		value |= AK4396_SMUTE;
38062306a36Sopenharmony_ci	for (i = 0; i < data->dacs; ++i)
38162306a36Sopenharmony_ci		ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic void set_wm8785_params(struct oxygen *chip,
38562306a36Sopenharmony_ci			      struct snd_pcm_hw_params *params)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
38862306a36Sopenharmony_ci	unsigned int value;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
39162306a36Sopenharmony_ci	if (params_rate(params) <= 48000)
39262306a36Sopenharmony_ci		value |= WM8785_OSR_SINGLE;
39362306a36Sopenharmony_ci	else if (params_rate(params) <= 96000)
39462306a36Sopenharmony_ci		value |= WM8785_OSR_DOUBLE;
39562306a36Sopenharmony_ci	else
39662306a36Sopenharmony_ci		value |= WM8785_OSR_QUAD;
39762306a36Sopenharmony_ci	if (value != data->wm8785_regs[0]) {
39862306a36Sopenharmony_ci		wm8785_write(chip, WM8785_R7, 0);
39962306a36Sopenharmony_ci		wm8785_write(chip, WM8785_R0, value);
40062306a36Sopenharmony_ci		wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void set_ak5385_params(struct oxygen *chip,
40562306a36Sopenharmony_ci			      struct snd_pcm_hw_params *params)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	unsigned int value;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (params_rate(params) <= 54000)
41062306a36Sopenharmony_ci		value = GPIO_AK5385_DFS_NORMAL;
41162306a36Sopenharmony_ci	else if (params_rate(params) <= 108000)
41262306a36Sopenharmony_ci		value = GPIO_AK5385_DFS_DOUBLE;
41362306a36Sopenharmony_ci	else
41462306a36Sopenharmony_ci		value = GPIO_AK5385_DFS_QUAD;
41562306a36Sopenharmony_ci	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
41662306a36Sopenharmony_ci			      value, GPIO_AK5385_DFS_MASK);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int rolloff_info(struct snd_kcontrol *ctl,
42462306a36Sopenharmony_ci			struct snd_ctl_elem_info *info)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	static const char *const names[2] = {
42762306a36Sopenharmony_ci		"Sharp Roll-off", "Slow Roll-off"
42862306a36Sopenharmony_ci	};
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 2, names);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int rolloff_get(struct snd_kcontrol *ctl,
43462306a36Sopenharmony_ci		       struct snd_ctl_elem_value *value)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
43762306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	value->value.enumerated.item[0] =
44062306a36Sopenharmony_ci		(data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0;
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int rolloff_put(struct snd_kcontrol *ctl,
44562306a36Sopenharmony_ci		       struct snd_ctl_elem_value *value)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
44862306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
44962306a36Sopenharmony_ci	unsigned int i;
45062306a36Sopenharmony_ci	int changed;
45162306a36Sopenharmony_ci	u8 reg;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
45462306a36Sopenharmony_ci	reg = data->ak4396_regs[0][AK4396_CONTROL_2];
45562306a36Sopenharmony_ci	if (value->value.enumerated.item[0])
45662306a36Sopenharmony_ci		reg |= AK4396_SLOW;
45762306a36Sopenharmony_ci	else
45862306a36Sopenharmony_ci		reg &= ~AK4396_SLOW;
45962306a36Sopenharmony_ci	changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
46062306a36Sopenharmony_ci	if (changed) {
46162306a36Sopenharmony_ci		for (i = 0; i < data->dacs; ++i)
46262306a36Sopenharmony_ci			ak4396_write(chip, i, AK4396_CONTROL_2, reg);
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
46562306a36Sopenharmony_ci	return changed;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic const struct snd_kcontrol_new rolloff_control = {
46962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
47062306a36Sopenharmony_ci	.name = "DAC Filter Playback Enum",
47162306a36Sopenharmony_ci	.info = rolloff_info,
47262306a36Sopenharmony_ci	.get = rolloff_get,
47362306a36Sopenharmony_ci	.put = rolloff_put,
47462306a36Sopenharmony_ci};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	static const char *const names[2] = {
47962306a36Sopenharmony_ci		"None", "High-pass Filter"
48062306a36Sopenharmony_ci	};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 2, names);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
48862306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	value->value.enumerated.item[0] =
49162306a36Sopenharmony_ci		(data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0;
49262306a36Sopenharmony_ci	return 0;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
49862306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
49962306a36Sopenharmony_ci	unsigned int reg;
50062306a36Sopenharmony_ci	int changed;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
50362306a36Sopenharmony_ci	reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL);
50462306a36Sopenharmony_ci	if (value->value.enumerated.item[0])
50562306a36Sopenharmony_ci		reg |= WM8785_HPFR | WM8785_HPFL;
50662306a36Sopenharmony_ci	changed = reg != data->wm8785_regs[WM8785_R2];
50762306a36Sopenharmony_ci	if (changed)
50862306a36Sopenharmony_ci		wm8785_write(chip, WM8785_R2, reg);
50962306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
51062306a36Sopenharmony_ci	return changed;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic const struct snd_kcontrol_new hpf_control = {
51462306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
51562306a36Sopenharmony_ci	.name = "ADC Filter Capture Enum",
51662306a36Sopenharmony_ci	.info = hpf_info,
51762306a36Sopenharmony_ci	.get = hpf_get,
51862306a36Sopenharmony_ci	.put = hpf_put,
51962306a36Sopenharmony_ci};
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int meridian_dig_source_info(struct snd_kcontrol *ctl,
52262306a36Sopenharmony_ci				    struct snd_ctl_elem_info *info)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	static const char *const names[2] = { "On-board", "Extension" };
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 2, names);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int claro_dig_source_info(struct snd_kcontrol *ctl,
53062306a36Sopenharmony_ci				 struct snd_ctl_elem_info *info)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	static const char *const names[2] = { "Optical", "Coaxial" };
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 2, names);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int meridian_dig_source_get(struct snd_kcontrol *ctl,
53862306a36Sopenharmony_ci				   struct snd_ctl_elem_value *value)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	value->value.enumerated.item[0] =
54362306a36Sopenharmony_ci		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
54462306a36Sopenharmony_ci		   GPIO_MERIDIAN_DIG_EXT);
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int claro_dig_source_get(struct snd_kcontrol *ctl,
54962306a36Sopenharmony_ci				struct snd_ctl_elem_value *value)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	value->value.enumerated.item[0] =
55462306a36Sopenharmony_ci		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
55562306a36Sopenharmony_ci		   GPIO_CLARO_DIG_COAX);
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int meridian_dig_source_put(struct snd_kcontrol *ctl,
56062306a36Sopenharmony_ci				   struct snd_ctl_elem_value *value)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
56362306a36Sopenharmony_ci	u16 old_reg, new_reg;
56462306a36Sopenharmony_ci	int changed;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
56762306a36Sopenharmony_ci	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
56862306a36Sopenharmony_ci	new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK;
56962306a36Sopenharmony_ci	if (value->value.enumerated.item[0] == 0)
57062306a36Sopenharmony_ci		new_reg |= GPIO_MERIDIAN_DIG_BOARD;
57162306a36Sopenharmony_ci	else
57262306a36Sopenharmony_ci		new_reg |= GPIO_MERIDIAN_DIG_EXT;
57362306a36Sopenharmony_ci	changed = new_reg != old_reg;
57462306a36Sopenharmony_ci	if (changed)
57562306a36Sopenharmony_ci		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
57662306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
57762306a36Sopenharmony_ci	return changed;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int claro_dig_source_put(struct snd_kcontrol *ctl,
58162306a36Sopenharmony_ci				struct snd_ctl_elem_value *value)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
58462306a36Sopenharmony_ci	u16 old_reg, new_reg;
58562306a36Sopenharmony_ci	int changed;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
58862306a36Sopenharmony_ci	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
58962306a36Sopenharmony_ci	new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
59062306a36Sopenharmony_ci	if (value->value.enumerated.item[0])
59162306a36Sopenharmony_ci		new_reg |= GPIO_CLARO_DIG_COAX;
59262306a36Sopenharmony_ci	changed = new_reg != old_reg;
59362306a36Sopenharmony_ci	if (changed)
59462306a36Sopenharmony_ci		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
59562306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
59662306a36Sopenharmony_ci	return changed;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic const struct snd_kcontrol_new meridian_dig_source_control = {
60062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
60162306a36Sopenharmony_ci	.name = "IEC958 Source Capture Enum",
60262306a36Sopenharmony_ci	.info = meridian_dig_source_info,
60362306a36Sopenharmony_ci	.get = meridian_dig_source_get,
60462306a36Sopenharmony_ci	.put = meridian_dig_source_put,
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic const struct snd_kcontrol_new claro_dig_source_control = {
60862306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
60962306a36Sopenharmony_ci	.name = "IEC958 Source Capture Enum",
61062306a36Sopenharmony_ci	.info = claro_dig_source_info,
61162306a36Sopenharmony_ci	.get = claro_dig_source_get,
61262306a36Sopenharmony_ci	.put = claro_dig_source_put,
61362306a36Sopenharmony_ci};
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic int generic_mixer_init(struct oxygen *chip)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int generic_wm8785_mixer_init(struct oxygen *chip)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	int err;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	err = generic_mixer_init(chip);
62562306a36Sopenharmony_ci	if (err < 0)
62662306a36Sopenharmony_ci		return err;
62762306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip));
62862306a36Sopenharmony_ci	if (err < 0)
62962306a36Sopenharmony_ci		return err;
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic int meridian_mixer_init(struct oxygen *chip)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	int err;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	err = generic_mixer_init(chip);
63862306a36Sopenharmony_ci	if (err < 0)
63962306a36Sopenharmony_ci		return err;
64062306a36Sopenharmony_ci	err = snd_ctl_add(chip->card,
64162306a36Sopenharmony_ci			  snd_ctl_new1(&meridian_dig_source_control, chip));
64262306a36Sopenharmony_ci	if (err < 0)
64362306a36Sopenharmony_ci		return err;
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int claro_mixer_init(struct oxygen *chip)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	int err;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	err = generic_wm8785_mixer_init(chip);
65262306a36Sopenharmony_ci	if (err < 0)
65362306a36Sopenharmony_ci		return err;
65462306a36Sopenharmony_ci	err = snd_ctl_add(chip->card,
65562306a36Sopenharmony_ci			  snd_ctl_new1(&claro_dig_source_control, chip));
65662306a36Sopenharmony_ci	if (err < 0)
65762306a36Sopenharmony_ci		return err;
65862306a36Sopenharmony_ci	return 0;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic int claro_halo_mixer_init(struct oxygen *chip)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	int err;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	err = generic_mixer_init(chip);
66662306a36Sopenharmony_ci	if (err < 0)
66762306a36Sopenharmony_ci		return err;
66862306a36Sopenharmony_ci	err = snd_ctl_add(chip->card,
66962306a36Sopenharmony_ci			  snd_ctl_new1(&claro_dig_source_control, chip));
67062306a36Sopenharmony_ci	if (err < 0)
67162306a36Sopenharmony_ci		return err;
67262306a36Sopenharmony_ci	return 0;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic void dump_ak4396_registers(struct oxygen *chip,
67662306a36Sopenharmony_ci				  struct snd_info_buffer *buffer)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
67962306a36Sopenharmony_ci	unsigned int dac, i;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	for (dac = 0; dac < data->dacs; ++dac) {
68262306a36Sopenharmony_ci		snd_iprintf(buffer, "\nAK4396 %u:", dac + 1);
68362306a36Sopenharmony_ci		for (i = 0; i < 5; ++i)
68462306a36Sopenharmony_ci			snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]);
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic void dump_wm8785_registers(struct oxygen *chip,
69062306a36Sopenharmony_ci				  struct snd_info_buffer *buffer)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct generic_data *data = chip->model_data;
69362306a36Sopenharmony_ci	unsigned int i;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	snd_iprintf(buffer, "\nWM8785:");
69662306a36Sopenharmony_ci	for (i = 0; i < 3; ++i)
69762306a36Sopenharmony_ci		snd_iprintf(buffer, " %03x", data->wm8785_regs[i]);
69862306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic void dump_oxygen_registers(struct oxygen *chip,
70262306a36Sopenharmony_ci				  struct snd_info_buffer *buffer)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	dump_ak4396_registers(chip, buffer);
70562306a36Sopenharmony_ci	dump_wm8785_registers(chip, buffer);
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic const struct oxygen_model model_generic = {
71162306a36Sopenharmony_ci	.shortname = "C-Media CMI8788",
71262306a36Sopenharmony_ci	.longname = "C-Media Oxygen HD Audio",
71362306a36Sopenharmony_ci	.chip = "CMI8788",
71462306a36Sopenharmony_ci	.init = generic_init,
71562306a36Sopenharmony_ci	.mixer_init = generic_wm8785_mixer_init,
71662306a36Sopenharmony_ci	.cleanup = generic_cleanup,
71762306a36Sopenharmony_ci	.resume = generic_resume,
71862306a36Sopenharmony_ci	.set_dac_params = set_ak4396_params,
71962306a36Sopenharmony_ci	.set_adc_params = set_wm8785_params,
72062306a36Sopenharmony_ci	.update_dac_volume = update_ak4396_volume,
72162306a36Sopenharmony_ci	.update_dac_mute = update_ak4396_mute,
72262306a36Sopenharmony_ci	.dump_registers = dump_oxygen_registers,
72362306a36Sopenharmony_ci	.dac_tlv = ak4396_db_scale,
72462306a36Sopenharmony_ci	.model_data_size = sizeof(struct generic_data),
72562306a36Sopenharmony_ci	.device_config = PLAYBACK_0_TO_I2S |
72662306a36Sopenharmony_ci			 PLAYBACK_1_TO_SPDIF |
72762306a36Sopenharmony_ci			 PLAYBACK_2_TO_AC97_1 |
72862306a36Sopenharmony_ci			 CAPTURE_0_FROM_I2S_1 |
72962306a36Sopenharmony_ci			 CAPTURE_1_FROM_SPDIF |
73062306a36Sopenharmony_ci			 CAPTURE_2_FROM_AC97_1 |
73162306a36Sopenharmony_ci			 AC97_CD_INPUT,
73262306a36Sopenharmony_ci	.dac_channels_pcm = 8,
73362306a36Sopenharmony_ci	.dac_channels_mixer = 8,
73462306a36Sopenharmony_ci	.dac_volume_min = 0,
73562306a36Sopenharmony_ci	.dac_volume_max = 255,
73662306a36Sopenharmony_ci	.function_flags = OXYGEN_FUNCTION_SPI |
73762306a36Sopenharmony_ci			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
73862306a36Sopenharmony_ci	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
73962306a36Sopenharmony_ci	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
74062306a36Sopenharmony_ci	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
74162306a36Sopenharmony_ci	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
74262306a36Sopenharmony_ci};
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int get_oxygen_model(struct oxygen *chip,
74562306a36Sopenharmony_ci			    const struct pci_device_id *id)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	static const char *const names[] = {
74862306a36Sopenharmony_ci		[MODEL_MERIDIAN]	= "AuzenTech X-Meridian",
74962306a36Sopenharmony_ci		[MODEL_MERIDIAN_2G]	= "AuzenTech X-Meridian 2G",
75062306a36Sopenharmony_ci		[MODEL_CLARO]		= "HT-Omega Claro",
75162306a36Sopenharmony_ci		[MODEL_CLARO_HALO]	= "HT-Omega Claro halo",
75262306a36Sopenharmony_ci		[MODEL_FANTASIA]	= "TempoTec HiFier Fantasia",
75362306a36Sopenharmony_ci		[MODEL_SERENADE]	= "TempoTec HiFier Serenade",
75462306a36Sopenharmony_ci		[MODEL_HG2PCI]		= "CMI8787-HG2PCI",
75562306a36Sopenharmony_ci		[MODEL_XONAR_DG]        = "Xonar DG",
75662306a36Sopenharmony_ci		[MODEL_XONAR_DGX]       = "Xonar DGX",
75762306a36Sopenharmony_ci	};
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	chip->model = model_generic;
76062306a36Sopenharmony_ci	switch (id->driver_data) {
76162306a36Sopenharmony_ci	case MODEL_MERIDIAN:
76262306a36Sopenharmony_ci	case MODEL_MERIDIAN_2G:
76362306a36Sopenharmony_ci		chip->model.init = meridian_init;
76462306a36Sopenharmony_ci		chip->model.mixer_init = meridian_mixer_init;
76562306a36Sopenharmony_ci		chip->model.resume = meridian_resume;
76662306a36Sopenharmony_ci		chip->model.set_adc_params = set_ak5385_params;
76762306a36Sopenharmony_ci		chip->model.dump_registers = dump_ak4396_registers;
76862306a36Sopenharmony_ci		chip->model.device_config = PLAYBACK_0_TO_I2S |
76962306a36Sopenharmony_ci					    PLAYBACK_1_TO_SPDIF |
77062306a36Sopenharmony_ci					    CAPTURE_0_FROM_I2S_2 |
77162306a36Sopenharmony_ci					    CAPTURE_1_FROM_SPDIF;
77262306a36Sopenharmony_ci		if (id->driver_data == MODEL_MERIDIAN)
77362306a36Sopenharmony_ci			chip->model.device_config |= AC97_CD_INPUT;
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci	case MODEL_CLARO:
77662306a36Sopenharmony_ci		chip->model.init = claro_init;
77762306a36Sopenharmony_ci		chip->model.mixer_init = claro_mixer_init;
77862306a36Sopenharmony_ci		chip->model.cleanup = claro_cleanup;
77962306a36Sopenharmony_ci		chip->model.suspend = claro_suspend;
78062306a36Sopenharmony_ci		chip->model.resume = claro_resume;
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci	case MODEL_CLARO_HALO:
78362306a36Sopenharmony_ci		chip->model.init = claro_halo_init;
78462306a36Sopenharmony_ci		chip->model.mixer_init = claro_halo_mixer_init;
78562306a36Sopenharmony_ci		chip->model.cleanup = claro_cleanup;
78662306a36Sopenharmony_ci		chip->model.suspend = claro_suspend;
78762306a36Sopenharmony_ci		chip->model.resume = claro_resume;
78862306a36Sopenharmony_ci		chip->model.set_adc_params = set_ak5385_params;
78962306a36Sopenharmony_ci		chip->model.dump_registers = dump_ak4396_registers;
79062306a36Sopenharmony_ci		chip->model.device_config = PLAYBACK_0_TO_I2S |
79162306a36Sopenharmony_ci					    PLAYBACK_1_TO_SPDIF |
79262306a36Sopenharmony_ci					    CAPTURE_0_FROM_I2S_2 |
79362306a36Sopenharmony_ci					    CAPTURE_1_FROM_SPDIF;
79462306a36Sopenharmony_ci		break;
79562306a36Sopenharmony_ci	case MODEL_FANTASIA:
79662306a36Sopenharmony_ci	case MODEL_SERENADE:
79762306a36Sopenharmony_ci	case MODEL_2CH_OUTPUT:
79862306a36Sopenharmony_ci	case MODEL_HG2PCI:
79962306a36Sopenharmony_ci		chip->model.shortname = "C-Media CMI8787";
80062306a36Sopenharmony_ci		chip->model.chip = "CMI8787";
80162306a36Sopenharmony_ci		if (id->driver_data == MODEL_FANTASIA)
80262306a36Sopenharmony_ci			chip->model.init = fantasia_init;
80362306a36Sopenharmony_ci		else
80462306a36Sopenharmony_ci			chip->model.init = stereo_output_init;
80562306a36Sopenharmony_ci		chip->model.resume = stereo_resume;
80662306a36Sopenharmony_ci		chip->model.mixer_init = generic_mixer_init;
80762306a36Sopenharmony_ci		chip->model.set_adc_params = set_no_params;
80862306a36Sopenharmony_ci		chip->model.dump_registers = dump_ak4396_registers;
80962306a36Sopenharmony_ci		chip->model.device_config = PLAYBACK_0_TO_I2S |
81062306a36Sopenharmony_ci					    PLAYBACK_1_TO_SPDIF;
81162306a36Sopenharmony_ci		if (id->driver_data == MODEL_FANTASIA) {
81262306a36Sopenharmony_ci			chip->model.device_config |= CAPTURE_0_FROM_I2S_1;
81362306a36Sopenharmony_ci			chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128);
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci		chip->model.dac_channels_pcm = 2;
81662306a36Sopenharmony_ci		chip->model.dac_channels_mixer = 2;
81762306a36Sopenharmony_ci		break;
81862306a36Sopenharmony_ci	case MODEL_XONAR_DG:
81962306a36Sopenharmony_ci	case MODEL_XONAR_DGX:
82062306a36Sopenharmony_ci		chip->model = model_xonar_dg;
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci	if (id->driver_data == MODEL_MERIDIAN ||
82462306a36Sopenharmony_ci	    id->driver_data == MODEL_MERIDIAN_2G ||
82562306a36Sopenharmony_ci	    id->driver_data == MODEL_CLARO_HALO) {
82662306a36Sopenharmony_ci		chip->model.misc_flags = OXYGEN_MISC_MIDI;
82762306a36Sopenharmony_ci		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci	if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data])
83062306a36Sopenharmony_ci		chip->model.shortname = names[id->driver_data];
83162306a36Sopenharmony_ci	return 0;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic int generic_oxygen_probe(struct pci_dev *pci,
83562306a36Sopenharmony_ci				const struct pci_device_id *pci_id)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	static int dev;
83862306a36Sopenharmony_ci	int err;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
84162306a36Sopenharmony_ci		return -ENODEV;
84262306a36Sopenharmony_ci	if (!enable[dev]) {
84362306a36Sopenharmony_ci		++dev;
84462306a36Sopenharmony_ci		return -ENOENT;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
84762306a36Sopenharmony_ci			       oxygen_ids, get_oxygen_model);
84862306a36Sopenharmony_ci	if (err >= 0)
84962306a36Sopenharmony_ci		++dev;
85062306a36Sopenharmony_ci	return err;
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic struct pci_driver oxygen_driver = {
85462306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
85562306a36Sopenharmony_ci	.id_table = oxygen_ids,
85662306a36Sopenharmony_ci	.probe = generic_oxygen_probe,
85762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
85862306a36Sopenharmony_ci	.driver = {
85962306a36Sopenharmony_ci		.pm = &oxygen_pci_pm,
86062306a36Sopenharmony_ci	},
86162306a36Sopenharmony_ci#endif
86262306a36Sopenharmony_ci};
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cimodule_pci_driver(oxygen_driver);
865