[PATCH v2 06/13] ASoC: Intel: catpt: PCM operations

Amadeusz Sławiński amadeuszx.slawinski at linux.intel.com
Tue Aug 11 14:17:29 CEST 2020



On 8/11/2020 12:00 PM, Cezary Rojewski wrote:
> DSP designed for Lynxpoint and Wildcat Point offers no dynamic topology
> i.e. all pipelines are already defined within firmware and host is
> relegated to allocing stream for predefined pins. This is represented by
> 'catpt_topology' member.
> 
> Implementation covers all available pin types:
> - system playback and capture
> - two offload streams
> - loopback (reference)
> - bluetooth playback and capture
> 
> PCM DAI operations differentiate between those pins as some (mainly
> offload) are to be handled differently - DSP expects wp updates on each
> notify_position notification.
> 
> System playback has no volume control capability as it is routed to
> mixer stream directly. Other primary streams - capture and two offloads
> - offer individual volume controls.
> 
> Compared to sound/soc/intel/haswell this configures SSP device format
> automatically on pcm creation.
> 
> Signed-off-by: Cezary Rojewski <cezary.rojewski at intel.com>
> ---

...

> +
> +static int catpt_set_ctlvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
> +{
> +	u32 dspvol;
> +	int ret, i;
> +
> +	if (ctlvol[0] == ctlvol[1]) {

This check seems bit suspicious to me, as CATPT_CHANNELS_MAX is 4 and 
you only check 2 of possible values here, while in else part, you loop 
over all possible 4 to set them? So there is chance that ctlvol[0] is 
equal to ctlvol[1], but ctlvol[2] and ctlvol[3] are different, what 
happens then?

> +		dspvol = ctlvol_to_dspvol(ctlvol[0]);
> +
> +		ret = catpt_ipc_set_volume(cdev, stream_id,
> +					   CATPT_ALL_CHANNELS_MASK, dspvol,
> +					   0, CATPT_AUDIO_CURVE_NONE);
> +	} else {
> +		for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
> +			dspvol = ctlvol_to_dspvol(ctlvol[i]);
> +
> +			ret = catpt_ipc_set_volume(cdev, stream_id,
> +						   i, dspvol,
> +						   0, CATPT_AUDIO_CURVE_NONE);
> +			if (ret)
> +				goto exit;
> +		}
> +	}
> +exit:
> +	if (ret)
> +		return CATPT_IPC_ERROR(ret);
> +	return 0;
> +}
> +
> +static int catpt_volume_info(struct snd_kcontrol *kcontrol,
> +			     struct snd_ctl_elem_info *uinfo)
> +{
> +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
> +	uinfo->count = CATPT_CHANNELS_MAX;
> +	uinfo->value.integer.min = 0;
> +	uinfo->value.integer.max = ARRAY_SIZE(volume_map) - 1;
> +	return 0;
> +}
> +
> +static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
> +				  struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component =
> +		snd_soc_kcontrol_component(kcontrol);
> +	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
> +	u32 dspvol;
> +	int i;
> +
> +	pm_runtime_get_sync(cdev->dev);
> +
> +	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
> +		dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i);
> +		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
> +	}
> +
> +	pm_runtime_mark_last_busy(cdev->dev);
> +	pm_runtime_put_autosuspend(cdev->dev);
> +
> +	return 0;
> +}
> +
> +static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
> +				  struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component =
> +		snd_soc_kcontrol_component(kcontrol);
> +	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
> +	int ret;
> +
> +	pm_runtime_get_sync(cdev->dev);
> +
> +	ret = catpt_set_ctlvol(cdev, cdev->mixer.mixer_hw_id,
> +			       ucontrol->value.integer.value);
> +
> +	pm_runtime_mark_last_busy(cdev->dev);
> +	pm_runtime_put_autosuspend(cdev->dev);
> +
> +	return ret;
> +}
> +
> +static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
> +				   struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component =
> +		snd_soc_kcontrol_component(kcontrol);
> +	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
> +	struct catpt_stream_runtime *stream;
> +	long *ctlvol = (long *)kcontrol->private_value;
> +	u32 dspvol;
> +	int i;
> +
> +	stream = catpt_stream_find(cdev, kcontrol->id.device);
> +	if (!stream) {
> +		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
> +			ucontrol->value.integer.value[i] = ctlvol[i];
> +		return 0;
> +	}
> +
> +	pm_runtime_get_sync(cdev->dev);
> +
> +	for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
> +		dspvol = catpt_stream_volume(cdev, stream, i);
> +		ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
> +	}
> +
> +	pm_runtime_mark_last_busy(cdev->dev);
> +	pm_runtime_put_autosuspend(cdev->dev);
> +
> +	return 0;
> +}
> +
> +static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
> +				   struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component =
> +		snd_soc_kcontrol_component(kcontrol);
> +	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
> +	struct catpt_stream_runtime *stream;
> +	long *ctlvol = (long *)kcontrol->private_value;
> +	int ret, i;
> +
> +	stream = catpt_stream_find(cdev, kcontrol->id.device);
> +	if (!stream) {
> +		for (i = 0; i < CATPT_CHANNELS_MAX; i++)
> +			ctlvol[i] = ucontrol->value.integer.value[i];
> +		return 0;
> +	}
> +
> +	pm_runtime_get_sync(cdev->dev);
> +
> +	ret = catpt_set_ctlvol(cdev, stream->info.stream_hw_id,
> +			       ucontrol->value.integer.value);
> +
> +	pm_runtime_mark_last_busy(cdev->dev);
> +	pm_runtime_put_autosuspend(cdev->dev);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < CATPT_CHANNELS_MAX; i++)
> +		ctlvol[i] = ucontrol->value.integer.value[i];
> +	return 0;
> +}
> +
> +static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol,
> +				     struct snd_ctl_elem_value *ucontrol)
> +{
> +	ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value;
> +	return 0;
> +}
> +
> +static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
> +				     struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component =
> +		snd_soc_kcontrol_component(kcontrol);
> +	struct catpt_dev *cdev = dev_get_drvdata(component->dev);
> +	struct catpt_stream_runtime *stream;
> +	bool mute;
> +	int ret;
> +
> +	mute = (bool)ucontrol->value.integer.value[0];
> +
> +	stream = catpt_stream_find(cdev, kcontrol->id.device);
> +	if (!stream) {
> +		*(bool *)kcontrol->private_value = mute;
> +		return 0;
> +	}
> +
> +	pm_runtime_get_sync(cdev->dev);
> +
> +	ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
> +
> +	pm_runtime_mark_last_busy(cdev->dev);
> +	pm_runtime_put_autosuspend(cdev->dev);
> +
> +	if (ret)
> +		return CATPT_IPC_ERROR(ret);
> +
> +	*(bool *)kcontrol->private_value = mute;
> +	return 0;
> +}
> +
> +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
> +
> +static int catpt_component_probe(struct snd_soc_component *component)
> +{
> +	struct snd_kcontrol_new templates[5];
> +	struct snd_kcontrol_new volctl = {
> +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> +		.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
> +			  SNDRV_CTL_ELEM_ACCESS_READWRITE,
> +		.info = catpt_volume_info,
> +		.tlv.p = catpt_volume_tlv,
> +	};
> +	void *p;
> +	int i;
> +
> +	/* master volume (mixer stream) */
> +	templates[0] = volctl;
> +	templates[0].name = "Master Playback Volume";
> +	templates[0].get = catpt_mixer_volume_get;
> +	templates[0].put = catpt_mixer_volume_put;
> +
> +	/* individual volume controls for offload and capture */
> +	for (i = 1; i < 4; i++) {
> +		/* volume storage for each of CATPT_CHANNELS_MAX */
> +		p = devm_kcalloc(component->dev, CATPT_CHANNELS_MAX,
> +				 sizeof(long), GFP_KERNEL);
> +		if (!p)
> +			return -ENOMEM;
> +		templates[i] = volctl;
> +		templates[i].get = catpt_stream_volume_get;
> +		templates[i].put = catpt_stream_volume_put;
> +		templates[i].private_value = (unsigned long)p;
> +	}
> +
> +	templates[1].name = "Media0 Playback Volume";
> +	templates[1].device = CATPT_PIN_ID_OFFLOAD1;
> +	templates[2].name = "Media1 Playback Volume";
> +	templates[2].device = CATPT_PIN_ID_OFFLOAD2;
> +	templates[3].name = "Mic Capture Volume";
> +	templates[3].device = CATPT_PIN_ID_CAPTURE1;
> +
> +	/* reference stream mute */
> +	p = devm_kzalloc(component->dev, sizeof(bool), GFP_KERNEL);
> +	if (!p)
> +		return -ENOMEM;
> +	templates[4] = (struct snd_kcontrol_new)
> +		SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)p,
> +				    catpt_loopback_switch_get,
> +				    catpt_loopback_switch_put);
> +	templates[4].device = CATPT_PIN_ID_REFERENCE;
> +
> +	return snd_soc_add_component_controls(component, templates,
> +					      ARRAY_SIZE(templates));
> +}
> +
> +static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol,
> +				  struct snd_ctl_elem_value *ucontrol)
> +{
> +	return 0;
> +}
> +
> +static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol,
> +				  struct snd_ctl_elem_value *ucontrol)
> +{
> +	return 0;
> +}
> +
> +static int catpt_waves_param_get(struct snd_kcontrol *kcontrol,
> +				 unsigned int __user *bytes,
> +				 unsigned int size)
> +{
> +	return 0;
> +}
> +
> +static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
> +				 const unsigned int __user *bytes,
> +				 unsigned int size)
> +{
> +	return 0;
> +}
> +
> +static const struct snd_kcontrol_new component_kcontrols[] = {
> +/* Enable or disable WAVES module */
> +SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
> +		    catpt_waves_switch_get, catpt_waves_switch_put),
> +/* WAVES module parameter control */
> +SND_SOC_BYTES_TLV("Waves Set Param", 128,
> +		  catpt_waves_param_get, catpt_waves_param_put),
> +};
> +
> +static const struct snd_soc_dapm_widget component_widgets[] = {
> +	SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
> +
> +	SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
> +};
> +
> +static const struct snd_soc_dapm_route component_routes[] = {
> +	{"Playback VMixer", NULL, "System Playback"},
> +	{"Playback VMixer", NULL, "Offload0 Playback"},
> +	{"Playback VMixer", NULL, "Offload1 Playback"},
> +
> +	{"SSP0 CODEC OUT", NULL, "Playback VMixer"},
> +
> +	{"Analog Capture", NULL, "SSP0 CODEC IN"},
> +	{"Loopback Capture", NULL, "SSP0 CODEC IN"},
> +
> +	{"SSP1 BT OUT", NULL, "Bluetooth Playback"},
> +	{"Bluetooth Capture", NULL, "SSP1 BT IN"},
> +};
> +
> +static const struct snd_soc_component_driver catpt_comp_driver = {
> +	.name = "catpt-platform",
> +
> +	.probe = catpt_component_probe,
> +	.pcm_construct = catpt_component_pcm_construct,
> +	.open = catpt_component_open,
> +	.pointer = catpt_component_pointer,
> +
> +	.controls = component_kcontrols,
> +	.num_controls = ARRAY_SIZE(component_kcontrols),
> +	.dapm_widgets = component_widgets,
> +	.num_dapm_widgets = ARRAY_SIZE(component_widgets),
> +	.dapm_routes = component_routes,
> +	.num_dapm_routes = ARRAY_SIZE(component_routes),
> +};
> +
> +int catpt_arm_stream_templates(struct catpt_dev *cdev)
> +{
> +	struct resource *res;
> +	u32 scratch_size = 0;
> +	int i, j;
> +
> +	for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) {
> +		struct catpt_stream_template *template;
> +		struct catpt_module_entry *entry;
> +		struct catpt_module_type *type;
> +
> +		template = catpt_topology[i];
> +		template->persistent_size = 0;
> +
> +		for (j = 0; j < template->num_entries; j++) {
> +			entry = &template->entries[j];
> +			type = &cdev->modules[entry->module_id];
> +
> +			if (!type->loaded)
> +				return -ENOENT;
> +
> +			entry->entry_point = type->entry_point;
> +			template->persistent_size += type->persistent_size;
> +			if (type->scratch_size > scratch_size)
> +				scratch_size = type->scratch_size;
> +		}
> +	}
> +
> +	if (scratch_size) {
> +		/* allocate single scratch area for all modules */
> +		res = catpt_request_region(&cdev->dram, scratch_size);
> +		if (!res)
> +			return -EBUSY;
> +		cdev->scratch = res;
> +	}
> +
> +	return 0;
> +}
> +
> +int catpt_register_plat_component(struct catpt_dev *cdev)
> +{
> +	struct snd_soc_component *component;
> +	int ret;
> +
> +	component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL);
> +	if (!component)
> +		return -ENOMEM;
> +
> +	ret = snd_soc_component_initialize(component, &catpt_comp_driver,
> +					   cdev->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	component->name = catpt_comp_driver.name;
> +	return snd_soc_add_component(component, dai_drivers,
> +				     ARRAY_SIZE(dai_drivers));
> +}
> 


More information about the Alsa-devel mailing list