summaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2014-02-20 14:54:28 +0100
committerJiri Kosina <jkosina@suse.cz>2014-02-20 14:54:28 +0100
commitd4263348f796f29546f90802177865dd4379dd0a (patch)
treeadcbdaebae584eee2f32fab95e826e8e49eef385 /drivers/media/radio
parentbe873ac782f5ff5ee6675f83929f4fe6737eead2 (diff)
parent6d0abeca3242a88cab8232e4acd7e2bf088f3bc2 (diff)
downloadblackbird-obmc-linux-d4263348f796f29546f90802177865dd4379dd0a.tar.gz
blackbird-obmc-linux-d4263348f796f29546f90802177865dd4379dd0a.zip
Merge branch 'master' into for-next
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/Kconfig43
-rw-r--r--drivers/media/radio/Makefile4
-rw-r--r--drivers/media/radio/radio-raremono.c387
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c81
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h1
-rw-r--r--drivers/media/radio/si4713/Kconfig40
-rw-r--r--drivers/media/radio/si4713/Makefile7
-rw-r--r--drivers/media/radio/si4713/radio-platform-si4713.c (renamed from drivers/media/radio/radio-si4713.c)0
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c540
-rw-r--r--drivers/media/radio/si4713/si4713.c (renamed from drivers/media/radio/si4713-i2c.c)279
-rw-r--r--drivers/media/radio/si4713/si4713.h (renamed from drivers/media/radio/si4713-i2c.h)4
-rw-r--r--drivers/media/radio/tea575x.c2
12 files changed, 1208 insertions, 180 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 6ecdc39bb366..192f36f2f4aa 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -21,6 +21,12 @@ config RADIO_SI470X
source "drivers/media/radio/si470x/Kconfig"
+config RADIO_SI4713
+ tristate "Silicon Labs Si4713 FM Radio with RDS Transmitter support"
+ depends on VIDEO_V4L2
+
+source "drivers/media/radio/si4713/Kconfig"
+
config RADIO_SI476X
tristate "Silicon Laboratories Si476x I2C FM Radio"
depends on I2C && VIDEO_V4L2
@@ -113,29 +119,6 @@ config RADIO_SHARK2
To compile this driver as a module, choose M here: the
module will be called radio-shark2.
-config I2C_SI4713
- tristate "I2C driver for Silicon Labs Si4713 device"
- depends on I2C && VIDEO_V4L2
- ---help---
- Say Y here if you want support to Si4713 I2C device.
- This device driver supports only i2c bus.
-
- To compile this driver as a module, choose M here: the
- module will be called si4713.
-
-config RADIO_SI4713
- tristate "Silicon Labs Si4713 FM Radio Transmitter support"
- depends on I2C && VIDEO_V4L2
- select I2C_SI4713
- ---help---
- Say Y here if you want support to Si4713 FM Radio Transmitter.
- This device can transmit audio through FM. It can transmit
- RDS and RBDS signals as well. This module is the v4l2 radio
- interface for the i2c driver of this device.
-
- To compile this driver as a module, choose M here: the
- module will be called radio-si4713.
-
config USB_KEENE
tristate "Keene FM Transmitter USB support"
depends on USB && VIDEO_V4L2
@@ -146,6 +129,20 @@ config USB_KEENE
To compile this driver as a module, choose M here: the
module will be called radio-keene.
+config USB_RAREMONO
+ tristate "Thanko's Raremono AM/FM/SW radio support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ The 'Thanko's Raremono' device contains the Si4734 chip from Silicon Labs Inc.
+ It is one of the very few or perhaps the only consumer USB radio device
+ to receive the AM/FM/SW bands.
+
+ Say Y here if you want to connect this type of AM/FM/SW receiver
+ to your computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-raremono.
+
config USB_MA901
tristate "Masterkit MA901 USB FM radio support"
depends on USB && VIDEO_V4L2
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 3b645601800d..120e791199b2 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -17,12 +17,11 @@ obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
-obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
-obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
obj-$(CONFIG_RADIO_SI476X) += radio-si476x.o
obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_RADIO_SI470X) += si470x/
+obj-$(CONFIG_RADIO_SI4713) += si4713/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
obj-$(CONFIG_USB_KEENE) += radio-keene.o
obj-$(CONFIG_USB_MA901) += radio-ma901.o
@@ -33,6 +32,7 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
obj-$(CONFIG_RADIO_WL128X) += wl128x/
obj-$(CONFIG_RADIO_TEA575X) += tea575x.o
+obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o
shark2-objs := radio-shark2.o radio-tea5777.o
diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c
new file mode 100644
index 000000000000..7b3bdbb1be73
--- /dev/null
+++ b/drivers/media/radio/radio-raremono.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <asm/unaligned.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+
+/*
+ * 'Thanko's Raremono' is a Japanese si4734-based AM/FM/SW USB receiver:
+ *
+ * http://www.raremono.jp/product/484.html/
+ *
+ * The USB protocol has been reversed engineered using wireshark, initially
+ * by Dinesh Ram <dinesh.ram@cern.ch> and finished by Hans Verkuil
+ * <hverkuil@xs4all.nl>.
+ *
+ * Sadly the firmware used in this product hides lots of goodies since the
+ * si4734 has more features than are supported by the firmware. Oh well...
+ */
+
+/* driver and module definitions */
+MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
+MODULE_DESCRIPTION("Thanko's Raremono AM/FM/SW Receiver USB driver");
+MODULE_LICENSE("GPL v2");
+
+/*
+ * The Device announces itself as Cygnal Integrated Products, Inc.
+ *
+ * The vendor and product IDs (and in fact all other lsusb information as
+ * well) are identical to the si470x Silicon Labs USB FM Radio Reference
+ * Design board, even though this card has a si4734 device. Clearly the
+ * designer of this product never bothered to change the USB IDs.
+ */
+
+/* USB Device ID List */
+static struct usb_device_id usb_raremono_device_table[] = {
+ {USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_raremono_device_table);
+
+#define BUFFER_LENGTH 64
+
+/* Timeout is set to a high value, could probably be reduced. Need more tests */
+#define USB_TIMEOUT 10000
+
+/* Frequency limits in KHz */
+#define FM_FREQ_RANGE_LOW 64000
+#define FM_FREQ_RANGE_HIGH 108000
+
+#define AM_FREQ_RANGE_LOW 520
+#define AM_FREQ_RANGE_HIGH 1710
+
+#define SW_FREQ_RANGE_LOW 2300
+#define SW_FREQ_RANGE_HIGH 26100
+
+enum { BAND_FM, BAND_AM, BAND_SW };
+
+static const struct v4l2_frequency_band bands[] = {
+ /* Band FM */
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = FM_FREQ_RANGE_LOW * 16,
+ .rangehigh = FM_FREQ_RANGE_HIGH * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ /* Band AM */
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = AM_FREQ_RANGE_LOW * 16,
+ .rangehigh = AM_FREQ_RANGE_HIGH * 16,
+ .modulation = V4L2_BAND_MODULATION_AM,
+ },
+ /* Band SW */
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 2,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = SW_FREQ_RANGE_LOW * 16,
+ .rangehigh = SW_FREQ_RANGE_HIGH * 16,
+ .modulation = V4L2_BAND_MODULATION_AM,
+ },
+};
+
+struct raremono_device {
+ struct usb_device *usbdev;
+ struct usb_interface *intf;
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+ struct mutex lock;
+
+ u8 *buffer;
+ u32 band;
+ unsigned curfreq;
+};
+
+static inline struct raremono_device *to_raremono_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct raremono_device, v4l2_dev);
+}
+
+/* Set frequency. */
+static int raremono_cmd_main(struct raremono_device *radio, unsigned band, unsigned freq)
+{
+ unsigned band_offset;
+ int ret;
+
+ switch (band) {
+ case BAND_FM:
+ band_offset = 1;
+ freq /= 10;
+ break;
+ case BAND_AM:
+ band_offset = 0;
+ break;
+ default:
+ band_offset = 2;
+ break;
+ }
+ radio->buffer[0] = 0x04 + band_offset;
+ radio->buffer[1] = freq >> 8;
+ radio->buffer[2] = freq & 0xff;
+
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x0300 + radio->buffer[0], 2,
+ radio->buffer, 3, USB_TIMEOUT);
+
+ if (ret < 0) {
+ dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ radio->curfreq = (band == BAND_FM) ? freq * 10 : freq;
+ return 0;
+}
+
+/* Handle unplugging the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_raremono_device_release.
+ */
+static void usb_raremono_disconnect(struct usb_interface *intf)
+{
+ struct raremono_device *radio = to_raremono_dev(usb_get_intfdata(intf));
+
+ dev_info(&intf->dev, "Thanko's Raremono disconnected\n");
+
+ mutex_lock(&radio->lock);
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(&radio->vdev);
+ v4l2_device_disconnect(&radio->v4l2_dev);
+ mutex_unlock(&radio->lock);
+ v4l2_device_put(&radio->v4l2_dev);
+}
+
+/*
+ * Linux Video interface
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct raremono_device *radio = video_drvdata(file);
+
+ strlcpy(v->driver, "radio-raremono", sizeof(v->driver));
+ strlcpy(v->card, "Thanko's Raremono", sizeof(v->card));
+ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ if (band->tuner != 0)
+ return -EINVAL;
+
+ if (band->index >= ARRAY_SIZE(bands))
+ return -EINVAL;
+
+ *band = bands[band->index];
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct raremono_device *radio = video_drvdata(file);
+ int ret;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ strlcpy(v->name, "AM/FM/SW", sizeof(v->name));
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = AM_FREQ_RANGE_LOW * 16;
+ v->rangehigh = FM_FREQ_RANGE_HIGH * 16;
+ v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ v->audmode = (radio->curfreq < FM_FREQ_RANGE_LOW) ?
+ V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
+ memset(radio->buffer, 1, BUFFER_LENGTH);
+ ret = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ 1, 0xa1, 0x030d, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
+
+ if (ret < 0) {
+ dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ v->signal = ((radio->buffer[1] & 0xf) << 8 | radio->buffer[2]) << 4;
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *v)
+{
+ return v->index ? -EINVAL : 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *f)
+{
+ struct raremono_device *radio = video_drvdata(file);
+ u32 freq = f->frequency;
+ unsigned band;
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ if (f->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) * 8)
+ band = BAND_FM;
+ else if (f->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) * 8)
+ band = BAND_AM;
+ else
+ band = BAND_SW;
+
+ freq = clamp_t(u32, f->frequency, bands[band].rangelow, bands[band].rangehigh);
+ return raremono_cmd_main(radio, band, freq / 16);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct raremono_device *radio = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = radio->curfreq * 16;
+ return 0;
+}
+
+/* File system interface */
+static const struct v4l2_file_operations usb_raremono_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops usb_raremono_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_enum_freq_bands = vidioc_enum_freq_bands,
+};
+
+/* check if the device is present and register with v4l and usb if it is */
+static int usb_raremono_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct raremono_device *radio;
+ int retval = 0;
+
+ radio = devm_kzalloc(&intf->dev, sizeof(struct raremono_device), GFP_KERNEL);
+ if (radio)
+ radio->buffer = devm_kmalloc(&intf->dev, BUFFER_LENGTH, GFP_KERNEL);
+
+ if (!radio || !radio->buffer)
+ return -ENOMEM;
+
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+
+ /*
+ * This device uses the same USB IDs as the si470x SiLabs reference
+ * design. So do an additional check: attempt to read the device ID
+ * from the si470x: the lower 12 bits are 0x0242 for the si470x. The
+ * Raremono always returns 0x0800 (the meaning of that is unknown, but
+ * at least it works).
+ *
+ * We use this check to determine which device we are dealing with.
+ */
+ msleep(20);
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ 1, 2,
+ radio->buffer, 3, 500);
+ if (retval != 3 ||
+ (get_unaligned_be16(&radio->buffer[1]) & 0xfff) == 0x0242) {
+ dev_info(&intf->dev, "this is not Thanko's Raremono.\n");
+ return -ENODEV;
+ }
+
+ dev_info(&intf->dev, "Thanko's Raremono connected: (%04X:%04X)\n",
+ id->idVendor, id->idProduct);
+
+ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register v4l2_device\n");
+ return retval;
+ }
+
+ mutex_init(&radio->lock);
+
+ strlcpy(radio->vdev.name, radio->v4l2_dev.name,
+ sizeof(radio->vdev.name));
+ radio->vdev.v4l2_dev = &radio->v4l2_dev;
+ radio->vdev.fops = &usb_raremono_fops;
+ radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops;
+ radio->vdev.lock = &radio->lock;
+ radio->vdev.release = video_device_release_empty;
+
+ usb_set_intfdata(intf, &radio->v4l2_dev);
+
+ video_set_drvdata(&radio->vdev, radio);
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
+
+ raremono_cmd_main(radio, BAND_FM, 95160);
+
+ retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
+ if (retval == 0) {
+ dev_info(&intf->dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&radio->vdev));
+ return 0;
+ }
+ dev_err(&intf->dev, "could not register video device\n");
+ v4l2_device_unregister(&radio->v4l2_dev);
+ return retval;
+}
+
+/* USB subsystem interface */
+static struct usb_driver usb_raremono_driver = {
+ .name = "radio-raremono",
+ .probe = usb_raremono_probe,
+ .disconnect = usb_raremono_disconnect,
+ .id_table = usb_raremono_device_table,
+};
+
+module_usb_driver(usb_raremono_driver);
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index d6d4d60261d5..07ef40595efd 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -137,6 +137,8 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
/* interrupt out endpoint 2 every 1 millisecond */
#define UNUSED_REPORT 23
+#define MAX_REPORT_SIZE 64
+
/**************************************************************************
@@ -208,7 +210,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
*/
static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
{
- unsigned char *report = (unsigned char *) buf;
+ unsigned char *report = buf;
int retval;
retval = usb_control_msg(radio->usbdev,
@@ -231,7 +233,7 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
*/
static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
{
- unsigned char *report = (unsigned char *) buf;
+ unsigned char *report = buf;
int retval;
retval = usb_control_msg(radio->usbdev,
@@ -254,15 +256,14 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
*/
int si470x_get_register(struct si470x_device *radio, int regnr)
{
- unsigned char buf[REGISTER_REPORT_SIZE];
int retval;
- buf[0] = REGISTER_REPORT(regnr);
+ radio->usb_buf[0] = REGISTER_REPORT(regnr);
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+ retval = si470x_get_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE);
if (retval >= 0)
- radio->registers[regnr] = get_unaligned_be16(&buf[1]);
+ radio->registers[regnr] = get_unaligned_be16(&radio->usb_buf[1]);
return (retval < 0) ? -EINVAL : 0;
}
@@ -273,13 +274,12 @@ int si470x_get_register(struct si470x_device *radio, int regnr)
*/
int si470x_set_register(struct si470x_device *radio, int regnr)
{
- unsigned char buf[REGISTER_REPORT_SIZE];
int retval;
- buf[0] = REGISTER_REPORT(regnr);
- put_unaligned_be16(radio->registers[regnr], &buf[1]);
+ radio->usb_buf[0] = REGISTER_REPORT(regnr);
+ put_unaligned_be16(radio->registers[regnr], &radio->usb_buf[1]);
- retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+ retval = si470x_set_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE);
return (retval < 0) ? -EINVAL : 0;
}
@@ -295,18 +295,17 @@ int si470x_set_register(struct si470x_device *radio, int regnr)
*/
static int si470x_get_all_registers(struct si470x_device *radio)
{
- unsigned char buf[ENTIRE_REPORT_SIZE];
int retval;
unsigned char regnr;
- buf[0] = ENTIRE_REPORT;
+ radio->usb_buf[0] = ENTIRE_REPORT;
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+ retval = si470x_get_report(radio, radio->usb_buf, ENTIRE_REPORT_SIZE);
if (retval >= 0)
for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
radio->registers[regnr] = get_unaligned_be16(
- &buf[regnr * RADIO_REGISTER_SIZE + 1]);
+ &radio->usb_buf[regnr * RADIO_REGISTER_SIZE + 1]);
return (retval < 0) ? -EINVAL : 0;
}
@@ -323,14 +322,13 @@ static int si470x_get_all_registers(struct si470x_device *radio)
static int si470x_set_led_state(struct si470x_device *radio,
unsigned char led_state)
{
- unsigned char buf[LED_REPORT_SIZE];
int retval;
- buf[0] = LED_REPORT;
- buf[1] = LED_COMMAND;
- buf[2] = led_state;
+ radio->usb_buf[0] = LED_REPORT;
+ radio->usb_buf[1] = LED_COMMAND;
+ radio->usb_buf[2] = led_state;
- retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+ retval = si470x_set_report(radio, radio->usb_buf, LED_REPORT_SIZE);
return (retval < 0) ? -EINVAL : 0;
}
@@ -346,19 +344,18 @@ static int si470x_set_led_state(struct si470x_device *radio,
*/
static int si470x_get_scratch_page_versions(struct si470x_device *radio)
{
- unsigned char buf[SCRATCH_REPORT_SIZE];
int retval;
- buf[0] = SCRATCH_REPORT;
+ radio->usb_buf[0] = SCRATCH_REPORT;
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+ retval = si470x_get_report(radio, radio->usb_buf, SCRATCH_REPORT_SIZE);
if (retval < 0)
dev_warn(&radio->intf->dev, "si470x_get_scratch: "
"si470x_get_report returned %d\n", retval);
else {
- radio->software_version = buf[1];
- radio->hardware_version = buf[2];
+ radio->software_version = radio->usb_buf[1];
+ radio->hardware_version = radio->usb_buf[2];
}
return (retval < 0) ? -EINVAL : 0;
@@ -509,6 +506,7 @@ static void si470x_usb_release(struct v4l2_device *v4l2_dev)
v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio->int_in_buffer);
kfree(radio->buffer);
+ kfree(radio->usb_buf);
kfree(radio);
}
@@ -593,6 +591,11 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
retval = -ENOMEM;
goto err_initial;
}
+ radio->usb_buf = kmalloc(MAX_REPORT_SIZE, GFP_KERNEL);
+ if (radio->usb_buf == NULL) {
+ retval = -ENOMEM;
+ goto err_radio;
+ }
radio->usbdev = interface_to_usbdev(intf);
radio->intf = intf;
radio->band = 1; /* Default to 76 - 108 MHz */
@@ -612,7 +615,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
if (!radio->int_in_endpoint) {
dev_info(&intf->dev, "could not find interrupt in endpoint\n");
retval = -EIO;
- goto err_radio;
+ goto err_usbbuf;
}
int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);
@@ -621,7 +624,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
if (!radio->int_in_buffer) {
dev_info(&intf->dev, "could not allocate int_in_buffer");
retval = -ENOMEM;
- goto err_radio;
+ goto err_usbbuf;
}
radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -632,6 +635,30 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
}
radio->v4l2_dev.release = si470x_usb_release;
+
+ /*
+ * The si470x SiLabs reference design uses the same USB IDs as
+ * 'Thanko's Raremono' si4734 based receiver. So check here which we
+ * have: attempt to read the device ID from the si470x: the lower 12
+ * bits should be 0x0242 for the si470x.
+ *
+ * We use this check to determine which device we are dealing with.
+ */
+ if (id->idVendor == 0x10c4 && id->idProduct == 0x818a) {
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ 1, 2,
+ radio->usb_buf, 3, 500);
+ if (retval != 3 ||
+ (get_unaligned_be16(&radio->usb_buf[1]) & 0xfff) != 0x0242) {
+ dev_info(&intf->dev, "this is not a si470x device.\n");
+ retval = -ENODEV;
+ goto err_urb;
+ }
+ }
+
retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
if (retval < 0) {
dev_err(&intf->dev, "couldn't register v4l2_device\n");
@@ -743,6 +770,8 @@ err_urb:
usb_free_urb(radio->int_in_urb);
err_intbuffer:
kfree(radio->int_in_buffer);
+err_usbbuf:
+ kfree(radio->usb_buf);
err_radio:
kfree(radio);
err_initial:
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 467e95575488..4b7660470e2f 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -167,6 +167,7 @@ struct si470x_device {
/* reference to USB and video device */
struct usb_device *usbdev;
struct usb_interface *intf;
+ char *usb_buf;
/* Interrupt endpoint handling */
char *int_in_buffer;
diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig
new file mode 100644
index 000000000000..a7c3ba85d12b
--- /dev/null
+++ b/drivers/media/radio/si4713/Kconfig
@@ -0,0 +1,40 @@
+config USB_SI4713
+ tristate "Silicon Labs Si4713 FM Radio Transmitter support with USB"
+ depends on USB && RADIO_SI4713
+ select SI4713
+ ---help---
+ This is a driver for USB devices with the Silicon Labs SI4713
+ chip. Currently these devices are known to work.
+ - 10c4:8244: Silicon Labs FM Transmitter USB device.
+
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-usb-si4713.
+
+config PLATFORM_SI4713
+ tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C"
+ depends on I2C && RADIO_SI4713
+ select SI4713
+ ---help---
+ This is a driver for I2C devices with the Silicon Labs SI4713
+ chip.
+
+ Say Y here if you want to connect this type of radio to your
+ computer's I2C port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-platform-si4713.
+
+config I2C_SI4713
+ tristate "Silicon Labs Si4713 FM Radio Transmitter support"
+ depends on I2C && RADIO_SI4713
+ ---help---
+ Say Y here if you want support to Si4713 FM Radio Transmitter.
+ This device can transmit audio through FM. It can transmit
+ RDS and RBDS signals as well. This module is the v4l2 radio
+ interface for the i2c driver of this device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called si4713.
diff --git a/drivers/media/radio/si4713/Makefile b/drivers/media/radio/si4713/Makefile
new file mode 100644
index 000000000000..ddaaf925e883
--- /dev/null
+++ b/drivers/media/radio/si4713/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for radios with Silicon Labs Si4713 FM Radio Transmitters
+#
+
+obj-$(CONFIG_I2C_SI4713) += si4713.o
+obj-$(CONFIG_USB_SI4713) += radio-usb-si4713.o
+obj-$(CONFIG_PLATFORM_SI4713) += radio-platform-si4713.o
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/si4713/radio-platform-si4713.c
index ba4cfc946868..ba4cfc946868 100644
--- a/drivers/media/radio/radio-si4713.c
+++ b/drivers/media/radio/si4713/radio-platform-si4713.c
diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c
new file mode 100644
index 000000000000..779855b74bcd
--- /dev/null
+++ b/drivers/media/radio/si4713/radio-usb-si4713.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
+ * All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+/* V4l includes */
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/si4713.h>
+
+#include "si4713.h"
+
+/* driver and module definitions */
+MODULE_AUTHOR("Dinesh Ram <dinesh.ram@cern.ch>");
+MODULE_DESCRIPTION("Si4713 FM Transmitter USB driver");
+MODULE_LICENSE("GPL v2");
+
+/* The Device announces itself as Cygnal Integrated Products, Inc. */
+#define USB_SI4713_VENDOR 0x10c4
+#define USB_SI4713_PRODUCT 0x8244
+
+#define BUFFER_LENGTH 64
+#define USB_TIMEOUT 1000
+#define USB_RESP_TIMEOUT 50000
+
+/* USB Device ID List */
+static struct usb_device_id usb_si4713_usb_device_table[] = {
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_SI4713_VENDOR, USB_SI4713_PRODUCT,
+ USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_si4713_usb_device_table);
+
+struct si4713_usb_device {
+ struct usb_device *usbdev;
+ struct usb_interface *intf;
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev *v4l2_subdev;
+ struct mutex lock;
+ struct i2c_adapter i2c_adapter;
+
+ u8 *buffer;
+};
+
+static inline struct si4713_usb_device *to_si4713_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct si4713_usb_device, v4l2_dev);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct si4713_usb_device *radio = video_drvdata(file);
+
+ strlcpy(v->driver, "radio-usb-si4713", sizeof(v->driver));
+ strlcpy(v->card, "Si4713 FM Transmitter", sizeof(v->card));
+ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
+ v->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int vidioc_g_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *vm)
+{
+ struct si4713_usb_device *radio = video_drvdata(file);
+
+ return v4l2_subdev_call(radio->v4l2_subdev, tuner, g_modulator, vm);
+}
+
+static int vidioc_s_modulator(struct file *file, void *priv,
+ const struct v4l2_modulator *vm)
+{
+ struct si4713_usb_device *radio = video_drvdata(file);
+
+ return v4l2_subdev_call(radio->v4l2_subdev, tuner, s_modulator, vm);
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *vf)
+{
+ struct si4713_usb_device *radio = video_drvdata(file);
+
+ return v4l2_subdev_call(radio->v4l2_subdev, tuner, s_frequency, vf);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *vf)
+{
+ struct si4713_usb_device *radio = video_drvdata(file);
+
+ return v4l2_subdev_call(radio->v4l2_subdev, tuner, g_frequency, vf);
+}
+
+static const struct v4l2_ioctl_ops usb_si4713_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_modulator = vidioc_g_modulator,
+ .vidioc_s_modulator = vidioc_s_modulator,
+ .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,
+};
+
+/* File system interface */
+static const struct v4l2_file_operations usb_si4713_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static void usb_si4713_video_device_release(struct v4l2_device *v4l2_dev)
+{
+ struct si4713_usb_device *radio = to_si4713_dev(v4l2_dev);
+ struct i2c_adapter *adapter = &radio->i2c_adapter;
+
+ i2c_del_adapter(adapter);
+ v4l2_device_unregister(&radio->v4l2_dev);
+ kfree(radio->buffer);
+ kfree(radio);
+}
+
+/*
+ * This command sequence emulates the behaviour of the Windows driver.
+ * The structure of these commands was determined by sniffing the
+ * usb traffic of the device during startup.
+ * Most likely, these commands make some queries to the device.
+ * Commands are sent to enquire parameters like the bus mode,
+ * component revision, boot mode, the device serial number etc.
+ *
+ * These commands are necessary to be sent in this order during startup.
+ * The device fails to powerup if these commands are not sent.
+ *
+ * The complete list of startup commands is given in the start_seq table below.
+ */
+static int si4713_send_startup_command(struct si4713_usb_device *radio)
+{
+ unsigned long until_jiffies = jiffies + usecs_to_jiffies(USB_RESP_TIMEOUT) + 1;
+ u8 *buffer = radio->buffer;
+ int retval;
+
+ /* send the command */
+ retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ 0x09, 0x21, 0x033f, 0, radio->buffer,
+ BUFFER_LENGTH, USB_TIMEOUT);
+ if (retval < 0)
+ return retval;
+
+ for (;;) {
+ /* receive the response */
+ retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ 0x01, 0xa1, 0x033f, 0, radio->buffer,
+ BUFFER_LENGTH, USB_TIMEOUT);
+ if (retval < 0)
+ return retval;
+ if (!radio->buffer[1]) {
+ /* USB traffic sniffing showed that some commands require
+ * additional checks. */
+ switch (buffer[1]) {
+ case 0x32:
+ if (radio->buffer[2] == 0)
+ return 0;
+ break;
+ case 0x14:
+ case 0x12:
+ if (radio->buffer[2] & SI4713_CTS)
+ return 0;
+ break;
+ case 0x06:
+ if ((radio->buffer[2] & SI4713_CTS) && radio->buffer[9] == 0x08)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ }
+ if (time_is_before_jiffies(until_jiffies))
+ return -EIO;
+ msleep(3);
+ }
+
+ return retval;
+}
+
+struct si4713_start_seq_table {
+ int len;
+ u8 payload[8];
+};
+
+/*
+ * Some of the startup commands that could be recognized are :
+ * (0x03): Get serial number of the board (Response : CB000-00-00)
+ * (0x06, 0x03, 0x03, 0x08, 0x01, 0x0f) : Get Component revision
+ */
+static struct si4713_start_seq_table start_seq[] = {
+
+ { 1, { 0x03 } },
+ { 2, { 0x32, 0x7f } },
+ { 6, { 0x06, 0x03, 0x03, 0x08, 0x01, 0x0f } },
+ { 2, { 0x14, 0x02 } },
+ { 2, { 0x09, 0x90 } },
+ { 3, { 0x08, 0x90, 0xfa } },
+ { 2, { 0x36, 0x01 } },
+ { 2, { 0x05, 0x03 } },
+ { 7, { 0x06, 0x00, 0x06, 0x0e, 0x01, 0x0f, 0x05 } },
+ { 1, { 0x12 } },
+ /* Commands that are sent after pressing the 'Initialize'
+ button in the windows application */
+ { 1, { 0x03 } },
+ { 1, { 0x01 } },
+ { 2, { 0x09, 0x90 } },
+ { 3, { 0x08, 0x90, 0xfa } },
+ { 1, { 0x34 } },
+ { 2, { 0x35, 0x01 } },
+ { 2, { 0x36, 0x01 } },
+ { 2, { 0x30, 0x09 } },
+ { 4, { 0x30, 0x06, 0x00, 0xe2 } },
+ { 3, { 0x31, 0x01, 0x30 } },
+ { 3, { 0x31, 0x04, 0x09 } },
+ { 2, { 0x05, 0x02 } },
+ { 6, { 0x06, 0x03, 0x03, 0x08, 0x01, 0x0f } },
+};
+
+static int si4713_start_seq(struct si4713_usb_device *radio)
+{
+ int retval = 0;
+ int i;
+
+ radio->buffer[0] = 0x3f;
+
+ for (i = 0; i < ARRAY_SIZE(start_seq); i++) {
+ int len = start_seq[i].len;
+ u8 *payload = start_seq[i].payload;
+
+ memcpy(radio->buffer + 1, payload, len);
+ memset(radio->buffer + len + 1, 0, BUFFER_LENGTH - 1 - len);
+ retval = si4713_send_startup_command(radio);
+ }
+
+ return retval;
+}
+
+static struct i2c_board_info si4713_board_info = {
+ I2C_BOARD_INFO("si4713", SI4713_I2C_ADDR_BUSEN_HIGH),
+};
+
+struct si4713_command_table {
+ int command_id;
+ u8 payload[8];
+};
+
+/*
+ * Structure of a command :
+ * Byte 1 : 0x3f (always)
+ * Byte 2 : 0x06 (send a command)
+ * Byte 3 : Unknown
+ * Byte 4 : Number of arguments + 1 (for the command byte)
+ * Byte 5 : Number of response bytes
+ */
+static struct si4713_command_table command_table[] = {
+
+ { SI4713_CMD_POWER_UP, { 0x00, SI4713_PWUP_NARGS + 1, SI4713_PWUP_NRESP} },
+ { SI4713_CMD_GET_REV, { 0x03, 0x01, SI4713_GETREV_NRESP } },
+ { SI4713_CMD_POWER_DOWN, { 0x00, 0x01, SI4713_PWDN_NRESP} },
+ { SI4713_CMD_SET_PROPERTY, { 0x00, SI4713_SET_PROP_NARGS + 1, SI4713_SET_PROP_NRESP } },
+ { SI4713_CMD_GET_PROPERTY, { 0x00, SI4713_GET_PROP_NARGS + 1, SI4713_GET_PROP_NRESP } },
+ { SI4713_CMD_TX_TUNE_FREQ, { 0x03, SI4713_TXFREQ_NARGS + 1, SI4713_TXFREQ_NRESP } },
+ { SI4713_CMD_TX_TUNE_POWER, { 0x03, SI4713_TXPWR_NARGS + 1, SI4713_TXPWR_NRESP } },
+ { SI4713_CMD_TX_TUNE_MEASURE, { 0x03, SI4713_TXMEA_NARGS + 1, SI4713_TXMEA_NRESP } },
+ { SI4713_CMD_TX_TUNE_STATUS, { 0x00, SI4713_TXSTATUS_NARGS + 1, SI4713_TXSTATUS_NRESP } },
+ { SI4713_CMD_TX_ASQ_STATUS, { 0x03, SI4713_ASQSTATUS_NARGS + 1, SI4713_ASQSTATUS_NRESP } },
+ { SI4713_CMD_GET_INT_STATUS, { 0x03, 0x01, SI4713_GET_STATUS_NRESP } },
+ { SI4713_CMD_TX_RDS_BUFF, { 0x03, SI4713_RDSBUFF_NARGS + 1, SI4713_RDSBUFF_NRESP } },
+ { SI4713_CMD_TX_RDS_PS, { 0x00, SI4713_RDSPS_NARGS + 1, SI4713_RDSPS_NRESP } },
+};
+
+static int send_command(struct si4713_usb_device *radio, u8 *payload, char *data, int len)
+{
+ int retval;
+
+ radio->buffer[0] = 0x3f;
+ radio->buffer[1] = 0x06;
+
+ memcpy(radio->buffer + 2, payload, 3);
+ memcpy(radio->buffer + 5, data, len);
+ memset(radio->buffer + 5 + len, 0, BUFFER_LENGTH - 5 - len);
+
+ /* send the command */
+ retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ 0x09, 0x21, 0x033f, 0, radio->buffer,
+ BUFFER_LENGTH, USB_TIMEOUT);
+
+ return retval < 0 ? retval : 0;
+}
+
+static int si4713_i2c_read(struct si4713_usb_device *radio, char *data, int len)
+{
+ unsigned long until_jiffies = jiffies + usecs_to_jiffies(USB_RESP_TIMEOUT) + 1;
+ int retval;
+
+ /* receive the response */
+ for (;;) {
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ 0x01, 0xa1, 0x033f, 0, radio->buffer,
+ BUFFER_LENGTH, USB_TIMEOUT);
+ if (retval < 0)
+ return retval;
+
+ /*
+ * Check that we get a valid reply back (buffer[1] == 0) and
+ * that CTS is set before returning, otherwise we wait and try
+ * again. The i2c driver also does the CTS check, but the timeouts
+ * used there are much too small for this USB driver, so we wait
+ * for it here.
+ */
+ if (radio->buffer[1] == 0 && (radio->buffer[2] & SI4713_CTS)) {
+ memcpy(data, radio->buffer + 2, len);
+ return 0;
+ }
+ if (time_is_before_jiffies(until_jiffies)) {
+ /* Zero the status value, ensuring CTS isn't set */
+ data[0] = 0;
+ return 0;
+ }
+ msleep(3);
+ }
+}
+
+static int si4713_i2c_write(struct si4713_usb_device *radio, char *data, int len)
+{
+ int retval = -EINVAL;
+ int i;
+
+ if (len > BUFFER_LENGTH - 5)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(command_table); i++) {
+ if (data[0] == command_table[i].command_id)
+ retval = send_command(radio, command_table[i].payload,
+ data, len);
+ }
+
+ return retval < 0 ? retval : 0;
+}
+
+static int si4713_transfer(struct i2c_adapter *i2c_adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct si4713_usb_device *radio = i2c_get_adapdata(i2c_adapter);
+ int retval = -EINVAL;
+ int i;
+
+ if (num <= 0)
+ return 0;
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD)
+ retval = si4713_i2c_read(radio, msgs[i].buf, msgs[i].len);
+ else
+ retval = si4713_i2c_write(radio, msgs[i].buf, msgs[i].len);
+ if (retval)
+ break;
+ }
+
+ return retval ? retval : num;
+}
+
+static u32 si4713_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm si4713_algo = {
+ .master_xfer = si4713_transfer,
+ .functionality = si4713_functionality,
+};
+
+/* This name value shows up in the sysfs filename associated
+ with this I2C adapter */
+static struct i2c_adapter si4713_i2c_adapter_template = {
+ .name = "si4713-i2c",
+ .owner = THIS_MODULE,
+ .algo = &si4713_algo,
+};
+
+static int si4713_register_i2c_adapter(struct si4713_usb_device *radio)
+{
+ radio->i2c_adapter = si4713_i2c_adapter_template;
+ /* set up sysfs linkage to our parent device */
+ radio->i2c_adapter.dev.parent = &radio->usbdev->dev;
+ i2c_set_adapdata(&radio->i2c_adapter, radio);
+
+ return i2c_add_adapter(&radio->i2c_adapter);
+}
+
+/* check if the device is present and register with v4l and usb if it is */
+static int usb_si4713_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct si4713_usb_device *radio;
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd;
+ int retval = -ENOMEM;
+
+ dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n",
+ id->idVendor, id->idProduct);
+
+ /* Initialize local device structure */
+ radio = kzalloc(sizeof(struct si4713_usb_device), GFP_KERNEL);
+ if (radio)
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
+
+ if (!radio || !radio->buffer) {
+ dev_err(&intf->dev, "kmalloc for si4713_usb_device failed\n");
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ mutex_init(&radio->lock);
+
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+ usb_set_intfdata(intf, &radio->v4l2_dev);
+
+ retval = si4713_start_seq(radio);
+ if (retval < 0)
+ goto err_v4l2;
+
+ 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;
+ }
+
+ retval = si4713_register_i2c_adapter(radio);
+ if (retval < 0) {
+ dev_err(&intf->dev, "could not register i2c device\n");
+ goto err_i2cdev;
+ }
+
+ adapter = &radio->i2c_adapter;
+ sd = v4l2_i2c_new_subdev_board(&radio->v4l2_dev, adapter,
+ &si4713_board_info, NULL);
+ radio->v4l2_subdev = sd;
+ if (!sd) {
+ dev_err(&intf->dev, "cannot get v4l2 subdevice\n");
+ retval = -ENODEV;
+ goto del_adapter;
+ }
+
+ radio->vdev.ctrl_handler = sd->ctrl_handler;
+ radio->v4l2_dev.release = usb_si4713_video_device_release;
+ strlcpy(radio->vdev.name, radio->v4l2_dev.name,
+ sizeof(radio->vdev.name));
+ radio->vdev.v4l2_dev = &radio->v4l2_dev;
+ radio->vdev.fops = &usb_si4713_fops;
+ radio->vdev.ioctl_ops = &usb_si4713_ioctl_ops;
+ radio->vdev.lock = &radio->lock;
+ radio->vdev.release = video_device_release_empty;
+ radio->vdev.vfl_dir = VFL_DIR_TX;
+
+ video_set_drvdata(&radio->vdev, radio);
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
+
+ retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
+ if (retval < 0) {
+ dev_err(&intf->dev, "could not register video device\n");
+ goto del_adapter;
+ }
+
+ dev_info(&intf->dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&radio->vdev));
+
+ return 0;
+
+del_adapter:
+ i2c_del_adapter(adapter);
+err_i2cdev:
+ v4l2_device_unregister(&radio->v4l2_dev);
+err_v4l2:
+ kfree(radio->buffer);
+ kfree(radio);
+ return retval;
+}
+
+static void usb_si4713_disconnect(struct usb_interface *intf)
+{
+ struct si4713_usb_device *radio = to_si4713_dev(usb_get_intfdata(intf));
+
+ dev_info(&intf->dev, "Si4713 development board now disconnected\n");
+
+ mutex_lock(&radio->lock);
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(&radio->vdev);
+ v4l2_device_disconnect(&radio->v4l2_dev);
+ mutex_unlock(&radio->lock);
+ v4l2_device_put(&radio->v4l2_dev);
+}
+
+/* USB subsystem interface */
+static struct usb_driver usb_si4713_driver = {
+ .name = "radio-usb-si4713",
+ .probe = usb_si4713_probe,
+ .disconnect = usb_si4713_disconnect,
+ .id_table = usb_si4713_usb_device_table,
+};
+
+module_usb_driver(usb_si4713_driver);
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713/si4713.c
index 9ec48ccbcf0b..07d5153811e8 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713/si4713.c
@@ -27,13 +27,12 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
-#include "si4713-i2c.h"
+#include "si4713.h"
/* module parameters */
static int debug;
@@ -45,23 +44,18 @@ MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");
MODULE_DESCRIPTION("I2C driver for Si4713 FM Radio Transmitter");
MODULE_VERSION("0.0.1");
-static const char *si4713_supply_names[SI4713_NUM_SUPPLIES] = {
- "vio",
- "vdd",
-};
-
#define DEFAULT_RDS_PI 0x00
#define DEFAULT_RDS_PTY 0x00
#define DEFAULT_RDS_DEVIATION 0x00C8
#define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003
#define DEFAULT_LIMITER_RTIME 0x1392
#define DEFAULT_LIMITER_DEV 0x102CA
-#define DEFAULT_PILOT_FREQUENCY 0x4A38
+#define DEFAULT_PILOT_FREQUENCY 0x4A38
#define DEFAULT_PILOT_DEVIATION 0x1A5E
#define DEFAULT_ACOMP_ATIME 0x0000
#define DEFAULT_ACOMP_RTIME 0xF4240L
#define DEFAULT_ACOMP_GAIN 0x0F
-#define DEFAULT_ACOMP_THRESHOLD (-0x28)
+#define DEFAULT_ACOMP_THRESHOLD (-0x28)
#define DEFAULT_MUTE 0x01
#define DEFAULT_POWER_LEVEL 88
#define DEFAULT_FREQUENCY 8800
@@ -213,6 +207,7 @@ static int si4713_send_command(struct si4713_device *sdev, const u8 command,
u8 response[], const int respn, const int usecs)
{
struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd);
+ unsigned long until_jiffies;
u8 data1[MAX_ARGS + 1];
int err;
@@ -228,30 +223,42 @@ static int si4713_send_command(struct si4713_device *sdev, const u8 command,
if (err != argn + 1) {
v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n",
command);
- return (err > 0) ? -EIO : err;
+ return err < 0 ? err : -EIO;
}
+ until_jiffies = jiffies + usecs_to_jiffies(usecs) + 1;
+
/* Wait response from interrupt */
- if (!wait_for_completion_timeout(&sdev->work,
+ if (client->irq) {
+ if (!wait_for_completion_timeout(&sdev->work,
usecs_to_jiffies(usecs) + 1))
- v4l2_warn(&sdev->sd,
+ v4l2_warn(&sdev->sd,
"(%s) Device took too much time to answer.\n",
__func__);
-
- /* Then get the response */
- err = i2c_master_recv(client, response, respn);
- if (err != respn) {
- v4l2_err(&sdev->sd,
- "Error while reading response for command 0x%02x\n",
- command);
- return (err > 0) ? -EIO : err;
}
- DBG_BUFFER(&sdev->sd, "Response", response, respn);
- if (check_command_failed(response[0]))
- return -EBUSY;
+ do {
+ err = i2c_master_recv(client, response, respn);
+ if (err != respn) {
+ v4l2_err(&sdev->sd,
+ "Error %d while reading response for command 0x%02x\n",
+ err, command);
+ return err < 0 ? err : -EIO;
+ }
- return 0;
+ DBG_BUFFER(&sdev->sd, "Response", response, respn);
+ if (!check_command_failed(response[0]))
+ return 0;
+
+ if (client->irq)
+ return -EBUSY;
+ if (usecs <= 1000)
+ usleep_range(usecs, 1000);
+ else
+ usleep_range(1000, 2000);
+ } while (time_is_after_jiffies(until_jiffies));
+
+ return -EBUSY;
}
/*
@@ -265,9 +272,9 @@ static int si4713_read_property(struct si4713_device *sdev, u16 prop, u32 *pv)
int err;
u8 val[SI4713_GET_PROP_NRESP];
/*
- * .First byte = 0
- * .Second byte = property's MSB
- * .Third byte = property's LSB
+ * .First byte = 0
+ * .Second byte = property's MSB
+ * .Third byte = property's LSB
*/
const u8 args[SI4713_GET_PROP_NARGS] = {
0x00,
@@ -302,11 +309,11 @@ static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val)
int rval;
u8 resp[SI4713_SET_PROP_NRESP];
/*
- * .First byte = 0
- * .Second byte = property's MSB
- * .Third byte = property's LSB
- * .Fourth byte = value's MSB
- * .Fifth byte = value's LSB
+ * .First byte = 0
+ * .Second byte = property's MSB
+ * .Third byte = property's LSB
+ * .Fourth byte = value's MSB
+ * .Fifth byte = value's LSB
*/
const u8 args[SI4713_SET_PROP_NARGS] = {
0x00,
@@ -344,31 +351,36 @@ static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val)
*/
static int si4713_powerup(struct si4713_device *sdev)
{
+ struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd);
int err;
u8 resp[SI4713_PWUP_NRESP];
/*
- * .First byte = Enabled interrupts and boot function
- * .Second byte = Input operation mode
+ * .First byte = Enabled interrupts and boot function
+ * .Second byte = Input operation mode
*/
- const u8 args[SI4713_PWUP_NARGS] = {
- SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX,
+ u8 args[SI4713_PWUP_NARGS] = {
+ SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX,
SI4713_PWUP_OPMOD_ANALOG,
};
if (sdev->power_state)
return 0;
- err = regulator_bulk_enable(ARRAY_SIZE(sdev->supplies),
- sdev->supplies);
- if (err) {
- v4l2_err(&sdev->sd, "Failed to enable supplies: %d\n", err);
- return err;
+ if (sdev->supplies) {
+ err = regulator_bulk_enable(sdev->supplies, sdev->supply_data);
+ if (err) {
+ v4l2_err(&sdev->sd, "Failed to enable supplies: %d\n", err);
+ return err;
+ }
}
if (gpio_is_valid(sdev->gpio_reset)) {
udelay(50);
gpio_set_value(sdev->gpio_reset, 1);
}
+ if (client->irq)
+ args[0] |= SI4713_PWUP_CTSIEN;
+
err = si4713_send_command(sdev, SI4713_CMD_POWER_UP,
args, ARRAY_SIZE(args),
resp, ARRAY_SIZE(resp),
@@ -380,13 +392,15 @@ static int si4713_powerup(struct si4713_device *sdev)
v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n");
sdev->power_state = POWER_ON;
- err = si4713_write_property(sdev, SI4713_GPO_IEN,
+ if (client->irq)
+ err = si4713_write_property(sdev, SI4713_GPO_IEN,
SI4713_STC_INT | SI4713_CTS);
- } else {
- if (gpio_is_valid(sdev->gpio_reset))
- gpio_set_value(sdev->gpio_reset, 0);
- err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies),
- sdev->supplies);
+ return err;
+ }
+ if (gpio_is_valid(sdev->gpio_reset))
+ gpio_set_value(sdev->gpio_reset, 0);
+ if (sdev->supplies) {
+ err = regulator_bulk_disable(sdev->supplies, sdev->supply_data);
if (err)
v4l2_err(&sdev->sd,
"Failed to disable supplies: %d\n", err);
@@ -418,11 +432,13 @@ static int si4713_powerdown(struct si4713_device *sdev)
v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n");
if (gpio_is_valid(sdev->gpio_reset))
gpio_set_value(sdev->gpio_reset, 0);
- err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies),
- sdev->supplies);
- if (err)
- v4l2_err(&sdev->sd,
- "Failed to disable supplies: %d\n", err);
+ if (sdev->supplies) {
+ err = regulator_bulk_disable(sdev->supplies,
+ sdev->supply_data);
+ if (err)
+ v4l2_err(&sdev->sd,
+ "Failed to disable supplies: %d\n", err);
+ }
sdev->power_state = POWER_OFF;
}
@@ -451,7 +467,7 @@ static int si4713_checkrev(struct si4713_device *sdev)
v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
} else {
- v4l2_err(&sdev->sd, "Invalid product number\n");
+ v4l2_err(&sdev->sd, "Invalid product number 0x%X\n", resp[1]);
rval = -EINVAL;
}
return rval;
@@ -465,39 +481,45 @@ static int si4713_checkrev(struct si4713_device *sdev)
*/
static int si4713_wait_stc(struct si4713_device *sdev, const int usecs)
{
- int err;
+ struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd);
u8 resp[SI4713_GET_STATUS_NRESP];
+ unsigned long start_jiffies = jiffies;
+ int err;
- /* Wait response from STC interrupt */
- if (!wait_for_completion_timeout(&sdev->work,
- usecs_to_jiffies(usecs) + 1))
+ if (client->irq &&
+ !wait_for_completion_timeout(&sdev->work, usecs_to_jiffies(usecs) + 1))
v4l2_warn(&sdev->sd,
- "%s: device took too much time to answer (%d usec).\n",
- __func__, usecs);
-
- /* Clear status bits */
- err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS,
- NULL, 0,
- resp, ARRAY_SIZE(resp),
- DEFAULT_TIMEOUT);
-
- if (err < 0)
- goto exit;
-
- v4l2_dbg(1, debug, &sdev->sd,
- "%s: status bits: 0x%02x\n", __func__, resp[0]);
-
- if (!(resp[0] & SI4713_STC_INT))
- err = -EIO;
-
-exit:
- return err;
+ "(%s) Device took too much time to answer.\n", __func__);
+
+ for (;;) {
+ /* Clear status bits */
+ err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS,
+ NULL, 0,
+ resp, ARRAY_SIZE(resp),
+ DEFAULT_TIMEOUT);
+ /* The USB device returns errors when it waits for the
+ * STC bit to be set. Hence polling */
+ if (err >= 0) {
+ v4l2_dbg(1, debug, &sdev->sd,
+ "%s: status bits: 0x%02x\n", __func__, resp[0]);
+
+ if (resp[0] & SI4713_STC_INT)
+ return 0;
+ }
+ if (jiffies_to_usecs(jiffies - start_jiffies) > usecs)
+ return err < 0 ? err : -EIO;
+ /* We sleep here for 3-4 ms in order to avoid flooding the device
+ * with USB requests. The si4713 USB driver was developed
+ * by reverse engineering the Windows USB driver. The windows
+ * driver also has a ~2.5 ms delay between responses. */
+ usleep_range(3000, 4000);
+ }
}
/*
* si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning
- * frequency between 76 and 108 MHz in 10 kHz units and
- * steps of 50 kHz.
+ * frequency between 76 and 108 MHz in 10 kHz units and
+ * steps of 50 kHz.
* @sdev: si4713_device structure for the device we are communicating
* @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz)
*/
@@ -506,9 +528,9 @@ static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency)
int err;
u8 val[SI4713_TXFREQ_NRESP];
/*
- * .First byte = 0
- * .Second byte = frequency's MSB
- * .Third byte = frequency's LSB
+ * .First byte = 0
+ * .Second byte = frequency's MSB
+ * .Third byte = frequency's LSB
*/
const u8 args[SI4713_TXFREQ_NARGS] = {
0x00,
@@ -535,14 +557,14 @@ static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency)
}
/*
- * si4713_tx_tune_power - Sets the RF voltage level between 88 and 115 dBuV in
- * 1 dB units. A value of 0x00 indicates off. The command
- * also sets the antenna tuning capacitance. A value of 0
- * indicates autotuning, and a value of 1 - 191 indicates
- * a manual override, which results in a tuning
- * capacitance of 0.25 pF x @antcap.
+ * si4713_tx_tune_power - Sets the RF voltage level between 88 and 120 dBuV in
+ * 1 dB units. A value of 0x00 indicates off. The command
+ * also sets the antenna tuning capacitance. A value of 0
+ * indicates autotuning, and a value of 1 - 191 indicates
+ * a manual override, which results in a tuning
+ * capacitance of 0.25 pF x @antcap.
* @sdev: si4713_device structure for the device we are communicating
- * @power: tuning power (88 - 115 dBuV, unit/step 1 dB)
+ * @power: tuning power (88 - 120 dBuV, unit/step 1 dB)
* @antcap: value of antenna tuning capacitor (0 - 191)
*/
static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power,
@@ -551,21 +573,21 @@ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power,
int err;
u8 val[SI4713_TXPWR_NRESP];
/*
- * .First byte = 0
- * .Second byte = 0
- * .Third byte = power
- * .Fourth byte = antcap
+ * .First byte = 0
+ * .Second byte = 0
+ * .Third byte = power
+ * .Fourth byte = antcap
*/
- const u8 args[SI4713_TXPWR_NARGS] = {
+ u8 args[SI4713_TXPWR_NARGS] = {
0x00,
0x00,
power,
antcap,
};
- if (((power > 0) && (power < SI4713_MIN_POWER)) ||
- power > SI4713_MAX_POWER || antcap > SI4713_MAX_ANTCAP)
- return -EDOM;
+ /* Map power values 1-87 to MIN_POWER (88) */
+ if (power > 0 && power < SI4713_MIN_POWER)
+ args[2] = power = SI4713_MIN_POWER;
err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER,
args, ARRAY_SIZE(args), val,
@@ -583,12 +605,12 @@ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power,
/*
* si4713_tx_tune_measure - Enters receive mode and measures the received noise
- * level in units of dBuV on the selected frequency.
- * The Frequency must be between 76 and 108 MHz in 10 kHz
- * units and steps of 50 kHz. The command also sets the
- * antenna tuning capacitance. A value of 0 means
- * autotuning, and a value of 1 to 191 indicates manual
- * override.
+ * level in units of dBuV on the selected frequency.
+ * The Frequency must be between 76 and 108 MHz in 10 kHz
+ * units and steps of 50 kHz. The command also sets the
+ * antenna tuning capacitance. A value of 0 means
+ * autotuning, and a value of 1 to 191 indicates manual
+ * override.
* @sdev: si4713_device structure for the device we are communicating
* @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz)
* @antcap: value of antenna tuning capacitor (0 - 191)
@@ -599,10 +621,10 @@ static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency,
int err;
u8 val[SI4713_TXMEA_NRESP];
/*
- * .First byte = 0
- * .Second byte = frequency's MSB
- * .Third byte = frequency's LSB
- * .Fourth byte = antcap
+ * .First byte = 0
+ * .Second byte = frequency's MSB
+ * .Third byte = frequency's LSB
+ * .Fourth byte = antcap
*/
const u8 args[SI4713_TXMEA_NARGS] = {
0x00,
@@ -632,11 +654,11 @@ static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency,
/*
* si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or
- * tx_tune_power commands. This command return the current
- * frequency, output voltage in dBuV, the antenna tunning
- * capacitance value and the received noise level. The
- * command also clears the stcint interrupt bit when the
- * first bit of its arguments is high.
+ * tx_tune_power commands. This command return the current
+ * frequency, output voltage in dBuV, the antenna tunning
+ * capacitance value and the received noise level. The
+ * command also clears the stcint interrupt bit when the
+ * first bit of its arguments is high.
* @sdev: si4713_device structure for the device we are communicating
* @intack: 0x01 to clear the seek/tune complete interrupt status indicator.
* @frequency: returned frequency
@@ -651,7 +673,7 @@ static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack,
int err;
u8 val[SI4713_TXSTATUS_NRESP];
/*
- * .First byte = intack bit
+ * .First byte = intack bit
*/
const u8 args[SI4713_TXSTATUS_NARGS] = {
intack & SI4713_INTACK_MASK,
@@ -812,8 +834,9 @@ static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name)
return rval;
}
-static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt)
+static int si4713_set_rds_radio_text(struct si4713_device *sdev, const char *rt)
{
+ static const char cr[RDS_RADIOTEXT_BLK_SIZE] = { RDS_CARRIAGE_RETURN, 0 };
int rval = 0, i;
u16 t_index = 0;
u8 b_index = 0, cr_inserted = 0;
@@ -837,7 +860,7 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt)
for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) {
if (!rt[t_index + i] ||
rt[t_index + i] == RDS_CARRIAGE_RETURN) {
- rt[t_index + i] = RDS_CARRIAGE_RETURN;
+ rt = cr;
cr_inserted = 1;
break;
}
@@ -1024,7 +1047,6 @@ static int si4713_initialize(struct si4713_device *sdev)
if (rval < 0)
return rval;
-
sdev->frequency = DEFAULT_FREQUENCY;
sdev->stereo = 1;
sdev->tune_rnl = DEFAULT_TUNE_RNL;
@@ -1345,7 +1367,7 @@ static int si4713_probe(struct i2c_client *client,
struct v4l2_ctrl_handler *hdl;
int rval, i;
- sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
+ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
if (!sdev) {
dev_err(&client->dev, "Failed to alloc video device.\n");
rval = -ENOMEM;
@@ -1362,13 +1384,14 @@ static int si4713_probe(struct i2c_client *client,
}
sdev->gpio_reset = pdata->gpio_reset;
gpio_direction_output(sdev->gpio_reset, 0);
+ sdev->supplies = pdata->supplies;
}
- for (i = 0; i < ARRAY_SIZE(sdev->supplies); i++)
- sdev->supplies[i].supply = si4713_supply_names[i];
+ for (i = 0; i < sdev->supplies; i++)
+ sdev->supply_data[i].supply = pdata->supply_names[i];
- rval = regulator_bulk_get(&client->dev, ARRAY_SIZE(sdev->supplies),
- sdev->supplies);
+ rval = regulator_bulk_get(&client->dev, sdev->supplies,
+ sdev->supply_data);
if (rval) {
dev_err(&client->dev, "Cannot get regulators: %d\n", rval);
goto free_gpio;
@@ -1420,8 +1443,8 @@ static int si4713_probe(struct i2c_client *client,
V4L2_CID_AUDIO_COMPRESSION_GAIN, 0, MAX_ACOMP_GAIN, 1,
DEFAULT_ACOMP_GAIN);
sdev->compression_threshold = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
- V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, MIN_ACOMP_THRESHOLD,
- MAX_ACOMP_THRESHOLD, 1,
+ V4L2_CID_AUDIO_COMPRESSION_THRESHOLD,
+ MIN_ACOMP_THRESHOLD, MAX_ACOMP_THRESHOLD, 1,
DEFAULT_ACOMP_THRESHOLD);
sdev->compression_attack_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, 0,
@@ -1443,9 +1466,11 @@ static int si4713_probe(struct i2c_client *client,
V4L2_CID_TUNE_PREEMPHASIS,
V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS);
sdev->tune_pwr_level = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
- V4L2_CID_TUNE_POWER_LEVEL, 0, 120, 1, DEFAULT_POWER_LEVEL);
+ V4L2_CID_TUNE_POWER_LEVEL, 0, SI4713_MAX_POWER,
+ 1, DEFAULT_POWER_LEVEL);
sdev->tune_ant_cap = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
- V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, 191, 1, 0);
+ V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, SI4713_MAX_ANTCAP,
+ 1, 0);
if (hdl->error) {
rval = hdl->error;
@@ -1481,7 +1506,7 @@ free_irq:
free_ctrls:
v4l2_ctrl_handler_free(hdl);
put_reg:
- regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies);
+ regulator_bulk_free(sdev->supplies, sdev->supply_data);
free_gpio:
if (gpio_is_valid(sdev->gpio_reset))
gpio_free(sdev->gpio_reset);
@@ -1505,7 +1530,7 @@ static int si4713_remove(struct i2c_client *client)
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies);
+ regulator_bulk_free(sdev->supplies, sdev->supply_data);
if (gpio_is_valid(sdev->gpio_reset))
gpio_free(sdev->gpio_reset);
kfree(sdev);
diff --git a/drivers/media/radio/si4713-i2c.h b/drivers/media/radio/si4713/si4713.h
index 25cdea26343b..4837cf6e0e1b 100644
--- a/drivers/media/radio/si4713-i2c.h
+++ b/drivers/media/radio/si4713/si4713.h
@@ -15,6 +15,7 @@
#ifndef SI4713_I2C_H
#define SI4713_I2C_H
+#include <linux/regulator/consumer.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
#include <media/si4713.h>
@@ -226,7 +227,8 @@ struct si4713_device {
struct v4l2_ctrl *tune_ant_cap;
};
struct completion work;
- struct regulator_bulk_data supplies[SI4713_NUM_SUPPLIES];
+ unsigned supplies;
+ struct regulator_bulk_data supply_data[SI4713_NUM_SUPPLIES];
int gpio_reset;
u32 power_state;
u32 rds_enabled;
diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c
index cef06981b7c9..7c14060a40b8 100644
--- a/drivers/media/radio/tea575x.c
+++ b/drivers/media/radio/tea575x.c
@@ -20,12 +20,12 @@
*
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <asm/io.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
OpenPOWER on IntegriCloud