162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * helper functions for Asus Xonar cards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <sound/core.h>
1062306a36Sopenharmony_ci#include <sound/control.h>
1162306a36Sopenharmony_ci#include <sound/pcm.h>
1262306a36Sopenharmony_ci#include <sound/pcm_params.h>
1362306a36Sopenharmony_ci#include "xonar.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define GPIO_CS53x1_M_MASK	0x000c
1762306a36Sopenharmony_ci#define GPIO_CS53x1_M_SINGLE	0x0000
1862306a36Sopenharmony_ci#define GPIO_CS53x1_M_DOUBLE	0x0004
1962306a36Sopenharmony_ci#define GPIO_CS53x1_M_QUAD	0x0008
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_civoid xonar_enable_output(struct oxygen *chip)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
2762306a36Sopenharmony_ci	msleep(data->anti_pop_delay);
2862306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_civoid xonar_disable_output(struct oxygen *chip)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void xonar_ext_power_gpio_changed(struct oxygen *chip)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
4162306a36Sopenharmony_ci	u8 has_power;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	has_power = !!(oxygen_read8(chip, data->ext_power_reg)
4462306a36Sopenharmony_ci		       & data->ext_power_bit);
4562306a36Sopenharmony_ci	if (has_power != data->has_power) {
4662306a36Sopenharmony_ci		data->has_power = has_power;
4762306a36Sopenharmony_ci		if (has_power) {
4862306a36Sopenharmony_ci			dev_notice(chip->card->dev, "power restored\n");
4962306a36Sopenharmony_ci		} else {
5062306a36Sopenharmony_ci			dev_crit(chip->card->dev,
5162306a36Sopenharmony_ci				   "Hey! Don't unplug the power cable!\n");
5262306a36Sopenharmony_ci			/* TODO: stop PCMs */
5362306a36Sopenharmony_ci		}
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_civoid xonar_init_ext_power(struct oxygen *chip)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	oxygen_set_bits8(chip, data->ext_power_int_reg,
6262306a36Sopenharmony_ci			 data->ext_power_bit);
6362306a36Sopenharmony_ci	chip->interrupt_mask |= OXYGEN_INT_GPIO;
6462306a36Sopenharmony_ci	chip->model.gpio_changed = xonar_ext_power_gpio_changed;
6562306a36Sopenharmony_ci	data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
6662306a36Sopenharmony_ci			     & data->ext_power_bit);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid xonar_init_cs53x1(struct oxygen *chip)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
7262306a36Sopenharmony_ci	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
7362306a36Sopenharmony_ci			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid xonar_set_cs53x1_params(struct oxygen *chip,
7762306a36Sopenharmony_ci			     struct snd_pcm_hw_params *params)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	unsigned int value;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (params_rate(params) <= 54000)
8262306a36Sopenharmony_ci		value = GPIO_CS53x1_M_SINGLE;
8362306a36Sopenharmony_ci	else if (params_rate(params) <= 108000)
8462306a36Sopenharmony_ci		value = GPIO_CS53x1_M_DOUBLE;
8562306a36Sopenharmony_ci	else
8662306a36Sopenharmony_ci		value = GPIO_CS53x1_M_QUAD;
8762306a36Sopenharmony_ci	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
8862306a36Sopenharmony_ci			      value, GPIO_CS53x1_M_MASK);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciint xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
9262306a36Sopenharmony_ci			      struct snd_ctl_elem_value *value)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
9562306a36Sopenharmony_ci	u16 bit = ctl->private_value;
9662306a36Sopenharmony_ci	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	value->value.integer.value[0] =
9962306a36Sopenharmony_ci		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciint xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
10462306a36Sopenharmony_ci			      struct snd_ctl_elem_value *value)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
10762306a36Sopenharmony_ci	u16 bit = ctl->private_value;
10862306a36Sopenharmony_ci	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
10962306a36Sopenharmony_ci	u16 old_bits, new_bits;
11062306a36Sopenharmony_ci	int changed;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
11362306a36Sopenharmony_ci	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
11462306a36Sopenharmony_ci	if (!!value->value.integer.value[0] ^ invert)
11562306a36Sopenharmony_ci		new_bits = old_bits | bit;
11662306a36Sopenharmony_ci	else
11762306a36Sopenharmony_ci		new_bits = old_bits & ~bit;
11862306a36Sopenharmony_ci	changed = new_bits != old_bits;
11962306a36Sopenharmony_ci	if (changed)
12062306a36Sopenharmony_ci		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
12162306a36Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
12262306a36Sopenharmony_ci	return changed;
12362306a36Sopenharmony_ci}
124