1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/init.h>
24#include <linux/kernel.h>
25#include <linux/device.h>
26#include <linux/spinlock.h>
27#include <linux/videodev2.h>
28
29#include <media/v4l2-device.h>
30
31#include <sound/core.h>
32#include <sound/control.h>
33#include <sound/tlv.h>
34
35#include "cx18-alsa.h"
36#include "cx18-driver.h"
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54static inline int dB_to_cx18_av_vol(int dB)
55{
56 if (dB < -96)
57 dB = -96;
58 else if (dB > 8)
59 dB = 8;
60 return (dB + 119) << 9;
61}
62
63static inline int cx18_av_vol_to_dB(int v)
64{
65 if (v < (23 << 9))
66 v = (23 << 9);
67 else if (v > (127 << 9))
68 v = (127 << 9);
69 return (v >> 9) - 119;
70}
71
72static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
73 struct snd_ctl_elem_info *uinfo)
74{
75 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
76 uinfo->count = 1;
77
78 uinfo->value.integer.min = -96;
79 uinfo->value.integer.max = 8;
80 uinfo->value.integer.step = 1;
81 return 0;
82}
83
84static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
85 struct snd_ctl_elem_value *uctl)
86{
87 struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
88 struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
89 struct v4l2_control vctrl;
90 int ret;
91
92 vctrl.id = V4L2_CID_AUDIO_VOLUME;
93 vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
94
95 snd_cx18_lock(cxsc);
96 ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
97 snd_cx18_unlock(cxsc);
98
99 if (!ret)
100 uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value);
101 return ret;
102}
103
104static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
105 struct snd_ctl_elem_value *uctl)
106{
107 struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
108 struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
109 struct v4l2_control vctrl;
110 int ret;
111
112 vctrl.id = V4L2_CID_AUDIO_VOLUME;
113 vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
114
115 snd_cx18_lock(cxsc);
116
117
118 ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
119
120 if (ret ||
121 (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
122
123
124 vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
125 ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl);
126 if (!ret)
127 ret = 1;
128 }
129 snd_cx18_unlock(cxsc);
130
131 return ret;
132}
133
134
135
136static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
137
138static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = {
139 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
140 .name = "Analog TV Capture Volume",
141 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
142 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
143 .info = snd_cx18_mixer_tv_volume_info,
144 .get = snd_cx18_mixer_tv_volume_get,
145 .put = snd_cx18_mixer_tv_volume_put,
146 .tlv.p = snd_cx18_mixer_tv_vol_db_scale
147};
148
149
150
151
152
153
154
155
156
157
158
159
160
161int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc)
162{
163 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
164 struct snd_card *sc = cxsc->sc;
165 int ret;
166
167 strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername));
168
169 ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc));
170 if (ret) {
171 CX18_ALSA_WARN("%s: failed to add %s control, err %d\n",
172 __func__, snd_cx18_mixer_tv_vol.name, ret);
173 }
174 return ret;
175}
176