1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * helper functions for Asus Xonar cards |
4 | * |
5 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <sound/core.h> |
10 | #include <sound/control.h> |
11 | #include <sound/pcm.h> |
12 | #include <sound/pcm_params.h> |
13 | #include "xonar.h" |
14 | |
15 | |
16 | #define GPIO_CS53x1_M_MASK 0x000c |
17 | #define GPIO_CS53x1_M_SINGLE 0x0000 |
18 | #define GPIO_CS53x1_M_DOUBLE 0x0004 |
19 | #define GPIO_CS53x1_M_QUAD 0x0008 |
20 | |
21 | |
22 | void xonar_enable_output(struct oxygen *chip) |
23 | { |
24 | struct xonar_generic *data = chip->model_data; |
25 | |
26 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, value: data->output_enable_bit); |
27 | msleep(msecs: data->anti_pop_delay); |
28 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, value: data->output_enable_bit); |
29 | } |
30 | |
31 | void xonar_disable_output(struct oxygen *chip) |
32 | { |
33 | struct xonar_generic *data = chip->model_data; |
34 | |
35 | oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, value: data->output_enable_bit); |
36 | } |
37 | |
38 | static void xonar_ext_power_gpio_changed(struct oxygen *chip) |
39 | { |
40 | struct xonar_generic *data = chip->model_data; |
41 | u8 has_power; |
42 | |
43 | has_power = !!(oxygen_read8(chip, reg: data->ext_power_reg) |
44 | & data->ext_power_bit); |
45 | if (has_power != data->has_power) { |
46 | data->has_power = has_power; |
47 | if (has_power) { |
48 | dev_notice(chip->card->dev, "power restored\n" ); |
49 | } else { |
50 | dev_crit(chip->card->dev, |
51 | "Hey! Don't unplug the power cable!\n" ); |
52 | /* TODO: stop PCMs */ |
53 | } |
54 | } |
55 | } |
56 | |
57 | void xonar_init_ext_power(struct oxygen *chip) |
58 | { |
59 | struct xonar_generic *data = chip->model_data; |
60 | |
61 | oxygen_set_bits8(chip, reg: data->ext_power_int_reg, |
62 | value: data->ext_power_bit); |
63 | chip->interrupt_mask |= OXYGEN_INT_GPIO; |
64 | chip->model.gpio_changed = xonar_ext_power_gpio_changed; |
65 | data->has_power = !!(oxygen_read8(chip, reg: data->ext_power_reg) |
66 | & data->ext_power_bit); |
67 | } |
68 | |
69 | void xonar_init_cs53x1(struct oxygen *chip) |
70 | { |
71 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK); |
72 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, |
73 | GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK); |
74 | } |
75 | |
76 | void xonar_set_cs53x1_params(struct oxygen *chip, |
77 | struct snd_pcm_hw_params *params) |
78 | { |
79 | unsigned int value; |
80 | |
81 | if (params_rate(p: params) <= 54000) |
82 | value = GPIO_CS53x1_M_SINGLE; |
83 | else if (params_rate(p: params) <= 108000) |
84 | value = GPIO_CS53x1_M_DOUBLE; |
85 | else |
86 | value = GPIO_CS53x1_M_QUAD; |
87 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, |
88 | value, GPIO_CS53x1_M_MASK); |
89 | } |
90 | |
91 | int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl, |
92 | struct snd_ctl_elem_value *value) |
93 | { |
94 | struct oxygen *chip = ctl->private_data; |
95 | u16 bit = ctl->private_value; |
96 | bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT; |
97 | |
98 | value->value.integer.value[0] = |
99 | !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert; |
100 | return 0; |
101 | } |
102 | |
103 | int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, |
104 | struct snd_ctl_elem_value *value) |
105 | { |
106 | struct oxygen *chip = ctl->private_data; |
107 | u16 bit = ctl->private_value; |
108 | bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT; |
109 | u16 old_bits, new_bits; |
110 | int changed; |
111 | |
112 | spin_lock_irq(lock: &chip->reg_lock); |
113 | old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); |
114 | if (!!value->value.integer.value[0] ^ invert) |
115 | new_bits = old_bits | bit; |
116 | else |
117 | new_bits = old_bits & ~bit; |
118 | changed = new_bits != old_bits; |
119 | if (changed) |
120 | oxygen_write16(chip, OXYGEN_GPIO_DATA, value: new_bits); |
121 | spin_unlock_irq(lock: &chip->reg_lock); |
122 | return changed; |
123 | } |
124 | |