diff options
Diffstat (limited to 'drivers/media/radio/radio-miropcm20.c')
| -rw-r--r-- | drivers/media/radio/radio-miropcm20.c | 185 | 
1 files changed, 80 insertions, 105 deletions
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4ff885445fd..a7e93d7477d 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -17,48 +17,36 @@  #include <linux/videodev2.h>  #include <media/v4l2-device.h>  #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h>  #include <sound/aci.h>  static int radio_nr = -1;  module_param(radio_nr, int, 0);  MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)"); -static int mono; -module_param(mono, bool, 0); -MODULE_PARM_DESC(mono, "Force tuner into mono mode."); -  struct pcm20 {  	struct v4l2_device v4l2_dev;  	struct video_device vdev; +	struct v4l2_ctrl_handler ctrl_handler;  	unsigned long freq; -	int muted; +	u32 audmode;  	struct snd_miro_aci *aci; +	struct mutex lock;  };  static struct pcm20 pcm20_card = { -	.freq   = 87*16000, -	.muted  = 1, +	.freq = 87 * 16000, +	.audmode = V4L2_TUNER_MODE_STEREO,  }; -static int pcm20_mute(struct pcm20 *dev, unsigned char mute) -{ -	dev->muted = mute; -	return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1); -} - -static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo) -{ -	return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1); -} -  static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)  {  	unsigned char freql;  	unsigned char freqh;  	struct snd_miro_aci *aci = dev->aci; -	dev->freq = freq; -  	freq /= 160;  	if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))  		freq /= 10;  /* I don't know exactly which version @@ -66,46 +54,68 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)  	freql = freq & 0xff;  	freqh = freq >> 8; -	pcm20_stereo(dev, !mono);  	return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);  }  static const struct v4l2_file_operations pcm20_fops = {  	.owner		= THIS_MODULE, -	.ioctl		= video_ioctl2, +	.open		= v4l2_fh_open, +	.poll		= v4l2_ctrl_poll, +	.release	= v4l2_fh_release, +	.unlocked_ioctl	= video_ioctl2,  };  static int vidioc_querycap(struct file *file, void *priv,  				struct v4l2_capability *v)  { +	struct pcm20 *dev = video_drvdata(file); +  	strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));  	strlcpy(v->card, "Miro PCM20", sizeof(v->card)); -	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); -	v->version = 0x1; -	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; +	snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name); +	v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; +	v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;  	return 0;  }  static int vidioc_g_tuner(struct file *file, void *priv,  				struct v4l2_tuner *v)  { -	if (v->index)	/* Only 1 tuner */ +	struct pcm20 *dev = video_drvdata(file); +	int res; + +	if (v->index)  		return -EINVAL;  	strlcpy(v->name, "FM", sizeof(v->name));  	v->type = V4L2_TUNER_RADIO;  	v->rangelow = 87*16000;  	v->rangehigh = 108*16000; -	v->signal = 0xffff; -	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; -	v->capability = V4L2_TUNER_CAP_LOW; -	v->audmode = V4L2_TUNER_MODE_MONO; +	res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTATION, -1, -1); +	v->signal = (res & 0x80) ? 0 : 0xffff; +	/* Note: stereo detection does not work if the audio is muted, +	   it will default to mono in that case. */ +	res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTEREO, -1, -1); +	v->rxsubchans = (res & 0x40) ? V4L2_TUNER_SUB_MONO : +					V4L2_TUNER_SUB_STEREO; +	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; +	v->audmode = dev->audmode;  	return 0;  }  static int vidioc_s_tuner(struct file *file, void *priv, -				struct v4l2_tuner *v) +				const struct v4l2_tuner *v)  { -	return v->index ? -EINVAL : 0; +	struct pcm20 *dev = video_drvdata(file); + +	if (v->index) +		return -EINVAL; +	if (v->audmode > V4L2_TUNER_MODE_STEREO) +		dev->audmode = V4L2_TUNER_MODE_STEREO; +	else +		dev->audmode = v->audmode; +	snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, +			dev->audmode == V4L2_TUNER_MODE_MONO, -1); +	return 0;  }  static int vidioc_g_frequency(struct file *file, void *priv, @@ -123,82 +133,28 @@ static int vidioc_g_frequency(struct file *file, void *priv,  static int vidioc_s_frequency(struct file *file, void *priv, -				struct v4l2_frequency *f) +				const struct v4l2_frequency *f)  {  	struct pcm20 *dev = video_drvdata(file);  	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)  		return -EINVAL; -	dev->freq = f->frequency; -	pcm20_setfreq(dev, f->frequency); +	dev->freq = clamp_t(u32, f->frequency, 87 * 16000U, 108 * 16000U); +	pcm20_setfreq(dev, dev->freq);  	return 0;  } -static int vidioc_queryctrl(struct file *file, void *priv, -				struct v4l2_queryctrl *qc) -{ -	switch (qc->id) { -	case V4L2_CID_AUDIO_MUTE: -		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); -	} -	return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, -				struct v4l2_control *ctrl) +static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl)  { -	struct pcm20 *dev = video_drvdata(file); +	struct pcm20 *dev = container_of(ctrl->handler, struct pcm20, ctrl_handler);  	switch (ctrl->id) {  	case V4L2_CID_AUDIO_MUTE: -		ctrl->value = dev->muted; -		break; -	default: -		return -EINVAL; +		snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1); +		return 0;  	} -	return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, -				struct v4l2_control *ctrl) -{ -	struct pcm20 *dev = video_drvdata(file); - -	switch (ctrl->id) { -	case V4L2_CID_AUDIO_MUTE: -		pcm20_mute(dev, ctrl->value); -		break; -	default: -		return -EINVAL; -	} -	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_ioctl_ops pcm20_ioctl_ops = { @@ -207,19 +163,20 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {  	.vidioc_s_tuner     = vidioc_s_tuner,  	.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, -	.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_log_status  = v4l2_ctrl_log_status, +	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event, +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops pcm20_ctrl_ops = { +	.s_ctrl = pcm20_s_ctrl,  };  static int __init pcm20_init(void)  {  	struct pcm20 *dev = &pcm20_card;  	struct v4l2_device *v4l2_dev = &dev->v4l2_dev; +	struct v4l2_ctrl_handler *hdl;  	int res;  	dev->aci = snd_aci_get_aci(); @@ -228,8 +185,8 @@ static int __init pcm20_init(void)  			 "you must load the snd-miro driver first!\n");  		return -ENODEV;  	} -	strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name)); - +	strlcpy(v4l2_dev->name, "radio-miropcm20", sizeof(v4l2_dev->name)); +	mutex_init(&dev->lock);  	res = v4l2_device_register(NULL, v4l2_dev);  	if (res < 0) { @@ -237,19 +194,35 @@ static int __init pcm20_init(void)  		return -EINVAL;  	} +	hdl = &dev->ctrl_handler; +	v4l2_ctrl_handler_init(hdl, 1); +	v4l2_ctrl_new_std(hdl, &pcm20_ctrl_ops, +			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); +	v4l2_dev->ctrl_handler = hdl; +	if (hdl->error) { +		res = hdl->error; +		v4l2_err(v4l2_dev, "Could not register control\n"); +		goto err_hdl; +	}  	strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));  	dev->vdev.v4l2_dev = v4l2_dev;  	dev->vdev.fops = &pcm20_fops;  	dev->vdev.ioctl_ops = &pcm20_ioctl_ops;  	dev->vdev.release = video_device_release_empty; +	dev->vdev.lock = &dev->lock; +	set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);  	video_set_drvdata(&dev->vdev, dev); +	snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, +			dev->audmode == V4L2_TUNER_MODE_MONO, -1); +	pcm20_setfreq(dev, dev->freq);  	if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) -		goto fail; +		goto err_hdl;  	v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");  	return 0; -fail: +err_hdl: +	v4l2_ctrl_handler_free(hdl);  	v4l2_device_unregister(v4l2_dev);  	return -EINVAL;  } @@ -263,6 +236,8 @@ static void __exit pcm20_cleanup(void)  	struct pcm20 *dev = &pcm20_card;  	video_unregister_device(&dev->vdev); +	snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, 1, -1); +	v4l2_ctrl_handler_free(&dev->ctrl_handler);  	v4l2_device_unregister(&dev->v4l2_dev);  }  | 
