File Index
Files and directories
Sign in
FILE

bcm2835-ctl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright 2011 Broadcom Corporation.  All rights reserved. */
  3
  4#include <sound/core.h>
  5#include <sound/control.h>
  6#include <sound/tlv.h>
  7#include <sound/asoundef.h>
  8
  9#include "bcm2835.h"
 10
 11/* volume maximum and minimum in terms of 0.01dB */
 12#define CTRL_VOL_MAX 400
 13#define CTRL_VOL_MIN -10239 /* originally -10240 */
 14
 15static int bcm2835_audio_set_chip_ctls(struct bcm2835_chip *chip)
 16{
 17	int i, err = 0;
 18
 19	/* change ctls for all substreams */
 20	for (i = 0; i < MAX_SUBSTREAMS; i++) {
 21		if (chip->alsa_stream[i]) {
 22			err = bcm2835_audio_set_ctls(chip->alsa_stream[i]);
 23			if (err < 0)
 24				break;
 25		}
 26	}
 27	return err;
 28}
 29
 30static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol,
 31				struct snd_ctl_elem_info *uinfo)
 32{
 33	if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
 34		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 35		uinfo->count = 1;
 36		uinfo->value.integer.min = CTRL_VOL_MIN;
 37		uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
 38	} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
 39		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 40		uinfo->count = 1;
 41		uinfo->value.integer.min = 0;
 42		uinfo->value.integer.max = 1;
 43	} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
 44		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 45		uinfo->count = 1;
 46		uinfo->value.integer.min = 0;
 47		uinfo->value.integer.max = AUDIO_DEST_MAX - 1;
 48	}
 49	return 0;
 50}
 51
 52static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol,
 53			       struct snd_ctl_elem_value *ucontrol)
 54{
 55	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 56
 57	mutex_lock(&chip->audio_mutex);
 58
 59	if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
 60		ucontrol->value.integer.value[0] = chip->volume;
 61	else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
 62		ucontrol->value.integer.value[0] = chip->mute;
 63	else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
 64		ucontrol->value.integer.value[0] = chip->dest;
 65
 66	mutex_unlock(&chip->audio_mutex);
 67	return 0;
 68}
 69
 70static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol,
 71			       struct snd_ctl_elem_value *ucontrol)
 72{
 73	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 74	struct snd_ctl_elem_info info;
 75	int val, *valp;
 76	int changed = 0;
 77
 78	if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
 79		valp = &chip->volume;
 80	else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
 81		valp = &chip->mute;
 82	else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
 83		valp = &chip->dest;
 84	else
 85		return -EINVAL;
 86
 87	val = ucontrol->value.integer.value[0];
 88
 89	snd_bcm2835_ctl_info(kcontrol, &info);
 90	if (val < info.value.integer.min || val > info.value.integer.max)
 91		return -EINVAL;
 92
 93	mutex_lock(&chip->audio_mutex);
 94	if (val != *valp) {
 95		*valp = val;
 96		changed = 1;
 97		if (bcm2835_audio_set_chip_ctls(chip))
 98			dev_err(chip->card->dev, "Failed to set ALSA controls..\n");
 99	}
100	mutex_unlock(&chip->audio_mutex);
101	return changed;
102}
103
104static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
105
106static const struct snd_kcontrol_new snd_bcm2835_ctl[] = {
107	{
108		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
109		.name = "PCM Playback Volume",
110		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
111		.private_value = PCM_PLAYBACK_VOLUME,
112		.info = snd_bcm2835_ctl_info,
113		.get = snd_bcm2835_ctl_get,
114		.put = snd_bcm2835_ctl_put,
115		.tlv = {.p = snd_bcm2835_db_scale}
116	},
117	{
118		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
119		.name = "PCM Playback Switch",
120		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
121		.private_value = PCM_PLAYBACK_MUTE,
122		.info = snd_bcm2835_ctl_info,
123		.get = snd_bcm2835_ctl_get,
124		.put = snd_bcm2835_ctl_put,
125	},
126};
127
128static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
129					  struct snd_ctl_elem_info *uinfo)
130{
131	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
132	uinfo->count = 1;
133	return 0;
134}
135
136static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
137					 struct snd_ctl_elem_value *ucontrol)
138{
139	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
140	int i;
141
142	mutex_lock(&chip->audio_mutex);
143
144	for (i = 0; i < 4; i++)
145		ucontrol->value.iec958.status[i] =
146			(chip->spdif_status >> (i * 8)) & 0xff;
147
148	mutex_unlock(&chip->audio_mutex);
149	return 0;
150}
151
152static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
153					 struct snd_ctl_elem_value *ucontrol)
154{
155	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
156	unsigned int val = 0;
157	int i, change;
158
159	mutex_lock(&chip->audio_mutex);
160
161	for (i = 0; i < 4; i++)
162		val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
163
164	change = val != chip->spdif_status;
165	chip->spdif_status = val;
166
167	mutex_unlock(&chip->audio_mutex);
168	return change;
169}
170
171static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
172				       struct snd_ctl_elem_info *uinfo)
173{
174	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
175	uinfo->count = 1;
176	return 0;
177}
178
179static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
180				      struct snd_ctl_elem_value *ucontrol)
181{
182	/*
183	 * bcm2835 supports only consumer mode and sets all other format flags
184	 * automatically. So the only thing left is signalling non-audio content
185	 */
186	ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
187	return 0;
188}
189
190static const struct snd_kcontrol_new snd_bcm2835_spdif[] = {
191	{
192		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
193		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
194		.info = snd_bcm2835_spdif_default_info,
195		.get = snd_bcm2835_spdif_default_get,
196		.put = snd_bcm2835_spdif_default_put
197	},
198	{
199		.access = SNDRV_CTL_ELEM_ACCESS_READ,
200		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
201		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
202		.info = snd_bcm2835_spdif_mask_info,
203		.get = snd_bcm2835_spdif_mask_get,
204	},
205};
206
207static int create_ctls(struct bcm2835_chip *chip, size_t size,
208		       const struct snd_kcontrol_new *kctls)
209{
210	int i, err;
211
212	for (i = 0; i < size; i++) {
213		err = snd_ctl_add(chip->card, snd_ctl_new1(&kctls[i], chip));
214		if (err < 0)
215			return err;
216	}
217	return 0;
218}
219
220int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip)
221{
222	strscpy(chip->card->mixername, "Broadcom Mixer", sizeof(chip->card->mixername));
223	return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl),
224			   snd_bcm2835_ctl);
225}
226
227int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip)
228{
229	int err;
230
231	strscpy(chip->card->mixername, "Broadcom Mixer", sizeof(chip->card->mixername));
232	err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), snd_bcm2835_ctl);
233	if (err < 0)
234		return err;
235	return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_spdif),
236			   snd_bcm2835_spdif);
237}
238
~/ Read-only file preview.