All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-13 22:17 Ondrej Zary
  2011-05-14 10:01 ` [alsa-devel] " Takashi Iwai
  2011-05-14 10:06   ` Hans Verkuil
  0 siblings, 2 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-13 22:17 UTC (permalink / raw)
  To: linux-media; +Cc: alsa-devel, Kernel development list

Hello,
the SF16-FMR2 radio card is basically a TEA5757 tuner connected to ISA bus but
the driver currently uses its own implementation. I converted the driver to
use generic tea575x-tuner implementation (see patch below) and it works fine.

But the tea575x-tuner module is located in /sound/i2c/other and the
corresponding Kconfig in /sound/pci. Should it be moved
to /drivers/media/radio or better leave it as is?

The card can optionally be equipped with PT2254A volume control (equivalent of
TC9154AP) so I need to extend tea575x-tuner to allow adding additional V4L2
controls (and add one if the chip is present). Is this OK or should be
something like v4l2_subdev used somehow?
(My card does not have the PT2254A chip so that code is untested.)


--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-14 00:14:23.000000000 +0200
@@ -54,6 +54,11 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	/* card driver can provide additional controls (e.g. volume) */
+	struct v4l2_queryctrl *extra_ctrls;	/* array of controls */
+	int num_extra_ctrls;		/* number of additional controls */
+	int (*g_extra_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
+	int (*s_extra_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-13 21:35:57.000000000 +0200
@@ -269,6 +269,7 @@ static int vidioc_s_audio(struct file *f
 static int vidioc_queryctrl(struct file *file, void *priv,
 					struct v4l2_queryctrl *qc)
 {
+	struct snd_tea575x *tea = video_drvdata(file);
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
@@ -278,6 +279,13 @@ static int vidioc_queryctrl(struct file
 			return 0;
 		}
 	}
+	for (i = 0; i < tea->num_extra_ctrls; i++) {
+		if (qc->id && qc->id == tea->extra_ctrls[i].id) {
+			memcpy(qc, &(tea->extra_ctrls[i]),
+						sizeof(*qc));
+			return 0;
+		}
+	}
 	return -EINVAL;
 }
 
@@ -291,6 +299,10 @@ static int vidioc_g_ctrl(struct file *fi
 		ctrl->value = tea->mute;
 		return 0;
 	}
+
+	if (tea->g_extra_ctrl)
+		return tea->g_extra_ctrl(file, priv, ctrl);
+
 	return -EINVAL;
 }
 
@@ -307,6 +319,10 @@ static int vidioc_s_ctrl(struct file *fi
 		}
 		return 0;
 	}
+
+	if (tea->s_extra_ctrl)
+		return tea->s_extra_ctrl(file, priv, ctrl);
+
 	return -EINVAL;
 }
 
--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-14 00:15:05.000000000 +0200
@@ -1,441 +1,206 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * interface
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	int volume;
+	struct snd_tea575x tea;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	unsigned long freq = dev->curfreq;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	fmr2_mute(dev->io);
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
+	outb(bits, fmr2->io);
 }
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (v->index > 0)
-		return -EINVAL;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	return v->index ? -EINVAL : 0;
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
-}
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* TC9154A/PT2254A volume control */
 
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_volume(struct fmr2 *fmr2, int volume)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(volume / 10) | TC9154A_ATT_MIN((volume % 10) / 2);
+	reg |= TC9154A_CHANNEL_LEFT | TC9154A_CHANNEL_RIGHT;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
+}
+
+static struct v4l2_queryctrl fmr2_volume_qctrl[] = {
+	{
+		.id		= V4L2_CID_AUDIO_VOLUME,
+		.name		= "Volume",
+		.minimum	= -68,
+		.maximum	= 0,
+		.step		= 2,
+		.default_value	= -68,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
 	}
-	return -EINVAL;
-}
+};
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
+static int fmr2_g_extra_ctrl(struct file *file, void *priv,
 					struct v4l2_control *ctrl)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct snd_tea575x *tea = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
+		ctrl->value = fmr2->volume;
 		return 0;
 	}
+
 	return -EINVAL;
 }
 
-static int vidioc_s_ctrl(struct file *file, void *priv,
+static int fmr2_s_extra_ctrl(struct file *file, void *priv,
 					struct v4l2_control *ctrl)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct snd_tea575x *tea = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
-		break;
 	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
-		break;
-	default:
-		return -EINVAL;
+		fmr2->volume = ctrl->value;
+		tc9154a_set_volume(fmr2, fmr2->volume);
+		return 0;
 	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
+	return -EINVAL;
 }
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
-
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
+
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
+		return -ENODEV;
 	}
 
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
-
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
-		release_region(fmr2->io, 2);
-		return -EINVAL;
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		fmr2->tea.extra_ctrls = fmr2_volume_qctrl;
+		fmr2->tea.num_extra_ctrls = 1;
+		fmr2->tea.g_extra_ctrl = fmr2_g_extra_ctrl;
+		fmr2->tea.s_extra_ctrl = fmr2_s_extra_ctrl;
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +208,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [alsa-devel] [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-13 22:17 [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface Ondrej Zary
@ 2011-05-14 10:01 ` Takashi Iwai
  2011-05-14 10:06   ` Hans Verkuil
  1 sibling, 0 replies; 38+ messages in thread
From: Takashi Iwai @ 2011-05-14 10:01 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

At Sat, 14 May 2011 00:17:23 +0200,
Ondrej Zary wrote:
> 
> Hello,
> the SF16-FMR2 radio card is basically a TEA5757 tuner connected to ISA bus but
> the driver currently uses its own implementation. I converted the driver to
> use generic tea575x-tuner implementation (see patch below) and it works fine.
> 
> But the tea575x-tuner module is located in /sound/i2c/other and the
> corresponding Kconfig in /sound/pci. Should it be moved
> to /drivers/media/radio or better leave it as is?

That would be a feasible option.
Actually no ALSA-specific data / function is used in tea575x-tuner.c,
so it'd be more natural to put it to drivers/media/radio.
(And remove the inclusion of <sound/*> from relevant files.)


thanks,

Takashi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-13 22:17 [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface Ondrej Zary
@ 2011-05-14 10:06   ` Hans Verkuil
  2011-05-14 10:06   ` Hans Verkuil
  1 sibling, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-14 10:06 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Saturday, May 14, 2011 00:17:23 Ondrej Zary wrote:
> Hello,
> the SF16-FMR2 radio card is basically a TEA5757 tuner connected to ISA bus but
> the driver currently uses its own implementation. I converted the driver to
> use generic tea575x-tuner implementation (see patch below) and it works fine.

Good!

> But the tea575x-tuner module is located in /sound/i2c/other and the
> corresponding Kconfig in /sound/pci. Should it be moved
> to /drivers/media/radio or better leave it as is?

Good question. It's in a weird place, but moving it may be more trouble than
it is worth. It might be a good idea to just try it and see if that improves
things or not.

> The card can optionally be equipped with PT2254A volume control (equivalent of
> TC9154AP) so I need to extend tea575x-tuner to allow adding additional V4L2
> controls (and add one if the chip is present). Is this OK or should be
> something like v4l2_subdev used somehow?
> (My card does not have the PT2254A chip so that code is untested.)

No, this isn't the right approach. This should be done using the control
framework (see Documentation/video4linux/v4l2-controls.txt).

In order to use that the tea575x-tuner driver needs to be converted to that
framework, and so should any V4L driver that uses it. It's a fair amount of
work, although it is worth doing since it will ensure that these drivers
fully and correctly implement the V4L2 control API.

Thank you for looking into this, BTW! It's been on my TODO list for a long
time but I never had time to actually work on it.

Regards,

	Hans

> 
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-14 00:14:23.000000000 +0200
> @@ -54,6 +54,11 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	/* card driver can provide additional controls (e.g. volume) */
> +	struct v4l2_queryctrl *extra_ctrls;	/* array of controls */
> +	int num_extra_ctrls;		/* number of additional controls */
> +	int (*g_extra_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
> +	int (*s_extra_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-13 21:35:57.000000000 +0200
> @@ -269,6 +269,7 @@ static int vidioc_s_audio(struct file *f
>  static int vidioc_queryctrl(struct file *file, void *priv,
>  					struct v4l2_queryctrl *qc)
>  {
> +	struct snd_tea575x *tea = video_drvdata(file);
>  	int i;
>  
>  	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> @@ -278,6 +279,13 @@ static int vidioc_queryctrl(struct file
>  			return 0;
>  		}
>  	}
> +	for (i = 0; i < tea->num_extra_ctrls; i++) {
> +		if (qc->id && qc->id == tea->extra_ctrls[i].id) {
> +			memcpy(qc, &(tea->extra_ctrls[i]),
> +						sizeof(*qc));
> +			return 0;
> +		}
> +	}
>  	return -EINVAL;
>  }
>  
> @@ -291,6 +299,10 @@ static int vidioc_g_ctrl(struct file *fi
>  		ctrl->value = tea->mute;
>  		return 0;
>  	}
> +
> +	if (tea->g_extra_ctrl)
> +		return tea->g_extra_ctrl(file, priv, ctrl);
> +
>  	return -EINVAL;
>  }
>  
> @@ -307,6 +319,10 @@ static int vidioc_s_ctrl(struct file *fi
>  		}
>  		return 0;
>  	}
> +
> +	if (tea->s_extra_ctrl)
> +		return tea->s_extra_ctrl(file, priv, ctrl);
> +
>  	return -EINVAL;
>  }
>  
> --- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
> +++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-14 00:15:05.000000000 +0200
> @@ -1,441 +1,206 @@
> -/* SF16FMR2 radio driver for Linux radio support
> - * heavily based on fmi driver...
> - * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
> +/* SF16-FMR2 radio driver for Linux
> + * Copyright (c) 2011 Ondrej Zary
>   *
> - * Notes on the hardware
> - *
> - *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
> - *  No volume control - only mute/unmute - you have to use line volume
> - *
> - *  For read stereo/mono you must wait 0.1 sec after set frequency and
> - *  card unmuted so I set frequency on unmute
> - *  Signal handling seem to work only on autoscanning (not implemented)
> - *
> - *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
> + * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
> + * but almost nothing remained here after conversion to generic TEA575x
> + * interface
>   */
>  
> +#include <linux/delay.h>
>  #include <linux/module.h>	/* Modules 			*/
>  #include <linux/init.h>		/* Initdata			*/
>  #include <linux/ioport.h>	/* request_region		*/
> -#include <linux/delay.h>	/* udelay			*/
> -#include <linux/videodev2.h>	/* kernel radio structs		*/
> -#include <linux/mutex.h>
> -#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
>  #include <linux/io.h>		/* outb, outb_p			*/
> -#include <media/v4l2-device.h>
> -#include <media/v4l2-ioctl.h>
> +#include <sound/tea575x-tuner.h>
>  
> -MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
> -MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
> +MODULE_AUTHOR("Ondrej Zary");
> +MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
>  MODULE_LICENSE("GPL");
>  
> -static int io = 0x384;
> -static int radio_nr = -1;
> -
> -module_param(io, int, 0);
> -MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
> -module_param(radio_nr, int, 0);
> -
> -#define RADIO_VERSION KERNEL_VERSION(0,0,2)
> -
> -#define AUD_VOL_INDEX 1
> -
> -#undef DEBUG
> -//#define DEBUG 1
> -
> -#ifdef DEBUG
> -# define  debug_print(s) printk s
> -#else
> -# define  debug_print(s)
> -#endif
> -
> -/* this should be static vars for module size */
> -struct fmr2
> -{
> -	struct v4l2_device v4l2_dev;
> -	struct video_device vdev;
> -	struct mutex lock;
> +struct fmr2 {
>  	int io;
> -	int curvol; /* 0-15 */
> -	int mute;
> -	int stereo; /* card is producing stereo audio */
> -	unsigned long curfreq; /* freq in kHz */
> -	int card_type;
> +	int volume;
> +	struct snd_tea575x tea;
>  };
>  
> +/* the port is hardwired so no need to support multiple cards */
> +#define FMR2_PORT	0x384
>  static struct fmr2 fmr2_card;
>  
> -/* hw precision is 12.5 kHz
> - * It is only useful to give freq in interval of 200 (=0.0125Mhz),
> - * other bits will be truncated
> - */
> -#define RSF16_ENCODE(x)	((x) / 200 + 856)
> -#define RSF16_MINFREQ (87 * 16000)
> -#define RSF16_MAXFREQ (108 * 16000)
> -
> -static inline void wait(int n, int io)
> -{
> -	for (; n; --n)
> -		inb(io);
> -}
> -
> -static void outbits(int bits, unsigned int data, int nWait, int io)
> -{
> -	int bit;
> -
> -	for (; --bits >= 0;) {
> -		bit = (data >> bits) & 1;
> -		outb(bit, io);
> -		wait(nWait, io);
> -		outb(bit | 2, io);
> -		wait(nWait, io);
> -		outb(bit, io);
> -		wait(nWait, io);
> -	}
> -}
> -
> -static inline void fmr2_mute(int io)
> -{
> -	outb(0x00, io);
> -	wait(4, io);
> -}
> -
> -static inline void fmr2_unmute(int io)
> -{
> -	outb(0x04, io);
> -	wait(4, io);
> -}
> -
> -static inline int fmr2_stereo_mode(int io)
> -{
> -	int n = inb(io);
> -
> -	outb(6, io);
> -	inb(io);
> -	n = ((n >> 3) & 1) ^ 1;
> -	debug_print((KERN_DEBUG "stereo: %d\n", n));
> -	return n;
> -}
> -
> -static int fmr2_product_info(struct fmr2 *dev)
> -{
> -	int n = inb(dev->io);
> -
> -	n &= 0xC1;
> -	if (n == 0) {
> -		/* this should support volume set */
> -		dev->card_type = 12;
> -		return 0;
> -	}
> -	/* not volume (mine is 11) */
> -	dev->card_type = (n == 128) ? 11 : 0;
> -	return n;
> -}
> -
> -static inline int fmr2_getsigstr(struct fmr2 *dev)
> -{
> -	/* !!! works only if scanning freq */
> -	int res = 0xffff;
> -
> -	outb(5, dev->io);
> -	wait(4, dev->io);
> -	if (!(inb(dev->io) & 1))
> -		res = 0;
> -	debug_print((KERN_DEBUG "signal: %d\n", res));
> -	return res;
> -}
> +/* TEA575x tuner pins */
> +#define STR_DATA	(1 << 0)
> +#define STR_CLK		(1 << 1)
> +#define STR_WREN	(1 << 2)
> +#define STR_MOST	(1 << 3)
> +/* PT2254A/TC9154A volume control pins */
> +#define PT_ST		(1 << 4)
> +#define PT_CK		(1 << 5)
> +#define PT_DATA		(1 << 6)
> +/* volume control presence pin */
> +#define FMR2_HASVOL	(1 << 7)
>  
> -/* set frequency and unmute card */
> -static int fmr2_setfreq(struct fmr2 *dev)
> +static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
>  {
> -	unsigned long freq = dev->curfreq;
> +	struct fmr2 *fmr2 = tea->private_data;
> +	u8 bits = 0;
>  
> -	fmr2_mute(dev->io);
> +	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
> +	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
> +	/* WRITE_ENABLE is inverted, DATA must be high during read */
> +	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
>  
> -	/* 0x42 for mono output
> -	 * 0x102 forward scanning
> -	 * 0x182 scansione avanti
> -	 */
> -	outbits(9, 0x2, 3, dev->io);
> -	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
> -
> -	fmr2_unmute(dev->io);
> -
> -	/* wait 0.11 sec */
> -	msleep(110);
> -
> -	/* NOTE if mute this stop radio
> -	   you must set freq on unmute */
> -	dev->stereo = fmr2_stereo_mode(dev->io);
> -	return 0;
> -}
> -
> -/* !!! not tested, in my card this does't work !!! */
> -static int fmr2_setvolume(struct fmr2 *dev)
> -{
> -	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
> -			0x110, 0x204, 0x210, 0x402,
> -			0x404, 0x408, 0x410, 0x801,
> -			0x802, 0x804, 0x808, 0x810 };
> -	int i, a;
> -	int n = vol[dev->curvol & 0x0f];
> -
> -	if (dev->card_type != 11)
> -		return 1;
> -
> -	for (i = 12; --i >= 0; ) {
> -		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 0x24, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -	}
> -	for (i = 6; --i >= 0; ) {
> -		a = ((0x18 >> i) & 1) << 6;
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 0x24, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -	}
> -	wait(4, dev->io);
> -	outb(0x14, dev->io);
> -	return 0;
> -}
> -
> -static int vidioc_querycap(struct file *file, void  *priv,
> -					struct v4l2_capability *v)
> -{
> -	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
> -	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
> -	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
> -	v->version = RADIO_VERSION;
> -	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
> -	return 0;
> +	outb(bits, fmr2->io);
>  }
>  
> -static int vidioc_g_tuner(struct file *file, void *priv,
> -					struct v4l2_tuner *v)
> +static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
>  {
> -	struct fmr2 *fmr2 = video_drvdata(file);
> -
> -	if (v->index > 0)
> -		return -EINVAL;
> +	struct fmr2 *fmr2 = tea->private_data;
> +	u8 bits = inb(fmr2->io);
>  
> -	strlcpy(v->name, "FM", sizeof(v->name));
> -	v->type = V4L2_TUNER_RADIO;
> -
> -	v->rangelow = RSF16_MINFREQ;
> -	v->rangehigh = RSF16_MAXFREQ;
> -	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
> -					V4L2_TUNER_SUB_MONO;
> -	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
> -	v->audmode = V4L2_TUNER_MODE_STEREO;
> -	mutex_lock(&fmr2->lock);
> -	v->signal = fmr2_getsigstr(fmr2);
> -	mutex_unlock(&fmr2->lock);
> -	return 0;
> +	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
> +		(bits & STR_MOST) ? TEA575X_MOST : 0;
>  }
>  
> -static int vidioc_s_tuner(struct file *file, void *priv,
> -					struct v4l2_tuner *v)
> +static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
>  {
> -	return v->index ? -EINVAL : 0;
>  }
>  
> -static int vidioc_s_frequency(struct file *file, void *priv,
> -					struct v4l2_frequency *f)
> -{
> -	struct fmr2 *fmr2 = video_drvdata(file);
> -
> -	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
> -		return -EINVAL;
> -	if (f->frequency < RSF16_MINFREQ ||
> -			f->frequency > RSF16_MAXFREQ)
> -		return -EINVAL;
> -	/* rounding in steps of 200 to match the freq
> -	   that will be used */
> -	fmr2->curfreq = (f->frequency / 200) * 200;
> -
> -	/* set card freq (if not muted) */
> -	if (fmr2->curvol && !fmr2->mute) {
> -		mutex_lock(&fmr2->lock);
> -		fmr2_setfreq(fmr2);
> -		mutex_unlock(&fmr2->lock);
> -	}
> -	return 0;
> -}
> -
> -static int vidioc_g_frequency(struct file *file, void *priv,
> -					struct v4l2_frequency *f)
> -{
> -	struct fmr2 *fmr2 = video_drvdata(file);
> -
> -	if (f->tuner != 0)
> -		return -EINVAL;
> -	f->type = V4L2_TUNER_RADIO;
> -	f->frequency = fmr2->curfreq;
> -	return 0;
> -}
> +static struct snd_tea575x_ops fmr2_tea_ops = {
> +	.set_pins = fmr2_tea575x_set_pins,
> +	.get_pins = fmr2_tea575x_get_pins,
> +	.set_direction = fmr2_tea575x_set_direction,
> +};
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> -{
> -	struct fmr2 *fmr2 = video_drvdata(file);
> +/* TC9154A/PT2254A volume control */
>  
> -	switch (qc->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
> -	case V4L2_CID_AUDIO_VOLUME:
> -		/* Only card_type == 11 implements volume */
> -		if (fmr2->card_type == 11)
> -			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
> -		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
> +/* 18-bit shift register bit definitions */
> +#define TC9154A_ATT_MAJ_0DB	(1 << 0)
> +#define TC9154A_ATT_MAJ_10DB	(1 << 1)
> +#define TC9154A_ATT_MAJ_20DB	(1 << 2)
> +#define TC9154A_ATT_MAJ_30DB	(1 << 3)
> +#define TC9154A_ATT_MAJ_40DB	(1 << 4)
> +#define TC9154A_ATT_MAJ_50DB	(1 << 5)
> +#define TC9154A_ATT_MAJ_60DB	(1 << 6)
> +
> +#define TC9154A_ATT_MIN_0DB	(1 << 7)
> +#define TC9154A_ATT_MIN_2DB	(1 << 8)
> +#define TC9154A_ATT_MIN_4DB	(1 << 9)
> +#define TC9154A_ATT_MIN_6DB	(1 << 10)
> +#define TC9154A_ATT_MIN_8DB	(1 << 11)
> +/* bit 12 is ignored */
> +#define TC9154A_CHANNEL_LEFT	(1 << 13)
> +#define TC9154A_CHANNEL_RIGHT	(1 << 14)
> +/* bits 15, 16, 17 must be 0 */
> +
> +#define	TC9154A_ATT_MAJ(x)	(1 << x)
> +#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
> +
> +static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
> +{
> +	if (!fmr2->tea.mute)
> +		pins |= STR_WREN;
> +
> +	outb(pins, fmr2->io);
> +}
> +
> +static void tc9154a_set_volume(struct fmr2 *fmr2, int volume)
> +{
> +	int i;
> +	u32 reg;
> +	u8 bit;
> +
> +	reg = TC9154A_ATT_MAJ(volume / 10) | TC9154A_ATT_MIN((volume % 10) / 2);
> +	reg |= TC9154A_CHANNEL_LEFT | TC9154A_CHANNEL_RIGHT;
> +	/* write 18-bit shift register, LSB first */
> +	for (i = 0; i < 18; i++) {
> +		bit = reg & (1 << i) ? PT_DATA : 0;
> +		tc9154a_set_pins(fmr2, bit);
> +		udelay(5);
> +		tc9154a_set_pins(fmr2, bit | PT_CK);
> +		udelay(5);
> +		tc9154a_set_pins(fmr2, bit);
> +	}
> +
> +	/* latch register data */
> +	udelay(5);
> +	tc9154a_set_pins(fmr2, PT_ST);
> +	udelay(5);
> +	tc9154a_set_pins(fmr2, 0);
> +}
> +
> +static struct v4l2_queryctrl fmr2_volume_qctrl[] = {
> +	{
> +		.id		= V4L2_CID_AUDIO_VOLUME,
> +		.name		= "Volume",
> +		.minimum	= -68,
> +		.maximum	= 0,
> +		.step		= 2,
> +		.default_value	= -68,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>  	}
> -	return -EINVAL;
> -}
> +};
>  
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> +static int fmr2_g_extra_ctrl(struct file *file, void *priv,
>  					struct v4l2_control *ctrl)
>  {
> -	struct fmr2 *fmr2 = video_drvdata(file);
> +	struct snd_tea575x *tea = video_drvdata(file);
> +	struct fmr2 *fmr2 = tea->private_data;
>  
>  	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = fmr2->mute;
> -		return 0;
>  	case V4L2_CID_AUDIO_VOLUME:
> -		ctrl->value = fmr2->curvol;
> +		ctrl->value = fmr2->volume;
>  		return 0;
>  	}
> +
>  	return -EINVAL;
>  }
>  
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> +static int fmr2_s_extra_ctrl(struct file *file, void *priv,
>  					struct v4l2_control *ctrl)
>  {
> -	struct fmr2 *fmr2 = video_drvdata(file);
> +	struct snd_tea575x *tea = video_drvdata(file);
> +	struct fmr2 *fmr2 = tea->private_data;
>  
>  	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		fmr2->mute = ctrl->value;
> -		break;
>  	case V4L2_CID_AUDIO_VOLUME:
> -		fmr2->curvol = ctrl->value;
> -		break;
> -	default:
> -		return -EINVAL;
> +		fmr2->volume = ctrl->value;
> +		tc9154a_set_volume(fmr2, fmr2->volume);
> +		return 0;
>  	}
>  
> -#ifdef DEBUG
> -	if (fmr2->curvol && !fmr2->mute)
> -		printk(KERN_DEBUG "unmute\n");
> -	else
> -		printk(KERN_DEBUG "mute\n");
> -#endif
> -
> -	mutex_lock(&fmr2->lock);
> -	if (fmr2->curvol && !fmr2->mute) {
> -		fmr2_setvolume(fmr2);
> -		/* Set frequency and unmute card */
> -		fmr2_setfreq(fmr2);
> -	} else
> -		fmr2_mute(fmr2->io);
> -	mutex_unlock(&fmr2->lock);
> -	return 0;
> -}
> -
> -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
> -{
> -	*i = 0;
> -	return 0;
> -}
> -
> -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
> -{
> -	return i ? -EINVAL : 0;
> -}
> -
> -static int vidioc_g_audio(struct file *file, void *priv,
> -					struct v4l2_audio *a)
> -{
> -	a->index = 0;
> -	strlcpy(a->name, "Radio", sizeof(a->name));
> -	a->capability = V4L2_AUDCAP_STEREO;
> -	return 0;
> -}
> -
> -static int vidioc_s_audio(struct file *file, void *priv,
> -					struct v4l2_audio *a)
> -{
> -	return a->index ? -EINVAL : 0;
> +	return -EINVAL;
>  }
>  
> -static const struct v4l2_file_operations fmr2_fops = {
> -	.owner          = THIS_MODULE,
> -	.unlocked_ioctl = video_ioctl2,
> -};
> -
> -static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
> -	.vidioc_querycap    = vidioc_querycap,
> -	.vidioc_g_tuner     = vidioc_g_tuner,
> -	.vidioc_s_tuner     = vidioc_s_tuner,
> -	.vidioc_g_audio     = vidioc_g_audio,
> -	.vidioc_s_audio     = vidioc_s_audio,
> -	.vidioc_g_input     = vidioc_g_input,
> -	.vidioc_s_input     = vidioc_s_input,
> -	.vidioc_g_frequency = vidioc_g_frequency,
> -	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
> -};
> -
>  static int __init fmr2_init(void)
>  {
>  	struct fmr2 *fmr2 = &fmr2_card;
> -	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
> -	int res;
>  
> -	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
> -	fmr2->io = io;
> -	fmr2->stereo = 1;
> -	mutex_init(&fmr2->lock);
> +	fmr2->io = FMR2_PORT;
>  
> -	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
> -		v4l2_err(v4l2_dev, "request_region failed!\n");
> +	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
> +		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
>  		return -EBUSY;
>  	}
>  
> -	res = v4l2_device_register(NULL, v4l2_dev);
> -	if (res < 0) {
> +	fmr2->tea.private_data = fmr2;
> +	fmr2->tea.ops = &fmr2_tea_ops;
> +	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
> +	strcpy(fmr2->tea.bus_info, "ISA");
> +
> +	if (snd_tea575x_init(&fmr2->tea)) {
> +		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
>  		release_region(fmr2->io, 2);
> -		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
> -		return res;
> +		return -ENODEV;
>  	}
>  
> -	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
> -	fmr2->vdev.v4l2_dev = v4l2_dev;
> -	fmr2->vdev.fops = &fmr2_fops;
> -	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
> -	fmr2->vdev.release = video_device_release_empty;
> -	video_set_drvdata(&fmr2->vdev, fmr2);
> -
> -	/* mute card - prevents noisy bootups */
> -	fmr2_mute(fmr2->io);
> -	fmr2_product_info(fmr2);
> -
> -	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
> -		v4l2_device_unregister(v4l2_dev);
> -		release_region(fmr2->io, 2);
> -		return -EINVAL;
> +	if (inb(fmr2->io) & FMR2_HASVOL) {
> +		fmr2->tea.extra_ctrls = fmr2_volume_qctrl;
> +		fmr2->tea.num_extra_ctrls = 1;
> +		fmr2->tea.g_extra_ctrl = fmr2_g_extra_ctrl;
> +		fmr2->tea.s_extra_ctrl = fmr2_s_extra_ctrl;
>  	}
>  
> -	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
> -	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
> +	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
>  	return 0;
>  }
>  
> @@ -443,22 +208,9 @@ static void __exit fmr2_exit(void)
>  {
>  	struct fmr2 *fmr2 = &fmr2_card;
>  
> -	video_unregister_device(&fmr2->vdev);
> -	v4l2_device_unregister(&fmr2->v4l2_dev);
> +	snd_tea575x_exit(&fmr2->tea);
>  	release_region(fmr2->io, 2);
>  }
>  
>  module_init(fmr2_init);
>  module_exit(fmr2_exit);
> -
> -#ifndef MODULE
> -
> -static int __init fmr2_setup_io(char *str)
> -{
> -	get_option(&str, &io);
> -	return 1;
> -}
> -
> -__setup("sf16fmr2=", fmr2_setup_io);
> -
> -#endif
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-14 10:06   ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-14 10:06 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Saturday, May 14, 2011 00:17:23 Ondrej Zary wrote:
> Hello,
> the SF16-FMR2 radio card is basically a TEA5757 tuner connected to ISA bus but
> the driver currently uses its own implementation. I converted the driver to
> use generic tea575x-tuner implementation (see patch below) and it works fine.

