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