Linux-libre 4.15.7-gnu
[librecmc/linux-libre.git] / drivers / media / pci / cx18 / cx18-alsa-mixer.c
1 /*
2  *  ALSA mixer controls for the
3  *  ALSA interface to cx18 PCM capture streams
4  *
5  *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  */
17
18 #include <linux/init.h>
19 #include <linux/kernel.h>
20 #include <linux/device.h>
21 #include <linux/spinlock.h>
22 #include <linux/videodev2.h>
23
24 #include <media/v4l2-device.h>
25
26 #include <sound/core.h>
27 #include <sound/control.h>
28 #include <sound/tlv.h>
29
30 #include "cx18-alsa.h"
31 #include "cx18-driver.h"
32
33 /*
34  * Note the cx18-av-core volume scale is funny, due to the alignment of the
35  * scale with another chip's range:
36  *
37  * v4l2_control value   /512    indicated dB    actual dB       reg 0x8d4
38  * 0x0000 - 0x01ff        0     -119            -96             228
39  * 0x0200 - 0x02ff        1     -118            -96             228
40  * ...
41  * 0x2c00 - 0x2dff       22      -97            -96             228
42  * 0x2e00 - 0x2fff       23      -96            -96             228
43  * 0x3000 - 0x31ff       24      -95            -95             226
44  * ...
45  * 0xee00 - 0xefff      119        0              0              36
46  * ...
47  * 0xfe00 - 0xffff      127       +8             +8              20
48  */
49 static inline int dB_to_cx18_av_vol(int dB)
50 {
51         if (dB < -96)
52                 dB = -96;
53         else if (dB > 8)
54                 dB = 8;
55         return (dB + 119) << 9;
56 }
57
58 static inline int cx18_av_vol_to_dB(int v)
59 {
60         if (v < (23 << 9))
61                 v = (23 << 9);
62         else if (v > (127 << 9))
63                 v = (127 << 9);
64         return (v >> 9) - 119;
65 }
66
67 static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
68                                       struct snd_ctl_elem_info *uinfo)
69 {
70         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
71         uinfo->count = 1;
72         /* We're already translating values, just keep this control in dB */
73         uinfo->value.integer.min  = -96;
74         uinfo->value.integer.max  =   8;
75         uinfo->value.integer.step =   1;
76         return 0;
77 }
78
79 static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
80                                      struct snd_ctl_elem_value *uctl)
81 {
82         struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
83         struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
84         struct v4l2_control vctrl;
85         int ret;
86
87         vctrl.id = V4L2_CID_AUDIO_VOLUME;
88         vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
89
90         snd_cx18_lock(cxsc);
91         ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
92         snd_cx18_unlock(cxsc);
93
94         if (!ret)
95                 uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value);
96         return ret;
97 }
98
99 static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
100                                      struct snd_ctl_elem_value *uctl)
101 {
102         struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
103         struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
104         struct v4l2_control vctrl;
105         int ret;
106
107         vctrl.id = V4L2_CID_AUDIO_VOLUME;
108         vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
109
110         snd_cx18_lock(cxsc);
111
112         /* Fetch current state */
113         ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
114
115         if (ret ||
116             (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
117
118                 /* Set, if needed */
119                 vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
120                 ret = v4l2_s_ctrl(cx->sd_av->ctrl_handler, &vctrl);
121                 if (!ret)
122                         ret = 1; /* Indicate control was changed w/o error */
123         }
124         snd_cx18_unlock(cxsc);
125
126         return ret;
127 }
128
129
130 /* This is a bit of overkill, the slider is already in dB internally */
131 static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
132
133 static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = {
134         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
135         .name = "Analog TV Capture Volume",
136         .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
137                   SNDRV_CTL_ELEM_ACCESS_TLV_READ,
138         .info = snd_cx18_mixer_tv_volume_info,
139         .get = snd_cx18_mixer_tv_volume_get,
140         .put = snd_cx18_mixer_tv_volume_put,
141         .tlv.p = snd_cx18_mixer_tv_vol_db_scale
142 };
143
144 /* FIXME - add mute switch and balance, bass, treble sliders:
145         V4L2_CID_AUDIO_MUTE
146
147         V4L2_CID_AUDIO_BALANCE
148
149         V4L2_CID_AUDIO_BASS
150         V4L2_CID_AUDIO_TREBLE
151 */
152
153 /* FIXME - add stereo, lang1, lang2, mono menu */
154 /* FIXME - add CS5345 I2S volume for HVR-1600 */
155
156 int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc)
157 {
158         struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
159         struct snd_card *sc = cxsc->sc;
160         int ret;
161
162         strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername));
163
164         ret = snd_ctl_add(sc, snd_ctl_new1(&snd_cx18_mixer_tv_vol, cxsc));
165         if (ret) {
166                 CX18_ALSA_WARN("%s: failed to add %s control, err %d\n",
167                                 __func__, snd_cx18_mixer_tv_vol.name, ret);
168         }
169         return ret;
170 }