Good!

> But the tea575x-tuner module is located in /sound/i2c/other and the
> corresponding Kconfig in /sound/pci. Should it be moved
> to /drivers/media/radio or better leave it as is?

Good question. It's in a weird place, but moving it may be more trouble than
it is worth. It might be a good idea to just try it and see if that improves
things or not.

> The card can optionally be equipped with PT2254A volume control (equivalent of
> TC9154AP) so I need to extend tea575x-tuner to allow adding additional V4L2
> controls (and add one if the chip is present). Is this OK or should be
> something like v4l2_subdev used somehow?
> (My card does not have the PT2254A chip so that code is untested.)

No, this isn't the right approach. This should be done using the control
framework (see Documentation/video4linux/v4l2-controls.txt).

In order to use that the tea575x-tuner driver needs to be converted to that
framework, and so should any V4L driver that uses it. It's a fair amount of
work, although it is worth doing since it will ensure that these drivers
fully and correctly implement the V4L2 control API.

Thank you for looking into this, BTW! It's been on my TODO list for a long
time but I never had time to actually work on it.

Regards,

	Hans

> 
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-14 00:14:23.000000000 +0200
> @@ -54,6 +54,11 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	/* card driver can provide additional controls (e.g. volume) */
> +	struct v4l2_queryctrl *extra_ctrls;	/* array of controls */
> +	int num_extra_ctrls;		/* number of additional controls */
> +	int (*g_extra_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
> +	int (*s_extra_ctrl)(struct file *file, void *fh, struct v4l2_control *a);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-13 21:35:57.000000000 +0200
> @@ -269,6 +269,7 @@ static int vidioc_s_audio(struct file *f
>  static int vidioc_queryctrl(struct file *file, void *priv,
>  					struct v4l2_queryctrl *qc)
>  {
> +	struct snd_tea575x *tea = video_drvdata(file);
>  	int i;
>  
>  	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> @@ -278,6 +279,13 @@ static int vidioc_queryctrl(struct file
>  			return 0;
>  		}
>  	}
> +	for (i = 0; i < tea->num_extra_ctrls; i++) {
> +		if (qc->id && qc->id == tea->extra_ctrls[i].id) {
> +			memcpy(qc, &(tea->extra_ctrls[i]),
> +						sizeof(*qc));
> +			return 0;
> +		}
> +	}
>  	return -EINVAL;
>  }
>  
> @@ -291,6 +299,10 @@ static int vidioc_g_ctrl(struct file *fi
>  		ctrl->value = tea->mute;
>  		return 0;
>  	}
> +
> +	if (tea->g_extra_ctrl)
> +		return tea->g_extra_ctrl(file, priv, ctrl);
> +
>  	return -EINVAL;
>  }
>  
> @@ -307,6 +319,10 @@ static int vidioc_s_ctrl(struct file *fi
>  		}
>  		return 0;
>  	}
> +
> +	if (tea->s_extra_ctrl)
> +		return tea->s_extra_ctrl(file, priv, ctrl);
> +
>  	return -EINVAL;
>  }
>  
> --- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
> +++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-14 00:15:05.000000000 +0200
> @@ -1,441 +1,206 @@
> -/* SF16FMR2 radio driver for Linux radio support
> - * heavily based on fmi driver...
> - * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
> +/* SF16-FMR2 radio driver for Linux
> + * Copyright (c) 2011 Ondrej Zary
>   *
> - * Notes on the hardware
> - *
> - *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
> - *  No volume control - only mute/unmute - you have to use line volume
> - *
> - *  For read stereo/mono you must wait 0.1 sec after set frequency and
> - *  card unmuted so I set frequency on unmute
> - *  Signal handling seem to work only on autoscanning (not implemented)
> - *
> - *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
> + * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
> + * but almost nothing remained here after conversion to generic TEA575x
> + * interface
>   */
>  
> +#include <linux/delay.h>
>  #include <linux/module.h>	/* Modules 			*/
>  #include <linux/init.h>		/* Initdata			*/
>  #include <linux/ioport.h>	/* request_region		*/
> -#include <linux/delay.h>	/* udelay			*/
> -#include <linux/videodev2.h>	/* kernel radio structs		*/
> -#include <linux/mutex.h>
> -#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
>  #include <linux/io.h>		/* outb, outb_p			*/
> -#include <media/v4l2-device.h>
> -#include <media/v4l2-ioctl.h>
> +#include <sound/tea575x-tuner.h>
>  
> -MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
> -MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
> +MODULE_AUTHOR("Ondrej Zary");
> +MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
>  MODULE_LICENSE("GPL");
>  
> -static int io = 0x384;
> -static int radio_nr = -1;
> -
> -module_param(io, int, 0);
> -MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
> -module_param(radio_nr, int, 0);
> -
> -#define RADIO_VERSION KERNEL_VERSION(0,0,2)
> -
> -#define AUD_VOL_INDEX 1
> -
> -#undef DEBUG
> -//#define DEBUG 1
> -
> -#ifdef DEBUG
> -# define  debug_print(s) printk s
> -#else
> -# define  debug_print(s)
> -#endif
> -
> -/* this should be static vars for module size */
> -struct fmr2
> -{
> -	struct v4l2_device v4l2_dev;
> -	struct video_device vdev;
> -	struct mutex lock;
> +struct fmr2 {
>  	int io;
> -	int curvol; /* 0-15 */
> -	int mute;
> -	int stereo; /* card is producing stereo audio */
> -	unsigned long curfreq; /* freq in kHz */
> -	int card_type;
> +	int volume;
> +	struct snd_tea575x tea;
>  };
>  
> +/* the port is hardwired so no need to support multiple cards */
> +#define FMR2_PORT	0x384
>  static struct fmr2 fmr2_card;
>  
> -/* hw precision is 12.5 kHz
> - * It is only useful to give freq in interval of 200 (=0.0125Mhz),
> - * other bits will be truncated
> - */
> -#define RSF16_ENCODE(x)	((x) / 200 + 856)
> -#define RSF16_MINFREQ (87 * 16000)
> -#define RSF16_MAXFREQ (108 * 16000)
> -
> -static inline void wait(int n, int io)
> -{
> -	for (; n; --n)
> -		inb(io);
> -}
> -
> -static void outbits(int bits, unsigned int data, int nWait, int io)
> -{
> -	int bit;
> -
> -	for (; --bits >= 0;) {
> -		bit = (data >> bits) & 1;
> -		outb(bit, io);
> -		wait(nWait, io);
> -		outb(bit | 2, io);
> -		wait(nWait, io);
> -		outb(bit, io);
> -		wait(nWait, io);
> -	}
> -}
> -
> -static inline void fmr2_mute(int io)
> -{
> -	outb(0x00, io);
> -	wait(4, io);
> -}
> -
> -static inline void fmr2_unmute(int io)
> -{
> -	outb(0x04, io);
> -	wait(4, io);
> -}
> -
> -static inline int fmr2_stereo_mode(int io)
> -{
> -	int n = inb(io);
> -
> -	outb(6, io);
> -	inb(io);
> -	n = ((n >> 3) & 1) ^ 1;
> -	debug_print((KERN_DEBUG "stereo: %d\n", n));
> -	return n;
> -}
> -
> -static int fmr2_product_info(struct fmr2 *dev)
> -{
> -	int n = inb(dev->io);
> -
> -	n &= 0xC1;
> -	if (n == 0) {
> -		/* this should support volume set */
> -		dev->card_type = 12;
> -		return 0;
> -	}
> -	/* not volume (mine is 11) */
> -	dev->card_type = (n == 128) ? 11 : 0;
> -	return n;
> -}
> -
> -static inline int fmr2_getsigstr(struct fmr2 *dev)
> -{
> -	/* !!! works only if scanning freq */
> -	int res = 0xffff;
> -
> -	outb(5, dev->io);
> -	wait(4, dev->io);
> -	if (!(inb(dev->io) & 1))
> -		res = 0;
> -	debug_print((KERN_DEBUG "signal: %d\n", res));
> -	return res;
> -}
> +/* TEA575x tuner pins */
> +#define STR_DATA	(1 << 0)
> +#define STR_CLK		(1 << 1)
> +#define STR_WREN	(1 << 2)
> +#define STR_MOST	(1 << 3)
> +/* PT2254A/TC9154A volume control pins */
> +#define PT_ST		(1 << 4)
> +#define PT_CK		(1 << 5)
> +#define PT_DATA		(1 << 6)
> +/* volume control presence pin */
> +#define FMR2_HASVOL	(1 << 7)
>  
> -/* set frequency and unmute card */
> -static int fmr2_setfreq(struct fmr2 *dev)
> +static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
>  {
> -	unsigned long freq = dev->curfreq;
> +	struct fmr2 *fmr2 = tea->private_data;
> +	u8 bits = 0;
>  
> -	fmr2_mute(dev->io);
> +	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
> +	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
> +	/* WRITE_ENABLE is inverted, DATA must be high during read */
> +	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
>  
> -	/* 0x42 for mono output
> -	 * 0x102 forward scanning
> -	 * 0x182 scansione avanti
> -	 */
> -	outbits(9, 0x2, 3, dev->io);
> -	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
> -
> -	fmr2_unmute(dev->io);
> -
> -	/* wait 0.11 sec */
> -	msleep(110);
> -
> -	/* NOTE if mute this stop radio
> -	   you must set freq on unmute */
> -	dev->stereo = fmr2_stereo_mode(dev->io);
> -	return 0;
> -}
> -
> -/* !!! not tested, in my card this does't work !!! */
> -static int fmr2_setvolume(struct fmr2 *dev)
> -{
> -	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
> -			0x110, 0x204, 0x210, 0x402,
> -			0x404, 0x408, 0x410, 0x801,
> -			0x802, 0x804, 0x808, 0x810 };
> -	int i, a;
> -	int n = vol[dev->curvol & 0x0f];
> -
> -	if (dev->card_type != 11)
> -		return 1;
> -
> -	for (i = 12; --i >= 0; ) {
> -		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 0x24, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -	}
> -	for (i = 6; --i >= 0; ) {
> -		a = ((0x18 >> i) & 1) << 6;
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 0x24, dev->io);
> -		wait(4, dev->io);
> -		outb(a | 4, dev->io);
> -		wait(4, dev->io);
> -	}
> -	wait(4, dev->io);
> -	outb(0x14, dev->io);
> -	return 0;
> -}
> -
> -static int vidioc_querycap(struct file *file, void  *priv,
> -					struct v4l2_capability *v)
> -{
> -	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
> -	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
> -	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
> -	v->version = RADIO_VERSION;
> -	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
> -	return 0;
> +	outb(bits, fmr2->io);
>  }
>  
> -static int vidioc_g_tuner(struct file *file, void *priv,
> -					struct v4l2_tuner *v)
> +static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
>  {
> -	struct fmr2 *fmr2 = video_drvdata(file);
> -
> -	if (v->index > 0)
> -		return -EINVAL;
> +	struct fmr2 *fmr2 = tea->private_data;
> +	u8 bits = inb(fmr2->io);
>  
> -	strlcpy(v->name, "FM", sizeof(v->name));
> -	v->type = V4L2_TUNER_RADIO;
> -
> -	v->rangelow = RSF16_MINFREQ;
> -	v->rangehigh = RSF16_MAXFREQ;
> -	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
> -					V4L2_TUNER_SUB_MONO;
> -	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
> -	v->audmode = V4L2_TUNER_MODE_STEREO;
> -	mutex_lock(&fmr2->lock);
> -	v->signal = fmr2_getsigstr(fmr2);
> -	mutex_unlock(&fmr2->lock);
> -	return 0;
> +	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
> +		(bits & STR_MOST) ? TEA575X_MOST : 0;
>  }
>  
> -static int vidioc_s_tuner(struct file *file, void *priv,
> -					struct v4l2_tuner *v)
> +static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
>  {
> -	return v->index ? -EINVAL : 0;
>  }
>  
> -static int vidioc_s_frequency(struct file *file, void *priv,
> -					struct v4l2_frequency *f)
> -{
> -	struct fmr2 *fmr2 = video_drvdata(file);
> -
> -	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
> -		return -EINVAL;
> -	if (f->frequency < RSF16_MINFREQ ||
> -			f->frequency > RSF16_MAXFREQ)
> -		return -EINVAL;
> -	/* rounding in steps of 200 to match the freq
> -	   that will be used */
> -	fmr2->curfreq = (f->frequency / 200) * 200;
> -
> -	/* set card freq (if not muted) */
> -	if (fmr2->curvol && !fmr2->mute) {
> -		mutex_lock(&fmr2->lock);
> -		fmr2_setfreq(fmr2);
> -		mutex_unlock(&fmr2->lock);
> -	}
> -	return 0;
> -}
> -
> -static int vidioc_g_frequency(struct file *file, void *priv,
> -					struct v4l2_frequency *f)
> -{
> -	struct fmr2 *fmr2 = video_drvdata(file);
> -
> -	if (f->tuner != 0)
> -		return -EINVAL;
> -	f->type = V4L2_TUNER_RADIO;
> -	f->frequency = fmr2->curfreq;
> -	return 0;
> -}
> +static struct snd_tea575x_ops fmr2_tea_ops = {
> +	.set_pins = fmr2_tea575x_set_pins,
> +	.get_pins = fmr2_tea575x_get_pins,
> +	.set_direction = fmr2_tea575x_set_direction,
> +};
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> -{
> -	struct fmr2 *fmr2 = video_drvdata(file);
> +/* TC9154A/PT2254A volume control */
>  
> -	switch (qc->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
> -	case V4L2_CID_AUDIO_VOLUME:
> -		/* Only card_type == 11 implements volume */
> -		if (fmr2->card_type == 11)
> -			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
> -		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
> +/* 18-bit shift register bit definitions */
> +#define TC9154A_ATT_MAJ_0DB	(1 << 0)
> +#define TC9154A_ATT_MAJ_10DB	(1 << 1)
> +#define TC9154A_ATT_MAJ_20DB	(1 << 2)
> +#define TC9154A_ATT_MAJ_30DB	(1 << 3)
> +#define TC9154A_ATT_MAJ_40DB	(1 << 4)
> +#define TC9154A_ATT_MAJ_50DB	(1 << 5)
> +#define TC9154A_ATT_MAJ_60DB	(1 << 6)
> +
> +#define TC9154A_ATT_MIN_0DB	(1 << 7)
> +#define TC9154A_ATT_MIN_2DB	(1 << 8)
> +#define TC9154A_ATT_MIN_4DB	(1 << 9)
> +#define TC9154A_ATT_MIN_6DB	(1 << 10)
> +#define TC9154A_ATT_MIN_8DB	(1 << 11)
> +/* bit 12 is ignored */
> +#define TC9154A_CHANNEL_LEFT	(1 << 13)
> +#define TC9154A_CHANNEL_RIGHT	(1 << 14)
> +/* bits 15, 16, 17 must be 0 */
> +
> +#define	TC9154A_ATT_MAJ(x)	(1 << x)
> +#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
> +
> +static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
> +{
> +	if (!fmr2->tea.mute)
> +		pins |= STR_WREN;
> +
> +	outb(pins, fmr2->io);
> +}
> +
> +static void tc9154a_set_volume(struct fmr2 *fmr2, int volume)
> +{
> +	int i;
> +	u32 reg;
> +	u8 bit;
> +
> +	reg = TC9154A_ATT_MAJ(volume / 10) | TC9154A_ATT_MIN((volume % 10) / 2);
> +	reg |= TC9154A_CHANNEL_LEFT | TC9154A_CHANNEL_RIGHT;
> +	/* write 18-bit shift register, LSB first */
> +	for (i = 0; i < 18; i++) {
> +		bit = reg & (1 << i) ? PT_DATA : 0;
> +		tc9154a_set_pins(fmr2, bit);
> +		udelay(5);
> +		tc9154a_set_pins(fmr2, bit | PT_CK);
> +		udelay(5);
> +		tc9154a_set_pins(fmr2, bit);
> +	}
> +
> +	/* latch register data */
> +	udelay(5);
> +	tc9154a_set_pins(fmr2, PT_ST);
> +	udelay(5);
> +	tc9154a_set_pins(fmr2, 0);
> +}
> +
> +static struct v4l2_queryctrl fmr2_volume_qctrl[] = {
> +	{
> +		.id		= V4L2_CID_AUDIO_VOLUME,
> +		.name		= "Volume",
> +		.minimum	= -68,
> +		.maximum	= 0,
> +		.step		= 2,
> +		.default_value	= -68,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
>  	}
> -	return -EINVAL;
> -}
> +};
>  
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> +static int fmr2_g_extra_ctrl(struct file *file, void *priv,
>  					struct v4l2_control *ctrl)
>  {
> -	struct fmr2 *fmr2 = video_drvdata(file);
> +	struct snd_tea575x *tea = video_drvdata(file);
> +	struct fmr2 *fmr2 = tea->private_data;
>  
>  	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = fmr2->mute;
> -		return 0;
>  	case V4L2_CID_AUDIO_VOLUME:
> -		ctrl->value = fmr2->curvol;
> +		ctrl->value = fmr2->volume;
>  		return 0;
>  	}
> +
>  	return -EINVAL;
>  }
>  
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> +static int fmr2_s_extra_ctrl(struct file *file, void *priv,
>  					struct v4l2_control *ctrl)
>  {
> -	struct fmr2 *fmr2 = video_drvdata(file);
> +	struct snd_tea575x *tea = video_drvdata(file);
> +	struct fmr2 *fmr2 = tea->private_data;
>  
>  	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		fmr2->mute = ctrl->value;
> -		break;
>  	case V4L2_CID_AUDIO_VOLUME:
> -		fmr2->curvol = ctrl->value;
> -		break;
> -	default:
> -		return -EINVAL;
> +		fmr2->volume = ctrl->value;
> +		tc9154a_set_volume(fmr2, fmr2->volume);
> +		return 0;
>  	}
>  
> -#ifdef DEBUG
> -	if (fmr2->curvol && !fmr2->mute)
> -		printk(KERN_DEBUG "unmute\n");
> -	else
> -		printk(KERN_DEBUG "mute\n");
> -#endif
> -
> -	mutex_lock(&fmr2->lock);
> -	if (fmr2->curvol && !fmr2->mute) {
> -		fmr2_setvolume(fmr2);
> -		/* Set frequency and unmute card */
> -		fmr2_setfreq(fmr2);
> -	} else
> -		fmr2_mute(fmr2->io);
> -	mutex_unlock(&fmr2->lock);
> -	return 0;
> -}
> -
> -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
> -{
> -	*i = 0;
> -	return 0;
> -}
> -
> -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
> -{
> -	return i ? -EINVAL : 0;
> -}
> -
> -static int vidioc_g_audio(struct file *file, void *priv,
> -					struct v4l2_audio *a)
> -{
> -	a->index = 0;
> -	strlcpy(a->name, "Radio", sizeof(a->name));
> -	a->capability = V4L2_AUDCAP_STEREO;
> -	return 0;
> -}
> -
> -static int vidioc_s_audio(struct file *file, void *priv,
> -					struct v4l2_audio *a)
> -{
> -	return a->index ? -EINVAL : 0;
> +	return -EINVAL;
>  }
>  
> -static const struct v4l2_file_operations fmr2_fops = {
> -	.owner          = THIS_MODULE,
> -	.unlocked_ioctl = video_ioctl2,
> -};
> -
> -static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
> -	.vidioc_querycap    = vidioc_querycap,
> -	.vidioc_g_tuner     = vidioc_g_tuner,
> -	.vidioc_s_tuner     = vidioc_s_tuner,
> -	.vidioc_g_audio     = vidioc_g_audio,
> -	.vidioc_s_audio     = vidioc_s_audio,
> -	.vidioc_g_input     = vidioc_g_input,
> -	.vidioc_s_input     = vidioc_s_input,
> -	.vidioc_g_frequency = vidioc_g_frequency,
> -	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
> -};
> -
>  static int __init fmr2_init(void)
>  {
>  	struct fmr2 *fmr2 = &fmr2_card;
> -	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
> -	int res;
>  
> -	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
> -	fmr2->io = io;
> -	fmr2->stereo = 1;
> -	mutex_init(&fmr2->lock);
> +	fmr2->io = FMR2_PORT;
>  
> -	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
> -		v4l2_err(v4l2_dev, "request_region failed!\n");
> +	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
> +		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
>  		return -EBUSY;
>  	}
>  
> -	res = v4l2_device_register(NULL, v4l2_dev);
> -	if (res < 0) {
> +	fmr2->tea.private_data = fmr2;
> +	fmr2->tea.ops = &fmr2_tea_ops;
> +	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
> +	strcpy(fmr2->tea.bus_info, "ISA");
> +
> +	if (snd_tea575x_init(&fmr2->tea)) {
> +		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
>  		release_region(fmr2->io, 2);
> -		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
> -		return res;
> +		return -ENODEV;
>  	}
>  
> -	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
> -	fmr2->vdev.v4l2_dev = v4l2_dev;
> -	fmr2->vdev.fops = &fmr2_fops;
> -	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
> -	fmr2->vdev.release = video_device_release_empty;
> -	video_set_drvdata(&fmr2->vdev, fmr2);
> -
> -	/* mute card - prevents noisy bootups */
> -	fmr2_mute(fmr2->io);
> -	fmr2_product_info(fmr2);
> -
> -	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
> -		v4l2_device_unregister(v4l2_dev);
> -		release_region(fmr2->io, 2);
> -		return -EINVAL;
> +	if (inb(fmr2->io) & FMR2_HASVOL) {
> +		fmr2->tea.extra_ctrls = fmr2_volume_qctrl;
> +		fmr2->tea.num_extra_ctrls = 1;
> +		fmr2->tea.g_extra_ctrl = fmr2_g_extra_ctrl;
> +		fmr2->tea.s_extra_ctrl = fmr2_s_extra_ctrl;
>  	}
>  
> -	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
> -	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
> +	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
>  	return 0;
>  }
>  
> @@ -443,22 +208,9 @@ static void __exit fmr2_exit(void)
>  {
>  	struct fmr2 *fmr2 = &fmr2_card;
>  
> -	video_unregister_device(&fmr2->vdev);
> -	v4l2_device_unregister(&fmr2->v4l2_dev);
> +	snd_tea575x_exit(&fmr2->tea);
>  	release_region(fmr2->io, 2);
>  }
>  
>  module_init(fmr2_init);
>  module_exit(fmr2_exit);
> -
> -#ifndef MODULE
> -
> -static int __init fmr2_setup_io(char *str)
> -{
> -	get_option(&str, &io);
> -	return 1;
> -}
> -
> -__setup("sf16fmr2=", fmr2_setup_io);
> -
> -#endif
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC] tea575x: convert to control framework
  2011-05-14 10:06   ` Hans Verkuil
@ 2011-05-15 19:41     ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-15 19:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert tea575x-tuner to use the new V4L2 control framework.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-15 20:34:54.000000000 +0200
@@ -23,8 +23,7 @@
  */
 
 #include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 
 #define TEA575X_FMIF	10700
 
@@ -54,6 +53,7 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-15 20:34:23.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,19 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
+		if (tea->mute != ctrl->val) {
+			tea->mute = ctrl->val;
 			snd_tea575x_set_freq(tea);
 		}
 		return 0;
 	}
+
 	return -EINVAL;
 }
 
@@ -355,9 +316,6 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
@@ -367,6 +325,10 @@ static struct video_device tea575x_radio
 	.release	= video_device_release,
 };
 
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
+};
+
 /*
  * initialize all the tea575x chips
  */
@@ -406,6 +368,17 @@ int snd_tea575x_init(struct snd_tea575x
 		return retval;
 	}
 
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		kfree(tea575x_radio_inst);
+		return retval;
+	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
 	snd_tea575x_set_freq(tea);
 	tea->vd = tea575x_radio_inst;
 
@@ -415,6 +388,7 @@ int snd_tea575x_init(struct snd_tea575x
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
 	if (tea->vd) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		video_unregister_device(tea->vd);
 		tea->vd = NULL;
 	}


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC] tea575x: convert to control framework
@ 2011-05-15 19:41     ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-15 19:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert tea575x-tuner to use the new V4L2 control framework.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-15 20:34:54.000000000 +0200
@@ -23,8 +23,7 @@
  */
 
 #include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 
 #define TEA575X_FMIF	10700
 
@@ -54,6 +53,7 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-15 20:34:23.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,19 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
+		if (tea->mute != ctrl->val) {
+			tea->mute = ctrl->val;
 			snd_tea575x_set_freq(tea);
 		}
 		return 0;
 	}
+
 	return -EINVAL;
 }
 
@@ -355,9 +316,6 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
@@ -367,6 +325,10 @@ static struct video_device tea575x_radio
 	.release	= video_device_release,
 };
 
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
+};
+
 /*
  * initialize all the tea575x chips
  */
@@ -406,6 +368,17 @@ int snd_tea575x_init(struct snd_tea575x
 		return retval;
 	}
 
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		kfree(tea575x_radio_inst);
+		return retval;
+	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
 	snd_tea575x_set_freq(tea);
 	tea->vd = tea575x_radio_inst;
 
@@ -415,6 +388,7 @@ int snd_tea575x_init(struct snd_tea575x
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
 	if (tea->vd) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		video_unregister_device(tea->vd);
 		tea->vd = NULL;
 	}


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-14 10:06   ` Hans Verkuil
@ 2011-05-15 20:18     ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-15 20:18 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Thanks, it's much simpler with the new control framework.
Do the negative volume control values make sense? The TC9154A chip can
attenuate the volume from 0 to -68dB in 2dB steps.

