321 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * oxfw-spkr.c - a part of driver for OXFW970/971 based devices4 *5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>6 */7 8#include "oxfw.h"9 10struct 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 21enum control_action { CTL_READ, CTL_WRITE };22enum control_attribute {23 CTL_MIN = 0x02,24 CTL_MAX = 0x03,25 CTL_CURRENT = 0x10,26};27 28static 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 else58 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 78error:79 kfree(buf);80 81 return err;82}83 84static 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 139error:140 kfree(buf);141 142 return err;143}144 145static 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 156static 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 178static 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 192static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };193 194static 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 207static 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 251int 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