Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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/init.h>
 26#include <linux/slab.h>
 27#include <linux/version.h>
 28#include <media/v4l2-dev.h>
 29#include <media/v4l2-ioctl.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		 (50UL * 16000)
 41#define FREQ_HI		(150UL * 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
 65/*
 66 * lowlevel part
 67 */
 68
 69static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
 70{
 71	u16 l;
 72	u8 data;
 73
 74	tea->ops->set_direction(tea, 1);
 75	udelay(16);
 76
 77	for (l = 25; l > 0; l--) {
 78		data = (val >> 24) & TEA575X_DATA;
 79		val <<= 1;			/* shift data */
 80		tea->ops->set_pins(tea, data | TEA575X_WREN);
 81		udelay(2);
 82		tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
 83		udelay(2);
 84		tea->ops->set_pins(tea, data | TEA575X_WREN);
 85		udelay(2);
 86	}
 87
 88	if (!tea->mute)
 89		tea->ops->set_pins(tea, 0);
 90}
 91
 92static unsigned int snd_tea575x_read(struct snd_tea575x *tea)
 93{
 94	u16 l, rdata;
 95	u32 data = 0;
 96
 97	tea->ops->set_direction(tea, 0);
 98	tea->ops->set_pins(tea, 0);
 99	udelay(16);
100
101	for (l = 24; l--;) {
102		tea->ops->set_pins(tea, TEA575X_CLK);
103		udelay(2);
104		if (!l)
105			tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
106		tea->ops->set_pins(tea, 0);
107		udelay(2);
108		data <<= 1;			/* shift data */
109		rdata = tea->ops->get_pins(tea);
110		if (!l)
111			tea->stereo = (rdata & TEA575X_MOST) ?  0 : 1;
112		if (rdata & TEA575X_DATA)
113			data++;
114		udelay(2);
115	}
116
117	if (tea->mute)
118		tea->ops->set_pins(tea, TEA575X_WREN);
119
120	return data;
121}
122
123static void snd_tea575x_get_freq(struct snd_tea575x *tea)
124{
125	unsigned long freq;
126
127	freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
128	/* freq *= 12.5 */
129	freq *= 125;
130	freq /= 10;
131	/* crystal fixup */
132	if (tea->tea5759)
133		freq += TEA575X_FMIF;
134	else
135		freq -= TEA575X_FMIF;
136
137	tea->freq = freq * 16;		/* from kHz */
138}
139
140static void snd_tea575x_set_freq(struct snd_tea575x *tea)
141{
142	unsigned long freq;
143
144	freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
145	freq /= 16;		/* to kHz */
146	/* crystal fixup */
147	if (tea->tea5759)
148		freq -= TEA575X_FMIF;
149	else
150		freq += TEA575X_FMIF;
151	/* freq /= 12.5 */
152	freq *= 10;
153	freq /= 125;
154
155	tea->val &= ~TEA575X_BIT_FREQ_MASK;
156	tea->val |= freq & TEA575X_BIT_FREQ_MASK;
157	snd_tea575x_write(tea, tea->val);
158}
159
160/*
161 * Linux Video interface
162 */
163
164static int vidioc_querycap(struct file *file, void  *priv,
165					struct v4l2_capability *v)
166{
167	struct snd_tea575x *tea = video_drvdata(file);
168
169	strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
170	strlcpy(v->card, tea->card, sizeof(v->card));
171	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
172	strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
173	v->version = RADIO_VERSION;
174	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
175	return 0;
176}
177
178static int vidioc_g_tuner(struct file *file, void *priv,
179					struct v4l2_tuner *v)
180{
181	struct snd_tea575x *tea = video_drvdata(file);
182
183	if (v->index > 0)
184		return -EINVAL;
185
186	snd_tea575x_read(tea);
187
188	strcpy(v->name, "FM");
189	v->type = V4L2_TUNER_RADIO;
190	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
191	v->rangelow = FREQ_LO;
192	v->rangehigh = FREQ_HI;
193	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
194	v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
195	v->signal = tea->tuned ? 0xffff : 0;
196
197	return 0;
198}
199
200static int vidioc_s_tuner(struct file *file, void *priv,
201					struct v4l2_tuner *v)
202{
203	if (v->index > 0)
204		return -EINVAL;
205	return 0;
206}
207
208static int vidioc_g_frequency(struct file *file, void *priv,
209					struct v4l2_frequency *f)
210{
211	struct snd_tea575x *tea = video_drvdata(file);
212
213	if (f->tuner != 0)
214		return -EINVAL;
215	f->type = V4L2_TUNER_RADIO;
216	snd_tea575x_get_freq(tea);
217	f->frequency = tea->freq;
218	return 0;
219}
220
221static int vidioc_s_frequency(struct file *file, void *priv,
222					struct v4l2_frequency *f)
223{
224	struct snd_tea575x *tea = video_drvdata(file);
225
226	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
227		return -EINVAL;
228
229	if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
230		return -EINVAL;
231
232	tea->freq = f->frequency;
233
234	snd_tea575x_set_freq(tea);
235
236	return 0;
237}
238
239static int vidioc_g_audio(struct file *file, void *priv,
240					struct v4l2_audio *a)
241{
242	if (a->index > 1)
243		return -EINVAL;
244
245	strcpy(a->name, "Radio");
246	a->capability = V4L2_AUDCAP_STEREO;
247	return 0;
248}
249
250static int vidioc_s_audio(struct file *file, void *priv,
251					struct v4l2_audio *a)
252{
253	if (a->index != 0)
254		return -EINVAL;
255	return 0;
256}
257
258static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
259{
260	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
261
262	switch (ctrl->id) {
263	case V4L2_CID_AUDIO_MUTE:
264		tea->mute = ctrl->val;
265		snd_tea575x_set_freq(tea);
266		return 0;
267	}
268
269	return -EINVAL;
270}
271
272static const struct v4l2_file_operations tea575x_fops = {
273	.owner		= THIS_MODULE,
274	.unlocked_ioctl	= video_ioctl2,
275};
276
277static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
278	.vidioc_querycap    = vidioc_querycap,
279	.vidioc_g_tuner     = vidioc_g_tuner,
280	.vidioc_s_tuner     = vidioc_s_tuner,
281	.vidioc_g_audio     = vidioc_g_audio,
282	.vidioc_s_audio     = vidioc_s_audio,
283	.vidioc_g_frequency = vidioc_g_frequency,
284	.vidioc_s_frequency = vidioc_s_frequency,
285};
286
287static struct video_device tea575x_radio = {
288	.name           = "tea575x-tuner",
289	.fops           = &tea575x_fops,
290	.ioctl_ops 	= &tea575x_ioctl_ops,
291	.release	= video_device_release_empty,
292};
293
294static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
295	.s_ctrl = tea575x_s_ctrl,
296};
297
298/*
299 * initialize all the tea575x chips
300 */
301int snd_tea575x_init(struct snd_tea575x *tea)
302{
303	int retval;
304
305	tea->mute = 1;
306
307	snd_tea575x_write(tea, 0x55AA);
308	if (snd_tea575x_read(tea) != 0x55AA)
309		return -ENODEV;
310
311	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
312	tea->freq = 90500 * 16;		/* 90.5Mhz default */
313	snd_tea575x_set_freq(tea);
314
315	tea->vd = tea575x_radio;
316	video_set_drvdata(&tea->vd, tea);
317	mutex_init(&tea->mutex);
318	tea->vd.lock = &tea->mutex;
319
320	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
321	tea->vd.ctrl_handler = &tea->ctrl_handler;
322	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
323	retval = tea->ctrl_handler.error;
324	if (retval) {
325		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
326		v4l2_ctrl_handler_free(&tea->ctrl_handler);
327		return retval;
328	}
329
330	if (tea->ext_init) {
331		retval = tea->ext_init(tea);
332		if (retval) {
333			v4l2_ctrl_handler_free(&tea->ctrl_handler);
334			return retval;
335		}
336	}
337
338	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
339
340	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
341	if (retval) {
342		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
343		v4l2_ctrl_handler_free(&tea->ctrl_handler);
344		return retval;
345	}
346
347	return 0;
348}
349
350void snd_tea575x_exit(struct snd_tea575x *tea)
351{
352	video_unregister_device(&tea->vd);
353	v4l2_ctrl_handler_free(&tea->ctrl_handler);
354}
355
356static int __init alsa_tea575x_module_init(void)
357{
358	return 0;
359}
360
361static void __exit alsa_tea575x_module_exit(void)
362{
363}
364
365module_init(alsa_tea575x_module_init)
366module_exit(alsa_tea575x_module_exit)
367
368EXPORT_SYMBOL(snd_tea575x_init);
369EXPORT_SYMBOL(snd_tea575x_exit);