--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-15 21:51:11.000000000 +0200
@@ -1,441 +1,178 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static int fmr2_product_info(struct fmr2 *dev)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	int n = inb(dev->io);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
+	outb(bits, fmr2->io);
 }
 
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	unsigned long freq = dev->curfreq;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
 }
 
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (v->index > 0)
-		return -EINVAL;
-
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
-}
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* TC9154A/PT2254A volume control */
 
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_volume(struct fmr2 *fmr2, int volume)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(volume / 10) | TC9154A_ATT_MIN((volume % 10) / 2);
+	reg |= TC9154A_CHANNEL_LEFT | TC9154A_CHANNEL_RIGHT;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
+		tc9154a_set_volume(fmr2, ctrl->val);
 		return 0;
 	}
-	return -EINVAL;
-}
 
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
-		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
+	return -EINVAL;
 }
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
 };
 
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
+
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
+		return -ENODEV;
 	}
 
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
-
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
-		release_region(fmr2->io, 2);
-		return -EINVAL;
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		v4l2_ctrl_new_std(&fmr2->tea.ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, -68, 0, 2, -34);
+		v4l2_ctrl_handler_setup(&fmr2->tea.ctrl_handler);
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +180,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-15 20:18     ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-15 20:18 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Thanks, it's much simpler with the new control framework.
Do the negative volume control values make sense? The TC9154A chip can
attenuate the volume from 0 to -68dB in 2dB steps.

