Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0be43754 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab
Browse files

V4L/DVB (6956): Add Radio support for em28xx

parent 3abee53e
Loading
Loading
Loading
Loading
+236 −60
Original line number Diff line number Diff line
@@ -62,12 +62,16 @@ static LIST_HEAD(em28xx_devlist);
static unsigned int card[]     = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int vbi_nr[]   = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };

module_param_array(card,  int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
module_param_array(radio_nr, int, NULL, 0444);
MODULE_PARM_DESC(card,     "card type");
MODULE_PARM_DESC(video_nr, "video device numbers");
MODULE_PARM_DESC(vbi_nr,   "vbi device numbers");
MODULE_PARM_DESC(radio_nr, "radio device numbers");

static unsigned int video_debug = 0;
module_param(video_debug,int,0644);
@@ -791,7 +795,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
	struct em28xx_fh      *fh  = priv;
	struct em28xx         *dev = fh->dev;

	f->type = V4L2_TUNER_ANALOG_TV;
	f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
	f->frequency = dev->ctl_freq;

	return 0;
@@ -811,7 +815,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
	if (0 != f->tuner)
		return -EINVAL;

	if (V4L2_TUNER_ANALOG_TV != f->type)
	if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
		return -EINVAL;
	if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
		return -EINVAL;

	mutex_lock(&dev->lock);
@@ -1148,6 +1154,102 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
	return 0;
}

/* ----------------------------------------------------------- */
/* RADIO ESPECIFIC IOCTLS                                      */
/* ----------------------------------------------------------- */

static int radio_querycap(struct file *file, void  *priv,
			  struct v4l2_capability *cap)
{
	struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;

	strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
	strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
	strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));

	cap->version = EM28XX_VERSION_CODE;
	cap->capabilities = V4L2_CAP_TUNER;
	return 0;
}

static int radio_g_tuner(struct file *file, void *priv,
			 struct v4l2_tuner *t)
{
	struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;

	if (unlikely(t->index > 0))
		return -EINVAL;

	strcpy(t->name, "Radio");
	t->type = V4L2_TUNER_RADIO;

	em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
	return 0;
}

static int radio_enum_input(struct file *file, void *priv,
			    struct v4l2_input *i)
{
	if (i->index != 0)
		return -EINVAL;
	strcpy(i->name, "Radio");
	i->type = V4L2_INPUT_TYPE_TUNER;

	return 0;
}

static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
	if (unlikely(a->index))
		return -EINVAL;

	strcpy(a->name, "Radio");
	return 0;
}

static int radio_s_tuner(struct file *file, void *priv,
			 struct v4l2_tuner *t)
{
	struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;

	if (0 != t->index)
		return -EINVAL;

	em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);

	return 0;
}

static int radio_s_audio(struct file *file, void *fh,
			 struct v4l2_audio *a)
{
	return 0;
}

static int radio_s_input(struct file *file, void *fh, unsigned int i)
{
	return 0;
}

static int radio_queryctrl(struct file *file, void *priv,
			   struct v4l2_queryctrl *qc)
{
	int i;

	if (qc->id <  V4L2_CID_BASE ||
		qc->id >= V4L2_CID_LASTP1)
		return -EINVAL;

	for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
		if (qc->id && qc->id == em28xx_qctrl[i].id) {
			memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
			return 0;
		}
	}

	return -EINVAL;
}

