From 2b9b325d73dc092e1b888c381b98dde57d394194 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:25 -0300 Subject: [media] radio-wl1273: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-wl1273.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 9b0c9fa0beb..6e55e93c0cf 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -2084,8 +2084,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; -- cgit v1.2.3-70-g09d2 From c84401c200423e4855bc990d8460cc0c3a2bb664 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:26 -0300 Subject: [media] wl128x: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/wl128x/fmdrv_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 602ef7ac8c2..a002234ed5d 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; -- cgit v1.2.3-70-g09d2 From 4834f4d1ff1dc574024e1a6de920ea99571090ff Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 12 Nov 2012 02:56:37 -0300 Subject: [media] media: add driver for Masterkit MA901 usb radio This patch creates a new usb-radio driver, radio-ma901.c, that supports Masterkit MA 901 USB FM radio devices. This device plugs into both the USB and an analog audio input or headphones, so this thing only deals with initialization and frequency setting. Signed-off-by: Alexey Klimov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 12 + drivers/media/radio/Makefile | 1 + drivers/media/radio/radio-ma901.c | 460 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 drivers/media/radio/radio-ma901.c (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b306..ead99285373 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 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index c03ce4fe74e..303eaebdb85 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-ma901.c b/drivers/media/radio/radio-ma901.c new file mode 100644 index 00000000000..c61f590029a --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Alexey Klimov " +#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); -- cgit v1.2.3-70-g09d2 From 480d3ca13e64ab06200ab2c4dbff462c2ac7494d Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Thu, 24 Jan 2013 19:28:57 -0300 Subject: [media] radio/si470x/radio-si470x.h: use IS_ENABLED() macro replace: #if defined(CONFIG_USB_SI470X) || \ defined(CONFIG_USB_SI470X_MODULE) with: #if IS_ENABLED(CONFIG_USB_SI470X) This change was made for: CONFIG_USB_SI470X, CONFIG_I2C_SI470X Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index 2f089b4252d..467e9557548 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 }; -- cgit v1.2.3-70-g09d2 From f122d9a83e5d1e73f34da9fb832c9b005030b9cb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 11:26:36 -0300 Subject: [media] radio-miropcm20: fix querycap Don't set version (done by the v4l2 core), fill in bus_info, set correct driver name and add device_caps support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 11f76ed4c6f..3a89e50ade6 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -79,11 +79,13 @@ static const struct v4l2_file_operations pcm20_fops = { 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; } @@ -229,7 +231,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); -- cgit v1.2.3-70-g09d2 From 1a64b63481f6ad5f2db6ee8f3cad31e882ad8a27 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:49:53 -0300 Subject: [media] radio-miropcm20: remove input/audio ioctls Such ioctls are not valid for radio devices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 3a89e50ade6..4b7c1640edb 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -178,32 +178,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, 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; -} - static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, @@ -213,10 +187,6 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .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, }; static int __init pcm20_init(void) -- cgit v1.2.3-70-g09d2 From 7f51a6108382dcbf2d5e9a268e86255d72318737 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:50:28 -0300 Subject: [media] radio-miropcm20: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 64 ++++++++++++++--------------------- 1 file changed, 26 insertions(+), 38 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4b7c1640edb..061ebcf4876 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int radio_nr = -1; @@ -30,6 +31,7 @@ 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; struct snd_miro_aci *aci; @@ -138,44 +140,16 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) { - 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); + 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; + pcm20_mute(dev, ctrl->val); + 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; + return -EINVAL; } static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { @@ -184,15 +158,17 @@ 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, +}; + +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(); @@ -210,6 +186,16 @@ 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; @@ -219,11 +205,12 @@ static int __init pcm20_init(void) video_set_drvdata(&dev->vdev, dev); 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; } @@ -237,6 +224,7 @@ static void __exit pcm20_cleanup(void) struct pcm20 *dev = &pcm20_card; video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); } -- cgit v1.2.3-70-g09d2 From f7c096f73561ba9754d4b13a6dc38e964194d784 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:54:33 -0300 Subject: [media] radio-miropcm20: add prio and control event support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 061ebcf4876..4ac813c5af1 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include static int radio_nr = -1; @@ -75,6 +77,9 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) 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, }; @@ -158,6 +163,9 @@ 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_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 = { @@ -202,6 +210,7 @@ static int __init pcm20_init(void) 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); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) -- cgit v1.2.3-70-g09d2 From f5e7cc4af36c631edf7dc73111d80af975f526a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:55:50 -0300 Subject: [media] radio-miropcm20: Fix audmode/tuner/frequency handling - instead of a mute module option, use audmode as per the spec. - clamp the frequency before setting it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 52 ++++++++++++++++------------------- 1 file changed, 24 insertions(+), 28 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4ac813c5af1..eb6cd86337a 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -26,44 +26,27 @@ 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 @@ -71,7 +54,6 @@ 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); } @@ -99,7 +81,9 @@ static int vidioc_querycap(struct file *file, void *priv, 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); + + if (v->index) return -EINVAL; strlcpy(v->name, "FM", sizeof(v->name)); v->type = V4L2_TUNER_RADIO; @@ -107,15 +91,23 @@ static int vidioc_g_tuner(struct file *file, void *priv, 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; + 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, @@ -140,8 +132,8 @@ 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); + dev->freq = clamp(f->frequency, 87 * 16000U, 108 * 16000U); + pcm20_setfreq(dev, dev->freq); return 0; } @@ -151,7 +143,7 @@ static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - pcm20_mute(dev, ctrl->val); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1); return 0; } return -EINVAL; @@ -212,6 +204,9 @@ static int __init pcm20_init(void) 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 err_hdl; @@ -233,6 +228,7 @@ 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); } -- cgit v1.2.3-70-g09d2 From 9bbc5820ffe1675c923ed0b82952cc64a610bd5d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 28 May 2012 11:10:01 -0300 Subject: [media] radio-miropcm20: fix signal and stereo indication Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/media/radio') diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index eb6cd86337a..3d0ff4404d1 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -82,6 +82,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct pcm20 *dev = video_drvdata(file); + int res; if (v->index) return -EINVAL; @@ -89,8 +90,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, 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; + 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; -- cgit v1.2.3-70-g09d2