--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-15 21:51:11.000000000 +0200
@@ -1,441 +1,178 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static int fmr2_product_info(struct fmr2 *dev)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	int n = inb(dev->io);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
+	outb(bits, fmr2->io);
 }
 
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	unsigned long freq = dev->curfreq;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
 }
 
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (v->index > 0)
-		return -EINVAL;
-
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
-}
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* TC9154A/PT2254A volume control */
 
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_volume(struct fmr2 *fmr2, int volume)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(volume / 10) | TC9154A_ATT_MIN((volume % 10) / 2);
+	reg |= TC9154A_CHANNEL_LEFT | TC9154A_CHANNEL_RIGHT;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
+		tc9154a_set_volume(fmr2, ctrl->val);
 		return 0;
 	}
-	return -EINVAL;
-}
 
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
-		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
+	return -EINVAL;
 }
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
 };
 
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
+
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
+		return -ENODEV;
 	}
 
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
-
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
-		release_region(fmr2->io, 2);
-		return -EINVAL;
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		v4l2_ctrl_new_std(&fmr2->tea.ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, -68, 0, 2, -34);
+		v4l2_ctrl_handler_setup(&fmr2->tea.ctrl_handler);
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +180,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-15 20:18     ` Ondrej Zary
@ 2011-05-15 21:26       ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-15 21:26 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> Thanks, it's much simpler with the new control framework.
> Do the negative volume control values make sense? The TC9154A chip can
> attenuate the volume from 0 to -68dB in 2dB steps.

It does make sense, but I think I would offset the values so they start at 0.
Mostly because there might be some old apps that set the volume to 0 when they
want to mute, which in this case is full volume.

I am not aware of any driver where a volume of 0 isn't the same as the lowest
volume possible, so in this particular case I would apply an offset.

I will have to do a closer review tomorrow or the day after. I think there are
a few subtleties that I need to look at. Ping me if you haven't heard from me
by Wednesday. I would really like to get these drivers up to spec now that I
have someone who can test them, and once that's done I hope that I never have
to look at them again :-) (Unlikely, but one can dream...)

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-15 21:26       ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-15 21:26 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> Thanks, it's much simpler with the new control framework.
> Do the negative volume control values make sense? The TC9154A chip can
> attenuate the volume from 0 to -68dB in 2dB steps.

It does make sense, but I think I would offset the values so they start at 0.
Mostly because there might be some old apps that set the volume to 0 when they
want to mute, which in this case is full volume.

I am not aware of any driver where a volume of 0 isn't the same as the lowest
volume possible, so in this particular case I would apply an offset.

I will have to do a closer review tomorrow or the day after. I think there are
a few subtleties that I need to look at. Ping me if you haven't heard from me
by Wednesday. I would really like to get these drivers up to spec now that I
have someone who can test them, and once that's done I hope that I never have
to look at them again :-) (Unlikely, but one can dream...)

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-15 21:26       ` Hans Verkuil
@ 2011-05-17 19:33         ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-17 19:33 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

Hi Ondrej!

On Sunday, May 15, 2011 23:26:33 Hans Verkuil wrote:
> On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> > Thanks, it's much simpler with the new control framework.
> > Do the negative volume control values make sense? The TC9154A chip can
> > attenuate the volume from 0 to -68dB in 2dB steps.
> 
> It does make sense, but I think I would offset the values so they start at 0.
> Mostly because there might be some old apps that set the volume to 0 when they
> want to mute, which in this case is full volume.
> 
> I am not aware of any driver where a volume of 0 isn't the same as the lowest
> volume possible, so in this particular case I would apply an offset.
> 
> I will have to do a closer review tomorrow or the day after. I think there are
> a few subtleties that I need to look at. Ping me if you haven't heard from me
> by Wednesday. I would really like to get these drivers up to spec now that I
> have someone who can test them, and once that's done I hope that I never have
> to look at them again :-) (Unlikely, but one can dream...)

OK, I looked at it a bit more and it needs to be changed a little bit. The
problem is that the VOLUME control is added after snd_tea575x_init, i.e.
after the video_register_device call. The video_register_device call should
be the last thing done before the init sequence returns. There may be applications
(dbus/hal) that open devices as soon as they appear, so doing more initialization
after the video node is registered is not a good idea (many older V4L drivers
make this mistake).

Perhaps creating a snd_tea575x_register function doing just the registration
may be a good idea. Or a callback before doing the video_register_device.

Another thing: the tea->mute field shouldn't be needed anymore. And the
'mute on init' bit in snd_tea575x_init can be removed as well since that
is automatically performed by v4l2_ctrl_handler_setup.

In addition, the .ioctl field in tea575x_fops can be replaced by .unlocked_ioctl.
The whole exclusive open stuff and the in_use field can be removed. The only
thing needed is a struct mutex in struct snd_tea575x, initialize it and set
tea575x_radio_inst->lock to the mutex. This will serialize all access safely.

To do this really right you should add struct v4l2_device to struct snd_tea575x
(the radio-sf16fmr2 driver has one, so you can use that as an example). With
that in place you can also add support for 'priority' handling. I'd say see
what you can do, and if it takes too much time then mail me the tea575x code
and the radio-sf16frm2 code and I'll finish it.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-17 19:33         ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-17 19:33 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

Hi Ondrej!

On Sunday, May 15, 2011 23:26:33 Hans Verkuil wrote:
> On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> > Thanks, it's much simpler with the new control framework.
> > Do the negative volume control values make sense? The TC9154A chip can
> > attenuate the volume from 0 to -68dB in 2dB steps.
> 
> It does make sense, but I think I would offset the values so they start at 0.
> Mostly because there might be some old apps that set the volume to 0 when they
> want to mute, which in this case is full volume.
> 
> I am not aware of any driver where a volume of 0 isn't the same as the lowest
> volume possible, so in this particular case I would apply an offset.
> 
> I will have to do a closer review tomorrow or the day after. I think there are
> a few subtleties that I need to look at. Ping me if you haven't heard from me
> by Wednesday. I would really like to get these drivers up to spec now that I
> have someone who can test them, and once that's done I hope that I never have
> to look at them again :-) (Unlikely, but one can dream...)

OK, I looked at it a bit more and it needs to be changed a little bit. The
problem is that the VOLUME control is added after snd_tea575x_init, i.e.
after the video_register_device call. The video_register_device call should
be the last thing done before the init sequence returns. There may be applications
(dbus/hal) that open devices as soon as they appear, so doing more initialization
after the video node is registered is not a good idea (many older V4L drivers
make this mistake).

Perhaps creating a snd_tea575x_register function doing just the registration
may be a good idea. Or a callback before doing the video_register_device.

Another thing: the tea->mute field shouldn't be needed anymore. And the
'mute on init' bit in snd_tea575x_init can be removed as well since that
is automatically performed by v4l2_ctrl_handler_setup.

In addition, the .ioctl field in tea575x_fops can be replaced by .unlocked_ioctl.
The whole exclusive open stuff and the in_use field can be removed. The only
thing needed is a struct mutex in struct snd_tea575x, initialize it and set
tea575x_radio_inst->lock to the mutex. This will serialize all access safely.

To do this really right you should add struct v4l2_device to struct snd_tea575x
(the radio-sf16fmr2 driver has one, so you can use that as an example). With
that in place you can also add support for 'priority' handling. I'd say see
what you can do, and if it takes too much time then mail me the tea575x code
and the radio-sf16frm2 code and I'll finish it.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-17 19:33         ` Hans Verkuil
  (?)
@ 2011-05-17 20:05         ` Ondrej Zary
  2011-05-18  6:26             ` Hans Verkuil
  -1 siblings, 1 reply; 38+ messages in thread
From: Ondrej Zary @ 2011-05-17 20:05 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

On Tuesday 17 May 2011 21:33:14 Hans Verkuil wrote:
> Hi Ondrej!
>
> On Sunday, May 15, 2011 23:26:33 Hans Verkuil wrote:
> > On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> > > Thanks, it's much simpler with the new control framework.
> > > Do the negative volume control values make sense? The TC9154A chip can
> > > attenuate the volume from 0 to -68dB in 2dB steps.
> >
> > It does make sense, but I think I would offset the values so they start
> > at 0. Mostly because there might be some old apps that set the volume to
> > 0 when they want to mute, which in this case is full volume.
> >
> > I am not aware of any driver where a volume of 0 isn't the same as the
> > lowest volume possible, so in this particular case I would apply an
> > offset.
> >
> > I will have to do a closer review tomorrow or the day after. I think
> > there are a few subtleties that I need to look at. Ping me if you haven't
> > heard from me by Wednesday. I would really like to get these drivers up
> > to spec now that I have someone who can test them, and once that's done I
> > hope that I never have to look at them again :-) (Unlikely, but one can
> > dream...)
>
> OK, I looked at it a bit more and it needs to be changed a little bit. The
> problem is that the VOLUME control is added after snd_tea575x_init, i.e.
> after the video_register_device call. The video_register_device call should
> be the last thing done before the init sequence returns. There may be
> applications (dbus/hal) that open devices as soon as they appear, so doing
> more initialization after the video node is registered is not a good idea
> (many older V4L drivers make this mistake).
>
> Perhaps creating a snd_tea575x_register function doing just the
> registration may be a good idea. Or a callback before doing the
> video_register_device.

OK, I'll reorder the lines in snd_tea575x_init function and add a callback 
that radio-sf16fmr2 can use.

Also upgraded my card with TC9154AP chip so I can actually test the volume 
control code (and it was broken in my previous patch...). The left and right 
channels can be separately controlled - is there a way to provide separate 
left and right volume controls? Or do I need to fake up a balance control?

> Another thing: the tea->mute field shouldn't be needed anymore. And the
> 'mute on init' bit in snd_tea575x_init can be removed as well since that
> is automatically performed by v4l2_ctrl_handler_setup.

Thought about this too but the snd_tea575x_write() and snd_tea575x_read() 
functions need to know the mute status. And these functions are also used to 
detect the tuner presence before initializing the controls. I don't see any 
elegant solution.

> In addition, the .ioctl field in tea575x_fops can be replaced by
> .unlocked_ioctl. The whole exclusive open stuff and the in_use field can be
> removed. The only thing needed is a struct mutex in struct snd_tea575x,
> initialize it and set tea575x_radio_inst->lock to the mutex. This will
> serialize all access safely.

I'll do this as a separate patch later.

> To do this really right you should add struct v4l2_device to struct
> snd_tea575x (the radio-sf16fmr2 driver has one, so you can use that as an
> example). With that in place you can also add support for 'priority'
> handling. I'd say see what you can do, and if it takes too much time then
> mail me the tea575x code and the radio-sf16frm2 code and I'll finish it.
>
> Regards,
>
> 	Hans



