18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * helper functions for Asus Xonar cards
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <sound/core.h>
108c2ecf20Sopenharmony_ci#include <sound/control.h>
118c2ecf20Sopenharmony_ci#include <sound/pcm.h>
128c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
138c2ecf20Sopenharmony_ci#include "xonar.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define GPIO_CS53x1_M_MASK	0x000c
178c2ecf20Sopenharmony_ci#define GPIO_CS53x1_M_SINGLE	0x0000
188c2ecf20Sopenharmony_ci#define GPIO_CS53x1_M_DOUBLE	0x0004
198c2ecf20Sopenharmony_ci#define GPIO_CS53x1_M_QUAD	0x0008
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_civoid xonar_enable_output(struct oxygen *chip)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
278c2ecf20Sopenharmony_ci	msleep(data->anti_pop_delay);
288c2ecf20Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_civoid xonar_disable_output(struct oxygen *chip)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void xonar_ext_power_gpio_changed(struct oxygen *chip)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
418c2ecf20Sopenharmony_ci	u8 has_power;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	has_power = !!(oxygen_read8(chip, data->ext_power_reg)
448c2ecf20Sopenharmony_ci		       & data->ext_power_bit);
458c2ecf20Sopenharmony_ci	if (has_power != data->has_power) {
468c2ecf20Sopenharmony_ci		data->has_power = has_power;
478c2ecf20Sopenharmony_ci		if (has_power) {
488c2ecf20Sopenharmony_ci			dev_notice(chip->card->dev, "power restored\n");
498c2ecf20Sopenharmony_ci		} else {
508c2ecf20Sopenharmony_ci			dev_crit(chip->card->dev,
518c2ecf20Sopenharmony_ci				   "Hey! Don't unplug the power cable!\n");
528c2ecf20Sopenharmony_ci			/* TODO: stop PCMs */
538c2ecf20Sopenharmony_ci		}
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_civoid xonar_init_ext_power(struct oxygen *chip)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct xonar_generic *data = chip->model_data;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	oxygen_set_bits8(chip, data->ext_power_int_reg,
628c2ecf20Sopenharmony_ci			 data->ext_power_bit);
638c2ecf20Sopenharmony_ci	chip->interrupt_mask |= OXYGEN_INT_GPIO;
648c2ecf20Sopenharmony_ci	chip->model.gpio_changed = xonar_ext_power_gpio_changed;
658c2ecf20Sopenharmony_ci	data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
668c2ecf20Sopenharmony_ci			     & data->ext_power_bit);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_civoid xonar_init_cs53x1(struct oxygen *chip)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
728c2ecf20Sopenharmony_ci	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
738c2ecf20Sopenharmony_ci			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_civoid xonar_set_cs53x1_params(struct oxygen *chip,
778c2ecf20Sopenharmony_ci			     struct snd_pcm_hw_params *params)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	unsigned int value;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (params_rate(params) <= 54000)
828c2ecf20Sopenharmony_ci		value = GPIO_CS53x1_M_SINGLE;
838c2ecf20Sopenharmony_ci	else if (params_rate(params) <= 108000)
848c2ecf20Sopenharmony_ci		value = GPIO_CS53x1_M_DOUBLE;
858c2ecf20Sopenharmony_ci	else
868c2ecf20Sopenharmony_ci		value = GPIO_CS53x1_M_QUAD;
878c2ecf20Sopenharmony_ci	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
888c2ecf20Sopenharmony_ci			      value, GPIO_CS53x1_M_MASK);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciint xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
928c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *value)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
958c2ecf20Sopenharmony_ci	u16 bit = ctl->private_value;
968c2ecf20Sopenharmony_ci	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	value->value.integer.value[0] =
998c2ecf20Sopenharmony_ci		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ciint xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
1048c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *value)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct oxygen *chip = ctl->private_data;
1078c2ecf20Sopenharmony_ci	u16 bit = ctl->private_value;
1088c2ecf20Sopenharmony_ci	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
1098c2ecf20Sopenharmony_ci	u16 old_bits, new_bits;
1108c2ecf20Sopenharmony_ci	int changed;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
1138c2ecf20Sopenharmony_ci	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
1148c2ecf20Sopenharmony_ci	if (!!value->value.integer.value[0] ^ invert)
1158c2ecf20Sopenharmony_ci		new_bits = old_bits | bit;
1168c2ecf20Sopenharmony_ci	else
1178c2ecf20Sopenharmony_ci		new_bits = old_bits & ~bit;
1188c2ecf20Sopenharmony_ci	changed = new_bits != old_bits;
1198c2ecf20Sopenharmony_ci	if (changed)
1208c2ecf20Sopenharmony_ci		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
1218c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
1228c2ecf20Sopenharmony_ci	return changed;
1238c2ecf20Sopenharmony_ci}
124