/drivers/media/radio/radio-wl1273.c
C | 2177 lines | 1636 code | 428 blank | 113 comment | 397 complexity | 4026bb3910fa59395bc4d966f0cbeb2b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
- /*
- * Driver for the Texas Instruments WL1273 FM radio.
- *
- * Copyright (C) 2011 Nokia Corporation
- * Author: Matti J. Aaltonen <matti.j.aaltonen@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <linux/delay.h>
- #include <linux/firmware.h>
- #include <linux/interrupt.h>
- #include <linux/mfd/wl1273-core.h>
- #include <linux/slab.h>
- #include <media/v4l2-common.h>
- #include <media/v4l2-ctrls.h>
- #include <media/v4l2-device.h>
- #include <media/v4l2-ioctl.h>
- #define DRIVER_DESC "Wl1273 FM Radio"
- #define WL1273_POWER_SET_OFF 0
- #define WL1273_POWER_SET_FM BIT(0)
- #define WL1273_POWER_SET_RDS BIT(1)
- #define WL1273_POWER_SET_RETENTION BIT(4)
- #define WL1273_PUPD_SET_OFF 0x00
- #define WL1273_PUPD_SET_ON 0x01
- #define WL1273_PUPD_SET_RETENTION 0x10
- #define WL1273_FREQ(x) (x * 10000 / 625)
- #define WL1273_INV_FREQ(x) (x * 625 / 10000)
- /*
- * static int radio_nr - The number of the radio device
- *
- * The default is 0.
- */
- static int radio_nr;
- module_param(radio_nr, int, 0);
- MODULE_PARM_DESC(radio_nr, "The number of the radio device. Default = 0");
- struct wl1273_device {
- char *bus_type;
- u8 forbidden;
- unsigned int preemphasis;
- unsigned int spacing;
- unsigned int tx_power;
- unsigned int rx_frequency;
- unsigned int tx_frequency;
- unsigned int rangelow;
- unsigned int rangehigh;
- unsigned int band;
- bool stereo;
- /* RDS */
- unsigned int rds_on;
- wait_queue_head_t read_queue;
- struct mutex lock; /* for serializing fm radio operations */
- struct completion busy;
- unsigned char *buffer;
- unsigned int buf_size;
- unsigned int rd_index;
- unsigned int wr_index;
- /* Selected interrupts */
- u16 irq_flags;
- u16 irq_received;
- struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_device v4l2dev;
- struct video_device videodev;
- struct device *dev;
- struct wl1273_core *core;
- struct file *owner;
- char *write_buf;
- unsigned int rds_users;
- };
- #define WL1273_IRQ_MASK (WL1273_FR_EVENT | \
- WL1273_POW_ENB_EVENT)
- /*
- * static unsigned int rds_buf - the number of RDS buffer blocks used.
- *
- * The default number is 100.
- */
- static unsigned int rds_buf = 100;
- module_param(rds_buf, uint, 0);
- MODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100");
- static int wl1273_fm_write_fw(struct wl1273_core *core,
- __u8 *fw, int len)
- {
- struct i2c_client *client = core->client;
- struct i2c_msg msg;
- int i, r = 0;
- msg.addr = client->addr;
- msg.flags = 0;
- for (i = 0; i <= len; i++) {
- msg.len = fw[0];
- msg.buf = fw + 1;
- fw += msg.len + 1;
- dev_dbg(&client->dev, "%s:len[%d]: %d\n", __func__, i, msg.len);
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r < 0 && i < len + 1)
- break;
- }
- dev_dbg(&client->dev, "%s: i: %d\n", __func__, i);
- dev_dbg(&client->dev, "%s: len + 1: %d\n", __func__, len + 1);
- /* Last transfer always fails. */
- if (i == len || r == 1)
- r = 0;
- return r;
- }
- #define WL1273_FIFO_HAS_DATA(status) (1 << 5 & status)
- #define WL1273_RDS_CORRECTABLE_ERROR (1 << 3)
- #define WL1273_RDS_UNCORRECTABLE_ERROR (1 << 4)
- static int wl1273_fm_rds(struct wl1273_device *radio)
- {
- struct wl1273_core *core = radio->core;
- struct i2c_client *client = core->client;
- u16 val;
- u8 b0 = WL1273_RDS_DATA_GET, status;
- struct v4l2_rds_data rds = { 0, 0, 0 };
- struct i2c_msg msg[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .buf = &b0,
- .len = 1,
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .buf = (u8 *) &rds,
- .len = sizeof(rds),
- }
- };
- int r;
- if (core->mode != WL1273_MODE_RX)
- return 0;
- r = core->read(core, WL1273_RDS_SYNC_GET, &val);
- if (r)
- return r;
- if ((val & 0x01) == 0) {
- /* RDS decoder not synchronized */
- return -EAGAIN;
- }
- /* copy all four RDS blocks to internal buffer */
- do {
- r = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
- if (r != ARRAY_SIZE(msg)) {
- dev_err(radio->dev, WL1273_FM_DRIVER_NAME
- ": %s: read_rds error r == %i)\n",
- __func__, r);
- }
- status = rds.block;
- if (!WL1273_FIFO_HAS_DATA(status))
- break;
- /* copy bits 0-2 (the block ID) to bits 3-5 */
- rds.block = V4L2_RDS_BLOCK_MSK & status;
- rds.block |= rds.block << 3;
- /* copy the error bits to standard positions */
- if (WL1273_RDS_UNCORRECTABLE_ERROR & status) {
- rds.block |= V4L2_RDS_BLOCK_ERROR;
- rds.block &= ~V4L2_RDS_BLOCK_CORRECTED;
- } else if (WL1273_RDS_CORRECTABLE_ERROR & status) {
- rds.block &= ~V4L2_RDS_BLOCK_ERROR;
- rds.block |= V4L2_RDS_BLOCK_CORRECTED;
- }
- /* copy RDS block to internal buffer */
- memcpy(&radio->buffer[radio->wr_index], &rds, RDS_BLOCK_SIZE);
- radio->wr_index += 3;
- /* wrap write pointer */
- if (radio->wr_index >= radio->buf_size)
- radio->wr_index = 0;
- /* check for overflow & start over */
- if (radio->wr_index == radio->rd_index) {
- dev_dbg(radio->dev, "RDS OVERFLOW");
- radio->rd_index = 0;
- radio->wr_index = 0;
- break;
- }
- } while (WL1273_FIFO_HAS_DATA(status));
- /* wake up read queue */
- if (radio->wr_index != radio->rd_index)
- wake_up_interruptible(&radio->read_queue);
- return 0;
- }
- static irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
- {
- struct wl1273_device *radio = dev_id;
- struct wl1273_core *core = radio->core;
- u16 flags;
- int r;
- r = core->read(core, WL1273_FLAG_GET, &flags);
- if (r)
- goto out;
- if (flags & WL1273_BL_EVENT) {
- radio->irq_received = flags;
- dev_dbg(radio->dev, "IRQ: BL\n");
- }
- if (flags & WL1273_RDS_EVENT) {
- msleep(200);
-