-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC v2] tea575x: convert to control framework
  2011-05-17 19:33         ` Hans Verkuil
@ 2011-05-17 21:45           ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-17 21:45 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert tea575x-tuner to use the new V4L2 control framework.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
@@ -23,8 +23,7 @@
  */
 
 #include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 
 #define TEA575X_FMIF	10700
 
@@ -54,6 +53,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	void (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-17 23:32:07.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,19 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
+		if (tea->mute != ctrl->val) {
+			tea->mute = ctrl->val;
 			snd_tea575x_set_freq(tea);
 		}
 		return 0;
 	}
+
 	return -EINVAL;
 }
 
@@ -355,9 +316,6 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
@@ -367,6 +325,10 @@ static struct video_device tea575x_radio
 	.release	= video_device_release,
 };
 
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
+};
+
 /*
  * initialize all the tea575x chips
  */
@@ -384,29 +346,39 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
 	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
+	if (!tea575x_radio_inst) {
 		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
 		return -ENOMEM;
 	}
 
 	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
+	video_set_drvdata(tea575x_radio_inst, tea);
 
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		kfree(tea575x_radio_inst);
+		return retval;
+	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 
-	video_set_drvdata(tea575x_radio_inst, tea);
+	if (tea->ext_init)
+		tea->ext_init(tea);
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
 	if (retval) {
 		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		kfree(tea575x_radio_inst);
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
 	tea->vd = tea575x_radio_inst;
 
 	return 0;
@@ -415,6 +387,7 @@ int snd_tea575x_init(struct snd_tea575x
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
 	if (tea->vd) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		video_unregister_device(tea->vd);
 		tea->vd = NULL;
 	}


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC v2] tea575x: convert to control framework
@ 2011-05-17 21:45           ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-17 21:45 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert tea575x-tuner to use the new V4L2 control framework.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
@@ -23,8 +23,7 @@
  */
 
 #include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 
 #define TEA575X_FMIF	10700
 
@@ -54,6 +53,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	void (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-17 23:32:07.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,19 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
+		if (tea->mute != ctrl->val) {
+			tea->mute = ctrl->val;
 			snd_tea575x_set_freq(tea);
 		}
 		return 0;
 	}
+
 	return -EINVAL;
 }
 
@@ -355,9 +316,6 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
@@ -367,6 +325,10 @@ static struct video_device tea575x_radio
 	.release	= video_device_release,
 };
 
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
+};
+
 /*
  * initialize all the tea575x chips
  */
@@ -384,29 +346,39 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
 	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
+	if (!tea575x_radio_inst) {
 		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
 		return -ENOMEM;
 	}
 
 	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
+	video_set_drvdata(tea575x_radio_inst, tea);
 
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		kfree(tea575x_radio_inst);
+		return retval;
+	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 
-	video_set_drvdata(tea575x_radio_inst, tea);
+	if (tea->ext_init)
+		tea->ext_init(tea);
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
 	if (retval) {
 		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		kfree(tea575x_radio_inst);
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
 	tea->vd = tea575x_radio_inst;
 
 	return 0;
@@ -415,6 +387,7 @@ int snd_tea575x_init(struct snd_tea575x
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
 	if (tea->vd) {
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		video_unregister_device(tea->vd);
 		tea->vd = NULL;
 	}


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH RFC v3] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-17 19:33         ` Hans Verkuil
                           ` (2 preceding siblings ...)
  (?)
@ 2011-05-17 21:45         ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-17 21:45 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
connected to ISA bus.
The card can optionally be equipped with PT2254A volume control (equivalent
of TC9154AP) - the volume setting is completely reworked and tested.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-17 23:24:37.000000000 +0200
@@ -1,441 +1,184 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static inline void wait(int n, int io)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	for (; n; --n)
-		inb(io);
-}
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
-	unsigned long freq = dev->curfreq;
-
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
+	outb(bits, fmr2->io);
 }
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	if (v->index > 0)
-		return -EINVAL;
-
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	return v->index ? -EINVAL : 0;
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
-}
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* TC9154A/PT2254A volume control */
 
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_volume(struct fmr2 *fmr2, int volume)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(volume / 10) | TC9154A_ATT_MIN((volume % 10) / 2);
+	reg |= TC9154A_CHANNEL_LEFT | TC9154A_CHANNEL_RIGHT;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
+		tc9154a_set_volume(fmr2, abs(ctrl->val - 68));
 		return 0;
 	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
-		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
-		break;
-	default:
-		return -EINVAL;
-	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
+	return -EINVAL;
 }
 
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
+};
 
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
+static void fmr2_tea_ext_init(struct snd_tea575x *tea)
 {
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
+	struct fmr2 *fmr2 = tea->private_data;
 
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 50);
+		v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+	}
 }
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
-
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
-		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
-	}
-
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	fmr2->tea.ext_init = fmr2_tea_ext_init;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
 
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		return -EINVAL;
+		return -ENODEV;
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +186,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] tea575x: convert to control framework
  2011-05-17 21:45           ` Ondrej Zary
@ 2011-05-18  6:21             ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-18  6:21 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Tuesday, May 17, 2011 23:45:07 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
> @@ -23,8 +23,7 @@
>   */
>  
>  #include <linux/videodev2.h>
> -#include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -54,6 +53,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	void (*ext_init)(struct snd_tea575x *tea);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-17 23:32:07.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,19 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> -
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> -		return 0;
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> +		if (tea->mute != ctrl->val) {

This test should be removed. s_ctrl is only called when the value actually
changes, and also during handler_setup. With this test the setup call will
actually fail to mute the device.

> +			tea->mute = ctrl->val;
>  			snd_tea575x_set_freq(tea);
>  		}
>  		return 0;
>  	}
> +
>  	return -EINVAL;
>  }
>  
> @@ -355,9 +316,6 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
> @@ -367,6 +325,10 @@ static struct video_device tea575x_radio
>  	.release	= video_device_release,
>  };
>  
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
> +};
> +
>  /*
>   * initialize all the tea575x chips
>   */
> @@ -384,29 +346,39 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
>  	tea575x_radio_inst = video_device_alloc();
> -	if (tea575x_radio_inst == NULL) {
> +	if (!tea575x_radio_inst) {
>  		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
>  		return -ENOMEM;
>  	}
>  
>  	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> +	video_set_drvdata(tea575x_radio_inst, tea);
>  
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
> +	if (retval) {
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		kfree(tea575x_radio_inst);
> +		return retval;
> +	}
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
>  
> -	video_set_drvdata(tea575x_radio_inst, tea);
> +	if (tea->ext_init)
> +		tea->ext_init(tea);
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
>  	if (retval) {
>  		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		kfree(tea575x_radio_inst);
>  		return retval;
>  	}

This device registration should be moved down to just before the return 0.

>  
> -	snd_tea575x_set_freq(tea);
>  	tea->vd = tea575x_radio_inst;
>  
>  	return 0;
> @@ -415,6 +387,7 @@ int snd_tea575x_init(struct snd_tea575x
>  void snd_tea575x_exit(struct snd_tea575x *tea)
>  {
>  	if (tea->vd) {
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		video_unregister_device(tea->vd);

Swap these lines: first unregister, then free the handler.

>  		tea->vd = NULL;
>  	}
> 
> 
> 

Thanks!

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] tea575x: convert to control framework
@ 2011-05-18  6:21             ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-18  6:21 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Tuesday, May 17, 2011 23:45:07 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
> @@ -23,8 +23,7 @@
>   */
>  
>  #include <linux/videodev2.h>
> -#include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -54,6 +53,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	void (*ext_init)(struct snd_tea575x *tea);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-17 23:32:07.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,19 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> -
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> -		return 0;
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> +		if (tea->mute != ctrl->val) {

This test should be removed. s_ctrl is only called when the value actually
changes, and also during handler_setup. With this test the setup call will
actually fail to mute the device.

> +			tea->mute = ctrl->val;
>  			snd_tea575x_set_freq(tea);
>  		}
>  		return 0;
>  	}
> +
>  	return -EINVAL;
>  }
>  
> @@ -355,9 +316,6 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
> @@ -367,6 +325,10 @@ static struct video_device tea575x_radio
>  	.release	= video_device_release,
>  };
>  
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
> +};
> +
>  /*
>   * initialize all the tea575x chips
>   */
> @@ -384,29 +346,39 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
>  	tea575x_radio_inst = video_device_alloc();
> -	if (tea575x_radio_inst == NULL) {
> +	if (!tea575x_radio_inst) {
>  		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
>  		return -ENOMEM;
>  	}
>  
>  	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> +	video_set_drvdata(tea575x_radio_inst, tea);
>  
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
> +	if (retval) {
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		kfree(tea575x_radio_inst);
> +		return retval;
> +	}
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
>  
> -	video_set_drvdata(tea575x_radio_inst, tea);
> +	if (tea->ext_init)
> +		tea->ext_init(tea);
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
>  	if (retval) {
>  		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		kfree(tea575x_radio_inst);
>  		return retval;
>  	}

This device registration should be moved down to just before the return 0.

>  
> -	snd_tea575x_set_freq(tea);
>  	tea->vd = tea575x_radio_inst;
>  
>  	return 0;
> @@ -415,6 +387,7 @@ int snd_tea575x_init(struct snd_tea575x
>  void snd_tea575x_exit(struct snd_tea575x *tea)
>  {
>  	if (tea->vd) {
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		video_unregister_device(tea->vd);

Swap these lines: first unregister, then free the handler.

>  		tea->vd = NULL;
>  	}
> 
> 
> 

Thanks!

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-17 20:05         ` Ondrej Zary
@ 2011-05-18  6:26             ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-18  6:26 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Tuesday, May 17, 2011 22:05:26 Ondrej Zary wrote:
> On Tuesday 17 May 2011 21:33:14 Hans Verkuil wrote:
> > Hi Ondrej!
> >
> > On Sunday, May 15, 2011 23:26:33 Hans Verkuil wrote:
> > > On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> > > > Thanks, it's much simpler with the new control framework.
> > > > Do the negative volume control values make sense? The TC9154A chip can
> > > > attenuate the volume from 0 to -68dB in 2dB steps.
> > >
> > > It does make sense, but I think I would offset the values so they start
> > > at 0. Mostly because there might be some old apps that set the volume to
> > > 0 when they want to mute, which in this case is full volume.
> > >
> > > I am not aware of any driver where a volume of 0 isn't the same as the
> > > lowest volume possible, so in this particular case I would apply an
> > > offset.
> > >
> > > I will have to do a closer review tomorrow or the day after. I think
> > > there are a few subtleties that I need to look at. Ping me if you haven't
> > > heard from me by Wednesday. I would really like to get these drivers up
> > > to spec now that I have someone who can test them, and once that's done I
> > > hope that I never have to look at them again :-) (Unlikely, but one can
> > > dream...)
> >
> > OK, I looked at it a bit more and it needs to be changed a little bit. The
> > problem is that the VOLUME control is added after snd_tea575x_init, i.e.
> > after the video_register_device call. The video_register_device call should
> > be the last thing done before the init sequence returns. There may be
> > applications (dbus/hal) that open devices as soon as they appear, so doing
> > more initialization after the video node is registered is not a good idea
> > (many older V4L drivers make this mistake).
> >
> > Perhaps creating a snd_tea575x_register function doing just the
> > registration may be a good idea. Or a callback before doing the
> > video_register_device.
> 
> OK, I'll reorder the lines in snd_tea575x_init function and add a callback 
> that radio-sf16fmr2 can use.
> 
> Also upgraded my card with TC9154AP chip so I can actually test the volume 
> control code (and it was broken in my previous patch...). The left and right 
> channels can be separately controlled - is there a way to provide separate 
> left and right volume controls? Or do I need to fake up a balance control?

A fake balance control would be the way to go. There are other drivers that
do it like that.

> > Another thing: the tea->mute field shouldn't be needed anymore. And the
> > 'mute on init' bit in snd_tea575x_init can be removed as well since that
> > is automatically performed by v4l2_ctrl_handler_setup.
> 
> Thought about this too but the snd_tea575x_write() and snd_tea575x_read() 
> functions need to know the mute status. And these functions are also used to 
> detect the tuner presence before initializing the controls. I don't see any 
> elegant solution.

What typically is done is that the mute v4l2_ctrl pointer is stored and
dereferenced to get the value. But in a simple case like this backing up
the value works just as well.

Regards,

	Hans

> > In addition, the .ioctl field in tea575x_fops can be replaced by
> > .unlocked_ioctl. The whole exclusive open stuff and the in_use field can be
> > removed. The only thing needed is a struct mutex in struct snd_tea575x,
> > initialize it and set tea575x_radio_inst->lock to the mutex. This will
> > serialize all access safely.
> 
> I'll do this as a separate patch later.
> 
> > To do this really right you should add struct v4l2_device to struct
> > snd_tea575x (the radio-sf16fmr2 driver has one, so you can use that as an
> > example). With that in place you can also add support for 'priority'
> > handling. I'd say see what you can do, and if it takes too much time then
> > mail me the tea575x code and the radio-sf16frm2 code and I'll finish it.
> >
> > Regards,
> >
> > 	Hans
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-18  6:26             ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-18  6:26 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Tuesday, May 17, 2011 22:05:26 Ondrej Zary wrote:
> On Tuesday 17 May 2011 21:33:14 Hans Verkuil wrote:
> > Hi Ondrej!
> >
> > On Sunday, May 15, 2011 23:26:33 Hans Verkuil wrote:
> > > On Sunday, May 15, 2011 22:18:21 Ondrej Zary wrote:
> > > > Thanks, it's much simpler with the new control framework.
> > > > Do the negative volume control values make sense? The TC9154A chip can
> > > > attenuate the volume from 0 to -68dB in 2dB steps.
> > >
> > > It does make sense, but I think I would offset the values so they start
> > > at 0. Mostly because there might be some old apps that set the volume to
> > > 0 when they want to mute, which in this case is full volume.
> > >
> > > I am not aware of any driver where a volume of 0 isn't the same as the
> > > lowest volume possible, so in this particular case I would apply an
> > > offset.
> > >
> > > I will have to do a closer review tomorrow or the day after. I think
> > > there are a few subtleties that I need to look at. Ping me if you haven't
> > > heard from me by Wednesday. I would really like to get these drivers up
> > > to spec now that I have someone who can test them, and once that's done I
> > > hope that I never have to look at them again :-) (Unlikely, but one can
> > > dream...)
> >
> > OK, I looked at it a bit more and it needs to be changed a little bit. The
> > problem is that the VOLUME control is added after snd_tea575x_init, i.e.
> > after the video_register_device call. The video_register_device call should
> > be the last thing done before the init sequence returns. There may be
> > applications (dbus/hal) that open devices as soon as they appear, so doing
> > more initialization after the video node is registered is not a good idea
> > (many older V4L drivers make this mistake).
> >
> > Perhaps creating a snd_tea575x_register function doing just the
> > registration may be a good idea. Or a callback before doing the
> > video_register_device.
> 
> OK, I'll reorder the lines in snd_tea575x_init function and add a callback 
> that radio-sf16fmr2 can use.
> 
> Also upgraded my card with TC9154AP chip so I can actually test the volume 
> control code (and it was broken in my previous patch...). The left and right 
> channels can be separately controlled - is there a way to provide separate 
> left and right volume controls? Or do I need to fake up a balance control?

A fake balance control would be the way to go. There are other drivers that
do it like that.

> > Another thing: the tea->mute field shouldn't be needed anymore. And the
> > 'mute on init' bit in snd_tea575x_init can be removed as well since that
> > is automatically performed by v4l2_ctrl_handler_setup.
> 
> Thought about this too but the snd_tea575x_write() and snd_tea575x_read() 
> functions need to know the mute status. And these functions are also used to 
> detect the tuner presence before initializing the controls. I don't see any 
> elegant solution.

What typically is done is that the mute v4l2_ctrl pointer is stored and
dereferenced to get the value. But in a simple case like this backing up
the value works just as well.

Regards,

	Hans

> > In addition, the .ioctl field in tea575x_fops can be replaced by
> > .unlocked_ioctl. The whole exclusive open stuff and the in_use field can be
> > removed. The only thing needed is a struct mutex in struct snd_tea575x,
> > initialize it and set tea575x_radio_inst->lock to the mutex. This will
> > serialize all access safely.
> 
> I'll do this as a separate patch later.
> 
> > To do this really right you should add struct v4l2_device to struct
> > snd_tea575x (the radio-sf16fmr2 driver has one, so you can use that as an
> > example). With that in place you can also add support for 'priority'
> > handling. I'd say see what you can do, and if it takes too much time then
> > mail me the tea575x code and the radio-sf16frm2 code and I'll finish it.
> >
> > Regards,
> >
> > 	Hans
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v3] tea575x: convert to control framework
  2011-05-18  6:21             ` Hans Verkuil
@ 2011-05-18 19:36               ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-18 19:36 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert tea575x-tuner to use the new V4L2 control framework. Also add
ext_init() callback that can be used by a card driver for additional
initialization right before registering the video device (for SF16-FMR2).

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
@@ -23,8 +23,7 @@
  */
 
 #include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 
 #define TEA575X_FMIF	10700
 
@@ -54,6 +53,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	void (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 21:33:05.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
 
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
 	return -EINVAL;
 }
 
@@ -355,9 +314,6 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
@@ -367,6 +323,10 @@ static struct video_device tea575x_radio
 	.release	= video_device_release,
 };
 
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
+};
+
 /*
  * initialize all the tea575x chips
  */
@@ -384,31 +344,43 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
 	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
+	if (!tea575x_radio_inst) {
 		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
 		return -ENOMEM;
 	}
 
 	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
-
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
-
 	video_set_drvdata(tea575x_radio_inst, tea);
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
 	if (retval) {
-		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		kfree(tea575x_radio_inst);
 		return retval;
 	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
+	if (tea->ext_init)
+		tea->ext_init(tea);
 
-	snd_tea575x_set_freq(tea);
 	tea->vd = tea575x_radio_inst;
 
+	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		kfree(tea575x_radio_inst);
+		tea->vd = NULL;
+		return retval;
+	}
+
 	return 0;
 }
 
