1218822Sdim// SPDX-License-Identifier: GPL-2.0-only
238889Sjdp/*
3218822Sdim * helper functions for Asus Xonar cards
4218822Sdim *
538889Sjdp * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6218822Sdim */
7218822Sdim
860484Sobrien#include <linux/delay.h>
9218822Sdim#include <sound/core.h>
10218822Sdim#include <sound/control.h>
11218822Sdim#include <sound/pcm.h>
1238889Sjdp#include <sound/pcm_params.h>
1338889Sjdp#include "xonar.h"
14218822Sdim
15218822Sdim
1638889Sjdp#define GPIO_CS53x1_M_MASK	0x000c
17218822Sdim#define GPIO_CS53x1_M_SINGLE	0x0000
18218822Sdim#define GPIO_CS53x1_M_DOUBLE	0x0004
1938889Sjdp#define GPIO_CS53x1_M_QUAD	0x0008
20218822Sdim
21218822Sdim
2260484Sobrienvoid xonar_enable_output(struct oxygen *chip)
23218822Sdim{
24218822Sdim	struct xonar_generic *data = chip->model_data;
2538889Sjdp
26218822Sdim	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
27218822Sdim	msleep(data->anti_pop_delay);
2860484Sobrien	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
29218822Sdim}
30218822Sdim
3160484Sobrienvoid xonar_disable_output(struct oxygen *chip)
32218822Sdim{
33218822Sdim	struct xonar_generic *data = chip->model_data;
3438889Sjdp
35218822Sdim	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
36218822Sdim}
37218822Sdim
3860484Sobrienstatic void xonar_ext_power_gpio_changed(struct oxygen *chip)
39218822Sdim{
40218822Sdim	struct xonar_generic *data = chip->model_data;
4138889Sjdp	u8 has_power;
42218822Sdim
43218822Sdim	has_power = !!(oxygen_read8(chip, data->ext_power_reg)
44218822Sdim		       & data->ext_power_bit);
4538889Sjdp	if (has_power != data->has_power) {
46218822Sdim		data->has_power = has_power;
47218822Sdim		if (has_power) {
4838889Sjdp			dev_notice(chip->card->dev, "power restored\n");
49218822Sdim		} else {
50218822Sdim			dev_crit(chip->card->dev,
51218822Sdim				   "Hey! Don't unplug the power cable!\n");
5238889Sjdp			/* TODO: stop PCMs */
53218822Sdim		}
54218822Sdim	}
5538889Sjdp}
56218822Sdim
57218822Sdimvoid xonar_init_ext_power(struct oxygen *chip)
5838889Sjdp{
59218822Sdim	struct xonar_generic *data = chip->model_data;
60218822Sdim
6138889Sjdp	oxygen_set_bits8(chip, data->ext_power_int_reg,
62218822Sdim			 data->ext_power_bit);
63218822Sdim	chip->interrupt_mask |= OXYGEN_INT_GPIO;
6438889Sjdp	chip->model.gpio_changed = xonar_ext_power_gpio_changed;
65218822Sdim	data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
6660484Sobrien			     & data->ext_power_bit);
6738889Sjdp}
68218822Sdim
6960484Sobrienvoid xonar_init_cs53x1(struct oxygen *chip)
7038889Sjdp{
71218822Sdim	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
72218822Sdim	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
7338889Sjdp			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
74218822Sdim}
75218822Sdim
7638889Sjdpvoid xonar_set_cs53x1_params(struct oxygen *chip,
77218822Sdim			     struct snd_pcm_hw_params *params)
78218822Sdim{
7938889Sjdp	unsigned int value;
80218822Sdim
81218822Sdim	if (params_rate(params) <= 54000)
8238889Sjdp		value = GPIO_CS53x1_M_SINGLE;
83218822Sdim	else if (params_rate(params) <= 108000)
8438889Sjdp		value = GPIO_CS53x1_M_DOUBLE;
8538889Sjdp	else
86218822Sdim		value = GPIO_CS53x1_M_QUAD;
87218822Sdim	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
8838889Sjdp			      value, GPIO_CS53x1_M_MASK);
89218822Sdim}
9038889Sjdp
9138889Sjdpint xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
92218822Sdim			      struct snd_ctl_elem_value *value)
9338889Sjdp{
9438889Sjdp	struct oxygen *chip = ctl->private_data;
95218822Sdim	u16 bit = ctl->private_value;
96218822Sdim	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
9760484Sobrien
98218822Sdim	value->value.integer.value[0] =
9938889Sjdp		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
10060484Sobrien	return 0;
101218822Sdim}
102218822Sdim
10360484Sobrienint xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
104218822Sdim			      struct snd_ctl_elem_value *value)
105218822Sdim{
106218822Sdim	struct oxygen *chip = ctl->private_data;
10760484Sobrien	u16 bit = ctl->private_value;
108218822Sdim	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
109218822Sdim	u16 old_bits, new_bits;
11060484Sobrien	int changed;
111218822Sdim
112218822Sdim	spin_lock_irq(&chip->reg_lock);
11377298Sobrien	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
114218822Sdim	if (!!value->value.integer.value[0] ^ invert)
115130561Sobrien		new_bits = old_bits | bit;
116130561Sobrien	else
117218822Sdim		new_bits = old_bits & ~bit;
118130561Sobrien	changed = new_bits != old_bits;
119130561Sobrien	if (changed)
120218822Sdim		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
121130561Sobrien	spin_unlock_irq(&chip->reg_lock);
122130561Sobrien	return changed;
123218822Sdim}
124218822Sdim