1/*
2 *  ALSA mixer controls for the
3 *  ALSA interface to ivtv PCM capture streams
4 *
5 *  Copyright (C) 2009,2012  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 *  You should have received a copy of the GNU General Public License
18 *  along with this program; if not, write to the Free Software
19 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 *  02111-1307  USA
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 "ivtv-alsa.h"
36#include "ivtv-driver.h"
37
38/*
39 * Note the cx25840-core volume scale is funny, due to the alignment of the
40 * scale with another chip's range:
41 *
42 * v4l2_control value	/512	indicated dB	actual dB	reg 0x8d4
43 * 0x0000 - 0x01ff	  0	-119		-96		228
44 * 0x0200 - 0x02ff	  1	-118		-96		228
45 * ...
46 * 0x2c00 - 0x2dff	 22	 -97		-96		228
47 * 0x2e00 - 0x2fff	 23	 -96		-96		228
48 * 0x3000 - 0x31ff	 24	 -95		-95		226
49 * ...
50 * 0xee00 - 0xefff	119	   0		  0		 36
51 * ...
52 * 0xfe00 - 0xffff	127	  +8		 +8		 20
53 */
54static inline int dB_to_cx25840_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 cx25840_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_ivtv_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	/* We're already translating values, just keep this control in dB */
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_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl,
85				     struct snd_ctl_elem_value *uctl)
86{
87	struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
88	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
89	struct v4l2_control vctrl;
90	int ret;
91
92	vctrl.id = V4L2_CID_AUDIO_VOLUME;
93	vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
94
95	snd_ivtv_lock(itvsc);
96	ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
97	snd_ivtv_unlock(itvsc);
98
99	if (!ret)
100		uctl->value.integer.value[0] = cx25840_vol_to_dB(vctrl.value);
101	return ret;
102}
103
104static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl,
105				     struct snd_ctl_elem_value *uctl)
106{
107	struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
108	struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
109	struct v4l2_control vctrl;
110	int ret;
111
112	vctrl.id = V4L2_CID_AUDIO_VOLUME;
113	vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
114
115	snd_ivtv_lock(itvsc);
116
117	/* Fetch current state */
118	ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
119
120	if (ret ||
121	    (cx25840_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
122
123		/* Set, if needed */
124		vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
125		ret = v4l2_subdev_call(itv->sd_audio, core, s_ctrl, &vctrl);
126		if (!ret)
127			ret = 1; /* Indicate control was changed w/o error */
128	}
129	snd_ivtv_unlock(itvsc);
130
131	return ret;
132}
133
134
135/* This is a bit of overkill, the slider is already in dB internally */
136static DECLARE_TLV_DB_SCALE(snd_ivtv_mixer_tv_vol_db_scale, -9600, 100, 0);
137
138static struct snd_kcontrol_new snd_ivtv_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_ivtv_mixer_tv_volume_info,
144	.get = snd_ivtv_mixer_tv_volume_get,
145	.put = snd_ivtv_mixer_tv_volume_put,
146	.tlv.p = snd_ivtv_mixer_tv_vol_db_scale
147};
148
149/* FIXME - add mute switch and balance, bass, treble sliders:
150	V4L2_CID_AUDIO_MUTE
151
152	V4L2_CID_AUDIO_BALANCE
153
154	V4L2_CID_AUDIO_BASS
155	V4L2_CID_AUDIO_TREBLE
156*/
157
158/* FIXME - add stereo, lang1, lang2, mono menu */
159/* FIXME - add I2S volume */
160
161int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc)
162{
163	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
164	struct snd_card *sc = itvsc->sc;
165	int ret;
166
167	strlcpy(sc->mixername, "CX2341[56] Mixer", sizeof(sc->mixername));
168
169	ret = snd_ctl_add(sc, snd_ctl_new1(snd_ivtv_mixer_tv_vol, itvsc));
170	if (ret) {
171		IVTV_ALSA_WARN("%s: failed to add %s control, err %d\n",
172			       __func__, snd_ivtv_mixer_tv_vol.name, ret);
173	}
174	return ret;
175}
176