@@ -416,6 +388,7 @@ void snd_tea575x_exit(struct snd_tea575x
 {
 	if (tea->vd) {
 		video_unregister_device(tea->vd);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		tea->vd = NULL;
 	}
 }


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v3] tea575x: convert to control framework
@ 2011-05-18 19:36               ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-18 19:36 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert tea575x-tuner to use the new V4L2 control framework. Also add
ext_init() callback that can be used by a card driver for additional
initialization right before registering the video device (for SF16-FMR2).

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
@@ -23,8 +23,7 @@
  */
 
 #include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 
 #define TEA575X_FMIF	10700
 
@@ -54,6 +53,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	void (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 21:33:05.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
 
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
 	return -EINVAL;
 }
 
@@ -355,9 +314,6 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
@@ -367,6 +323,10 @@ static struct video_device tea575x_radio
 	.release	= video_device_release,
 };
 
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
+};
+
 /*
  * initialize all the tea575x chips
  */
@@ -384,31 +344,43 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
 	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
+	if (!tea575x_radio_inst) {
 		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
 		return -ENOMEM;
 	}
 
 	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
-
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
-
 	video_set_drvdata(tea575x_radio_inst, tea);
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
 	if (retval) {
-		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		kfree(tea575x_radio_inst);
 		return retval;
 	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
+	if (tea->ext_init)
+		tea->ext_init(tea);
 
-	snd_tea575x_set_freq(tea);
 	tea->vd = tea575x_radio_inst;
 
+	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		kfree(tea575x_radio_inst);
+		tea->vd = NULL;
+		return retval;
+	}
+
 	return 0;
 }
 
@@ -416,6 +388,7 @@ void snd_tea575x_exit(struct snd_tea575x
 {
 	if (tea->vd) {
 		video_unregister_device(tea->vd);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		tea->vd = NULL;
 	}
 }


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v3] tea575x: convert to control framework
  2011-05-18 19:36               ` Ondrej Zary
@ 2011-05-18 20:25                 ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-18 20:25 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Wednesday, May 18, 2011 21:36:27 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework. Also add
> ext_init() callback that can be used by a card driver for additional
> initialization right before registering the video device (for SF16-FMR2).

Just one comment left, see below...

> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
> @@ -23,8 +23,7 @@
>   */
>  
>  #include <linux/videodev2.h>
> -#include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -54,6 +53,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	void (*ext_init)(struct snd_tea575x *tea);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 21:33:05.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> +		tea->mute = ctrl->val;
> +		snd_tea575x_set_freq(tea);
>  		return 0;
>  	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
>  
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> -			snd_tea575x_set_freq(tea);
> -		}
> -		return 0;
> -	}
>  	return -EINVAL;
>  }
>  
> @@ -355,9 +314,6 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
> @@ -367,6 +323,10 @@ static struct video_device tea575x_radio
>  	.release	= video_device_release,
>  };
>  
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
> +};
> +
>  /*
>   * initialize all the tea575x chips
>   */
> @@ -384,31 +344,43 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
>  	tea575x_radio_inst = video_device_alloc();

video_device_alloc allocates the struct video_device. It would be easier
to just embed that struct in the snd_tea575x struct. It prevents having to
allocate memory twice and the test below can be removed entirely.

The .release field above can be set to video_device_release_empty, which
is the appropriate release callback for embedded video_device structs.

> -	if (tea575x_radio_inst == NULL) {
> +	if (!tea575x_radio_inst) {
>  		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
>  		return -ENOMEM;
>  	}
>  
>  	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> -
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> -
>  	video_set_drvdata(tea575x_radio_inst, tea);
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
>  	if (retval) {
> -		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> +		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		kfree(tea575x_radio_inst);
>  		return retval;
>  	}
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
> +
> +	if (tea->ext_init)
> +		tea->ext_init(tea);
>  
> -	snd_tea575x_set_freq(tea);
>  	tea->vd = tea575x_radio_inst;
>  
> +	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
> +	if (retval) {
> +		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);

If you decide *not* to embed the video_device struct, then you have to call
video_device_release() here to free the video_device struct, otherwise that
memory will leak.

> +		kfree(tea575x_radio_inst);
> +		tea->vd = NULL;
> +		return retval;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -416,6 +388,7 @@ void snd_tea575x_exit(struct snd_tea575x
>  {
>  	if (tea->vd) {
>  		video_unregister_device(tea->vd);
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		tea->vd = NULL;
>  	}
>  }
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v3] tea575x: convert to control framework
@ 2011-05-18 20:25                 ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-18 20:25 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Wednesday, May 18, 2011 21:36:27 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework. Also add
> ext_init() callback that can be used by a card driver for additional
> initialization right before registering the video device (for SF16-FMR2).

Just one comment left, see below...

> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-17 22:35:19.000000000 +0200
> @@ -23,8 +23,7 @@
>   */
>  
>  #include <linux/videodev2.h>
> -#include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -54,6 +53,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	void (*ext_init)(struct snd_tea575x *tea);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 21:33:05.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> +		tea->mute = ctrl->val;
> +		snd_tea575x_set_freq(tea);
>  		return 0;
>  	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
>  
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> -			snd_tea575x_set_freq(tea);
> -		}
> -		return 0;
> -	}
>  	return -EINVAL;
>  }
>  
> @@ -355,9 +314,6 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
> @@ -367,6 +323,10 @@ static struct video_device tea575x_radio
>  	.release	= video_device_release,
>  };
>  
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
> +};
> +
>  /*
>   * initialize all the tea575x chips
>   */
> @@ -384,31 +344,43 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
>  	tea575x_radio_inst = video_device_alloc();

video_device_alloc allocates the struct video_device. It would be easier
to just embed that struct in the snd_tea575x struct. It prevents having to
allocate memory twice and the test below can be removed entirely.

The .release field above can be set to video_device_release_empty, which
is the appropriate release callback for embedded video_device structs.

> -	if (tea575x_radio_inst == NULL) {
> +	if (!tea575x_radio_inst) {
>  		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
>  		return -ENOMEM;
>  	}
>  
>  	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> -
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> -
>  	video_set_drvdata(tea575x_radio_inst, tea);
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea575x_radio_inst->ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
>  	if (retval) {
> -		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> +		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		kfree(tea575x_radio_inst);
>  		return retval;
>  	}
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
> +
> +	if (tea->ext_init)
> +		tea->ext_init(tea);
>  
> -	snd_tea575x_set_freq(tea);
>  	tea->vd = tea575x_radio_inst;
>  
> +	retval = video_register_device(tea575x_radio_inst, VFL_TYPE_RADIO, radio_nr);
> +	if (retval) {
> +		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);

If you decide *not* to embed the video_device struct, then you have to call
video_device_release() here to free the video_device struct, otherwise that
memory will leak.

> +		kfree(tea575x_radio_inst);
> +		tea->vd = NULL;
> +		return retval;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -416,6 +388,7 @@ void snd_tea575x_exit(struct snd_tea575x
>  {
>  	if (tea->vd) {
>  		video_unregister_device(tea->vd);
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		tea->vd = NULL;
>  	}
>  }
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v4] tea575x: convert to control framework
  2011-05-18 20:25                 ` Hans Verkuil
@ 2011-05-18 21:35                   ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-18 21:35 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert tea575x-tuner to use the new V4L2 control framework. Also add
ext_init() callback that can be used by a card driver for additional
initialization right before registering the video device (for SF16-FMR2).

Also embed struct video_device to struct snd_tea575x to simplify the code.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-18 23:14:48.000000000 +0200
@@ -23,8 +23,8 @@
  */
 
 #include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
 
 #define TEA575X_FMIF	10700
 
@@ -42,7 +42,8 @@ struct snd_tea575x_ops {
 };
 
 struct snd_tea575x {
-	struct video_device *vd;	/* video device */
+	struct video_device vd;		/* video device */
+	bool initialized;
 	bool tea5759;			/* 5759 chip is present */
 	bool mute;			/* Device is muted? */
 	bool stereo;			/* receiving stereo */
@@ -54,6 +55,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	void (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 23:22:44.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
 
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
 	return -EINVAL;
 }
 
@@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
 	.name           = "tea575x-tuner",
 	.fops           = &tea575x_fops,
 	.ioctl_ops 	= &tea575x_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
 };
 
 /*
@@ -373,7 +333,6 @@ static struct video_device tea575x_radio
 int snd_tea575x_init(struct snd_tea575x *tea)
 {
 	int retval;
-	struct video_device *tea575x_radio_inst;
 
 	tea->mute = 1;
 
@@ -384,39 +343,44 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
-	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
-		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
-		return -ENOMEM;
-	}
+	memcpy(&tea->vd, &tea575x_radio, sizeof(tea575x_radio));
+	video_set_drvdata(&tea->vd, tea);
 
-	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea->vd.ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		return retval;
+	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
+	if (tea->ext_init)
+		tea->ext_init(tea);
 
-	video_set_drvdata(tea575x_radio_inst, tea);
+	tea->initialized = 1;
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
 	if (retval) {
 		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
-		kfree(tea575x_radio_inst);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		tea->initialized = 0;
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
-	tea->vd = tea575x_radio_inst;
-
 	return 0;
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
-	if (tea->vd) {
-		video_unregister_device(tea->vd);
-		tea->vd = NULL;
+	if (tea->initialized) {
+		video_unregister_device(&tea->vd);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		tea->initialized = 0;
 	}
 }
 


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v4] tea575x: convert to control framework
@ 2011-05-18 21:35                   ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-18 21:35 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert tea575x-tuner to use the new V4L2 control framework. Also add
ext_init() callback that can be used by a card driver for additional
initialization right before registering the video device (for SF16-FMR2).

Also embed struct video_device to struct snd_tea575x to simplify the code.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-18 23:14:48.000000000 +0200
@@ -23,8 +23,8 @@
  */
 
 #include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
 
 #define TEA575X_FMIF	10700
 
@@ -42,7 +42,8 @@ struct snd_tea575x_ops {
 };
 
 struct snd_tea575x {
-	struct video_device *vd;	/* video device */
+	struct video_device vd;		/* video device */
+	bool initialized;
 	bool tea5759;			/* 5759 chip is present */
 	bool mute;			/* Device is muted? */
 	bool stereo;			/* receiving stereo */
@@ -54,6 +55,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	void (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 23:22:44.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
 
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
 	return -EINVAL;
 }
 
@@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
 	.name           = "tea575x-tuner",
 	.fops           = &tea575x_fops,
 	.ioctl_ops 	= &tea575x_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
 };
 
 /*
@@ -373,7 +333,6 @@ static struct video_device tea575x_radio
 int snd_tea575x_init(struct snd_tea575x *tea)
 {
 	int retval;
-	struct video_device *tea575x_radio_inst;
 
 	tea->mute = 1;
 
@@ -384,39 +343,44 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
-	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
-		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
-		return -ENOMEM;
-	}
+	memcpy(&tea->vd, &tea575x_radio, sizeof(tea575x_radio));
+	video_set_drvdata(&tea->vd, tea);
 
-	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea->vd.ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		return retval;
+	}
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
+	if (tea->ext_init)
+		tea->ext_init(tea);
 
-	video_set_drvdata(tea575x_radio_inst, tea);
+	tea->initialized = 1;
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
 	if (retval) {
 		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
-		kfree(tea575x_radio_inst);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		tea->initialized = 0;
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
-	tea->vd = tea575x_radio_inst;
-
 	return 0;
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
-	if (tea->vd) {
-		video_unregister_device(tea->vd);
-		tea->vd = NULL;
+	if (tea->initialized) {
+		video_unregister_device(&tea->vd);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		tea->initialized = 0;
 	}
 }
 


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v4] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-18  6:26             ` Hans Verkuil
@ 2011-05-18 21:38               ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-18 21:38 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
connected to ISA bus.
The card can optionally be equipped with PT2254A volume control (equivalent
of TC9154AP) - the volume setting is completely reworked (with balance control
added) and tested.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/sound/pci/Kconfig	2011-05-15 18:50:18.000000000 +0200
+++ linux-2.6.39-rc2/sound/pci/Kconfig	2011-05-17 23:35:30.000000000 +0200
@@ -565,8 +565,8 @@ config SND_FM801_TEA575X_BOOL
 
 config SND_TEA575X
 	tristate
-	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
-	default SND_FM801 || SND_ES1968
+	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
+	default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
 
 source "sound/pci/hda/Kconfig"
 
--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-18 22:41:07.000000000 +0200
@@ -1,441 +1,204 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
+	struct v4l2_ctrl *volume;
+	struct v4l2_ctrl *balance;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
-	unsigned long freq = dev->curfreq;
-
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	if (v->index > 0)
-		return -EINVAL;
-
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
+	outb(bits, fmr2->io);
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
+/* TC9154A/PT2254A volume control */
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2);
+	reg |= channel;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
+}
+
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
+	int volume, balance, left, right;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
+		volume = ctrl->val;
+		balance = fmr2->balance->cur.val;
 		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
+	case V4L2_CID_AUDIO_BALANCE:
+		balance = ctrl->val;
+		volume = fmr2->volume->cur.val;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	left = right = volume;
+	if (balance < 0)
+		right = max(0, right + balance);
+	if (balance > 0)
+		left = max(0, left - balance);
+
+	tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT);
+	tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT);
 
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
 	return 0;
 }
 
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
+};
 
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
+static void fmr2_tea_ext_init(struct snd_tea575x *tea)
 {
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
+	struct fmr2 *fmr2 = tea->private_data;
 
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
+		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
+		v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+	}
 }
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
-
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
-		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
-	}
-
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	fmr2->tea.ext_init = fmr2_tea_ext_init;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
 
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		return -EINVAL;
+		return -ENODEV;
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +206,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v4] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-18 21:38               ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-18 21:38 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
connected to ISA bus.
The card can optionally be equipped with PT2254A volume control (equivalent
of TC9154AP) - the volume setting is completely reworked (with balance control
added) and tested.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/sound/pci/Kconfig	2011-05-15 18:50:18.000000000 +0200
+++ linux-2.6.39-rc2/sound/pci/Kconfig	2011-05-17 23:35:30.000000000 +0200
@@ -565,8 +565,8 @@ config SND_FM801_TEA575X_BOOL
 
 config SND_TEA575X
 	tristate
-	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
-	default SND_FM801 || SND_ES1968
+	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
+	default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
 
 source "sound/pci/hda/Kconfig"
 
--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-18 22:41:07.000000000 +0200
@@ -1,441 +1,204 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
+	struct v4l2_ctrl *volume;
+	struct v4l2_ctrl *balance;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
-	unsigned long freq = dev->curfreq;
-
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	if (v->index > 0)
-		return -EINVAL;
-
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
+	outb(bits, fmr2->io);
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
+/* TC9154A/PT2254A volume control */
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2);
+	reg |= channel;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
+}
+
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
+	int volume, balance, left, right;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
+		volume = ctrl->val;
+		balance = fmr2->balance->cur.val;
 		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
