static int firewave_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static unsigned int stereo_rates[] = { 48000, 96000 }; struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); /* two channels work only at 48/96 kHz */ if (snd_interval_max(channels) < 6) return snd_interval_list(rate, 2, stereo_rates, 0); return 0; }
/** * snd_pcm_hw_param_value_max * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Return the maximum value for field PAR. */ static unsigned int snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir) { if (hw_is_mask(var)) { if (dir) *dir = 0; return snd_mask_max(hw_param_mask_c(params, var)); } if (hw_is_interval(var)) { const struct snd_interval *i = hw_param_interval_c(params, var); if (dir) *dir = - (int) i->openmax; return snd_interval_max(i); } return -EINVAL; }
static int firewave_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static const struct snd_interval all_channels = { .min = 6, .max = 6 }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* 32/44.1 kHz work only with all six channels */ if (snd_interval_max(rate) < 48000) return snd_interval_refine(channels, &all_channels); return 0; } static int firewave_constraints(struct snd_pcm_runtime *runtime) { static unsigned int channels_list[] = { 2, 6 }; static struct snd_pcm_hw_constraint_list channels_list_constraint = { .count = 2, .list = channels_list, }; int err; runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000; runtime->hw.channels_max = 6; err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &channels_list_constraint); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, firewave_rate_constraint, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, firewave_channels_constraint, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; return 0; } static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) { runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; return 0; } static int fwspk_open(struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 4 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, .periods_min = 1, .periods_max = UINT_MAX, }; struct fwspk *fwspk = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; runtime->hw = hardware; err = fwspk->device_info->pcm_constraints(runtime); if (err < 0) return err; err = snd_pcm_limit_hw_rates(runtime); if (err < 0) return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, UINT_MAX); if (err < 0) return err; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) return err; return 0; } static int fwspk_close(struct snd_pcm_substream *substream) { return 0; } static void fwspk_stop_stream(struct fwspk *fwspk) { if (fwspk->stream_running) { amdtp_out_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); fwspk->stream_running = false; } } static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) { u8 *buf; int err; buf = kmalloc(8, GFP_KERNEL); if (!buf) return -ENOMEM; buf[0] = 0x00; /* AV/C, CONTROL */ buf[1] = 0xff; /* unit */ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ buf[3] = 0x00; /* plug 0 */ buf[4] = 0x90; /* format: audio */ buf[5] = 0x00 | sfc; /* AM824, frequency */ buf[6] = 0xff; /* SYT (not used) */ buf[7] = 0xff; err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); if (err < 0) goto error; if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { dev_err(&fwspk->unit->device, "failed to set sample rate\n"); err = -EIO; goto error; } err = 0; error: kfree(buf); return err; } static int fwspk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) goto error; amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); amdtp_out_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); err = fwspk_set_rate(fwspk, fwspk->stream.sfc); if (err < 0) goto err_buffer; return 0; err_buffer: snd_pcm_lib_free_vmalloc_buffer(substream); error: return err; } static int fwspk_hw_free(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int fwspk_prepare(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); if (amdtp_out_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); if (!fwspk->stream_running) { err = cmp_connection_establish(&fwspk->connection, amdtp_out_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex; err = amdtp_out_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) goto err_connection; fwspk->stream_running = true; } mutex_unlock(&fwspk->mutex); amdtp_out_stream_pcm_prepare(&fwspk->stream); return 0; err_connection: cmp_connection_break(&fwspk->connection); err_mutex: mutex_unlock(&fwspk->mutex); return err; } static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) { struct fwspk *fwspk = substream->private_data; struct snd_pcm_substream *pcm; switch (cmd) { case SNDRV_PCM_TRIGGER_START: pcm = substream; break; case SNDRV_PCM_TRIGGER_STOP: pcm = NULL; break; default: return -EINVAL; } amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); return 0; } static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; return amdtp_out_stream_pcm_pointer(&fwspk->stream); } static int fwspk_create_pcm(struct fwspk *fwspk) { static struct snd_pcm_ops ops = { .open = fwspk_open, .close = fwspk_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = fwspk_hw_params, .hw_free = fwspk_hw_free, .prepare = fwspk_prepare, .trigger = fwspk_trigger, .pointer = fwspk_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); if (err < 0) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; fwspk->pcm->ops = &ops; return 0; } enum control_action { CTL_READ, CTL_WRITE }; enum control_attribute { CTL_MIN = 0x02, CTL_MAX = 0x03, CTL_CURRENT = 0x10, }; static int fwspk_mute_command(struct fwspk *fwspk, bool *value, enum control_action action) { u8 *buf; u8 response_ok; int err; buf = kmalloc(11, GFP_KERNEL); if (!buf) return -ENOMEM; if (action == CTL_READ) { buf[0] = 0x01; /* AV/C, STATUS */ response_ok = 0x0c; /* STABLE */ } else { buf[0] = 0x00; /* AV/C, CONTROL */ response_ok = 0x09; /* ACCEPTED */ } buf[1] = 0x08; /* audio unit 0 */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x81; /* function block type: feature */ buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ buf[5] = 0x10; /* control attribute: current */ buf[6] = 0x02; /* selector length */ buf[7] = 0x00; /* audio channel number */ buf[8] = 0x01; /* control selector: mute */ buf[9] = 0x01; /* control data length */ if (action == CTL_READ) buf[10] = 0xff; else buf[10] = *value ? 0x70 : 0x60; err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); if (err < 0) goto error; if (err < 11) { dev_err(&fwspk->unit->device, "short FCP response\n"); err = -EIO; goto error; } if (buf[0] != response_ok) { dev_err(&fwspk->unit->device, "mute command failed\n"); err = -EIO; goto error; } if (action == CTL_READ) *value = buf[10] == 0x70; err = 0; error: kfree(buf); return err; }