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