+	case V4L2_CID_AUDIO_BALANCE:
+		balance = ctrl->val;
+		volume = fmr2->volume->cur.val;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	left = right = volume;
+	if (balance < 0)
+		right = max(0, right + balance);
+	if (balance > 0)
+		left = max(0, left - balance);
+
+	tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT);
+	tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT);
 
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
 	return 0;
 }
 
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
+};
 
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
+static void fmr2_tea_ext_init(struct snd_tea575x *tea)
 {
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
+	struct fmr2 *fmr2 = tea->private_data;
 
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
+		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
+		v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+	}
 }
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
-
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
-		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
-	}
-
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	fmr2->tea.ext_init = fmr2_tea_ext_init;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
 
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		return -EINVAL;
+		return -ENODEV;
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +206,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v4] tea575x: convert to control framework
  2011-05-18 21:35                   ` Ondrej Zary
@ 2011-05-19  6:45                     ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-19  6:45 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Wednesday, May 18, 2011 23:35:22 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework. Also add
> ext_init() callback that can be used by a card driver for additional
> initialization right before registering the video device (for SF16-FMR2).
> 
> Also embed struct video_device to struct snd_tea575x to simplify the code.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-18 23:14:48.000000000 +0200
> @@ -23,8 +23,8 @@
>   */
>  
>  #include <linux/videodev2.h>
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -42,7 +42,8 @@ struct snd_tea575x_ops {
>  };
>  
>  struct snd_tea575x {
> -	struct video_device *vd;	/* video device */
> +	struct video_device vd;		/* video device */
> +	bool initialized;

This isn't needed, more about that below.

>  	bool tea5759;			/* 5759 chip is present */
>  	bool mute;			/* Device is muted? */
>  	bool stereo;			/* receiving stereo */
> @@ -54,6 +55,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	void (*ext_init)(struct snd_tea575x *tea);

ext_init should return int to pass back errors.

>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 23:22:44.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> +		tea->mute = ctrl->val;
> +		snd_tea575x_set_freq(tea);
>  		return 0;
>  	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
>  
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> -			snd_tea575x_set_freq(tea);
> -		}
> -		return 0;
> -	}
>  	return -EINVAL;
>  }
>  
> @@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
>  	.name           = "tea575x-tuner",
>  	.fops           = &tea575x_fops,
>  	.ioctl_ops 	= &tea575x_ioctl_ops,
> -	.release	= video_device_release,
> +	.release	= video_device_release_empty,
> +};
> +
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
>  };
>  
>  /*
> @@ -373,7 +333,6 @@ static struct video_device tea575x_radio
>  int snd_tea575x_init(struct snd_tea575x *tea)
>  {
>  	int retval;
> -	struct video_device *tea575x_radio_inst;
>  
>  	tea->mute = 1;
>  
> @@ -384,39 +343,44 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
> -	tea575x_radio_inst = video_device_alloc();
> -	if (tea575x_radio_inst == NULL) {
> -		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
> -		return -ENOMEM;
> -	}
> +	memcpy(&tea->vd, &tea575x_radio, sizeof(tea575x_radio));

You can use tea->vd = tea575x_radio instead of memcpy.

> +	video_set_drvdata(&tea->vd, tea);
>  
> -	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea->vd.ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
> +	if (retval) {
> +		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		return retval;
> +	}
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);

It's better to move this after the ext_init call, then the driver implementing
ext_init doesn't have to call v4l2_ctrl_handler_setup again.

>  
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> +	if (tea->ext_init)
> +		tea->ext_init(tea);

ext_init can return an error. In particular, radio-sf16fmr2 should return
tea->ctrl_handler.error after adding the two new controls since adding
those controls might have failed.

>  
> -	video_set_drvdata(tea575x_radio_inst, tea);
> +	tea->initialized = 1;
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
>  	if (retval) {
>  		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> -		kfree(tea575x_radio_inst);
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		tea->initialized = 0;
>  		return retval;
>  	}
>  
> -	snd_tea575x_set_freq(tea);
> -	tea->vd = tea575x_radio_inst;
> -
>  	return 0;
>  }
>  
>  void snd_tea575x_exit(struct snd_tea575x *tea)
>  {
> -	if (tea->vd) {
> -		video_unregister_device(tea->vd);
> -		tea->vd = NULL;
> +	if (tea->initialized) {
> +		video_unregister_device(&tea->vd);

video_unregister_device is smart enough to detect a failed video_register_device
call, so there is no need to use this 'initialized' field.

> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		tea->initialized = 0;
>  	}
>  }
>  
> 
> 
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v4] tea575x: convert to control framework
@ 2011-05-19  6:45                     ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-19  6:45 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Wednesday, May 18, 2011 23:35:22 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework. Also add
> ext_init() callback that can be used by a card driver for additional
> initialization right before registering the video device (for SF16-FMR2).
> 
> Also embed struct video_device to struct snd_tea575x to simplify the code.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-18 23:14:48.000000000 +0200
> @@ -23,8 +23,8 @@
>   */
>  
>  #include <linux/videodev2.h>
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -42,7 +42,8 @@ struct snd_tea575x_ops {
>  };
>  
>  struct snd_tea575x {
> -	struct video_device *vd;	/* video device */
> +	struct video_device vd;		/* video device */
> +	bool initialized;

This isn't needed, more about that below.

>  	bool tea5759;			/* 5759 chip is present */
>  	bool mute;			/* Device is muted? */
>  	bool stereo;			/* receiving stereo */
> @@ -54,6 +55,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	void (*ext_init)(struct snd_tea575x *tea);

ext_init should return int to pass back errors.

>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-18 23:22:44.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> +		tea->mute = ctrl->val;
> +		snd_tea575x_set_freq(tea);
>  		return 0;
>  	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
>  
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> -			snd_tea575x_set_freq(tea);
> -		}
> -		return 0;
> -	}
>  	return -EINVAL;
>  }
>  
> @@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
>  	.name           = "tea575x-tuner",
>  	.fops           = &tea575x_fops,
>  	.ioctl_ops 	= &tea575x_ioctl_ops,
> -	.release	= video_device_release,
> +	.release	= video_device_release_empty,
> +};
> +
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
>  };
>  
>  /*
> @@ -373,7 +333,6 @@ static struct video_device tea575x_radio
>  int snd_tea575x_init(struct snd_tea575x *tea)
>  {
>  	int retval;
> -	struct video_device *tea575x_radio_inst;
>  
>  	tea->mute = 1;
>  
> @@ -384,39 +343,44 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
> -	tea575x_radio_inst = video_device_alloc();
> -	if (tea575x_radio_inst == NULL) {
> -		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
> -		return -ENOMEM;
> -	}
> +	memcpy(&tea->vd, &tea575x_radio, sizeof(tea575x_radio));

You can use tea->vd = tea575x_radio instead of memcpy.

> +	video_set_drvdata(&tea->vd, tea);
>  
> -	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea->vd.ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
> +	if (retval) {
> +		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		return retval;
> +	}
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);

It's better to move this after the ext_init call, then the driver implementing
ext_init doesn't have to call v4l2_ctrl_handler_setup again.

>  
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> +	if (tea->ext_init)
> +		tea->ext_init(tea);

ext_init can return an error. In particular, radio-sf16fmr2 should return
tea->ctrl_handler.error after adding the two new controls since adding
those controls might have failed.

>  
> -	video_set_drvdata(tea575x_radio_inst, tea);
> +	tea->initialized = 1;
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
>  	if (retval) {
>  		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> -		kfree(tea575x_radio_inst);
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		tea->initialized = 0;
>  		return retval;
>  	}
>  
> -	snd_tea575x_set_freq(tea);
> -	tea->vd = tea575x_radio_inst;
> -
>  	return 0;
>  }
>  
>  void snd_tea575x_exit(struct snd_tea575x *tea)
>  {
> -	if (tea->vd) {
> -		video_unregister_device(tea->vd);
> -		tea->vd = NULL;
> +	if (tea->initialized) {
> +		video_unregister_device(&tea->vd);

video_unregister_device is smart enough to detect a failed video_register_device
call, so there is no need to use this 'initialized' field.

> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		tea->initialized = 0;
>  	}
>  }
>  
> 
> 
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v5] tea575x: convert to control framework
  2011-05-19  6:45                     ` Hans Verkuil
@ 2011-05-19 16:11                       ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-19 16:11 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert tea575x-tuner to use the new V4L2 control framework. Also add
ext_init() callback that can be used by a card driver for additional
initialization right before registering the video device (for SF16-FMR2).

Also embed struct video_device to struct snd_tea575x to simplify the code.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-19 17:27:45.000000000 +0200
@@ -23,8 +23,8 @@
  */
 
 #include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
 
 #define TEA575X_FMIF	10700
 
@@ -42,7 +42,7 @@ struct snd_tea575x_ops {
 };
 
 struct snd_tea575x {
-	struct video_device *vd;	/* video device */
+	struct video_device vd;		/* video device */
 	bool tea5759;			/* 5759 chip is present */
 	bool mute;			/* Device is muted? */
 	bool stereo;			/* receiving stereo */
@@ -54,6 +54,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	int (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-19 17:31:45.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
-	return -EINVAL;
-}
 
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
 	return -EINVAL;
 }
 
@@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
 	.name           = "tea575x-tuner",
 	.fops           = &tea575x_fops,
 	.ioctl_ops 	= &tea575x_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
 };
 
 /*
@@ -373,7 +333,6 @@ static struct video_device tea575x_radio
 int snd_tea575x_init(struct snd_tea575x *tea)
 {
 	int retval;
-	struct video_device *tea575x_radio_inst;
 
 	tea->mute = 1;
 
@@ -384,40 +343,45 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
-	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
-		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
-		return -ENOMEM;
-	}
+	tea->vd = tea575x_radio;
+	video_set_drvdata(&tea->vd, tea);
 
-	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea->vd.ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		return retval;
+	}
 
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
+	if (tea->ext_init) {
+		retval = tea->ext_init(tea);
+		if (retval) {
+			v4l2_ctrl_handler_free(&tea->ctrl_handler);
+			return retval;
+		}
+	}
 
-	video_set_drvdata(tea575x_radio_inst, tea);
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
 	if (retval) {
 		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
-		kfree(tea575x_radio_inst);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
-	tea->vd = tea575x_radio_inst;
-
 	return 0;
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
-	if (tea->vd) {
-		video_unregister_device(tea->vd);
-		tea->vd = NULL;
-	}
+	video_unregister_device(&tea->vd);
+	v4l2_ctrl_handler_free(&tea->ctrl_handler);
 }
 
 static int __init alsa_tea575x_module_init(void)


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v5] tea575x: convert to control framework
@ 2011-05-19 16:11                       ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-19 16:11 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert tea575x-tuner to use the new V4L2 control framework. Also add
ext_init() callback that can be used by a card driver for additional
initialization right before registering the video device (for SF16-FMR2).

Also embed struct video_device to struct snd_tea575x to simplify the code.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-19 17:27:45.000000000 +0200
@@ -23,8 +23,8 @@
  */
 
 #include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
 
 #define TEA575X_FMIF	10700
 
@@ -42,7 +42,7 @@ struct snd_tea575x_ops {
 };
 
 struct snd_tea575x {
-	struct video_device *vd;	/* video device */
+	struct video_device vd;		/* video device */
 	bool tea5759;			/* 5759 chip is present */
 	bool mute;			/* Device is muted? */
 	bool stereo;			/* receiving stereo */
@@ -54,6 +54,8 @@ struct snd_tea575x {
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	int (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
--- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
+++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-19 17:31:45.000000000 +0200
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
-	return -EINVAL;
-}
 
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
 	return -EINVAL;
 }
 
@@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
 	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
 	.name           = "tea575x-tuner",
 	.fops           = &tea575x_fops,
 	.ioctl_ops 	= &tea575x_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
 };
 
 /*
@@ -373,7 +333,6 @@ static struct video_device tea575x_radio
 int snd_tea575x_init(struct snd_tea575x *tea)
 {
 	int retval;
-	struct video_device *tea575x_radio_inst;
 
 	tea->mute = 1;
 
@@ -384,40 +343,45 @@ int snd_tea575x_init(struct snd_tea575x
 	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
-	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
-		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
-		return -ENOMEM;
-	}
+	tea->vd = tea575x_radio;
+	video_set_drvdata(&tea->vd, tea);
 
-	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea->vd.ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		return retval;
+	}
 
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
+	if (tea->ext_init) {
+		retval = tea->ext_init(tea);
+		if (retval) {
+			v4l2_ctrl_handler_free(&tea->ctrl_handler);
+			return retval;
+		}
+	}
 
-	video_set_drvdata(tea575x_radio_inst, tea);
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
 
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
 	if (retval) {
 		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
-		kfree(tea575x_radio_inst);
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
-	tea->vd = tea575x_radio_inst;
-
 	return 0;
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
-	if (tea->vd) {
-		video_unregister_device(tea->vd);
-		tea->vd = NULL;
-	}
+	video_unregister_device(&tea->vd);
+	v4l2_ctrl_handler_free(&tea->ctrl_handler);
 }
 
 static int __init alsa_tea575x_module_init(void)


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v5] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-19  6:45                     ` Hans Verkuil
@ 2011-05-19 16:15                       ` Ondrej Zary
  -1 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-19 16:15 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, alsa-devel, Kernel development list

Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
connected to ISA bus.
The card can optionally be equipped with PT2254A volume control (equivalent
of TC9154AP) - the volume setting is completely reworked (with balance control
added) and tested.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/sound/pci/Kconfig	2011-05-15 18:50:18.000000000 +0200
+++ linux-2.6.39-rc2/sound/pci/Kconfig	2011-05-17 23:35:30.000000000 +0200
@@ -565,8 +565,8 @@ config SND_FM801_TEA575X_BOOL
 
 config SND_TEA575X
 	tristate
-	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
-	default SND_FM801 || SND_ES1968
+	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
+	default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
 
 source "sound/pci/hda/Kconfig"
 
--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-19 17:56:08.000000000 +0200
@@ -1,441 +1,209 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
+	struct v4l2_ctrl *volume;
+	struct v4l2_ctrl *balance;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
-	unsigned long freq = dev->curfreq;
-
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (v->index > 0)
-		return -EINVAL;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
+	outb(bits, fmr2->io);
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
+/* TC9154A/PT2254A volume control */
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2);
+	reg |= channel;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
+}
+
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
+	int volume, balance, left, right;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
+		volume = ctrl->val;
+		balance = fmr2->balance->cur.val;
 		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
+	case V4L2_CID_AUDIO_BALANCE:
+		balance = ctrl->val;
+		volume = fmr2->volume->cur.val;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	left = right = volume;
+	if (balance < 0)
+		right = max(0, right + balance);
+	if (balance > 0)
+		left = max(0, left - balance);
 
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
+	tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT);
+	tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT);
 
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
 	return 0;
 }
 
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
+};
+
+static int fmr2_tea_ext_init(struct snd_tea575x *tea)
 {
-	return a->index ? -EINVAL : 0;
-}
+	struct fmr2 *fmr2 = tea->private_data;
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
+		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
+		if (tea->ctrl_handler.error) {
+			printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");
+			return tea->ctrl_handler.error;
+		}
+	}
 
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
+	return 0;
+}
 
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
-		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
-	}
-
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	fmr2->tea.ext_init = fmr2_tea_ext_init;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
 
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		return -EINVAL;
+		return -ENODEV;
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +211,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v5] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-19 16:15                       ` Ondrej Zary
  0 siblings, 0 replies; 38+ messages in thread
From: Ondrej Zary @ 2011-05-19 16:15 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: alsa-devel, Kernel development list, linux-media

Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
connected to ISA bus.
The card can optionally be equipped with PT2254A volume control (equivalent
of TC9154AP) - the volume setting is completely reworked (with balance control
added) and tested.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

--- linux-2.6.39-rc2-/sound/pci/Kconfig	2011-05-15 18:50:18.000000000 +0200
+++ linux-2.6.39-rc2/sound/pci/Kconfig	2011-05-17 23:35:30.000000000 +0200
@@ -565,8 +565,8 @@ config SND_FM801_TEA575X_BOOL
 
 config SND_TEA575X
 	tristate
-	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
-	default SND_FM801 || SND_ES1968
+	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
+	default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
 
 source "sound/pci/hda/Kconfig"
 
--- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-19 17:56:08.000000000 +0200
@@ -1,441 +1,209 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
+	struct v4l2_ctrl *volume;
+	struct v4l2_ctrl *balance;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
-
-static inline void wait(int n, int io)
-{
-	for (; n; --n)
-		inb(io);
-}
-
-static void outbits(int bits, unsigned int data, int nWait, int io)
-{
-	int bit;
-
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
-	}
-}
-
-static inline void fmr2_mute(int io)
-{
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
-	unsigned long freq = dev->curfreq;
-
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this does't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (v->index > 0)
-		return -EINVAL;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
 
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
 
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
+	outb(bits, fmr2->io);
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
 }
 
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
 
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
+/* TC9154A/PT2254A volume control */
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2);
+	reg |= channel;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
+	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
+}
+
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
+	int volume, balance, left, right;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
+		volume = ctrl->val;
+		balance = fmr2->balance->cur.val;
 		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
+	case V4L2_CID_AUDIO_BALANCE:
+		balance = ctrl->val;
+		volume = fmr2->volume->cur.val;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
-
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
+	left = right = volume;
+	if (balance < 0)
+		right = max(0, right + balance);
+	if (balance > 0)
+		left = max(0, left - balance);
 
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
+	tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT);
+	tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT);
 
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
 	return 0;
 }
 
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
+};
+
+static int fmr2_tea_ext_init(struct snd_tea575x *tea)
 {
-	return a->index ? -EINVAL : 0;
-}
+	struct fmr2 *fmr2 = tea->private_data;
 
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
-};
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
+		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
+		if (tea->ctrl_handler.error) {
+			printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");
+			return tea->ctrl_handler.error;
+		}
+	}
 
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
+	return 0;
+}
 
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
-		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
-	}
-
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	fmr2->tea.ext_init = fmr2_tea_ext_init;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
 
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		return -EINVAL;
+		return -ENODEV;
 	}
 
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +211,9 @@ static void __exit fmr2_exit(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v5] tea575x: convert to control framework
  2011-05-19 16:11                       ` Ondrej Zary
