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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.