/*
 * em28xx_v4l2_open()
 * inits the device and starts isoc transfer
@@ -1155,7 +1257,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
	int minor = iminor(inode);
	int errCode = 0;
	int errCode = 0, radio = 0;
	struct em28xx *h,*dev = NULL;
	struct em28xx_fh *fh;

@@ -1168,6 +1270,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
			dev  = h;
			dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
		}
		if (h->radio_dev &&
		    h->radio_dev->minor == minor) {
			radio = 1;
			dev   = h;
		}
	}
	if (NULL == dev)
		return -ENODEV;
@@ -1183,6 +1290,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
	}
	mutex_lock(&dev->lock);
	fh->dev = dev;
	fh->radio = radio;
	filp->private_data = fh;

	if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
@@ -1207,6 +1315,10 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)

		em28xx_empty_framequeues(dev);
	}
	if (fh->radio) {
		em28xx_videodbg("video_open: setting radio device\n");
		em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL);
	}

	dev->users++;

@@ -1229,12 +1341,30 @@ static void em28xx_release_resources(struct em28xx *dev)
				dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
				dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
	list_del(&dev->devlist);
	video_unregister_device(dev->vdev);
	if (dev->radio_dev) {
		if (-1 != dev->radio_dev->minor)
			video_unregister_device(dev->radio_dev);
		else
			video_device_release(dev->radio_dev);
		dev->radio_dev = NULL;
	}
	if (dev->vbi_dev) {
		if (-1 != dev->vbi_dev->minor)
			video_unregister_device(dev->vbi_dev);
		else
			video_device_release(dev->vbi_dev);
		dev->vbi_dev = NULL;
	}
	if (dev->vdev) {
		if (-1 != dev->vdev->minor)
			video_unregister_device(dev->vdev);
		else
			video_device_release(dev->vdev);
		dev->vdev = NULL;
	}
	em28xx_i2c_unregister(dev);
	usb_put_dev(dev->udev);


	/* Mark device as unused */
	em28xx_devused&=~(1<<dev->devno);
}
@@ -1555,6 +1685,15 @@ static const struct file_operations em28xx_v4l_fops = {
	.compat_ioctl  = v4l_compat_ioctl32,
};

static const struct file_operations radio_fops = {
	.owner         = THIS_MODULE,
	.open          = em28xx_v4l2_open,
	.release       = em28xx_v4l2_close,
	.ioctl	       = video_ioctl2,
	.compat_ioctl  = v4l_compat_ioctl32,
	.llseek        = no_llseek,
};

static const struct video_device em28xx_video_template = {
	.fops                       = &em28xx_v4l_fops,
	.release                    = video_device_release,
@@ -1595,6 +1734,25 @@ static const struct video_device em28xx_video_template = {
	.current_norm               = V4L2_STD_PAL,
};

static struct video_device em28xx_radio_template = {
	.name                 = "em28xx-radio",
	.type                 = VID_TYPE_TUNER,
	.fops                 = &radio_fops,
	.minor                = -1,
	.vidioc_querycap      = radio_querycap,
	.vidioc_g_tuner       = radio_g_tuner,
	.vidioc_enum_input    = radio_enum_input,
	.vidioc_g_audio       = radio_g_audio,
	.vidioc_s_tuner       = radio_s_tuner,
	.vidioc_s_audio       = radio_s_audio,
	.vidioc_s_input       = radio_s_input,
	.vidioc_queryctrl     = radio_queryctrl,
	.vidioc_g_ctrl        = vidioc_g_ctrl,
	.vidioc_s_ctrl        = vidioc_s_ctrl,
	.vidioc_g_frequency   = vidioc_g_frequency,
	.vidioc_s_frequency   = vidioc_s_frequency,
};

/******************************** usb interface *****************************************/


@@ -1637,6 +1795,29 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
}
EXPORT_SYMBOL(em28xx_unregister_extension);

struct video_device *em28xx_vdev_init(struct em28xx *dev,
				      const struct video_device *template,
				      const int type,
				      const char *type_name)
{
	struct video_device *vfd;

	vfd = video_device_alloc();
	if (NULL == vfd)
		return NULL;
	*vfd = *template;
	vfd->minor   = -1;
	vfd->dev = &dev->udev->dev;
	vfd->release = video_device_release;
	vfd->type = type;

	snprintf(vfd->name, sizeof(vfd->name), "%s %s",
		 dev->name, type_name);

	return vfd;
}


