1/* 2 * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips 3 * 4 * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz> 5 * 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 02111-1307 USA 20 * 21 */ 22 23#include <asm/io.h> 24#include <linux/delay.h> 25#include <linux/interrupt.h> 26#include <linux/init.h> 27#include <linux/slab.h> 28#include <linux/version.h> 29#include <sound/core.h> 30#include <sound/tea575x-tuner.h> 31 32MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 33MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); 34MODULE_LICENSE("GPL"); 35 36static int radio_nr = -1; 37module_param(radio_nr, int, 0); 38 39#define RADIO_VERSION KERNEL_VERSION(0, 0, 2) 40#define FREQ_LO (87 * 16000) 41#define FREQ_HI (108 * 16000) 42 43/* 44 * definitions 45 */ 46 47#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */ 48#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */ 49#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */ 50#define TEA575X_BIT_BAND_MASK (3<<20) 51#define TEA575X_BIT_BAND_FM (0<<20) 52#define TEA575X_BIT_BAND_MW (1<<20) 53#define TEA575X_BIT_BAND_LW (1<<21) 54#define TEA575X_BIT_BAND_SW (1<<22) 55#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */ 56#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */ 57#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */ 58#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */ 59#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */ 60#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */ 61#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */ 62#define TEA575X_BIT_DUMMY (1<<15) /* buffer */ 63#define TEA575X_BIT_FREQ_MASK 0x7fff 64 65static struct v4l2_queryctrl radio_qctrl[] = { 66 { 67 .id = V4L2_CID_AUDIO_MUTE, 68 .name = "Mute", 69 .minimum = 0, 70 .maximum = 1, 71 .default_value = 1, 72 .type = V4L2_CTRL_TYPE_BOOLEAN, 73 } 74}; 75 76/* 77 * lowlevel part 78 */ 79 80static void snd_tea575x_set_freq(struct snd_tea575x *tea) 81{ 82 unsigned long freq; 83 84 freq = tea->freq / 16; /* to kHz */ 85 if (freq > 108000) 86 freq = 108000; 87 if (freq < 87000) 88 freq = 87000; 89 /* crystal fixup */ 90 if (tea->tea5759) 91 freq -= tea->freq_fixup; 92 else 93 freq += tea->freq_fixup; 94 /* freq /= 12.5 */ 95 freq *= 10; 96 freq /= 125; 97 98 tea->val &= ~TEA575X_BIT_FREQ_MASK; 99 tea->val |= freq & TEA575X_BIT_FREQ_MASK; 100 tea->ops->write(tea, tea->val); 101} 102 103/* 104 * Linux Video interface 105 */ 106 107static int vidioc_querycap(struct file *file, void *priv, 108 struct v4l2_capability *v) 109{ 110 struct snd_tea575x *tea = video_drvdata(file); 111 112 strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757"); 113 strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); 114 strlcpy(v->card, "Maestro Radio", sizeof(v->card)); 115 sprintf(v->bus_info, "PCI"); 116 v->version = RADIO_VERSION; 117 v->capabilities = V4L2_CAP_TUNER; 118 return 0; 119} 120 121static int vidioc_g_tuner(struct file *file, void *priv, 122 struct v4l2_tuner *v) 123{ 124 if (v->index > 0) 125 return -EINVAL; 126 127 strcpy(v->name, "FM"); 128 v->type = V4L2_TUNER_RADIO; 129 v->rangelow = FREQ_LO; 130 v->rangehigh = FREQ_HI; 131 v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; 132 v->capability = V4L2_TUNER_CAP_LOW; 133 v->audmode = V4L2_TUNER_MODE_MONO; 134 v->signal = 0xffff; 135 return 0; 136} 137 138static int vidioc_s_tuner(struct file *file, void *priv, 139 struct v4l2_tuner *v) 140{ 141 if (v->index > 0) 142 return -EINVAL; 143 return 0; 144} 145 146static int vidioc_g_frequency(struct file *file, void *priv, 147 struct v4l2_frequency *f) 148{ 149 struct snd_tea575x *tea = video_drvdata(file); 150 151 f->type = V4L2_TUNER_RADIO; 152 f->frequency = tea->freq; 153 return 0; 154} 155 156static int vidioc_s_frequency(struct file *file, void *priv, 157 struct v4l2_frequency *f) 158{ 159 struct snd_tea575x *tea = video_drvdata(file); 160 161 if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) 162 return -EINVAL; 163 164 tea->freq = f->frequency; 165 166 snd_tea575x_set_freq(tea); 167 168 return 0; 169} 170 171static int vidioc_g_audio(struct file *file, void *priv, 172 struct v4l2_audio *a) 173{ 174 if (a->index > 1) 175 return -EINVAL; 176 177 strcpy(a->name, "Radio"); 178 a->capability = V4L2_AUDCAP_STEREO; 179 return 0; 180} 181 182static int vidioc_s_audio(struct file *file, void *priv, 183 struct v4l2_audio *a) 184{ 185 if (a->index != 0) 186 return -EINVAL; 187 return 0; 188} 189 190static int vidioc_queryctrl(struct file *file, void *priv, 191 struct v4l2_queryctrl *qc) 192{ 193 int i; 194 195 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 196 if (qc->id && qc->id == radio_qctrl[i].id) { 197 memcpy(qc, &(radio_qctrl[i]), 198 sizeof(*qc)); 199 return 0; 200 } 201 } 202 return -EINVAL; 203} 204 205static int vidioc_g_ctrl(struct file *file, void *priv, 206 struct v4l2_control *ctrl) 207{ 208 struct snd_tea575x *tea = video_drvdata(file); 209 210 switch (ctrl->id) { 211 case V4L2_CID_AUDIO_MUTE: 212 if (tea->ops->mute) { 213 ctrl->value = tea->mute; 214 return 0; 215 } 216 } 217 return -EINVAL; 218} 219 220static int vidioc_s_ctrl(struct file *file, void *priv, 221 struct v4l2_control *ctrl) 222{ 223 struct snd_tea575x *tea = video_drvdata(file); 224 225 switch (ctrl->id) { 226 case V4L2_CID_AUDIO_MUTE: 227 if (tea->ops->mute) { 228 tea->ops->mute(tea, ctrl->value); 229 tea->mute = ctrl->value; 230 return 0; 231 } 232 } 233 return -EINVAL; 234} 235 236static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 237{ 238 *i = 0; 239 return 0; 240} 241 242static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 243{ 244 if (i != 0) 245 return -EINVAL; 246 return 0; 247} 248 249static int snd_tea575x_exclusive_open(struct file *file) 250{ 251 struct snd_tea575x *tea = video_drvdata(file); 252 253 return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0; 254} 255 256static int snd_tea575x_exclusive_release(struct file *file) 257{ 258 struct snd_tea575x *tea = video_drvdata(file); 259 260 clear_bit(0, &tea->in_use); 261 return 0; 262} 263 264static const struct v4l2_file_operations tea575x_fops = { 265 .owner = THIS_MODULE, 266 .open = snd_tea575x_exclusive_open, 267 .release = snd_tea575x_exclusive_release, 268 .ioctl = video_ioctl2, 269}; 270 271static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { 272 .vidioc_querycap = vidioc_querycap, 273 .vidioc_g_tuner = vidioc_g_tuner, 274 .vidioc_s_tuner = vidioc_s_tuner, 275 .vidioc_g_audio = vidioc_g_audio, 276 .vidioc_s_audio = vidioc_s_audio, 277 .vidioc_g_input = vidioc_g_input, 278 .vidioc_s_input = vidioc_s_input, 279 .vidioc_g_frequency = vidioc_g_frequency, 280 .vidioc_s_frequency = vidioc_s_frequency, 281 .vidioc_queryctrl = vidioc_queryctrl, 282 .vidioc_g_ctrl = vidioc_g_ctrl, 283 .vidioc_s_ctrl = vidioc_s_ctrl, 284}; 285 286static struct video_device tea575x_radio = { 287 .name = "tea575x-tuner", 288 .fops = &tea575x_fops, 289 .ioctl_ops = &tea575x_ioctl_ops, 290 .release = video_device_release, 291}; 292 293/* 294 * initialize all the tea575x chips 295 */ 296void snd_tea575x_init(struct snd_tea575x *tea) 297{ 298 int retval; 299 unsigned int val; 300 struct video_device *tea575x_radio_inst; 301 302 val = tea->ops->read(tea); 303 if (val == 0x1ffffff || val == 0) { 304 snd_printk(KERN_ERR 305 "tea575x-tuner: Cannot find TEA575x chip\n"); 306 return; 307 } 308 309 tea->in_use = 0; 310 tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; 311 tea->freq = 90500 * 16; /* 90.5Mhz default */ 312 313 tea575x_radio_inst = video_device_alloc(); 314 if (tea575x_radio_inst == NULL) { 315 printk(KERN_ERR "tea575x-tuner: not enough memory\n"); 316 return; 317 } 318 319 memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio)); 320 321 strcpy(tea575x_radio.name, tea->tea5759 ? 322 "TEA5759 radio" : "TEA5757 radio"); 323 324 video_set_drvdata(tea575x_radio_inst, tea); 325 326 retval = video_register_device(tea575x_radio_inst, 327 VFL_TYPE_RADIO, radio_nr); 328 if (retval) { 329 printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); 330 kfree(tea575x_radio_inst); 331 return; 332 } 333 334 snd_tea575x_set_freq(tea); 335 336 /* mute on init */ 337 if (tea->ops->mute) { 338 tea->ops->mute(tea, 1); 339 tea->mute = 1; 340 } 341 tea->vd = tea575x_radio_inst; 342} 343 344void snd_tea575x_exit(struct snd_tea575x *tea) 345{ 346 if (tea->vd) { 347 video_unregister_device(tea->vd); 348 tea->vd = NULL; 349 } 350} 351 352static int __init alsa_tea575x_module_init(void) 353{ 354 return 0; 355} 356 357static void __exit alsa_tea575x_module_exit(void) 358{ 359} 360 361module_init(alsa_tea575x_module_init) 362module_exit(alsa_tea575x_module_exit) 363 364EXPORT_SYMBOL(snd_tea575x_init); 365EXPORT_SYMBOL(snd_tea575x_exit); 366