1*da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f3a0e32aSTakashi Sakamoto /*
3f3a0e32aSTakashi Sakamoto * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
4f3a0e32aSTakashi Sakamoto *
5f3a0e32aSTakashi Sakamoto * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6f3a0e32aSTakashi Sakamoto */
7f3a0e32aSTakashi Sakamoto
8f3a0e32aSTakashi Sakamoto #include "oxfw.h"
9f3a0e32aSTakashi Sakamoto
1040540de5STakashi Sakamoto struct fw_spkr {
1140540de5STakashi Sakamoto bool mute;
1240540de5STakashi Sakamoto s16 volume[6];
1340540de5STakashi Sakamoto s16 volume_min;
1440540de5STakashi Sakamoto s16 volume_max;
153e2f4570STakashi Sakamoto
163e2f4570STakashi Sakamoto unsigned int mixer_channels;
173e2f4570STakashi Sakamoto u8 mute_fb_id;
183e2f4570STakashi Sakamoto u8 volume_fb_id;
1940540de5STakashi Sakamoto };
2040540de5STakashi Sakamoto
21f3a0e32aSTakashi Sakamoto enum control_action { CTL_READ, CTL_WRITE };
22f3a0e32aSTakashi Sakamoto enum control_attribute {
23f3a0e32aSTakashi Sakamoto CTL_MIN = 0x02,
24f3a0e32aSTakashi Sakamoto CTL_MAX = 0x03,
25f3a0e32aSTakashi Sakamoto CTL_CURRENT = 0x10,
26f3a0e32aSTakashi Sakamoto };
27f3a0e32aSTakashi Sakamoto
avc_audio_feature_mute(struct fw_unit * unit,u8 fb_id,bool * value,enum control_action action)28eab8e4e4STakashi Sakamoto static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
29f3a0e32aSTakashi Sakamoto enum control_action action)
30f3a0e32aSTakashi Sakamoto {
31f3a0e32aSTakashi Sakamoto u8 *buf;
32f3a0e32aSTakashi Sakamoto u8 response_ok;
33f3a0e32aSTakashi Sakamoto int err;
34f3a0e32aSTakashi Sakamoto
35f3a0e32aSTakashi Sakamoto buf = kmalloc(11, GFP_KERNEL);
36f3a0e32aSTakashi Sakamoto if (!buf)
37f3a0e32aSTakashi Sakamoto return -ENOMEM;
38f3a0e32aSTakashi Sakamoto
39f3a0e32aSTakashi Sakamoto if (action == CTL_READ) {
40f3a0e32aSTakashi Sakamoto buf[0] = 0x01; /* AV/C, STATUS */
41f3a0e32aSTakashi Sakamoto response_ok = 0x0c; /* STABLE */
42f3a0e32aSTakashi Sakamoto } else {
43f3a0e32aSTakashi Sakamoto buf[0] = 0x00; /* AV/C, CONTROL */
44f3a0e32aSTakashi Sakamoto response_ok = 0x09; /* ACCEPTED */
45f3a0e32aSTakashi Sakamoto }
46f3a0e32aSTakashi Sakamoto buf[1] = 0x08; /* audio unit 0 */
47f3a0e32aSTakashi Sakamoto buf[2] = 0xb8; /* FUNCTION BLOCK */
48f3a0e32aSTakashi Sakamoto buf[3] = 0x81; /* function block type: feature */
49eab8e4e4STakashi Sakamoto buf[4] = fb_id; /* function block ID */
50f3a0e32aSTakashi Sakamoto buf[5] = 0x10; /* control attribute: current */
51f3a0e32aSTakashi Sakamoto buf[6] = 0x02; /* selector length */
52f3a0e32aSTakashi Sakamoto buf[7] = 0x00; /* audio channel number */
53f3a0e32aSTakashi Sakamoto buf[8] = 0x01; /* control selector: mute */
54f3a0e32aSTakashi Sakamoto buf[9] = 0x01; /* control data length */
55f3a0e32aSTakashi Sakamoto if (action == CTL_READ)
56f3a0e32aSTakashi Sakamoto buf[10] = 0xff;
57f3a0e32aSTakashi Sakamoto else
58f3a0e32aSTakashi Sakamoto buf[10] = *value ? 0x70 : 0x60;
59f3a0e32aSTakashi Sakamoto
60eab8e4e4STakashi Sakamoto err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
61f3a0e32aSTakashi Sakamoto if (err < 0)
62f3a0e32aSTakashi Sakamoto goto error;
63f3a0e32aSTakashi Sakamoto if (err < 11) {
64eab8e4e4STakashi Sakamoto dev_err(&unit->device, "short FCP response\n");
65f3a0e32aSTakashi Sakamoto err = -EIO;
66f3a0e32aSTakashi Sakamoto goto error;
67f3a0e32aSTakashi Sakamoto }
68f3a0e32aSTakashi Sakamoto if (buf[0] != response_ok) {
69eab8e4e4STakashi Sakamoto dev_err(&unit->device, "mute command failed\n");
70f3a0e32aSTakashi Sakamoto err = -EIO;
71f3a0e32aSTakashi Sakamoto goto error;
72f3a0e32aSTakashi Sakamoto }
73f3a0e32aSTakashi Sakamoto if (action == CTL_READ)
74f3a0e32aSTakashi Sakamoto *value = buf[10] == 0x70;
75f3a0e32aSTakashi Sakamoto
76f3a0e32aSTakashi Sakamoto err = 0;
77f3a0e32aSTakashi Sakamoto
78f3a0e32aSTakashi Sakamoto error:
79f3a0e32aSTakashi Sakamoto kfree(buf);
80f3a0e32aSTakashi Sakamoto
81f3a0e32aSTakashi Sakamoto return err;
82f3a0e32aSTakashi Sakamoto }
83f3a0e32aSTakashi Sakamoto
avc_audio_feature_volume(struct fw_unit * unit,u8 fb_id,s16 * value,unsigned int channel,enum control_attribute attribute,enum control_action action)84eab8e4e4STakashi Sakamoto static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
85f3a0e32aSTakashi Sakamoto unsigned int channel,
86f3a0e32aSTakashi Sakamoto enum control_attribute attribute,
87f3a0e32aSTakashi Sakamoto enum control_action action)
88f3a0e32aSTakashi Sakamoto {
89f3a0e32aSTakashi Sakamoto u8 *buf;
90f3a0e32aSTakashi Sakamoto u8 response_ok;
91f3a0e32aSTakashi Sakamoto int err;
92f3a0e32aSTakashi Sakamoto
93f3a0e32aSTakashi Sakamoto buf = kmalloc(12, GFP_KERNEL);
94f3a0e32aSTakashi Sakamoto if (!buf)
95f3a0e32aSTakashi Sakamoto return -ENOMEM;
96f3a0e32aSTakashi Sakamoto
97f3a0e32aSTakashi Sakamoto if (action == CTL_READ) {
98f3a0e32aSTakashi Sakamoto buf[0] = 0x01; /* AV/C, STATUS */
99f3a0e32aSTakashi Sakamoto response_ok = 0x0c; /* STABLE */
100f3a0e32aSTakashi Sakamoto } else {
101f3a0e32aSTakashi Sakamoto buf[0] = 0x00; /* AV/C, CONTROL */
102f3a0e32aSTakashi Sakamoto response_ok = 0x09; /* ACCEPTED */
103f3a0e32aSTakashi Sakamoto }
104f3a0e32aSTakashi Sakamoto buf[1] = 0x08; /* audio unit 0 */
105f3a0e32aSTakashi Sakamoto buf[2] = 0xb8; /* FUNCTION BLOCK */
106f3a0e32aSTakashi Sakamoto buf[3] = 0x81; /* function block type: feature */
107eab8e4e4STakashi Sakamoto buf[4] = fb_id; /* function block ID */
108f3a0e32aSTakashi Sakamoto buf[5] = attribute; /* control attribute */
109f3a0e32aSTakashi Sakamoto buf[6] = 0x02; /* selector length */
110f3a0e32aSTakashi Sakamoto buf[7] = channel; /* audio channel number */
111f3a0e32aSTakashi Sakamoto buf[8] = 0x02; /* control selector: volume */
112f3a0e32aSTakashi Sakamoto buf[9] = 0x02; /* control data length */
113f3a0e32aSTakashi Sakamoto if (action == CTL_READ) {
114f3a0e32aSTakashi Sakamoto buf[10] = 0xff;
115f3a0e32aSTakashi Sakamoto buf[11] = 0xff;
116f3a0e32aSTakashi Sakamoto } else {
117f3a0e32aSTakashi Sakamoto buf[10] = *value >> 8;
118f3a0e32aSTakashi Sakamoto buf[11] = *value;
119f3a0e32aSTakashi Sakamoto }
120f3a0e32aSTakashi Sakamoto
121eab8e4e4STakashi Sakamoto err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
122f3a0e32aSTakashi Sakamoto if (err < 0)
123f3a0e32aSTakashi Sakamoto goto error;
124f3a0e32aSTakashi Sakamoto if (err < 12) {
125eab8e4e4STakashi Sakamoto dev_err(&unit->device, "short FCP response\n");
126f3a0e32aSTakashi Sakamoto err = -EIO;
127f3a0e32aSTakashi Sakamoto goto error;
128f3a0e32aSTakashi Sakamoto }
129f3a0e32aSTakashi Sakamoto if (buf[0] != response_ok) {
130eab8e4e4STakashi Sakamoto dev_err(&unit->device, "volume command failed\n");
131f3a0e32aSTakashi Sakamoto err = -EIO;
132f3a0e32aSTakashi Sakamoto goto error;
133f3a0e32aSTakashi Sakamoto }
134f3a0e32aSTakashi Sakamoto if (action == CTL_READ)
135f3a0e32aSTakashi Sakamoto *value = (buf[10] << 8) | buf[11];
136f3a0e32aSTakashi Sakamoto
137f3a0e32aSTakashi Sakamoto err = 0;
138f3a0e32aSTakashi Sakamoto
139f3a0e32aSTakashi Sakamoto error:
140f3a0e32aSTakashi Sakamoto kfree(buf);
141f3a0e32aSTakashi Sakamoto
142f3a0e32aSTakashi Sakamoto return err;
143f3a0e32aSTakashi Sakamoto }
144f3a0e32aSTakashi Sakamoto
spkr_mute_get(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)14529aa09acSTakashi Sakamoto static int spkr_mute_get(struct snd_kcontrol *control,
146f3a0e32aSTakashi Sakamoto struct snd_ctl_elem_value *value)
147f3a0e32aSTakashi Sakamoto {
148f3a0e32aSTakashi Sakamoto struct snd_oxfw *oxfw = control->private_data;
14940540de5STakashi Sakamoto struct fw_spkr *spkr = oxfw->spec;
150f3a0e32aSTakashi Sakamoto
15140540de5STakashi Sakamoto value->value.integer.value[0] = !spkr->mute;
152f3a0e32aSTakashi Sakamoto
153f3a0e32aSTakashi Sakamoto return 0;
154f3a0e32aSTakashi Sakamoto }
155f3a0e32aSTakashi Sakamoto
spkr_mute_put(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)15629aa09acSTakashi Sakamoto static int spkr_mute_put(struct snd_kcontrol *control,
157f3a0e32aSTakashi Sakamoto struct snd_ctl_elem_value *value)
158f3a0e32aSTakashi Sakamoto {
159f3a0e32aSTakashi Sakamoto struct snd_oxfw *oxfw = control->private_data;
16040540de5STakashi Sakamoto struct fw_spkr *spkr = oxfw->spec;
161f3a0e32aSTakashi Sakamoto bool mute;
162f3a0e32aSTakashi Sakamoto int err;
163f3a0e32aSTakashi Sakamoto
164f3a0e32aSTakashi Sakamoto mute = !value->value.integer.value[0];
165f3a0e32aSTakashi Sakamoto
16640540de5STakashi Sakamoto if (mute == spkr->mute)
167f3a0e32aSTakashi Sakamoto return 0;
168f3a0e32aSTakashi Sakamoto
1693e2f4570STakashi Sakamoto err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
1703e2f4570STakashi Sakamoto CTL_WRITE);
171f3a0e32aSTakashi Sakamoto if (err < 0)
172f3a0e32aSTakashi Sakamoto return err;
17340540de5STakashi Sakamoto spkr->mute = mute;
174f3a0e32aSTakashi Sakamoto
175f3a0e32aSTakashi Sakamoto return 1;
176f3a0e32aSTakashi Sakamoto }
177f3a0e32aSTakashi Sakamoto
spkr_volume_info(struct snd_kcontrol * control,struct snd_ctl_elem_info * info)17829aa09acSTakashi Sakamoto static int spkr_volume_info(struct snd_kcontrol *control,
179f3a0e32aSTakashi Sakamoto struct snd_ctl_elem_info *info)
180f3a0e32aSTakashi Sakamoto {
181f3a0e32aSTakashi Sakamoto struct snd_oxfw *oxfw = control->private_data;
18240540de5STakashi Sakamoto struct fw_spkr *spkr = oxfw->spec;
183f3a0e32aSTakashi Sakamoto
184f3a0e32aSTakashi Sakamoto info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1853e2f4570STakashi Sakamoto info->count = spkr->mixer_channels;
18640540de5STakashi Sakamoto info->value.integer.min = spkr->volume_min;
18740540de5STakashi Sakamoto info->value.integer.max = spkr->volume_max;
188f3a0e32aSTakashi Sakamoto
189f3a0e32aSTakashi Sakamoto return 0;
190f3a0e32aSTakashi Sakamoto }
191f3a0e32aSTakashi Sakamoto
192f3a0e32aSTakashi Sakamoto static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
193f3a0e32aSTakashi Sakamoto
spkr_volume_get(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)19429aa09acSTakashi Sakamoto static int spkr_volume_get(struct snd_kcontrol *control,
195f3a0e32aSTakashi Sakamoto struct snd_ctl_elem_value *value)
196f3a0e32aSTakashi Sakamoto {
197f3a0e32aSTakashi Sakamoto struct snd_oxfw *oxfw = control->private_data;
19840540de5STakashi Sakamoto struct fw_spkr *spkr = oxfw->spec;
199f3a0e32aSTakashi Sakamoto unsigned int i;
200f3a0e32aSTakashi Sakamoto
2013e2f4570STakashi Sakamoto for (i = 0; i < spkr->mixer_channels; ++i)
20240540de5STakashi Sakamoto value->value.integer.value[channel_map[i]] = spkr->volume[i];
203f3a0e32aSTakashi Sakamoto
204f3a0e32aSTakashi Sakamoto return 0;
205f3a0e32aSTakashi Sakamoto }
206f3a0e32aSTakashi Sakamoto
spkr_volume_put(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)20729aa09acSTakashi Sakamoto static int spkr_volume_put(struct snd_kcontrol *control,
208f3a0e32aSTakashi Sakamoto struct snd_ctl_elem_value *value)
209f3a0e32aSTakashi Sakamoto {
210f3a0e32aSTakashi Sakamoto struct snd_oxfw *oxfw = control->private_data;
21140540de5STakashi Sakamoto struct fw_spkr *spkr = oxfw->spec;
212f3a0e32aSTakashi Sakamoto unsigned int i, changed_channels;
213f3a0e32aSTakashi Sakamoto bool equal_values = true;
214f3a0e32aSTakashi Sakamoto s16 volume;
215f3a0e32aSTakashi Sakamoto int err;
216f3a0e32aSTakashi Sakamoto
2173e2f4570STakashi Sakamoto for (i = 0; i < spkr->mixer_channels; ++i) {
21840540de5STakashi Sakamoto if (value->value.integer.value[i] < spkr->volume_min ||
21940540de5STakashi Sakamoto value->value.integer.value[i] > spkr->volume_max)
220f3a0e32aSTakashi Sakamoto return -EINVAL;
221f3a0e32aSTakashi Sakamoto if (value->value.integer.value[i] !=
222f3a0e32aSTakashi Sakamoto value->value.integer.value[0])
223f3a0e32aSTakashi Sakamoto equal_values = false;
224f3a0e32aSTakashi Sakamoto }
225f3a0e32aSTakashi Sakamoto
226f3a0e32aSTakashi Sakamoto changed_channels = 0;
2273e2f4570STakashi Sakamoto for (i = 0; i < spkr->mixer_channels; ++i)
228f3a0e32aSTakashi Sakamoto if (value->value.integer.value[channel_map[i]] !=
22940540de5STakashi Sakamoto spkr->volume[i])
230f3a0e32aSTakashi Sakamoto changed_channels |= 1 << (i + 1);
231f3a0e32aSTakashi Sakamoto
232f3a0e32aSTakashi Sakamoto if (equal_values && changed_channels != 0)
233f3a0e32aSTakashi Sakamoto changed_channels = 1 << 0;
234f3a0e32aSTakashi Sakamoto
2353e2f4570STakashi Sakamoto for (i = 0; i <= spkr->mixer_channels; ++i) {
236f3a0e32aSTakashi Sakamoto volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
237f3a0e32aSTakashi Sakamoto if (changed_channels & (1 << i)) {
238eab8e4e4STakashi Sakamoto err = avc_audio_feature_volume(oxfw->unit,
2393e2f4570STakashi Sakamoto spkr->volume_fb_id, &volume,
240eab8e4e4STakashi Sakamoto i, CTL_CURRENT, CTL_WRITE);
241f3a0e32aSTakashi Sakamoto if (err < 0)
242f3a0e32aSTakashi Sakamoto return err;
243f3a0e32aSTakashi Sakamoto }
244f3a0e32aSTakashi Sakamoto if (i > 0)
24540540de5STakashi Sakamoto spkr->volume[i - 1] = volume;
246f3a0e32aSTakashi Sakamoto }
247f3a0e32aSTakashi Sakamoto
248f3a0e32aSTakashi Sakamoto return changed_channels != 0;
249f3a0e32aSTakashi Sakamoto }
250f3a0e32aSTakashi Sakamoto
snd_oxfw_add_spkr(struct snd_oxfw * oxfw,bool is_lacie)2513e2f4570STakashi Sakamoto int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
252f3a0e32aSTakashi Sakamoto {
253f3a0e32aSTakashi Sakamoto static const struct snd_kcontrol_new controls[] = {
254f3a0e32aSTakashi Sakamoto {
255f3a0e32aSTakashi Sakamoto .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
256f3a0e32aSTakashi Sakamoto .name = "PCM Playback Switch",
257f3a0e32aSTakashi Sakamoto .info = snd_ctl_boolean_mono_info,
25829aa09acSTakashi Sakamoto .get = spkr_mute_get,
25929aa09acSTakashi Sakamoto .put = spkr_mute_put,
260f3a0e32aSTakashi Sakamoto },
261f3a0e32aSTakashi Sakamoto {
262f3a0e32aSTakashi Sakamoto .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
263f3a0e32aSTakashi Sakamoto .name = "PCM Playback Volume",
26429aa09acSTakashi Sakamoto .info = spkr_volume_info,
26529aa09acSTakashi Sakamoto .get = spkr_volume_get,
26629aa09acSTakashi Sakamoto .put = spkr_volume_put,
267f3a0e32aSTakashi Sakamoto },
268f3a0e32aSTakashi Sakamoto };
26940540de5STakashi Sakamoto struct fw_spkr *spkr;
270f3a0e32aSTakashi Sakamoto unsigned int i, first_ch;
271f3a0e32aSTakashi Sakamoto int err;
272f3a0e32aSTakashi Sakamoto
27372bc8c43STakashi Sakamoto spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
27472bc8c43STakashi Sakamoto GFP_KERNEL);
27572bc8c43STakashi Sakamoto if (!spkr)
27640540de5STakashi Sakamoto return -ENOMEM;
27740540de5STakashi Sakamoto oxfw->spec = spkr;
27840540de5STakashi Sakamoto
2793e2f4570STakashi Sakamoto if (is_lacie) {
2803e2f4570STakashi Sakamoto spkr->mixer_channels = 1;
2813e2f4570STakashi Sakamoto spkr->mute_fb_id = 0x01;
2823e2f4570STakashi Sakamoto spkr->volume_fb_id = 0x01;
2833e2f4570STakashi Sakamoto } else {
2843e2f4570STakashi Sakamoto spkr->mixer_channels = 6;
2853e2f4570STakashi Sakamoto spkr->mute_fb_id = 0x01;
2863e2f4570STakashi Sakamoto spkr->volume_fb_id = 0x02;
2873e2f4570STakashi Sakamoto }
2883e2f4570STakashi Sakamoto
2893e2f4570STakashi Sakamoto err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
2903e2f4570STakashi Sakamoto &spkr->volume_min, 0, CTL_MIN, CTL_READ);
291f3a0e32aSTakashi Sakamoto if (err < 0)
292f3a0e32aSTakashi Sakamoto return err;
2933e2f4570STakashi Sakamoto err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
2943e2f4570STakashi Sakamoto &spkr->volume_max, 0, CTL_MAX, CTL_READ);
295f3a0e32aSTakashi Sakamoto if (err < 0)
296f3a0e32aSTakashi Sakamoto return err;
297f3a0e32aSTakashi Sakamoto
2983e2f4570STakashi Sakamoto err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
2993e2f4570STakashi Sakamoto CTL_READ);
300f3a0e32aSTakashi Sakamoto if (err < 0)
301f3a0e32aSTakashi Sakamoto return err;
302f3a0e32aSTakashi Sakamoto
3033e2f4570STakashi Sakamoto first_ch = spkr->mixer_channels == 1 ? 0 : 1;
3043e2f4570STakashi Sakamoto for (i = 0; i < spkr->mixer_channels; ++i) {
3053e2f4570STakashi Sakamoto err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
3063e2f4570STakashi Sakamoto &spkr->volume[i], first_ch + i,
3073e2f4570STakashi Sakamoto CTL_CURRENT, CTL_READ);
308f3a0e32aSTakashi Sakamoto if (err < 0)
309f3a0e32aSTakashi Sakamoto return err;
310f3a0e32aSTakashi Sakamoto }
311f3a0e32aSTakashi Sakamoto
312f3a0e32aSTakashi Sakamoto for (i = 0; i < ARRAY_SIZE(controls); ++i) {
313f3a0e32aSTakashi Sakamoto err = snd_ctl_add(oxfw->card,
314f3a0e32aSTakashi Sakamoto snd_ctl_new1(&controls[i], oxfw));
315f3a0e32aSTakashi Sakamoto if (err < 0)
316f3a0e32aSTakashi Sakamoto return err;
317f3a0e32aSTakashi Sakamoto }
318f3a0e32aSTakashi Sakamoto
319f3a0e32aSTakashi Sakamoto return 0;
320f3a0e32aSTakashi Sakamoto }
321