/*
 * em28xx_init_dev()
 * allocates and inits the device structs, registers i2c bus and v4l device
@@ -1710,40 +1891,55 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,

	errCode = em28xx_config(dev);

	list_add_tail(&dev->devlist, &em28xx_devlist);

	/* allocate and fill video video_device struct */
	dev->vdev = video_device_alloc();
	dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template,
					  VID_TYPE_CAPTURE, "video");
	if (NULL == dev->vdev) {
		em28xx_errdev("cannot allocate video_device.\n");
		em28xx_devused&=~(1<<dev->devno);
		kfree(dev);
		return -ENOMEM;
		goto fail_unreg;
	}
	memcpy(dev->vdev, &em28xx_video_template,
					sizeof(em28xx_video_template));
	dev->vdev->type = VID_TYPE_CAPTURE;
	if (dev->has_tuner)
		dev->vdev->type |= VID_TYPE_TUNER;
	dev->vdev->dev = &dev->udev->dev;
	snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name),
		 "%s#%d %s", "em28xx", dev->devno, "video");

	/* register v4l2 video video_device */
	retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
				       video_nr[dev->devno]);
	if (retval) {
		em28xx_errdev("unable to register video device (error=%i).\n",
			      retval);
		goto fail_unreg;
	}

	/* Allocate and fill vbi video_device struct */
	dev->vbi_dev = video_device_alloc();
	if (NULL == dev->vbi_dev) {
	dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
					  VFL_TYPE_VBI, "vbi");
	/* register v4l2 vbi video_device */
	if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
					vbi_nr[dev->devno]) < 0) {
		em28xx_errdev("unable to register vbi device\n");
		retval = -ENODEV;
		goto fail_unreg;
	}

	if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
		dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
					VFL_TYPE_RADIO, "radio");
		if (NULL == dev->radio_dev) {
			em28xx_errdev("cannot allocate video_device.\n");
		kfree(dev->vdev);
		em28xx_devused&=~(1<<dev->devno);
		kfree(dev);
		return -ENOMEM;
			goto fail_unreg;
		}
		retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
					    radio_nr[dev->devno]);
		if (retval < 0) {
			em28xx_errdev("can't register radio device\n");
			goto fail_unreg;
		}
		em28xx_info("Registered radio device as /dev/radio%d\n",
			    dev->radio_dev->minor & 0x1f);
	}
	memcpy(dev->vbi_dev, &em28xx_video_template,
					sizeof(em28xx_video_template));
	dev->vbi_dev->type = VFL_TYPE_VBI;
	dev->vbi_dev->dev = &dev->udev->dev;
	snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name),
				"%s#%d %s", "em28xx", dev->devno, "vbi");

	list_add_tail(&dev->devlist,&em28xx_devlist);

	if (dev->has_msp34xx) {
		/* Send a reset to other chips via gpio */
@@ -1755,32 +1951,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,

	video_mux(dev, 0);

	/* register v4l2 video video_device */
	if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
					 video_nr[dev->devno]))) {
		em28xx_errdev("unable to register video device (error=%i).\n",
			      retval);
		mutex_unlock(&dev->lock);
		list_del(&dev->devlist);
		video_device_release(dev->vdev);
		em28xx_devused&=~(1<<dev->devno);
		kfree(dev);
		return -ENODEV;
	}

	/* register v4l2 vbi video_device */
	if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
					vbi_nr[dev->devno]) < 0) {
		printk("unable to register vbi device\n");
		mutex_unlock(&dev->lock);
		list_del(&dev->devlist);
		video_device_release(dev->vbi_dev);
		video_device_release(dev->vdev);
		em28xx_devused&=~(1<<dev->devno);
		kfree(dev);
		return -ENODEV;
	}

	em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
				dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
				dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
@@ -1795,6 +1965,12 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
	mutex_unlock(&em28xx_extension_devlist_lock);

	return 0;

fail_unreg:
	em28xx_release_resources(dev);
	mutex_unlock(&dev->lock);
	kfree(dev);
	return retval;
}

#if defined(CONFIG_MODULES) && defined(MODULE)
+2 −0
Original line number Diff line number Diff line
@@ -191,6 +191,7 @@ struct em28xx_board {
	enum em28xx_decoder decoder;

	struct em28xx_input       input[MAX_EM28XX_INPUT];
	struct em28xx_input	  radio;
};

struct em28xx_eeprom {
@@ -307,6 +308,7 @@ struct em28xx {
	struct list_head inqueue, outqueue;
	wait_queue_head_t open, wait_frame, wait_stream;
	struct video_device *vbi_dev;
	struct video_device *radio_dev;

	unsigned char eedata[256];