~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/oxfw/oxfw-spkr.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
  4  *
  5  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  6  */
  7 
  8 #include "oxfw.h"
  9 
 10 struct fw_spkr {
 11         bool mute;
 12         s16 volume[6];
 13         s16 volume_min;
 14         s16 volume_max;
 15 
 16         unsigned int mixer_channels;
 17         u8 mute_fb_id;
 18         u8 volume_fb_id;
 19 };
 20 
 21 enum control_action { CTL_READ, CTL_WRITE };
 22 enum control_attribute {
 23         CTL_MIN         = 0x02,
 24         CTL_MAX         = 0x03,
 25         CTL_CURRENT     = 0x10,
 26 };
 27 
 28 static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
 29                                   enum control_action action)
 30 {
 31         u8 *buf;
 32         u8 response_ok;
 33         int err;
 34 
 35         buf = kmalloc(11, GFP_KERNEL);
 36         if (!buf)
 37                 return -ENOMEM;
 38 
 39         if (action == CTL_READ) {
 40                 buf[0] = 0x01;          /* AV/C, STATUS */
 41                 response_ok = 0x0c;     /*       STABLE */
 42         } else {
 43                 buf[0] = 0x00;          /* AV/C, CONTROL */
 44                 response_ok = 0x09;     /*       ACCEPTED */
 45         }
 46         buf[1] = 0x08;                  /* audio unit 0 */
 47         buf[2] = 0xb8;                  /* FUNCTION BLOCK */
 48         buf[3] = 0x81;                  /* function block type: feature */
 49         buf[4] = fb_id;                 /* function block ID */
 50         buf[5] = 0x10;                  /* control attribute: current */
 51         buf[6] = 0x02;                  /* selector length */
 52         buf[7] = 0x00;                  /* audio channel number */
 53         buf[8] = 0x01;                  /* control selector: mute */
 54         buf[9] = 0x01;                  /* control data length */
 55         if (action == CTL_READ)
 56                 buf[10] = 0xff;
 57         else
 58                 buf[10] = *value ? 0x70 : 0x60;
 59 
 60         err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
 61         if (err < 0)
 62                 goto error;
 63         if (err < 11) {
 64                 dev_err(&unit->device, "short FCP response\n");
 65                 err = -EIO;
 66                 goto error;
 67         }
 68         if (buf[0] != response_ok) {
 69                 dev_err(&unit->device, "mute command failed\n");
 70                 err = -EIO;
 71                 goto error;
 72         }
 73         if (action == CTL_READ)
 74                 *value = buf[10] == 0x70;
 75 
 76         err = 0;
 77 
 78 error:
 79         kfree(buf);
 80 
 81         return err;
 82 }
 83 
 84 static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
 85                                     unsigned int channel,
 86                                     enum control_attribute attribute,
 87                                     enum control_action action)
 88 {
 89         u8 *buf;
 90         u8 response_ok;
 91         int err;
 92 
 93         buf = kmalloc(12, GFP_KERNEL);
 94         if (!buf)
 95                 return -ENOMEM;
 96 
 97         if (action == CTL_READ) {
 98                 buf[0] = 0x01;          /* AV/C, STATUS */
 99                 response_ok = 0x0c;     /*       STABLE */
100         } else {
101                 buf[0] = 0x00;          /* AV/C, CONTROL */
102                 response_ok = 0x09;     /*       ACCEPTED */
103         }
104         buf[1] = 0x08;                  /* audio unit 0 */
105         buf[2] = 0xb8;                  /* FUNCTION BLOCK */
106         buf[3] = 0x81;                  /* function block type: feature */
107         buf[4] = fb_id;                 /* function block ID */
108         buf[5] = attribute;             /* control attribute */
109         buf[6] = 0x02;                  /* selector length */
110         buf[7] = channel;               /* audio channel number */
111         buf[8] = 0x02;                  /* control selector: volume */
112         buf[9] = 0x02;                  /* control data length */
113         if (action == CTL_READ) {
114                 buf[10] = 0xff;
115                 buf[11] = 0xff;
116         } else {
117                 buf[10] = *value >> 8;
118                 buf[11] = *value;
119         }
120 
121         err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
122         if (err < 0)
123                 goto error;
124         if (err < 12) {
125                 dev_err(&unit->device, "short FCP response\n");
126                 err = -EIO;
127                 goto error;
128         }
129         if (buf[0] != response_ok) {
130                 dev_err(&unit->device, "volume command failed\n");
131                 err = -EIO;
132                 goto error;
133         }
134         if (action == CTL_READ)
135                 *value = (buf[10] << 8) | buf[11];
136 
137         err = 0;
138 
139 error:
140         kfree(buf);
141 
142         return err;
143 }
144 
145 static int spkr_mute_get(struct snd_kcontrol *control,
146                          struct snd_ctl_elem_value *value)
147 {
148         struct snd_oxfw *oxfw = control->private_data;
149         struct fw_spkr *spkr = oxfw->spec;
150 
151         value->value.integer.value[0] = !spkr->mute;
152 
153         return 0;
154 }
155 
156 static int spkr_mute_put(struct snd_kcontrol *control,
157                          struct snd_ctl_elem_value *value)
158 {
159         struct snd_oxfw *oxfw = control->private_data;
160         struct fw_spkr *spkr = oxfw->spec;
161         bool mute;
162         int err;
163 
164         mute = !value->value.integer.value[0];
165 
166         if (mute == spkr->mute)
167                 return 0;
168 
169         err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
170                                      CTL_WRITE);
171         if (err < 0)
172                 return err;
173         spkr->mute = mute;
174 
175         return 1;
176 }
177 
178 static int spkr_volume_info(struct snd_kcontrol *control,
179                             struct snd_ctl_elem_info *info)
180 {
181         struct snd_oxfw *oxfw = control->private_data;
182         struct fw_spkr *spkr = oxfw->spec;
183 
184         info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
185         info->count = spkr->mixer_channels;
186         info->value.integer.min = spkr->volume_min;
187         info->value.integer.max = spkr->volume_max;
188 
189         return 0;
190 }
191 
192 static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
193 
194 static int spkr_volume_get(struct snd_kcontrol *control,
195                            struct snd_ctl_elem_value *value)
196 {
197         struct snd_oxfw *oxfw = control->private_data;
198         struct fw_spkr *spkr = oxfw->spec;
199         unsigned int i;
200 
201         for (i = 0; i < spkr->mixer_channels; ++i)
202                 value->value.integer.value[channel_map[i]] = spkr->volume[i];
203 
204         return 0;
205 }
206 
207 static int spkr_volume_put(struct snd_kcontrol *control,
208                            struct snd_ctl_elem_value *value)
209 {
210         struct snd_oxfw *oxfw = control->private_data;
211         struct fw_spkr *spkr = oxfw->spec;
212         unsigned int i, changed_channels;
213         bool equal_values = true;
214         s16 volume;
215         int err;
216 
217         for (i = 0; i < spkr->mixer_channels; ++i) {
218                 if (value->value.integer.value[i] < spkr->volume_min ||
219                     value->value.integer.value[i] > spkr->volume_max)
220                         return -EINVAL;
221                 if (value->value.integer.value[i] !=
222                     value->value.integer.value[0])
223                         equal_values = false;
224         }
225 
226         changed_channels = 0;
227         for (i = 0; i < spkr->mixer_channels; ++i)
228                 if (value->value.integer.value[channel_map[i]] !=
229                                                         spkr->volume[i])
230                         changed_channels |= 1 << (i + 1);
231 
232         if (equal_values && changed_channels != 0)
233                 changed_channels = 1 << 0;
234 
235         for (i = 0; i <= spkr->mixer_channels; ++i) {
236                 volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
237                 if (changed_channels & (1 << i)) {
238                         err = avc_audio_feature_volume(oxfw->unit,
239                                                   spkr->volume_fb_id, &volume,
240                                                   i, CTL_CURRENT, CTL_WRITE);
241                         if (err < 0)
242                                 return err;
243                 }
244                 if (i > 0)
245                         spkr->volume[i - 1] = volume;
246         }
247 
248         return changed_channels != 0;
249 }
250 
251 int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
252 {
253         static const struct snd_kcontrol_new controls[] = {
254                 {
255                         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
256                         .name = "PCM Playback Switch",
257                         .info = snd_ctl_boolean_mono_info,
258                         .get = spkr_mute_get,
259                         .put = spkr_mute_put,
260                 },
261                 {
262                         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
263                         .name = "PCM Playback Volume",
264                         .info = spkr_volume_info,
265                         .get = spkr_volume_get,
266                         .put = spkr_volume_put,
267                 },
268         };
269         struct fw_spkr *spkr;
270         unsigned int i, first_ch;
271         int err;
272 
273         spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
274                             GFP_KERNEL);
275         if (!spkr)
276                 return -ENOMEM;
277         oxfw->spec = spkr;
278 
279         if (is_lacie) {
280                 spkr->mixer_channels = 1;
281                 spkr->mute_fb_id = 0x01;
282                 spkr->volume_fb_id = 0x01;
283         } else {
284                 spkr->mixer_channels = 6;
285                 spkr->mute_fb_id = 0x01;
286                 spkr->volume_fb_id = 0x02;
287         }
288 
289         err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
290                                        &spkr->volume_min, 0, CTL_MIN, CTL_READ);
291         if (err < 0)
292                 return err;
293         err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
294                                        &spkr->volume_max, 0, CTL_MAX, CTL_READ);
295         if (err < 0)
296                 return err;
297 
298         err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
299                                      CTL_READ);
300         if (err < 0)
301                 return err;
302 
303         first_ch = spkr->mixer_channels == 1 ? 0 : 1;
304         for (i = 0; i < spkr->mixer_channels; ++i) {
305                 err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
306                                                &spkr->volume[i], first_ch + i,
307                                                CTL_CURRENT, CTL_READ);
308                 if (err < 0)
309                         return err;
310         }
311 
312         for (i = 0; i < ARRAY_SIZE(controls); ++i) {
313                 err = snd_ctl_add(oxfw->card,
314                                   snd_ctl_new1(&controls[i], oxfw));
315                 if (err < 0)
316                         return err;
317         }
318 
319         return 0;
320 }
321 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php