1// SPDX-License-Identifier: GPL-2.0
2/*
3 * PC-Speaker driver for Linux
4 *
5 * Mixer implementation.
6 * Copyright (C) 2001-2008  Stas Sergeev
7 */
8
9#include <sound/core.h>
10#include <sound/control.h>
11#include "pcsp.h"
12
13
14static int pcsp_enable_info(struct snd_kcontrol *kcontrol,
15			    struct snd_ctl_elem_info *uinfo)
16{
17	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
18	uinfo->count = 1;
19	uinfo->value.integer.min = 0;
20	uinfo->value.integer.max = 1;
21	return 0;
22}
23
24static int pcsp_enable_get(struct snd_kcontrol *kcontrol,
25			   struct snd_ctl_elem_value *ucontrol)
26{
27	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
28	ucontrol->value.integer.value[0] = chip->enable;
29	return 0;
30}
31
32static int pcsp_enable_put(struct snd_kcontrol *kcontrol,
33			   struct snd_ctl_elem_value *ucontrol)
34{
35	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
36	int changed = 0;
37	int enab = ucontrol->value.integer.value[0];
38	if (enab != chip->enable) {
39		chip->enable = enab;
40		changed = 1;
41	}
42	return changed;
43}
44
45static int pcsp_treble_info(struct snd_kcontrol *kcontrol,
46			    struct snd_ctl_elem_info *uinfo)
47{
48	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
49	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
50	uinfo->count = 1;
51	uinfo->value.enumerated.items = chip->max_treble + 1;
52	if (uinfo->value.enumerated.item > chip->max_treble)
53		uinfo->value.enumerated.item = chip->max_treble;
54	sprintf(uinfo->value.enumerated.name, "%lu",
55		(unsigned long)PCSP_CALC_RATE(uinfo->value.enumerated.item));
56	return 0;
57}
58
59static int pcsp_treble_get(struct snd_kcontrol *kcontrol,
60			   struct snd_ctl_elem_value *ucontrol)
61{
62	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
63	ucontrol->value.enumerated.item[0] = chip->treble;
64	return 0;
65}
66
67static int pcsp_treble_put(struct snd_kcontrol *kcontrol,
68			   struct snd_ctl_elem_value *ucontrol)
69{
70	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
71	int changed = 0;
72	int treble = ucontrol->value.enumerated.item[0];
73	if (treble != chip->treble) {
74		chip->treble = treble;
75#if PCSP_DEBUG
76		printk(KERN_INFO "PCSP: rate set to %li\n", PCSP_RATE());
77#endif
78		changed = 1;
79	}
80	return changed;
81}
82
83static int pcsp_pcspkr_info(struct snd_kcontrol *kcontrol,
84			    struct snd_ctl_elem_info *uinfo)
85{
86	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
87	uinfo->count = 1;
88	uinfo->value.integer.min = 0;
89	uinfo->value.integer.max = 1;
90	return 0;
91}
92
93static int pcsp_pcspkr_get(struct snd_kcontrol *kcontrol,
94			   struct snd_ctl_elem_value *ucontrol)
95{
96	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
97	ucontrol->value.integer.value[0] = chip->pcspkr;
98	return 0;
99}
100
101static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
102			   struct snd_ctl_elem_value *ucontrol)
103{
104	struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
105	int changed = 0;
106	int spkr = ucontrol->value.integer.value[0];
107	if (spkr != chip->pcspkr) {
108		chip->pcspkr = spkr;
109		changed = 1;
110	}
111	return changed;
112}
113
114#define PCSP_MIXER_CONTROL(ctl_type, ctl_name) \
115{ \
116	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER, \
117	.name =		ctl_name, \
118	.info =		pcsp_##ctl_type##_info, \
119	.get =		pcsp_##ctl_type##_get, \
120	.put =		pcsp_##ctl_type##_put, \
121}
122
123static const struct snd_kcontrol_new snd_pcsp_controls_pcm[] = {
124	PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
125	PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
126};
127
128static const struct snd_kcontrol_new snd_pcsp_controls_spkr[] = {
129	PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
130};
131
132static int snd_pcsp_ctls_add(struct snd_pcsp *chip,
133			     const struct snd_kcontrol_new *ctls, int num)
134{
135	int i, err;
136	struct snd_card *card = chip->card;
137	for (i = 0; i < num; i++) {
138		err = snd_ctl_add(card, snd_ctl_new1(ctls + i, chip));
139		if (err < 0)
140			return err;
141	}
142	return 0;
143}
144
145int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
146{
147	int err;
148	struct snd_card *card = chip->card;
149
150	if (!nopcm) {
151		err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_pcm,
152			ARRAY_SIZE(snd_pcsp_controls_pcm));
153		if (err < 0)
154			return err;
155	}
156	err = snd_pcsp_ctls_add(chip, snd_pcsp_controls_spkr,
157		ARRAY_SIZE(snd_pcsp_controls_spkr));
158	if (err < 0)
159		return err;
160
161	strcpy(card->mixername, "PC-Speaker");
162
163	return 0;
164}
165