@ 2011-05-19 20:54                         ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-19 20:54 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Thursday, May 19, 2011 18:11:49 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework. Also add
> ext_init() callback that can be used by a card driver for additional
> initialization right before registering the video device (for SF16-FMR2).
> 
> Also embed struct video_device to struct snd_tea575x to simplify the code.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

Acked-by: Hans Verkuil <hverkuil@xs4all.nl>

> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-19 17:27:45.000000000 +0200
> @@ -23,8 +23,8 @@
>   */
>  
>  #include <linux/videodev2.h>
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -42,7 +42,7 @@ struct snd_tea575x_ops {
>  };
>  
>  struct snd_tea575x {
> -	struct video_device *vd;	/* video device */
> +	struct video_device vd;		/* video device */
>  	bool tea5759;			/* 5759 chip is present */
>  	bool mute;			/* Device is muted? */
>  	bool stereo;			/* receiving stereo */
> @@ -54,6 +54,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	int (*ext_init)(struct snd_tea575x *tea);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-19 17:31:45.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> +		tea->mute = ctrl->val;
> +		snd_tea575x_set_freq(tea);
>  		return 0;
>  	}
> -	return -EINVAL;
> -}
>  
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> -
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> -			snd_tea575x_set_freq(tea);
> -		}
> -		return 0;
> -	}
>  	return -EINVAL;
>  }
>  
> @@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
>  	.name           = "tea575x-tuner",
>  	.fops           = &tea575x_fops,
>  	.ioctl_ops 	= &tea575x_ioctl_ops,
> -	.release	= video_device_release,
> +	.release	= video_device_release_empty,
> +};
> +
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
>  };
>  
>  /*
> @@ -373,7 +333,6 @@ static struct video_device tea575x_radio
>  int snd_tea575x_init(struct snd_tea575x *tea)
>  {
>  	int retval;
> -	struct video_device *tea575x_radio_inst;
>  
>  	tea->mute = 1;
>  
> @@ -384,40 +343,45 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
> -	tea575x_radio_inst = video_device_alloc();
> -	if (tea575x_radio_inst == NULL) {
> -		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
> -		return -ENOMEM;
> -	}
> +	tea->vd = tea575x_radio;
> +	video_set_drvdata(&tea->vd, tea);
>  
> -	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea->vd.ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
> +	if (retval) {
> +		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		return retval;
> +	}
>  
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> +	if (tea->ext_init) {
> +		retval = tea->ext_init(tea);
> +		if (retval) {
> +			v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +			return retval;
> +		}
> +	}
>  
> -	video_set_drvdata(tea575x_radio_inst, tea);
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
>  	if (retval) {
>  		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> -		kfree(tea575x_radio_inst);
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		return retval;
>  	}
>  
> -	snd_tea575x_set_freq(tea);
> -	tea->vd = tea575x_radio_inst;
> -
>  	return 0;
>  }
>  
>  void snd_tea575x_exit(struct snd_tea575x *tea)
>  {
> -	if (tea->vd) {
> -		video_unregister_device(tea->vd);
> -		tea->vd = NULL;
> -	}
> +	video_unregister_device(&tea->vd);
> +	v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  }
>  
>  static int __init alsa_tea575x_module_init(void)
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v5] tea575x: convert to control framework
@ 2011-05-19 20:54                         ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-19 20:54 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Thursday, May 19, 2011 18:11:49 Ondrej Zary wrote:
> Convert tea575x-tuner to use the new V4L2 control framework. Also add
> ext_init() callback that can be used by a card driver for additional
> initialization right before registering the video device (for SF16-FMR2).
> 
> Also embed struct video_device to struct snd_tea575x to simplify the code.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

Acked-by: Hans Verkuil <hverkuil@xs4all.nl>

> 
> --- linux-2.6.39-rc2-/include/sound/tea575x-tuner.h	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/include/sound/tea575x-tuner.h	2011-05-19 17:27:45.000000000 +0200
> @@ -23,8 +23,8 @@
>   */
>  
>  #include <linux/videodev2.h>
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-dev.h>
> -#include <media/v4l2-ioctl.h>
>  
>  #define TEA575X_FMIF	10700
>  
> @@ -42,7 +42,7 @@ struct snd_tea575x_ops {
>  };
>  
>  struct snd_tea575x {
> -	struct video_device *vd;	/* video device */
> +	struct video_device vd;		/* video device */
>  	bool tea5759;			/* 5759 chip is present */
>  	bool mute;			/* Device is muted? */
>  	bool stereo;			/* receiving stereo */
> @@ -54,6 +54,8 @@ struct snd_tea575x {
>  	void *private_data;
>  	u8 card[32];
>  	u8 bus_info[32];
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	int (*ext_init)(struct snd_tea575x *tea);
>  };
>  
>  int snd_tea575x_init(struct snd_tea575x *tea);
> --- linux-2.6.39-rc2-/sound/i2c/other/tea575x-tuner.c	2011-05-13 19:39:23.000000000 +0200
> +++ linux-2.6.39-rc2/sound/i2c/other/tea575x-tuner.c	2011-05-19 17:31:45.000000000 +0200
> @@ -22,11 +22,11 @@
>  
>  #include <asm/io.h>
>  #include <linux/delay.h>
> -#include <linux/interrupt.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
>  #include <linux/version.h>
> -#include <sound/core.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
>  #include <sound/tea575x-tuner.h>
>  
>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
> @@ -62,17 +62,6 @@ module_param(radio_nr, int, 0);
>  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
>  #define TEA575X_BIT_FREQ_MASK	0x7fff
>  
> -static struct v4l2_queryctrl radio_qctrl[] = {
> -	{
> -		.id            = V4L2_CID_AUDIO_MUTE,
> -		.name          = "Mute",
> -		.minimum       = 0,
> -		.maximum       = 1,
> -		.default_value = 1,
> -		.type          = V4L2_CTRL_TYPE_BOOLEAN,
> -	}
> -};
> -
>  /*
>   * lowlevel part
>   */
> @@ -266,47 +255,17 @@ static int vidioc_s_audio(struct file *f
>  	return 0;
>  }
>  
> -static int vidioc_queryctrl(struct file *file, void *priv,
> -					struct v4l2_queryctrl *qc)
> +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
> -		if (qc->id && qc->id == radio_qctrl[i].id) {
> -			memcpy(qc, &(radio_qctrl[i]),
> -						sizeof(*qc));
> -			return 0;
> -		}
> -	}
> -	return -EINVAL;
> -}
> -
> -static int vidioc_g_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> +	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_AUDIO_MUTE:
> -		ctrl->value = tea->mute;
> +		tea->mute = ctrl->val;
> +		snd_tea575x_set_freq(tea);
>  		return 0;
>  	}
> -	return -EINVAL;
> -}
>  
> -static int vidioc_s_ctrl(struct file *file, void *priv,
> -					struct v4l2_control *ctrl)
> -{
> -	struct snd_tea575x *tea = video_drvdata(file);
> -
> -	switch (ctrl->id) {
> -	case V4L2_CID_AUDIO_MUTE:
> -		if (tea->mute != ctrl->value) {
> -			tea->mute = ctrl->value;
> -			snd_tea575x_set_freq(tea);
> -		}
> -		return 0;
> -	}
>  	return -EINVAL;
>  }
>  
> @@ -355,16 +314,17 @@ static const struct v4l2_ioctl_ops tea57
>  	.vidioc_s_input     = vidioc_s_input,
>  	.vidioc_g_frequency = vidioc_g_frequency,
>  	.vidioc_s_frequency = vidioc_s_frequency,
> -	.vidioc_queryctrl   = vidioc_queryctrl,
> -	.vidioc_g_ctrl      = vidioc_g_ctrl,
> -	.vidioc_s_ctrl      = vidioc_s_ctrl,
>  };
>  
>  static struct video_device tea575x_radio = {
>  	.name           = "tea575x-tuner",
>  	.fops           = &tea575x_fops,
>  	.ioctl_ops 	= &tea575x_ioctl_ops,
> -	.release	= video_device_release,
> +	.release	= video_device_release_empty,
> +};
> +
> +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
> +	.s_ctrl = tea575x_s_ctrl,
>  };
>  
>  /*
> @@ -373,7 +333,6 @@ static struct video_device tea575x_radio
>  int snd_tea575x_init(struct snd_tea575x *tea)
>  {
>  	int retval;
> -	struct video_device *tea575x_radio_inst;
>  
>  	tea->mute = 1;
>  
> @@ -384,40 +343,45 @@ int snd_tea575x_init(struct snd_tea575x
>  	tea->in_use = 0;
>  	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
>  	tea->freq = 90500 * 16;		/* 90.5Mhz default */
> +	snd_tea575x_set_freq(tea);
>  
> -	tea575x_radio_inst = video_device_alloc();
> -	if (tea575x_radio_inst == NULL) {
> -		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
> -		return -ENOMEM;
> -	}
> +	tea->vd = tea575x_radio;
> +	video_set_drvdata(&tea->vd, tea);
>  
> -	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
> +	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
> +	tea->vd.ctrl_handler = &tea->ctrl_handler;
> +	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
> +	retval = tea->ctrl_handler.error;
> +	if (retval) {
> +		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +		return retval;
> +	}
>  
> -	strcpy(tea575x_radio.name, tea->tea5759 ?
> -				   "TEA5759 radio" : "TEA5757 radio");
> +	if (tea->ext_init) {
> +		retval = tea->ext_init(tea);
> +		if (retval) {
> +			v4l2_ctrl_handler_free(&tea->ctrl_handler);
> +			return retval;
> +		}
> +	}
>  
> -	video_set_drvdata(tea575x_radio_inst, tea);
> +	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
>  
> -	retval = video_register_device(tea575x_radio_inst,
> -				       VFL_TYPE_RADIO, radio_nr);
> +	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
>  	if (retval) {
>  		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
> -		kfree(tea575x_radio_inst);
> +		v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  		return retval;
>  	}
>  
> -	snd_tea575x_set_freq(tea);
> -	tea->vd = tea575x_radio_inst;
> -
>  	return 0;
>  }
>  
>  void snd_tea575x_exit(struct snd_tea575x *tea)
>  {
> -	if (tea->vd) {
> -		video_unregister_device(tea->vd);
> -		tea->vd = NULL;
> -	}
> +	video_unregister_device(&tea->vd);
> +	v4l2_ctrl_handler_free(&tea->ctrl_handler);
>  }
>  
>  static int __init alsa_tea575x_module_init(void)
> 
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v5] radio-sf16fmr2: convert to generic TEA575x interface
  2011-05-19 16:15                       ` Ondrej Zary
@ 2011-05-19 20:56                         ` Hans Verkuil
  -1 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-19 20:56 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: linux-media, alsa-devel, Kernel development list

On Thursday, May 19, 2011 18:15:43 Ondrej Zary wrote:
> Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
> driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
> connected to ISA bus.
> The card can optionally be equipped with PT2254A volume control (equivalent
> of TC9154AP) - the volume setting is completely reworked (with balance control
> added) and tested.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

Acked-by: Hans Verkuil <hverkuil@xs4all.nl>

Except for one tiny little typo:

> --- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
> +++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-19 17:56:08.000000000 +0200
> +static int fmr2_tea_ext_init(struct snd_tea575x *tea)
>  {
> -	return a->index ? -EINVAL : 0;
> -}
> +	struct fmr2 *fmr2 = tea->private_data;
>  
> -static const struct v4l2_file_operations fmr2_fops = {
> -	.owner          = THIS_MODULE,
> -	.unlocked_ioctl = video_ioctl2,
> -};
> +	if (inb(fmr2->io) & FMR2_HASVOL) {
> +		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
> +		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
> +		if (tea->ctrl_handler.error) {
> +			printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");

contrls -> controls

> +			return tea->ctrl_handler.error;
> +		}
> +	}

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v5] radio-sf16fmr2: convert to generic TEA575x interface
@ 2011-05-19 20:56                         ` Hans Verkuil
  0 siblings, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2011-05-19 20:56 UTC (permalink / raw)
  To: Ondrej Zary; +Cc: alsa-devel, Kernel development list, linux-media

On Thursday, May 19, 2011 18:15:43 Ondrej Zary wrote:
> Convert radio-sf16fmr2 to use generic TEA575x implementation. Most of the
> driver code goes away as SF16-FMR2 is basically just a TEA5757 tuner
> connected to ISA bus.
> The card can optionally be equipped with PT2254A volume control (equivalent
> of TC9154AP) - the volume setting is completely reworked (with balance control
> added) and tested.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

Acked-by: Hans Verkuil <hverkuil@xs4all.nl>

Except for one tiny little typo:

> --- linux-2.6.39-rc2-/drivers/media/radio/radio-sf16fmr2.c	2011-04-06 03:30:43.000000000 +0200
> +++ linux-2.6.39-rc2/drivers/media/radio/radio-sf16fmr2.c	2011-05-19 17:56:08.000000000 +0200
> +static int fmr2_tea_ext_init(struct snd_tea575x *tea)
>  {
> -	return a->index ? -EINVAL : 0;
> -}
> +	struct fmr2 *fmr2 = tea->private_data;
>  
> -static const struct v4l2_file_operations fmr2_fops = {
> -	.owner          = THIS_MODULE,
> -	.unlocked_ioctl = video_ioctl2,
> -};
> +	if (inb(fmr2->io) & FMR2_HASVOL) {
> +		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
> +		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
> +		if (tea->ctrl_handler.error) {
> +			printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");

contrls -> controls

> +			return tea->ctrl_handler.error;
> +		}
> +	}

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2011-05-19 20:57 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-13 22:17 [PATCH RFC] radio-sf16fmr2: convert to generic TEA575x interface Ondrej Zary
2011-05-14 10:01 ` [alsa-devel] " Takashi Iwai
2011-05-14 10:06 ` Hans Verkuil
2011-05-14 10:06   ` Hans Verkuil
2011-05-15 19:41   ` [PATCH RFC] tea575x: convert to control framework Ondrej Zary
2011-05-15 19:41     ` Ondrej Zary
2011-05-15 20:18   ` [PATCH RFC v2] radio-sf16fmr2: convert to generic TEA575x interface Ondrej Zary
2011-05-15 20:18     ` Ondrej Zary
2011-05-15 21:26     ` Hans Verkuil
2011-05-15 21:26       ` Hans Verkuil
2011-05-17 19:33       ` Hans Verkuil
2011-05-17 19:33         ` Hans Verkuil
2011-05-17 20:05         ` Ondrej Zary
2011-05-18  6:26           ` Hans Verkuil
2011-05-18  6:26             ` Hans Verkuil
2011-05-18 21:38             ` [PATCH v4] " Ondrej Zary
2011-05-18 21:38               ` Ondrej Zary
2011-05-17 21:45         ` [PATCH RFC v2] tea575x: convert to control framework Ondrej Zary
2011-05-17 21:45           ` Ondrej Zary
2011-05-18  6:21           ` Hans Verkuil
2011-05-18  6:21             ` Hans Verkuil
2011-05-18 19:36             ` [PATCH v3] " Ondrej Zary
2011-05-18 19:36               ` Ondrej Zary
2011-05-18 20:25               ` Hans Verkuil
2011-05-18 20:25                 ` Hans Verkuil
2011-05-18 21:35                 ` [PATCH v4] " Ondrej Zary
2011-05-18 21:35                   ` Ondrej Zary
2011-05-19  6:45                   ` Hans Verkuil
2011-05-19  6:45                     ` Hans Verkuil
2011-05-19 16:11                     ` [PATCH v5] " Ondrej Zary
2011-05-19 16:11                       ` Ondrej Zary
2011-05-19 20:54                       ` Hans Verkuil
2011-05-19 20:54                         ` Hans Verkuil
2011-05-19 16:15                     ` [PATCH v5] radio-sf16fmr2: convert to generic TEA575x interface Ondrej Zary
2011-05-19 16:15                       ` Ondrej Zary
2011-05-19 20:56                       ` Hans Verkuil
2011-05-19 20:56                         ` Hans Verkuil
2011-05-17 21:45         ` [PATCH RFC v3] " Ondrej Zary

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.