diff options
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/Kconfig | 14 | ||||
-rw-r--r-- | drivers/media/radio/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/radio/radio-keene.c | 1 | ||||
-rw-r--r-- | drivers/media/radio/radio-ma901.c | 460 | ||||
-rw-r--r-- | drivers/media/radio/radio-maxiradio.c | 7 | ||||
-rw-r--r-- | drivers/media/radio/radio-miropcm20.c | 173 | ||||
-rw-r--r-- | drivers/media/radio/radio-sf16fmi.c | 2 | ||||
-rw-r--r-- | drivers/media/radio/radio-sf16fmr2.c | 19 | ||||
-rw-r--r-- | drivers/media/radio/radio-si4713.c | 1 | ||||
-rw-r--r-- | drivers/media/radio/radio-tea5764.c | 8 | ||||
-rw-r--r-- | drivers/media/radio/radio-timb.c | 6 | ||||
-rw-r--r-- | drivers/media/radio/radio-wl1273.c | 8 | ||||
-rw-r--r-- | drivers/media/radio/saa7706h.c | 8 | ||||
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-i2c.c | 8 | ||||
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x.h | 4 | ||||
-rw-r--r-- | drivers/media/radio/tef6862.c | 8 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/Kconfig | 2 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_common.c | 3 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_v4l2.c | 10 |
19 files changed, 599 insertions, 144 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b3066..24e64a09884c 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -124,6 +124,18 @@ config USB_KEENE To compile this driver as a module, choose M here: the module will be called radio-keene. +config USB_MA901 + tristate "Masterkit MA901 USB FM radio support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers or headphones. + + To compile this driver as a module, choose M here: the + module will be called radio-ma901. + config RADIO_TEA5764 tristate "TEA5764 I2C FM radio support" depends on I2C && VIDEO_V4L2 @@ -180,7 +192,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && GENERIC_HARDIRQS select MFD_CORE select MFD_WL1273_CORE select FW_LOADER diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index c03ce4fe74e9..303eaebdb85a 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_USB_KEENE) += radio-keene.o +obj-$(CONFIG_USB_MA901) += radio-ma901.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index e10e525f33e5..296941a9ae25 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -374,6 +374,7 @@ static int usb_keene_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_keene_ioctl_ops; radio->vdev.lock = &radio->lock; radio->vdev.release = video_device_release_empty; + radio->vdev.vfl_dir = VFL_DIR_TX; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c new file mode 100644 index 000000000000..c61f590029ad --- /dev/null +++ b/drivers/media/radio/radio-ma901.c @@ -0,0 +1,460 @@ +/* + * Driver for the MasterKit MA901 USB FM radio. This device plugs + * into the USB port and an analog audio input or headphones, so this thing + * only deals with initialization, frequency setting, volume. + * + * Copyright (c) 2012 Alexey Klimov <klimov.linux@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <linux/usb.h> +#include <linux/mutex.h> + +#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>" +#define DRIVER_DESC "Masterkit MA901 USB FM radio driver" +#define DRIVER_VERSION "0.0.1" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +#define USB_MA901_VENDOR 0x16c0 +#define USB_MA901_PRODUCT 0x05df + +/* dev_warn macro with driver name */ +#define MA901_DRIVER_NAME "radio-ma901" +#define ma901radio_dev_warn(dev, fmt, arg...) \ + dev_warn(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +#define ma901radio_dev_err(dev, fmt, arg...) \ + dev_err(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +/* Probably USB_TIMEOUT should be modified in module parameter */ +#define BUFFER_LENGTH 8 +#define USB_TIMEOUT 500 + +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + +#define MA901_VOLUME_MAX 16 +#define MA901_VOLUME_MIN 0 + +/* Commands that device should understand + * List isn't full and will be updated with implementation of new functions + */ +#define MA901_RADIO_SET_FREQ 0x03 +#define MA901_RADIO_SET_VOLUME 0x04 +#define MA901_RADIO_SET_MONO_STEREO 0x05 + +/* Comfortable defines for ma901radio_set_stereo */ +#define MA901_WANT_STEREO 0x50 +#define MA901_WANT_MONO 0xd0 + +/* module parameter */ +static int radio_nr = -1; +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, "Radio file number"); + +/* Data for one (physical) device */ +struct ma901radio_device { + /* reference to USB and video device */ + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + + u8 *buffer; + struct mutex lock; /* buffer locking */ + int curfreq; + u16 volume; + int stereo; + bool muted; +}; + +static inline struct ma901radio_device *to_ma901radio_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct ma901radio_device, v4l2_dev); +} + +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int ma901radio_set_freq(struct ma901radio_device *radio, int freq) +{ + unsigned int freq_send = 0x300 + (freq >> 5) / 25; + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_FREQ; + radio->buffer[2] = ((freq_send >> 8) & 0xff) + 0x80; + radio->buffer[3] = freq_send & 0xff; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->curfreq = freq; + return 0; +} + +static int ma901radio_set_volume(struct ma901radio_device *radio, u16 vol_to_set) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_VOLUME; + radio->buffer[2] = 0xc2; + radio->buffer[3] = vol_to_set + 0x20; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->volume = vol_to_set; + return retval; +} + +static int ma901_set_stereo(struct ma901radio_device *radio, u8 stereo) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_MONO_STEREO; + radio->buffer[2] = stereo; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (retval < 0) + return retval; + + if (stereo == MA901_WANT_STEREO) + radio->stereo = V4L2_TUNER_MODE_STEREO; + else + radio->stereo = V4L2_TUNER_MODE_MONO; + + return retval; +} + +/* Handle unplugging the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_ma901radio_device_release. + */ +static void usb_ma901radio_disconnect(struct usb_interface *intf) +{ + struct ma901radio_device *radio = to_ma901radio_dev(usb_get_intfdata(intf)); + + mutex_lock(&radio->lock); + video_unregister_device(&radio->vdev); + usb_set_intfdata(intf, NULL); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +/* vidioc_querycap - query device capabilities */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-ma901", sizeof(v->driver)); + strlcpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +/* vidioc_g_tuner - get tuner attributes */ +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + v->signal = 0; + + /* TODO: the same words like in _probe() goes here. + * When receiving of stats will be implemented then we can call + * ma901radio_get_stat(). + * retval = ma901radio_get_stat(radio, &is_stereo, &v->signal); + */ + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN * FREQ_MUL; + v->rangehigh = FREQ_MAX * FREQ_MUL; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + /* v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; */ + v->audmode = radio->stereo ? + V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + return 0; +} + +/* vidioc_s_tuner - set tuner attributes */ +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + /* mono/stereo selector */ + switch (v->audmode) { + case V4L2_TUNER_MODE_MONO: + return ma901_set_stereo(radio, MA901_WANT_MONO); + default: + return ma901_set_stereo(radio, MA901_WANT_STEREO); + } +} + +/* vidioc_s_frequency - set tuner radio frequency */ +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + + return ma901radio_set_freq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); +} + +/* vidioc_g_frequency - get tuner radio frequency */ +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->frequency = radio->curfreq; + + return 0; +} + +static int usb_ma901radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ma901radio_device *radio = + container_of(ctrl->handler, struct ma901radio_device, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: /* set volume */ + return ma901radio_set_volume(radio, (u16)ctrl->val); + } + + return -EINVAL; +} + +/* TODO: Should we really need to implement suspend and resume functions? + * Radio has it's own memory and will continue playing if power is present + * on usb port and on resume it will start to play again based on freq, volume + * values in device memory. + */ +static int usb_ma901radio_suspend(struct usb_interface *intf, pm_message_t message) +{ + return 0; +} + +static int usb_ma901radio_resume(struct usb_interface *intf) +{ + return 0; +} + +static const struct v4l2_ctrl_ops usb_ma901radio_ctrl_ops = { + .s_ctrl = usb_ma901radio_s_ctrl, +}; + +/* File system interface */ +static const struct v4l2_file_operations usb_ma901radio_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usb_ma901radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void usb_ma901radio_release(struct v4l2_device *v4l2_dev) +{ + struct ma901radio_device *radio = to_ma901radio_dev(v4l2_dev); + + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->buffer); + kfree(radio); +} + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_ma901radio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ma901radio_device *radio; + int retval = 0; + + radio = kzalloc(sizeof(struct ma901radio_device), GFP_KERNEL); + if (!radio) { + dev_err(&intf->dev, "kzalloc for ma901radio_device failed\n"); + retval = -ENOMEM; + goto err; + } + + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + if (!radio->buffer) { + dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); + retval = -ENOMEM; + goto err_nobuf; + } + + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + goto err_v4l2; + } + + v4l2_ctrl_handler_init(&radio->hdl, 1); + + /* TODO:It looks like this radio doesn't have mute/unmute control + * and windows program just emulate it using volume control. + * Let's plan to do the same in this driver. + * + * v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + * V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + */ + + v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, MA901_VOLUME_MIN, + MA901_VOLUME_MAX, 1, MA901_VOLUME_MAX); + + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_ctrl; + } + mutex_init(&radio->lock); + + radio->v4l2_dev.ctrl_handler = &radio->hdl; + radio->v4l2_dev.release = usb_ma901radio_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_ma901radio_fops; + radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; + radio->vdev.release = video_device_release_empty; + radio->vdev.lock = &radio->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); + radio->curfreq = 95.21 * FREQ_MUL; + + video_set_drvdata(&radio->vdev, radio); + + /* TODO: we can get some statistics (freq, volume) from device + * but it's not implemented yet. After insertion in usb-port radio + * setups frequency and starts playing without any initialization. + * So we don't call usb_ma901radio_init/get_stat() here. + * retval = usb_ma901radio_init(radio); + */ + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, + radio_nr); + if (retval < 0) { + dev_err(&intf->dev, "could not register video device\n"); + goto err_vdev; + } + + return 0; + +err_vdev: + v4l2_ctrl_handler_free(&radio->hdl); +err_ctrl: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); +err_nobuf: + kfree(radio); +err: + return retval; +} + +/* USB Device ID List */ +static struct usb_device_id usb_ma901radio_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(USB_MA901_VENDOR, USB_MA901_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_ma901radio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_ma901radio_driver = { + .name = MA901_DRIVER_NAME, + .probe = usb_ma901radio_probe, + .disconnect = usb_ma901radio_disconnect, + .suspend = usb_ma901radio_suspend, + .resume = usb_ma901radio_resume, + .reset_resume = usb_ma901radio_resume, + .id_table = usb_ma901radio_device_table, +}; + +module_usb_driver(usb_ma901radio_driver); diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index b415211d0c4b..bd4d3a7cdadd 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -114,7 +114,8 @@ static struct snd_tea575x_ops maxiradio_tea_ops = { .set_direction = maxiradio_tea575x_set_direction, }; -static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int maxiradio_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct maxiradio *dev; struct v4l2_device *v4l2_dev; @@ -172,7 +173,7 @@ errfr: return retval; } -static void __devexit maxiradio_remove(struct pci_dev *pdev) +static void maxiradio_remove(struct pci_dev *pdev) { struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); struct maxiradio *dev = to_maxiradio(v4l2_dev); @@ -196,7 +197,7 @@ static struct pci_driver maxiradio_driver = { .name = "radio-maxiradio", .id_table = maxiradio_pci_tbl, .probe = maxiradio_probe, - .remove = __devexit_p(maxiradio_remove), + .remove = maxiradio_remove, }; static int __init maxiradio_init(void) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 11f76ed4c6fb..3d0ff4404d12 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -17,49 +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 bool 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 @@ -67,46 +54,66 @@ 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, + .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) { - return v->index ? -EINVAL : 0; + struct pcm20 *dev = video_drvdata(file); + + if (v->index) + return -EINVAL; + if (v->audmode > V4L2_TUNER_MODE_STEREO) + v->audmode = V4L2_TUNER_MODE_STEREO; + snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, + v->audmode == V4L2_TUNER_MODE_MONO, -1); + return 0; } static int vidioc_g_frequency(struct file *file, void *priv, @@ -131,75 +138,21 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - dev->freq = f->frequency; - pcm20_setfreq(dev, f->frequency); - 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) -{ - struct pcm20 *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - break; - default: - return -EINVAL; - } + dev->freq = clamp(f->frequency, 87 * 16000U, 108 * 16000U); + pcm20_setfreq(dev, dev->freq); return 0; } -static int vidioc_s_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: - pcm20_mute(dev, ctrl->value); - break; - default: - return -EINVAL; + snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1); + return 0; } - 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, - const struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; + return -EINVAL; } static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { @@ -208,19 +161,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(); @@ -229,7 +183,7 @@ 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); @@ -238,20 +192,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; } @@ -265,6 +234,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); } diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index c260a2a354b1..637a55564958 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -265,7 +265,7 @@ static const struct v4l2_ioctl_ops fmi_ioctl_ops = { }; /* ladis: this is my card. does any other types exist? */ -static struct isapnp_device_id id_table[] __devinitdata = { +static struct isapnp_device_id id_table[] = { /* SF16-FMI */ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 4efcbec74c52..9c0990457a7c 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -197,13 +197,13 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) return 0; } -static struct pnp_device_id fmr2_pnp_ids[] __devinitdata = { +static struct pnp_device_id fmr2_pnp_ids[] = { { .id = "MFRad13" }, /* tuner subdevice of SF16-FMD2 */ { .id = "" } }; MODULE_DEVICE_TABLE(pnp, fmr2_pnp_ids); -static int __devinit fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io) +static int fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io) { int err, i; char *card_name = fmr2->is_fmd2 ? "SF16-FMD2" : "SF16-FMR2"; @@ -249,7 +249,7 @@ static int __devinit fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io) return 0; } -static int __devinit fmr2_isa_match(struct device *pdev, unsigned int ndev) +static int fmr2_isa_match(struct device *pdev, unsigned int ndev) { struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); if (!fmr2) @@ -265,8 +265,7 @@ static int __devinit fmr2_isa_match(struct device *pdev, unsigned int ndev) return 1; } -static int __devinit fmr2_pnp_probe(struct pnp_dev *pdev, - const struct pnp_device_id *id) +static int fmr2_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id) { int ret; struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); @@ -285,7 +284,7 @@ static int __devinit fmr2_pnp_probe(struct pnp_dev *pdev, return 0; } -static void __devexit fmr2_remove(struct fmr2 *fmr2) +static void fmr2_remove(struct fmr2 *fmr2) { snd_tea575x_exit(&fmr2->tea); release_region(fmr2->io, 2); @@ -293,7 +292,7 @@ static void __devexit fmr2_remove(struct fmr2 *fmr2) kfree(fmr2); } -static int __devexit fmr2_isa_remove(struct device *pdev, unsigned int ndev) +static int fmr2_isa_remove(struct device *pdev, unsigned int ndev) { fmr2_remove(dev_get_drvdata(pdev)); dev_set_drvdata(pdev, NULL); @@ -301,7 +300,7 @@ static int __devexit fmr2_isa_remove(struct device *pdev, unsigned int ndev) return 0; } -static void __devexit fmr2_pnp_remove(struct pnp_dev *pdev) +static void fmr2_pnp_remove(struct pnp_dev *pdev) { fmr2_remove(pnp_get_drvdata(pdev)); pnp_set_drvdata(pdev, NULL); @@ -309,7 +308,7 @@ static void __devexit fmr2_pnp_remove(struct pnp_dev *pdev) struct isa_driver fmr2_isa_driver = { .match = fmr2_isa_match, - .remove = __devexit_p(fmr2_isa_remove), + .remove = fmr2_isa_remove, .driver = { .name = "radio-sf16fmr2", }, @@ -319,7 +318,7 @@ struct pnp_driver fmr2_pnp_driver = { .name = "radio-sf16fmr2", .id_table = fmr2_pnp_ids, .probe = fmr2_pnp_probe, - .remove = __devexit_p(fmr2_pnp_remove), + .remove = fmr2_pnp_remove, }; static int __init fmr2_init(void) diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index a082e400ed0f..1507c9d508d7 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c @@ -250,6 +250,7 @@ static struct video_device radio_si4713_vdev_template = { .name = "radio-si4713", .release = video_device_release, .ioctl_ops = &radio_si4713_ioctl_ops, + .vfl_dir = VFL_DIR_TX, }; /* Platform driver interface */ diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 36aec575e0ec..1978516af67e 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -493,8 +493,8 @@ static struct video_device tea5764_radio_template = { }; /* I2C probe: check if the device exists and register with v4l if it is */ -static int __devinit tea5764_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tea5764_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct tea5764_device *radio; struct tea5764_regs *r; @@ -552,7 +552,7 @@ errfr: return ret; } -static int __devexit tea5764_i2c_remove(struct i2c_client *client) +static int tea5764_i2c_remove(struct i2c_client *client) { struct tea5764_device *radio = i2c_get_clientdata(client); @@ -578,7 +578,7 @@ static struct i2c_driver tea5764_i2c_driver = { .owner = THIS_MODULE, }, .probe = tea5764_i2c_probe, - .remove = __devexit_p(tea5764_i2c_remove), + .remove = tea5764_i2c_remove, .id_table = tea5764_id, }; diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 5cf07779f4bb..b87effeb5dc6 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -145,7 +145,7 @@ static const struct v4l2_file_operations timbradio_fops = { .unlocked_ioctl = video_ioctl2, }; -static int __devinit timbradio_probe(struct platform_device *pdev) +static int timbradio_probe(struct platform_device *pdev) { struct timb_radio_platform_data *pdata = pdev->dev.platform_data; struct timbradio *tr; @@ -201,7 +201,7 @@ err: return err; } -static int __devexit timbradio_remove(struct platform_device *pdev) +static int timbradio_remove(struct platform_device *pdev) { struct timbradio *tr = platform_get_drvdata(pdev); @@ -219,7 +219,7 @@ static struct platform_driver timbradio_platform_driver = { .owner = THIS_MODULE, }, .probe = timbradio_probe, - .remove = __devexit_p(timbradio_remove), + .remove = timbradio_remove, }; module_platform_driver(timbradio_platform_driver); diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 9b0c9fa0beb8..02151e0e6e63 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1971,6 +1971,7 @@ static struct video_device wl1273_viddev_template = { .ioctl_ops = &wl1273_ioctl_ops, .name = WL1273_FM_DRIVER_NAME, .release = wl1273_vdev_release, + .vfl_dir = VFL_DIR_TX, }; static int wl1273_fm_radio_remove(struct platform_device *pdev) @@ -1990,7 +1991,7 @@ static int wl1273_fm_radio_remove(struct platform_device *pdev) return 0; } -static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) +static int wl1273_fm_radio_probe(struct platform_device *pdev) { struct wl1273_core **core = pdev->dev.platform_data; struct wl1273_device *radio; @@ -2084,8 +2085,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) } /* V4L2 configuration */ - memcpy(&radio->videodev, &wl1273_viddev_template, - sizeof(wl1273_viddev_template)); + radio->videodev = wl1273_viddev_template; radio->videodev.v4l2_dev = &radio->v4l2dev; @@ -2145,7 +2145,7 @@ pdata_err: static struct platform_driver wl1273_fm_radio_driver = { .probe = wl1273_fm_radio_probe, - .remove = __devexit_p(wl1273_fm_radio_remove), + .remove = wl1273_fm_radio_remove, .driver = { .name = "wl1273_fm_radio", .owner = THIS_MODULE, diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 54db36ccb9ee..06c06cc9ff25 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -373,8 +373,8 @@ static const struct v4l2_subdev_ops saa7706h_ops = { * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int __devinit saa7706h_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int saa7706h_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct saa7706h_state *state; struct v4l2_subdev *sd; @@ -418,7 +418,7 @@ err: return err; } -static int __devexit saa7706h_remove(struct i2c_client *client) +static int saa7706h_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -441,7 +441,7 @@ static struct i2c_driver saa7706h_driver = { .name = DRIVER_NAME, }, .probe = saa7706h_probe, - .remove = __devexit_p(saa7706h_remove), + .remove = saa7706h_remove, .id_table = saa7706h_id, }; diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 4ef55ec8045e..e5fc9acd0c4f 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -347,8 +347,8 @@ end: /* * si470x_i2c_probe - probe for the device */ -static int __devinit si470x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int si470x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct si470x_device *radio; int retval = 0; @@ -451,7 +451,7 @@ err_initial: /* * si470x_i2c_remove - remove the device */ -static __devexit int si470x_i2c_remove(struct i2c_client *client) +static int si470x_i2c_remove(struct i2c_client *client) { struct si470x_device *radio = i2c_get_clientdata(client); @@ -514,7 +514,7 @@ static struct i2c_driver si470x_i2c_driver = { #endif }, .probe = si470x_i2c_probe, - .remove = __devexit_p(si470x_i2c_remove), + .remove = si470x_i2c_remove, .id_table = si470x_i2c_id, }; diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index 2f089b4252df..467e95575488 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -163,7 +163,7 @@ struct si470x_device { struct completion completion; bool status_rssi_auto_update; /* Does RSSI get updated automatic? */ -#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) +#if IS_ENABLED(CONFIG_USB_SI470X) /* reference to USB and video device */ struct usb_device *usbdev; struct usb_interface *intf; @@ -179,7 +179,7 @@ struct si470x_device { unsigned char hardware_version; #endif -#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) +#if IS_ENABLED(CONFIG_I2C_SI470X) struct i2c_client *client; #endif }; diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 06d47e5cce9f..b18c2dc268ba 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -165,8 +165,8 @@ static const struct v4l2_subdev_ops tef6862_ops = { * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int __devinit tef6862_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tef6862_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct tef6862_state *state; struct v4l2_subdev *sd; @@ -189,7 +189,7 @@ static int __devinit tef6862_probe(struct i2c_client *client, return 0; } -static int __devexit tef6862_remove(struct i2c_client *client) +static int tef6862_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -211,7 +211,7 @@ static struct i2c_driver tef6862_driver = { .name = DRIVER_NAME, }, .probe = tef6862_probe, - .remove = __devexit_p(tef6862_remove), + .remove = tef6862_remove, .id_table = tef6862_id, }; diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig index ea1e6545df36..f359be7e9dd9 100644 --- a/drivers/media/radio/wl128x/Kconfig +++ b/drivers/media/radio/wl128x/Kconfig @@ -4,7 +4,7 @@ menu "Texas Instruments WL128x FM driver (ST based)" config RADIO_WL128X tristate "Texas Instruments WL128x FM Radio" - depends on VIDEO_V4L2 && RFKILL && GPIOLIB + depends on VIDEO_V4L2 && RFKILL && GPIOLIB && TTY select TI_ST if NET help Choose Y here if you have this FM radio chip. diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 602ef7ac8c24..a002234ed5de 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -1563,8 +1563,7 @@ int fmc_prepare(struct fmdev *fmdev) fmdev->irq_info.mask = FM_MAL_EVENT; /* Region info */ - memcpy(&fmdev->rx.region, ®ion_configs[default_radio_region], - sizeof(struct region_info)); + fmdev->rx.region = region_configs[default_radio_region]; fmdev->rx.mute_mode = FM_MUTE_OFF; fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 048de4536036..0a8ee8fab924 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -518,6 +518,16 @@ static struct video_device fm_viddev_template = { .ioctl_ops = &fm_drv_ioctl_ops, .name = FM_DRV_NAME, .release = video_device_release, + /* + * To ensure both the tuner and modulator ioctls are accessible we + * set the vfl_dir to M2M to indicate this. + * + * It is not really a mem2mem device of course, but it can both receive + * and transmit using the same radio device. It's the only radio driver + * that does this and it should really be split in two radio devices, + * but that would affect applications using this driver. + */ + .vfl_dir = VFL_DIR_M2M, }; int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) |