18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the Texas Instruments WL1273 FM radio.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Nokia Corporation
68c2ecf20Sopenharmony_ci * Author: Matti J. Aaltonen <matti.j.aaltonen@nokia.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/firmware.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/mfd/wl1273-core.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
168c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
178c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
188c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define DRIVER_DESC "Wl1273 FM Radio"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define WL1273_POWER_SET_OFF		0
238c2ecf20Sopenharmony_ci#define WL1273_POWER_SET_FM		BIT(0)
248c2ecf20Sopenharmony_ci#define WL1273_POWER_SET_RDS		BIT(1)
258c2ecf20Sopenharmony_ci#define WL1273_POWER_SET_RETENTION	BIT(4)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define WL1273_PUPD_SET_OFF		0x00
288c2ecf20Sopenharmony_ci#define WL1273_PUPD_SET_ON		0x01
298c2ecf20Sopenharmony_ci#define WL1273_PUPD_SET_RETENTION	0x10
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define WL1273_FREQ(x)		(x * 10000 / 625)
328c2ecf20Sopenharmony_ci#define WL1273_INV_FREQ(x)	(x * 625 / 10000)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * static int radio_nr - The number of the radio device
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * The default is 0.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic int radio_nr;
408c2ecf20Sopenharmony_cimodule_param(radio_nr, int, 0);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "The number of the radio device. Default = 0");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct wl1273_device {
448c2ecf20Sopenharmony_ci	char *bus_type;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	u8 forbidden;
478c2ecf20Sopenharmony_ci	unsigned int preemphasis;
488c2ecf20Sopenharmony_ci	unsigned int spacing;
498c2ecf20Sopenharmony_ci	unsigned int tx_power;
508c2ecf20Sopenharmony_ci	unsigned int rx_frequency;
518c2ecf20Sopenharmony_ci	unsigned int tx_frequency;
528c2ecf20Sopenharmony_ci	unsigned int rangelow;
538c2ecf20Sopenharmony_ci	unsigned int rangehigh;
548c2ecf20Sopenharmony_ci	unsigned int band;
558c2ecf20Sopenharmony_ci	bool stereo;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* RDS */
588c2ecf20Sopenharmony_ci	unsigned int rds_on;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	wait_queue_head_t read_queue;
618c2ecf20Sopenharmony_ci	struct mutex lock; /* for serializing fm radio operations */
628c2ecf20Sopenharmony_ci	struct completion busy;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	unsigned char *buffer;
658c2ecf20Sopenharmony_ci	unsigned int buf_size;
668c2ecf20Sopenharmony_ci	unsigned int rd_index;
678c2ecf20Sopenharmony_ci	unsigned int wr_index;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Selected interrupts */
708c2ecf20Sopenharmony_ci	u16 irq_flags;
718c2ecf20Sopenharmony_ci	u16 irq_received;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler ctrl_handler;
748c2ecf20Sopenharmony_ci	struct v4l2_device v4l2dev;
758c2ecf20Sopenharmony_ci	struct video_device videodev;
768c2ecf20Sopenharmony_ci	struct device *dev;
778c2ecf20Sopenharmony_ci	struct wl1273_core *core;
788c2ecf20Sopenharmony_ci	struct file *owner;
798c2ecf20Sopenharmony_ci	char *write_buf;
808c2ecf20Sopenharmony_ci	unsigned int rds_users;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define WL1273_IRQ_MASK	 (WL1273_FR_EVENT		|	\
848c2ecf20Sopenharmony_ci			  WL1273_POW_ENB_EVENT)
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * static unsigned int rds_buf - the number of RDS buffer blocks used.
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci * The default number is 100.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_cistatic unsigned int rds_buf = 100;
928c2ecf20Sopenharmony_cimodule_param(rds_buf, uint, 0);
938c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100");
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int wl1273_fm_write_fw(struct wl1273_core *core,
968c2ecf20Sopenharmony_ci			      __u8 *fw, int len)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct i2c_client *client = core->client;
998c2ecf20Sopenharmony_ci	struct i2c_msg msg;
1008c2ecf20Sopenharmony_ci	int i, r = 0;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	msg.addr = client->addr;
1038c2ecf20Sopenharmony_ci	msg.flags = 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for (i = 0; i <= len; i++) {
1068c2ecf20Sopenharmony_ci		msg.len = fw[0];
1078c2ecf20Sopenharmony_ci		msg.buf = fw + 1;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		fw += msg.len + 1;
1108c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s:len[%d]: %d\n", __func__, i, msg.len);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		r = i2c_transfer(client->adapter, &msg, 1);
1138c2ecf20Sopenharmony_ci		if (r < 0 && i < len + 1)
1148c2ecf20Sopenharmony_ci			break;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s: i: %d\n", __func__, i);
1188c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s: len + 1: %d\n", __func__, len + 1);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Last transfer always fails. */
1218c2ecf20Sopenharmony_ci	if (i == len || r == 1)
1228c2ecf20Sopenharmony_ci		r = 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return r;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#define WL1273_FIFO_HAS_DATA(status)	(1 << 5 & status)
1288c2ecf20Sopenharmony_ci#define WL1273_RDS_CORRECTABLE_ERROR	(1 << 3)
1298c2ecf20Sopenharmony_ci#define WL1273_RDS_UNCORRECTABLE_ERROR	(1 << 4)
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int wl1273_fm_rds(struct wl1273_device *radio)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
1348c2ecf20Sopenharmony_ci	struct i2c_client *client = core->client;
1358c2ecf20Sopenharmony_ci	u16 val;
1368c2ecf20Sopenharmony_ci	u8 b0 = WL1273_RDS_DATA_GET, status;
1378c2ecf20Sopenharmony_ci	struct v4l2_rds_data rds = { 0, 0, 0 };
1388c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
1398c2ecf20Sopenharmony_ci		{
1408c2ecf20Sopenharmony_ci			.addr = client->addr,
1418c2ecf20Sopenharmony_ci			.flags = 0,
1428c2ecf20Sopenharmony_ci			.buf = &b0,
1438c2ecf20Sopenharmony_ci			.len = 1,
1448c2ecf20Sopenharmony_ci		},
1458c2ecf20Sopenharmony_ci		{
1468c2ecf20Sopenharmony_ci			.addr = client->addr,
1478c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
1488c2ecf20Sopenharmony_ci			.buf = (u8 *) &rds,
1498c2ecf20Sopenharmony_ci			.len = sizeof(rds),
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	};
1528c2ecf20Sopenharmony_ci	int r;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (core->mode != WL1273_MODE_RX)
1558c2ecf20Sopenharmony_ci		return 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_RDS_SYNC_GET, &val);
1588c2ecf20Sopenharmony_ci	if (r)
1598c2ecf20Sopenharmony_ci		return r;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if ((val & 0x01) == 0) {
1628c2ecf20Sopenharmony_ci		/* RDS decoder not synchronized */
1638c2ecf20Sopenharmony_ci		return -EAGAIN;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* copy all four RDS blocks to internal buffer */
1678c2ecf20Sopenharmony_ci	do {
1688c2ecf20Sopenharmony_ci		r = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
1698c2ecf20Sopenharmony_ci		if (r != ARRAY_SIZE(msg)) {
1708c2ecf20Sopenharmony_ci			dev_err(radio->dev, WL1273_FM_DRIVER_NAME
1718c2ecf20Sopenharmony_ci				": %s: read_rds error r == %i)\n",
1728c2ecf20Sopenharmony_ci				__func__, r);
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		status = rds.block;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		if (!WL1273_FIFO_HAS_DATA(status))
1788c2ecf20Sopenharmony_ci			break;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		/* copy bits 0-2 (the block ID) to bits 3-5 */
1818c2ecf20Sopenharmony_ci		rds.block = V4L2_RDS_BLOCK_MSK & status;
1828c2ecf20Sopenharmony_ci		rds.block |= rds.block << 3;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		/* copy the error bits to standard positions */
1858c2ecf20Sopenharmony_ci		if (WL1273_RDS_UNCORRECTABLE_ERROR & status) {
1868c2ecf20Sopenharmony_ci			rds.block |= V4L2_RDS_BLOCK_ERROR;
1878c2ecf20Sopenharmony_ci			rds.block &= ~V4L2_RDS_BLOCK_CORRECTED;
1888c2ecf20Sopenharmony_ci		} else if  (WL1273_RDS_CORRECTABLE_ERROR & status) {
1898c2ecf20Sopenharmony_ci			rds.block &= ~V4L2_RDS_BLOCK_ERROR;
1908c2ecf20Sopenharmony_ci			rds.block |= V4L2_RDS_BLOCK_CORRECTED;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		/* copy RDS block to internal buffer */
1948c2ecf20Sopenharmony_ci		memcpy(&radio->buffer[radio->wr_index], &rds, RDS_BLOCK_SIZE);
1958c2ecf20Sopenharmony_ci		radio->wr_index += 3;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		/* wrap write pointer */
1988c2ecf20Sopenharmony_ci		if (radio->wr_index >= radio->buf_size)
1998c2ecf20Sopenharmony_ci			radio->wr_index = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		/* check for overflow & start over */
2028c2ecf20Sopenharmony_ci		if (radio->wr_index == radio->rd_index) {
2038c2ecf20Sopenharmony_ci			dev_dbg(radio->dev, "RDS OVERFLOW");
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			radio->rd_index = 0;
2068c2ecf20Sopenharmony_ci			radio->wr_index = 0;
2078c2ecf20Sopenharmony_ci			break;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci	} while (WL1273_FIFO_HAS_DATA(status));
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* wake up read queue */
2128c2ecf20Sopenharmony_ci	if (radio->wr_index != radio->rd_index)
2138c2ecf20Sopenharmony_ci		wake_up_interruptible(&radio->read_queue);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct wl1273_device *radio = dev_id;
2218c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
2228c2ecf20Sopenharmony_ci	u16 flags;
2238c2ecf20Sopenharmony_ci	int r;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_FLAG_GET, &flags);
2268c2ecf20Sopenharmony_ci	if (r)
2278c2ecf20Sopenharmony_ci		goto out;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (flags & WL1273_BL_EVENT) {
2308c2ecf20Sopenharmony_ci		radio->irq_received = flags;
2318c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: BL\n");
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (flags & WL1273_RDS_EVENT) {
2358c2ecf20Sopenharmony_ci		msleep(200);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		wl1273_fm_rds(radio);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (flags & WL1273_BBLK_EVENT)
2418c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: BBLK\n");
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (flags & WL1273_LSYNC_EVENT)
2448c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: LSYNC\n");
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (flags & WL1273_LEV_EVENT) {
2478c2ecf20Sopenharmony_ci		u16 level;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_RSSI_LVL_GET, &level);
2508c2ecf20Sopenharmony_ci		if (r)
2518c2ecf20Sopenharmony_ci			goto out;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		if (level > 14)
2548c2ecf20Sopenharmony_ci			dev_dbg(radio->dev, "IRQ: LEV: 0x%x04\n", level);
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (flags & WL1273_IFFR_EVENT)
2588c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: IFFR\n");
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (flags & WL1273_PI_EVENT)
2618c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: PI\n");
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (flags & WL1273_PD_EVENT)
2648c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: PD\n");
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (flags & WL1273_STIC_EVENT)
2678c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: STIC\n");
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (flags & WL1273_MAL_EVENT)
2708c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: MAL\n");
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (flags & WL1273_POW_ENB_EVENT) {
2738c2ecf20Sopenharmony_ci		complete(&radio->busy);
2748c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "NOT BUSY\n");
2758c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: POW_ENB\n");
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (flags & WL1273_SCAN_OVER_EVENT)
2798c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: SCAN_OVER\n");
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (flags & WL1273_ERROR_EVENT)
2828c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: ERROR\n");
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (flags & WL1273_FR_EVENT) {
2858c2ecf20Sopenharmony_ci		u16 freq;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "IRQ: FR:\n");
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		if (core->mode == WL1273_MODE_RX) {
2908c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_TUNER_MODE_SET,
2918c2ecf20Sopenharmony_ci					TUNER_MODE_STOP_SEARCH);
2928c2ecf20Sopenharmony_ci			if (r) {
2938c2ecf20Sopenharmony_ci				dev_err(radio->dev,
2948c2ecf20Sopenharmony_ci					"%s: TUNER_MODE_SET fails: %d\n",
2958c2ecf20Sopenharmony_ci					__func__, r);
2968c2ecf20Sopenharmony_ci				goto out;
2978c2ecf20Sopenharmony_ci			}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci			r = core->read(core, WL1273_FREQ_SET, &freq);
3008c2ecf20Sopenharmony_ci			if (r)
3018c2ecf20Sopenharmony_ci				goto out;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci			if (radio->band == WL1273_BAND_JAPAN)
3048c2ecf20Sopenharmony_ci				radio->rx_frequency = WL1273_BAND_JAPAN_LOW +
3058c2ecf20Sopenharmony_ci					freq * 50;
3068c2ecf20Sopenharmony_ci			else
3078c2ecf20Sopenharmony_ci				radio->rx_frequency = WL1273_BAND_OTHER_LOW +
3088c2ecf20Sopenharmony_ci					freq * 50;
3098c2ecf20Sopenharmony_ci			/*
3108c2ecf20Sopenharmony_ci			 *  The driver works better with this msleep,
3118c2ecf20Sopenharmony_ci			 *  the documentation doesn't mention it.
3128c2ecf20Sopenharmony_ci			 */
3138c2ecf20Sopenharmony_ci			usleep_range(10000, 15000);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci			dev_dbg(radio->dev, "%dkHz\n", radio->rx_frequency);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		} else {
3188c2ecf20Sopenharmony_ci			r = core->read(core, WL1273_CHANL_SET, &freq);
3198c2ecf20Sopenharmony_ci			if (r)
3208c2ecf20Sopenharmony_ci				goto out;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci			dev_dbg(radio->dev, "%dkHz\n", freq);
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "%s: NOT BUSY\n", __func__);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ciout:
3288c2ecf20Sopenharmony_ci	core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
3298c2ecf20Sopenharmony_ci	complete(&radio->busy);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
3378c2ecf20Sopenharmony_ci	int r = 0;
3388c2ecf20Sopenharmony_ci	unsigned long t;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (freq < WL1273_BAND_TX_LOW) {
3418c2ecf20Sopenharmony_ci		dev_err(radio->dev,
3428c2ecf20Sopenharmony_ci			"Frequency out of range: %d < %d\n", freq,
3438c2ecf20Sopenharmony_ci			WL1273_BAND_TX_LOW);
3448c2ecf20Sopenharmony_ci		return -ERANGE;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (freq > WL1273_BAND_TX_HIGH) {
3488c2ecf20Sopenharmony_ci		dev_err(radio->dev,
3498c2ecf20Sopenharmony_ci			"Frequency out of range: %d > %d\n", freq,
3508c2ecf20Sopenharmony_ci			WL1273_BAND_TX_HIGH);
3518c2ecf20Sopenharmony_ci		return -ERANGE;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 *  The driver works better with this sleep,
3568c2ecf20Sopenharmony_ci	 *  the documentation doesn't mention it.
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	usleep_range(5000, 10000);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: freq: %d kHz\n", __func__, freq);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* Set the current tx channel */
3638c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_CHANL_SET, freq / 10);
3648c2ecf20Sopenharmony_ci	if (r)
3658c2ecf20Sopenharmony_ci		return r;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	reinit_completion(&radio->busy);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* wait for the FR IRQ */
3708c2ecf20Sopenharmony_ci	t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
3718c2ecf20Sopenharmony_ci	if (!t)
3728c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "WL1273_CHANL_SET: %lu\n", t);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Enable the output power */
3778c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_POWER_ENB_SET, 1);
3788c2ecf20Sopenharmony_ci	if (r)
3798c2ecf20Sopenharmony_ci		return r;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	reinit_completion(&radio->busy);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* wait for the POWER_ENB IRQ */
3848c2ecf20Sopenharmony_ci	t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
3858c2ecf20Sopenharmony_ci	if (!t)
3868c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	radio->tx_frequency = freq;
3898c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "WL1273_POWER_ENB_SET: %lu\n", t);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return	0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int wl1273_fm_set_rx_freq(struct wl1273_device *radio, unsigned int freq)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
3978c2ecf20Sopenharmony_ci	int r, f;
3988c2ecf20Sopenharmony_ci	unsigned long t;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (freq < radio->rangelow) {
4018c2ecf20Sopenharmony_ci		dev_err(radio->dev,
4028c2ecf20Sopenharmony_ci			"Frequency out of range: %d < %d\n", freq,
4038c2ecf20Sopenharmony_ci			radio->rangelow);
4048c2ecf20Sopenharmony_ci		r = -ERANGE;
4058c2ecf20Sopenharmony_ci		goto err;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (freq > radio->rangehigh) {
4098c2ecf20Sopenharmony_ci		dev_err(radio->dev,
4108c2ecf20Sopenharmony_ci			"Frequency out of range: %d > %d\n", freq,
4118c2ecf20Sopenharmony_ci			radio->rangehigh);
4128c2ecf20Sopenharmony_ci		r = -ERANGE;
4138c2ecf20Sopenharmony_ci		goto err;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: %dkHz\n", __func__, freq);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (radio->band == WL1273_BAND_JAPAN)
4218c2ecf20Sopenharmony_ci		f = (freq - WL1273_BAND_JAPAN_LOW) / 50;
4228c2ecf20Sopenharmony_ci	else
4238c2ecf20Sopenharmony_ci		f = (freq - WL1273_BAND_OTHER_LOW) / 50;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_FREQ_SET, f);
4268c2ecf20Sopenharmony_ci	if (r) {
4278c2ecf20Sopenharmony_ci		dev_err(radio->dev, "FREQ_SET fails\n");
4288c2ecf20Sopenharmony_ci		goto err;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET);
4328c2ecf20Sopenharmony_ci	if (r) {
4338c2ecf20Sopenharmony_ci		dev_err(radio->dev, "TUNER_MODE_SET fails\n");
4348c2ecf20Sopenharmony_ci		goto err;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	reinit_completion(&radio->busy);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000));
4408c2ecf20Sopenharmony_ci	if (!t) {
4418c2ecf20Sopenharmony_ci		dev_err(radio->dev, "%s: TIMEOUT\n", __func__);
4428c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	radio->rd_index = 0;
4468c2ecf20Sopenharmony_ci	radio->wr_index = 0;
4478c2ecf20Sopenharmony_ci	radio->rx_frequency = freq;
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_cierr:
4508c2ecf20Sopenharmony_ci	return r;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int wl1273_fm_get_freq(struct wl1273_device *radio)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
4568c2ecf20Sopenharmony_ci	unsigned int freq;
4578c2ecf20Sopenharmony_ci	u16 f;
4588c2ecf20Sopenharmony_ci	int r;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_RX) {
4618c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_FREQ_SET, &f);
4628c2ecf20Sopenharmony_ci		if (r)
4638c2ecf20Sopenharmony_ci			return r;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "Freq get: 0x%04x\n", f);
4668c2ecf20Sopenharmony_ci		if (radio->band == WL1273_BAND_JAPAN)
4678c2ecf20Sopenharmony_ci			freq = WL1273_BAND_JAPAN_LOW + 50 * f;
4688c2ecf20Sopenharmony_ci		else
4698c2ecf20Sopenharmony_ci			freq = WL1273_BAND_OTHER_LOW + 50 * f;
4708c2ecf20Sopenharmony_ci	} else {
4718c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_CHANL_SET, &f);
4728c2ecf20Sopenharmony_ci		if (r)
4738c2ecf20Sopenharmony_ci			return r;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		freq = f * 10;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return freq;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci/**
4828c2ecf20Sopenharmony_ci * wl1273_fm_upload_firmware_patch() -	Upload the firmware.
4838c2ecf20Sopenharmony_ci * @radio:				A pointer to the device struct.
4848c2ecf20Sopenharmony_ci *
4858c2ecf20Sopenharmony_ci * The firmware file consists of arrays of bytes where the first byte
4868c2ecf20Sopenharmony_ci * gives the array length. The first byte in the file gives the
4878c2ecf20Sopenharmony_ci * number of these arrays.
4888c2ecf20Sopenharmony_ci */
4898c2ecf20Sopenharmony_cistatic int wl1273_fm_upload_firmware_patch(struct wl1273_device *radio)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
4928c2ecf20Sopenharmony_ci	unsigned int packet_num;
4938c2ecf20Sopenharmony_ci	const struct firmware *fw_p;
4948c2ecf20Sopenharmony_ci	const char *fw_name = "radio-wl1273-fw.bin";
4958c2ecf20Sopenharmony_ci	struct device *dev = radio->dev;
4968c2ecf20Sopenharmony_ci	__u8 *ptr;
4978c2ecf20Sopenharmony_ci	int r;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s:\n", __func__);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	/*
5028c2ecf20Sopenharmony_ci	 * Uploading the firmware patch is not always necessary,
5038c2ecf20Sopenharmony_ci	 * so we only print an info message.
5048c2ecf20Sopenharmony_ci	 */
5058c2ecf20Sopenharmony_ci	if (request_firmware(&fw_p, fw_name, dev)) {
5068c2ecf20Sopenharmony_ci		dev_info(dev, "%s - %s not found\n", __func__, fw_name);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		return 0;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	ptr = (__u8 *) fw_p->data;
5128c2ecf20Sopenharmony_ci	packet_num = ptr[0];
5138c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s: packets: %d\n", __func__, packet_num);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	r = wl1273_fm_write_fw(core, ptr + 1, packet_num);
5168c2ecf20Sopenharmony_ci	if (r) {
5178c2ecf20Sopenharmony_ci		dev_err(dev, "FW upload error: %d\n", r);
5188c2ecf20Sopenharmony_ci		goto out;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* ignore possible error here */
5228c2ecf20Sopenharmony_ci	core->write(core, WL1273_RESET, 0);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - download OK, r: %d\n", __func__, r);
5258c2ecf20Sopenharmony_ciout:
5268c2ecf20Sopenharmony_ci	release_firmware(fw_p);
5278c2ecf20Sopenharmony_ci	return r;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic int wl1273_fm_stop(struct wl1273_device *radio)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_RX) {
5358c2ecf20Sopenharmony_ci		int r = core->write(core, WL1273_POWER_SET,
5368c2ecf20Sopenharmony_ci				    WL1273_POWER_SET_OFF);
5378c2ecf20Sopenharmony_ci		if (r)
5388c2ecf20Sopenharmony_ci			dev_err(radio->dev, "%s: POWER_SET fails: %d\n",
5398c2ecf20Sopenharmony_ci				__func__, r);
5408c2ecf20Sopenharmony_ci	} else if (core->mode == WL1273_MODE_TX) {
5418c2ecf20Sopenharmony_ci		int r = core->write(core, WL1273_PUPD_SET,
5428c2ecf20Sopenharmony_ci				    WL1273_PUPD_SET_OFF);
5438c2ecf20Sopenharmony_ci		if (r)
5448c2ecf20Sopenharmony_ci			dev_err(radio->dev,
5458c2ecf20Sopenharmony_ci				"%s: PUPD_SET fails: %d\n", __func__, r);
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if (core->pdata->disable) {
5498c2ecf20Sopenharmony_ci		core->pdata->disable();
5508c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "Back to reset\n");
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic int wl1273_fm_start(struct wl1273_device *radio, int new_mode)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
5598c2ecf20Sopenharmony_ci	struct wl1273_fm_platform_data *pdata = core->pdata;
5608c2ecf20Sopenharmony_ci	struct device *dev = radio->dev;
5618c2ecf20Sopenharmony_ci	int r = -EINVAL;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (pdata->enable && core->mode == WL1273_MODE_OFF) {
5648c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "Out of reset\n");
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		pdata->enable();
5678c2ecf20Sopenharmony_ci		msleep(250);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (new_mode == WL1273_MODE_RX) {
5718c2ecf20Sopenharmony_ci		u16 val = WL1273_POWER_SET_FM;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		if (radio->rds_on)
5748c2ecf20Sopenharmony_ci			val |= WL1273_POWER_SET_RDS;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		/* If this fails try again */
5778c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_POWER_SET, val);
5788c2ecf20Sopenharmony_ci		if (r) {
5798c2ecf20Sopenharmony_ci			msleep(100);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_POWER_SET, val);
5828c2ecf20Sopenharmony_ci			if (r) {
5838c2ecf20Sopenharmony_ci				dev_err(dev, "%s: POWER_SET fails\n", __func__);
5848c2ecf20Sopenharmony_ci				goto fail;
5858c2ecf20Sopenharmony_ci			}
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		/* rds buffer configuration */
5898c2ecf20Sopenharmony_ci		radio->wr_index = 0;
5908c2ecf20Sopenharmony_ci		radio->rd_index = 0;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	} else if (new_mode == WL1273_MODE_TX) {
5938c2ecf20Sopenharmony_ci		/* If this fails try again once */
5948c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_ON);
5958c2ecf20Sopenharmony_ci		if (r) {
5968c2ecf20Sopenharmony_ci			msleep(100);
5978c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_PUPD_SET,
5988c2ecf20Sopenharmony_ci					WL1273_PUPD_SET_ON);
5998c2ecf20Sopenharmony_ci			if (r) {
6008c2ecf20Sopenharmony_ci				dev_err(dev, "%s: PUPD_SET fails\n", __func__);
6018c2ecf20Sopenharmony_ci				goto fail;
6028c2ecf20Sopenharmony_ci			}
6038c2ecf20Sopenharmony_ci		}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		if (radio->rds_on) {
6068c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_RDS_DATA_ENB, 1);
6078c2ecf20Sopenharmony_ci			if (r) {
6088c2ecf20Sopenharmony_ci				dev_err(dev, "%s: RDS_DATA_ENB ON fails\n",
6098c2ecf20Sopenharmony_ci					__func__);
6108c2ecf20Sopenharmony_ci				goto fail;
6118c2ecf20Sopenharmony_ci			}
6128c2ecf20Sopenharmony_ci		} else {
6138c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_RDS_DATA_ENB, 0);
6148c2ecf20Sopenharmony_ci			if (r) {
6158c2ecf20Sopenharmony_ci				dev_err(dev, "%s: RDS_DATA_ENB OFF fails\n",
6168c2ecf20Sopenharmony_ci					__func__);
6178c2ecf20Sopenharmony_ci				goto fail;
6188c2ecf20Sopenharmony_ci			}
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci	} else {
6218c2ecf20Sopenharmony_ci		dev_warn(dev, "%s: Illegal mode.\n", __func__);
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_OFF) {
6258c2ecf20Sopenharmony_ci		r = wl1273_fm_upload_firmware_patch(radio);
6268c2ecf20Sopenharmony_ci		if (r)
6278c2ecf20Sopenharmony_ci			dev_warn(dev, "Firmware upload failed.\n");
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		/*
6308c2ecf20Sopenharmony_ci		 * Sometimes the chip is in a wrong power state at this point.
6318c2ecf20Sopenharmony_ci		 * So we set the power once again.
6328c2ecf20Sopenharmony_ci		 */
6338c2ecf20Sopenharmony_ci		if (new_mode == WL1273_MODE_RX) {
6348c2ecf20Sopenharmony_ci			u16 val = WL1273_POWER_SET_FM;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci			if (radio->rds_on)
6378c2ecf20Sopenharmony_ci				val |= WL1273_POWER_SET_RDS;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_POWER_SET, val);
6408c2ecf20Sopenharmony_ci			if (r) {
6418c2ecf20Sopenharmony_ci				dev_err(dev, "%s: POWER_SET fails\n", __func__);
6428c2ecf20Sopenharmony_ci				goto fail;
6438c2ecf20Sopenharmony_ci			}
6448c2ecf20Sopenharmony_ci		} else if (new_mode == WL1273_MODE_TX) {
6458c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_PUPD_SET,
6468c2ecf20Sopenharmony_ci					WL1273_PUPD_SET_ON);
6478c2ecf20Sopenharmony_ci			if (r) {
6488c2ecf20Sopenharmony_ci				dev_err(dev, "%s: PUPD_SET fails\n", __func__);
6498c2ecf20Sopenharmony_ci				goto fail;
6508c2ecf20Sopenharmony_ci			}
6518c2ecf20Sopenharmony_ci		}
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	return 0;
6558c2ecf20Sopenharmony_cifail:
6568c2ecf20Sopenharmony_ci	if (pdata->disable)
6578c2ecf20Sopenharmony_ci		pdata->disable();
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s: return: %d\n", __func__, r);
6608c2ecf20Sopenharmony_ci	return r;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int wl1273_fm_suspend(struct wl1273_device *radio)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
6668c2ecf20Sopenharmony_ci	int r;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* Cannot go from OFF to SUSPENDED */
6698c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_RX)
6708c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_POWER_SET,
6718c2ecf20Sopenharmony_ci				WL1273_POWER_SET_RETENTION);
6728c2ecf20Sopenharmony_ci	else if (core->mode == WL1273_MODE_TX)
6738c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_PUPD_SET,
6748c2ecf20Sopenharmony_ci				WL1273_PUPD_SET_RETENTION);
6758c2ecf20Sopenharmony_ci	else
6768c2ecf20Sopenharmony_ci		r = -EINVAL;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (r) {
6798c2ecf20Sopenharmony_ci		dev_err(radio->dev, "%s: POWER_SET fails: %d\n", __func__, r);
6808c2ecf20Sopenharmony_ci		goto out;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ciout:
6848c2ecf20Sopenharmony_ci	return r;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic int wl1273_fm_set_mode(struct wl1273_device *radio, int mode)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
6908c2ecf20Sopenharmony_ci	struct device *dev = radio->dev;
6918c2ecf20Sopenharmony_ci	int old_mode;
6928c2ecf20Sopenharmony_ci	int r;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s\n", __func__);
6958c2ecf20Sopenharmony_ci	dev_dbg(dev, "Forbidden modes: 0x%02x\n", radio->forbidden);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	old_mode = core->mode;
6988c2ecf20Sopenharmony_ci	if (mode & radio->forbidden) {
6998c2ecf20Sopenharmony_ci		r = -EPERM;
7008c2ecf20Sopenharmony_ci		goto out;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	switch (mode) {
7048c2ecf20Sopenharmony_ci	case WL1273_MODE_RX:
7058c2ecf20Sopenharmony_ci	case WL1273_MODE_TX:
7068c2ecf20Sopenharmony_ci		r = wl1273_fm_start(radio, mode);
7078c2ecf20Sopenharmony_ci		if (r) {
7088c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Cannot start.\n", __func__);
7098c2ecf20Sopenharmony_ci			wl1273_fm_stop(radio);
7108c2ecf20Sopenharmony_ci			goto out;
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci		core->mode = mode;
7148c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
7158c2ecf20Sopenharmony_ci		if (r) {
7168c2ecf20Sopenharmony_ci			dev_err(dev, "INT_MASK_SET fails.\n");
7178c2ecf20Sopenharmony_ci			goto out;
7188c2ecf20Sopenharmony_ci		}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		/* remember previous settings */
7218c2ecf20Sopenharmony_ci		if (mode == WL1273_MODE_RX) {
7228c2ecf20Sopenharmony_ci			r = wl1273_fm_set_rx_freq(radio, radio->rx_frequency);
7238c2ecf20Sopenharmony_ci			if (r) {
7248c2ecf20Sopenharmony_ci				dev_err(dev, "set freq fails: %d.\n", r);
7258c2ecf20Sopenharmony_ci				goto out;
7268c2ecf20Sopenharmony_ci			}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci			r = core->set_volume(core, core->volume);
7298c2ecf20Sopenharmony_ci			if (r) {
7308c2ecf20Sopenharmony_ci				dev_err(dev, "set volume fails: %d.\n", r);
7318c2ecf20Sopenharmony_ci				goto out;
7328c2ecf20Sopenharmony_ci			}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s: Set vol: %d.\n", __func__,
7358c2ecf20Sopenharmony_ci				core->volume);
7368c2ecf20Sopenharmony_ci		} else {
7378c2ecf20Sopenharmony_ci			r = wl1273_fm_set_tx_freq(radio, radio->tx_frequency);
7388c2ecf20Sopenharmony_ci			if (r) {
7398c2ecf20Sopenharmony_ci				dev_err(dev, "set freq fails: %d.\n", r);
7408c2ecf20Sopenharmony_ci				goto out;
7418c2ecf20Sopenharmony_ci			}
7428c2ecf20Sopenharmony_ci		}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "%s: Set audio mode.\n", __func__);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci		r = core->set_audio(core, core->audio_mode);
7478c2ecf20Sopenharmony_ci		if (r)
7488c2ecf20Sopenharmony_ci			dev_err(dev, "Cannot set audio mode.\n");
7498c2ecf20Sopenharmony_ci		break;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	case WL1273_MODE_OFF:
7528c2ecf20Sopenharmony_ci		r = wl1273_fm_stop(radio);
7538c2ecf20Sopenharmony_ci		if (r)
7548c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Off fails: %d\n", __func__, r);
7558c2ecf20Sopenharmony_ci		else
7568c2ecf20Sopenharmony_ci			core->mode = WL1273_MODE_OFF;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		break;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	case WL1273_MODE_SUSPENDED:
7618c2ecf20Sopenharmony_ci		r = wl1273_fm_suspend(radio);
7628c2ecf20Sopenharmony_ci		if (r)
7638c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Suspend fails: %d\n", __func__, r);
7648c2ecf20Sopenharmony_ci		else
7658c2ecf20Sopenharmony_ci			core->mode = WL1273_MODE_SUSPENDED;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		break;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	default:
7708c2ecf20Sopenharmony_ci		dev_err(dev, "%s: Unknown mode: %d\n", __func__, mode);
7718c2ecf20Sopenharmony_ci		r = -EINVAL;
7728c2ecf20Sopenharmony_ci		break;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ciout:
7758c2ecf20Sopenharmony_ci	if (r)
7768c2ecf20Sopenharmony_ci		core->mode = old_mode;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	return r;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic int wl1273_fm_set_seek(struct wl1273_device *radio,
7828c2ecf20Sopenharmony_ci			      unsigned int wrap_around,
7838c2ecf20Sopenharmony_ci			      unsigned int seek_upward,
7848c2ecf20Sopenharmony_ci			      int level)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
7878c2ecf20Sopenharmony_ci	int r = 0;
7888c2ecf20Sopenharmony_ci	unsigned int dir = (seek_upward == 0) ? 0 : 1;
7898c2ecf20Sopenharmony_ci	unsigned int f;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	f = radio->rx_frequency;
7928c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "rx_frequency: %d\n", f);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (dir && f + radio->spacing <= radio->rangehigh)
7958c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rx_freq(radio, f + radio->spacing);
7968c2ecf20Sopenharmony_ci	else if (dir && wrap_around)
7978c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rx_freq(radio, radio->rangelow);
7988c2ecf20Sopenharmony_ci	else if (f - radio->spacing >= radio->rangelow)
7998c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rx_freq(radio, f - radio->spacing);
8008c2ecf20Sopenharmony_ci	else if (wrap_around)
8018c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rx_freq(radio, radio->rangehigh);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (r)
8048c2ecf20Sopenharmony_ci		goto out;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (level < SCHAR_MIN || level > SCHAR_MAX)
8078c2ecf20Sopenharmony_ci		return -EINVAL;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	reinit_completion(&radio->busy);
8108c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: BUSY\n", __func__);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
8138c2ecf20Sopenharmony_ci	if (r)
8148c2ecf20Sopenharmony_ci		goto out;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_SEARCH_LVL_SET, level);
8198c2ecf20Sopenharmony_ci	if (r)
8208c2ecf20Sopenharmony_ci		goto out;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_SEARCH_DIR_SET, dir);
8238c2ecf20Sopenharmony_ci	if (r)
8248c2ecf20Sopenharmony_ci		goto out;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK);
8278c2ecf20Sopenharmony_ci	if (r)
8288c2ecf20Sopenharmony_ci		goto out;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* wait for the FR IRQ */
8318c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000));
8328c2ecf20Sopenharmony_ci	if (!(radio->irq_received & WL1273_BL_EVENT)) {
8338c2ecf20Sopenharmony_ci		r = -ETIMEDOUT;
8348c2ecf20Sopenharmony_ci		goto out;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	radio->irq_received &= ~WL1273_BL_EVENT;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (!wrap_around)
8408c2ecf20Sopenharmony_ci		goto out;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	/* Wrap around */
8438c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "Wrap around in HW seek.\n");
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (seek_upward)
8468c2ecf20Sopenharmony_ci		f = radio->rangelow;
8478c2ecf20Sopenharmony_ci	else
8488c2ecf20Sopenharmony_ci		f = radio->rangehigh;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	r = wl1273_fm_set_rx_freq(radio, f);
8518c2ecf20Sopenharmony_ci	if (r)
8528c2ecf20Sopenharmony_ci		goto out;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	reinit_completion(&radio->busy);
8558c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: BUSY\n", __func__);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK);
8588c2ecf20Sopenharmony_ci	if (r)
8598c2ecf20Sopenharmony_ci		goto out;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/* wait for the FR IRQ */
8628c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000)))
8638c2ecf20Sopenharmony_ci		r = -ETIMEDOUT;
8648c2ecf20Sopenharmony_ciout:
8658c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: Err: %d\n", __func__, r);
8668c2ecf20Sopenharmony_ci	return r;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci/**
8708c2ecf20Sopenharmony_ci * wl1273_fm_get_tx_ctune() -	Get the TX tuning capacitor value.
8718c2ecf20Sopenharmony_ci * @radio:			A pointer to the device struct.
8728c2ecf20Sopenharmony_ci */
8738c2ecf20Sopenharmony_cistatic unsigned int wl1273_fm_get_tx_ctune(struct wl1273_device *radio)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
8768c2ecf20Sopenharmony_ci	struct device *dev = radio->dev;
8778c2ecf20Sopenharmony_ci	u16 val;
8788c2ecf20Sopenharmony_ci	int r;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_OFF ||
8818c2ecf20Sopenharmony_ci	    core->mode == WL1273_MODE_SUSPENDED)
8828c2ecf20Sopenharmony_ci		return -EPERM;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_READ_FMANT_TUNE_VALUE, &val);
8858c2ecf20Sopenharmony_ci	if (r) {
8868c2ecf20Sopenharmony_ci		dev_err(dev, "%s: read error: %d\n", __func__, r);
8878c2ecf20Sopenharmony_ci		goto out;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ciout:
8918c2ecf20Sopenharmony_ci	return val;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci/**
8958c2ecf20Sopenharmony_ci * wl1273_fm_set_preemphasis() - Set the TX pre-emphasis value.
8968c2ecf20Sopenharmony_ci * @radio:			 A pointer to the device struct.
8978c2ecf20Sopenharmony_ci * @preemphasis:		 The new pre-amphasis value.
8988c2ecf20Sopenharmony_ci *
8998c2ecf20Sopenharmony_ci * Possible pre-emphasis values are: V4L2_PREEMPHASIS_DISABLED,
9008c2ecf20Sopenharmony_ci * V4L2_PREEMPHASIS_50_uS and V4L2_PREEMPHASIS_75_uS.
9018c2ecf20Sopenharmony_ci */
9028c2ecf20Sopenharmony_cistatic int wl1273_fm_set_preemphasis(struct wl1273_device *radio,
9038c2ecf20Sopenharmony_ci				     unsigned int preemphasis)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
9068c2ecf20Sopenharmony_ci	int r;
9078c2ecf20Sopenharmony_ci	u16 em;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_OFF ||
9108c2ecf20Sopenharmony_ci	    core->mode == WL1273_MODE_SUSPENDED)
9118c2ecf20Sopenharmony_ci		return -EPERM;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	mutex_lock(&core->lock);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	switch (preemphasis) {
9168c2ecf20Sopenharmony_ci	case V4L2_PREEMPHASIS_DISABLED:
9178c2ecf20Sopenharmony_ci		em = 1;
9188c2ecf20Sopenharmony_ci		break;
9198c2ecf20Sopenharmony_ci	case V4L2_PREEMPHASIS_50_uS:
9208c2ecf20Sopenharmony_ci		em = 0;
9218c2ecf20Sopenharmony_ci		break;
9228c2ecf20Sopenharmony_ci	case V4L2_PREEMPHASIS_75_uS:
9238c2ecf20Sopenharmony_ci		em = 2;
9248c2ecf20Sopenharmony_ci		break;
9258c2ecf20Sopenharmony_ci	default:
9268c2ecf20Sopenharmony_ci		r = -EINVAL;
9278c2ecf20Sopenharmony_ci		goto out;
9288c2ecf20Sopenharmony_ci	}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_PREMPH_SET, em);
9318c2ecf20Sopenharmony_ci	if (r)
9328c2ecf20Sopenharmony_ci		goto out;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	radio->preemphasis = preemphasis;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ciout:
9378c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
9388c2ecf20Sopenharmony_ci	return r;
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic int wl1273_fm_rds_on(struct wl1273_device *radio)
9428c2ecf20Sopenharmony_ci{
9438c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
9448c2ecf20Sopenharmony_ci	int r;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
9478c2ecf20Sopenharmony_ci	if (radio->rds_on)
9488c2ecf20Sopenharmony_ci		return 0;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_POWER_SET,
9518c2ecf20Sopenharmony_ci			WL1273_POWER_SET_FM | WL1273_POWER_SET_RDS);
9528c2ecf20Sopenharmony_ci	if (r)
9538c2ecf20Sopenharmony_ci		goto out;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	r = wl1273_fm_set_rx_freq(radio, radio->rx_frequency);
9568c2ecf20Sopenharmony_ci	if (r)
9578c2ecf20Sopenharmony_ci		dev_err(radio->dev, "set freq fails: %d.\n", r);
9588c2ecf20Sopenharmony_ciout:
9598c2ecf20Sopenharmony_ci	return r;
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_cistatic int wl1273_fm_rds_off(struct wl1273_device *radio)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
9658c2ecf20Sopenharmony_ci	int r;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (!radio->rds_on)
9688c2ecf20Sopenharmony_ci		return 0;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	radio->irq_flags &= ~WL1273_RDS_EVENT;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags);
9738c2ecf20Sopenharmony_ci	if (r)
9748c2ecf20Sopenharmony_ci		goto out;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* Service pending read */
9778c2ecf20Sopenharmony_ci	wake_up_interruptible(&radio->read_queue);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_FM);
9828c2ecf20Sopenharmony_ci	if (r)
9838c2ecf20Sopenharmony_ci		goto out;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	r = wl1273_fm_set_rx_freq(radio, radio->rx_frequency);
9868c2ecf20Sopenharmony_ci	if (r)
9878c2ecf20Sopenharmony_ci		dev_err(radio->dev, "set freq fails: %d.\n", r);
9888c2ecf20Sopenharmony_ciout:
9898c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: exiting...\n", __func__);
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	return r;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic int wl1273_fm_set_rds(struct wl1273_device *radio, unsigned int new_mode)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	int r = 0;
9978c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_OFF ||
10008c2ecf20Sopenharmony_ci	    core->mode == WL1273_MODE_SUSPENDED)
10018c2ecf20Sopenharmony_ci		return -EPERM;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	if (new_mode == WL1273_RDS_RESET) {
10048c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_RDS_CNTRL_SET, 1);
10058c2ecf20Sopenharmony_ci		return r;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_OFF) {
10098c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_RDS_DATA_ENB, 0);
10108c2ecf20Sopenharmony_ci	} else if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_ON) {
10118c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_RDS_DATA_ENB, 1);
10128c2ecf20Sopenharmony_ci	} else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_OFF) {
10138c2ecf20Sopenharmony_ci		r = wl1273_fm_rds_off(radio);
10148c2ecf20Sopenharmony_ci	} else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_ON) {
10158c2ecf20Sopenharmony_ci		r = wl1273_fm_rds_on(radio);
10168c2ecf20Sopenharmony_ci	} else {
10178c2ecf20Sopenharmony_ci		dev_err(radio->dev, "%s: Unknown mode: %d\n",
10188c2ecf20Sopenharmony_ci			__func__, new_mode);
10198c2ecf20Sopenharmony_ci		r = -EINVAL;
10208c2ecf20Sopenharmony_ci	}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (!r)
10238c2ecf20Sopenharmony_ci		radio->rds_on = (new_mode == WL1273_RDS_ON) ? true : false;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return r;
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf,
10298c2ecf20Sopenharmony_ci				    size_t count, loff_t *ppos)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
10328c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
10338c2ecf20Sopenharmony_ci	u16 val;
10348c2ecf20Sopenharmony_ci	int r;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (core->mode != WL1273_MODE_TX)
10398c2ecf20Sopenharmony_ci		return count;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (radio->rds_users == 0) {
10428c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "%s: RDS not on.\n", __func__);
10438c2ecf20Sopenharmony_ci		return 0;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
10478c2ecf20Sopenharmony_ci		return -EINTR;
10488c2ecf20Sopenharmony_ci	/*
10498c2ecf20Sopenharmony_ci	 * Multiple processes can open the device, but only
10508c2ecf20Sopenharmony_ci	 * one gets to write to it.
10518c2ecf20Sopenharmony_ci	 */
10528c2ecf20Sopenharmony_ci	if (radio->owner && radio->owner != file) {
10538c2ecf20Sopenharmony_ci		r = -EBUSY;
10548c2ecf20Sopenharmony_ci		goto out;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci	radio->owner = file;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* Manual Mode */
10598c2ecf20Sopenharmony_ci	if (count > 255)
10608c2ecf20Sopenharmony_ci		val = 255;
10618c2ecf20Sopenharmony_ci	else
10628c2ecf20Sopenharmony_ci		val = count;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	core->write(core, WL1273_RDS_CONFIG_DATA_SET, val);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	if (copy_from_user(radio->write_buf + 1, buf, val)) {
10678c2ecf20Sopenharmony_ci		r = -EFAULT;
10688c2ecf20Sopenharmony_ci		goto out;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "Count: %d\n", val);
10728c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "From user: \"%s\"\n", radio->write_buf);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	radio->write_buf[0] = WL1273_RDS_DATA_SET;
10758c2ecf20Sopenharmony_ci	core->write_data(core, radio->write_buf, val + 1);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	r = val;
10788c2ecf20Sopenharmony_ciout:
10798c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	return r;
10828c2ecf20Sopenharmony_ci}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic __poll_t wl1273_fm_fops_poll(struct file *file,
10858c2ecf20Sopenharmony_ci					struct poll_table_struct *pts)
10868c2ecf20Sopenharmony_ci{
10878c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
10888c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	if (radio->owner && radio->owner != file)
10918c2ecf20Sopenharmony_ci		return EPOLLERR;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	radio->owner = file;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_RX) {
10968c2ecf20Sopenharmony_ci		poll_wait(file, &radio->read_queue, pts);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		if (radio->rd_index != radio->wr_index)
10998c2ecf20Sopenharmony_ci			return EPOLLIN | EPOLLRDNORM;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	} else if (core->mode == WL1273_MODE_TX) {
11028c2ecf20Sopenharmony_ci		return EPOLLOUT | EPOLLWRNORM;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	return 0;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int wl1273_fm_fops_open(struct file *file)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
11118c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
11128c2ecf20Sopenharmony_ci	int r = 0;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_RX && radio->rds_on &&
11178c2ecf20Sopenharmony_ci	    !radio->rds_users) {
11188c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "%s: Mode: %d\n", __func__, core->mode);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&core->lock))
11218c2ecf20Sopenharmony_ci			return -EINTR;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci		radio->irq_flags |= WL1273_RDS_EVENT;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_INT_MASK_SET,
11268c2ecf20Sopenharmony_ci				radio->irq_flags);
11278c2ecf20Sopenharmony_ci		if (r) {
11288c2ecf20Sopenharmony_ci			mutex_unlock(&core->lock);
11298c2ecf20Sopenharmony_ci			goto out;
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci		radio->rds_users++;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci		mutex_unlock(&core->lock);
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ciout:
11378c2ecf20Sopenharmony_ci	return r;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_cistatic int wl1273_fm_fops_release(struct file *file)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
11438c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
11448c2ecf20Sopenharmony_ci	int r = 0;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (radio->rds_users > 0) {
11498c2ecf20Sopenharmony_ci		radio->rds_users--;
11508c2ecf20Sopenharmony_ci		if (radio->rds_users == 0) {
11518c2ecf20Sopenharmony_ci			mutex_lock(&core->lock);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci			radio->irq_flags &= ~WL1273_RDS_EVENT;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci			if (core->mode == WL1273_MODE_RX) {
11568c2ecf20Sopenharmony_ci				r = core->write(core,
11578c2ecf20Sopenharmony_ci						WL1273_INT_MASK_SET,
11588c2ecf20Sopenharmony_ci						radio->irq_flags);
11598c2ecf20Sopenharmony_ci				if (r) {
11608c2ecf20Sopenharmony_ci					mutex_unlock(&core->lock);
11618c2ecf20Sopenharmony_ci					goto out;
11628c2ecf20Sopenharmony_ci				}
11638c2ecf20Sopenharmony_ci			}
11648c2ecf20Sopenharmony_ci			mutex_unlock(&core->lock);
11658c2ecf20Sopenharmony_ci		}
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (file == radio->owner)
11698c2ecf20Sopenharmony_ci		radio->owner = NULL;
11708c2ecf20Sopenharmony_ciout:
11718c2ecf20Sopenharmony_ci	return r;
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf,
11758c2ecf20Sopenharmony_ci				   size_t count, loff_t *ppos)
11768c2ecf20Sopenharmony_ci{
11778c2ecf20Sopenharmony_ci	int r = 0;
11788c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
11798c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
11808c2ecf20Sopenharmony_ci	unsigned int block_count = 0;
11818c2ecf20Sopenharmony_ci	u16 val;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	if (core->mode != WL1273_MODE_RX)
11868c2ecf20Sopenharmony_ci		return 0;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	if (radio->rds_users == 0) {
11898c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "%s: RDS not on.\n", __func__);
11908c2ecf20Sopenharmony_ci		return 0;
11918c2ecf20Sopenharmony_ci	}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
11948c2ecf20Sopenharmony_ci		return -EINTR;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	/*
11978c2ecf20Sopenharmony_ci	 * Multiple processes can open the device, but only
11988c2ecf20Sopenharmony_ci	 * one at a time gets read access.
11998c2ecf20Sopenharmony_ci	 */
12008c2ecf20Sopenharmony_ci	if (radio->owner && radio->owner != file) {
12018c2ecf20Sopenharmony_ci		r = -EBUSY;
12028c2ecf20Sopenharmony_ci		goto out;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci	radio->owner = file;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_RDS_SYNC_GET, &val);
12078c2ecf20Sopenharmony_ci	if (r) {
12088c2ecf20Sopenharmony_ci		dev_err(radio->dev, "%s: Get RDS_SYNC fails.\n", __func__);
12098c2ecf20Sopenharmony_ci		goto out;
12108c2ecf20Sopenharmony_ci	} else if (val == 0) {
12118c2ecf20Sopenharmony_ci		dev_info(radio->dev, "RDS_SYNC: Not synchronized\n");
12128c2ecf20Sopenharmony_ci		r = -ENODATA;
12138c2ecf20Sopenharmony_ci		goto out;
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/* block if no new data available */
12178c2ecf20Sopenharmony_ci	while (radio->wr_index == radio->rd_index) {
12188c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
12198c2ecf20Sopenharmony_ci			r = -EWOULDBLOCK;
12208c2ecf20Sopenharmony_ci			goto out;
12218c2ecf20Sopenharmony_ci		}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "%s: Wait for RDS data.\n", __func__);
12248c2ecf20Sopenharmony_ci		if (wait_event_interruptible(radio->read_queue,
12258c2ecf20Sopenharmony_ci					     radio->wr_index !=
12268c2ecf20Sopenharmony_ci					     radio->rd_index) < 0) {
12278c2ecf20Sopenharmony_ci			r = -EINTR;
12288c2ecf20Sopenharmony_ci			goto out;
12298c2ecf20Sopenharmony_ci		}
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/* calculate block count from byte count */
12338c2ecf20Sopenharmony_ci	count /= RDS_BLOCK_SIZE;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* copy RDS blocks from the internal buffer and to user buffer */
12368c2ecf20Sopenharmony_ci	while (block_count < count) {
12378c2ecf20Sopenharmony_ci		if (radio->rd_index == radio->wr_index)
12388c2ecf20Sopenharmony_ci			break;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		/* always transfer complete RDS blocks */
12418c2ecf20Sopenharmony_ci		if (copy_to_user(buf, &radio->buffer[radio->rd_index],
12428c2ecf20Sopenharmony_ci				 RDS_BLOCK_SIZE))
12438c2ecf20Sopenharmony_ci			break;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci		/* increment and wrap the read pointer */
12468c2ecf20Sopenharmony_ci		radio->rd_index += RDS_BLOCK_SIZE;
12478c2ecf20Sopenharmony_ci		if (radio->rd_index >= radio->buf_size)
12488c2ecf20Sopenharmony_ci			radio->rd_index = 0;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci		/* increment counters */
12518c2ecf20Sopenharmony_ci		block_count++;
12528c2ecf20Sopenharmony_ci		buf += RDS_BLOCK_SIZE;
12538c2ecf20Sopenharmony_ci		r += RDS_BLOCK_SIZE;
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ciout:
12578c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: exit\n", __func__);
12588c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	return r;
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations wl1273_fops = {
12648c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
12658c2ecf20Sopenharmony_ci	.read		= wl1273_fm_fops_read,
12668c2ecf20Sopenharmony_ci	.write		= wl1273_fm_fops_write,
12678c2ecf20Sopenharmony_ci	.poll		= wl1273_fm_fops_poll,
12688c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
12698c2ecf20Sopenharmony_ci	.open		= wl1273_fm_fops_open,
12708c2ecf20Sopenharmony_ci	.release	= wl1273_fm_fops_release,
12718c2ecf20Sopenharmony_ci};
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_querycap(struct file *file, void *priv,
12748c2ecf20Sopenharmony_ci				     struct v4l2_capability *capability)
12758c2ecf20Sopenharmony_ci{
12768c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	strscpy(capability->driver, WL1273_FM_DRIVER_NAME,
12818c2ecf20Sopenharmony_ci		sizeof(capability->driver));
12828c2ecf20Sopenharmony_ci	strscpy(capability->card, "TI Wl1273 FM Radio",
12838c2ecf20Sopenharmony_ci		sizeof(capability->card));
12848c2ecf20Sopenharmony_ci	strscpy(capability->bus_info, radio->bus_type,
12858c2ecf20Sopenharmony_ci		sizeof(capability->bus_info));
12868c2ecf20Sopenharmony_ci	return 0;
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_g_input(struct file *file, void *priv,
12908c2ecf20Sopenharmony_ci				    unsigned int *i)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	*i = 0;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	return 0;
12998c2ecf20Sopenharmony_ci}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_input(struct file *file, void *priv,
13028c2ecf20Sopenharmony_ci				    unsigned int i)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	if (i != 0)
13098c2ecf20Sopenharmony_ci		return -EINVAL;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	return 0;
13128c2ecf20Sopenharmony_ci}
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci/**
13158c2ecf20Sopenharmony_ci * wl1273_fm_set_tx_power() -	Set the transmission power value.
13168c2ecf20Sopenharmony_ci * @radio:			A pointer to the device struct.
13178c2ecf20Sopenharmony_ci * @power:			The new power value.
13188c2ecf20Sopenharmony_ci */
13198c2ecf20Sopenharmony_cistatic int wl1273_fm_set_tx_power(struct wl1273_device *radio, u16 power)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
13228c2ecf20Sopenharmony_ci	int r;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_OFF ||
13258c2ecf20Sopenharmony_ci	    core->mode == WL1273_MODE_SUSPENDED)
13268c2ecf20Sopenharmony_ci		return -EPERM;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	mutex_lock(&core->lock);
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	/* Convert the dBuV value to chip presentation */
13318c2ecf20Sopenharmony_ci	r = core->write(core, WL1273_POWER_LEV_SET, 122 - power);
13328c2ecf20Sopenharmony_ci	if (r)
13338c2ecf20Sopenharmony_ci		goto out;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	radio->tx_power = power;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ciout:
13388c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
13398c2ecf20Sopenharmony_ci	return r;
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci#define WL1273_SPACING_50kHz	1
13438c2ecf20Sopenharmony_ci#define WL1273_SPACING_100kHz	2
13448c2ecf20Sopenharmony_ci#define WL1273_SPACING_200kHz	4
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_cistatic int wl1273_fm_tx_set_spacing(struct wl1273_device *radio,
13478c2ecf20Sopenharmony_ci				    unsigned int spacing)
13488c2ecf20Sopenharmony_ci{
13498c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
13508c2ecf20Sopenharmony_ci	int r;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (spacing == 0) {
13538c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_SCAN_SPACING_SET,
13548c2ecf20Sopenharmony_ci				WL1273_SPACING_100kHz);
13558c2ecf20Sopenharmony_ci		radio->spacing = 100;
13568c2ecf20Sopenharmony_ci	} else if (spacing - 50000 < 25000) {
13578c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_SCAN_SPACING_SET,
13588c2ecf20Sopenharmony_ci				WL1273_SPACING_50kHz);
13598c2ecf20Sopenharmony_ci		radio->spacing = 50;
13608c2ecf20Sopenharmony_ci	} else if (spacing - 100000 < 50000) {
13618c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_SCAN_SPACING_SET,
13628c2ecf20Sopenharmony_ci				WL1273_SPACING_100kHz);
13638c2ecf20Sopenharmony_ci		radio->spacing = 100;
13648c2ecf20Sopenharmony_ci	} else {
13658c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_SCAN_SPACING_SET,
13668c2ecf20Sopenharmony_ci				WL1273_SPACING_200kHz);
13678c2ecf20Sopenharmony_ci		radio->spacing = 200;
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	return r;
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct wl1273_device *radio = ctrl->priv;
13768c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
13818c2ecf20Sopenharmony_ci		return -EINTR;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	switch (ctrl->id) {
13848c2ecf20Sopenharmony_ci	case  V4L2_CID_TUNE_ANTENNA_CAPACITOR:
13858c2ecf20Sopenharmony_ci		ctrl->val = wl1273_fm_get_tx_ctune(radio);
13868c2ecf20Sopenharmony_ci		break;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	default:
13898c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "%s: Unknown IOCTL: %d\n",
13908c2ecf20Sopenharmony_ci			 __func__, ctrl->id);
13918c2ecf20Sopenharmony_ci		break;
13928c2ecf20Sopenharmony_ci	}
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	return 0;
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci#define WL1273_MUTE_SOFT_ENABLE    (1 << 0)
14008c2ecf20Sopenharmony_ci#define WL1273_MUTE_AC             (1 << 1)
14018c2ecf20Sopenharmony_ci#define WL1273_MUTE_HARD_LEFT      (1 << 2)
14028c2ecf20Sopenharmony_ci#define WL1273_MUTE_HARD_RIGHT     (1 << 3)
14038c2ecf20Sopenharmony_ci#define WL1273_MUTE_SOFT_FORCE     (1 << 4)
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_cistatic inline struct wl1273_device *to_radio(struct v4l2_ctrl *ctrl)
14068c2ecf20Sopenharmony_ci{
14078c2ecf20Sopenharmony_ci	return container_of(ctrl->handler, struct wl1273_device, ctrl_handler);
14088c2ecf20Sopenharmony_ci}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_ctrl(struct v4l2_ctrl *ctrl)
14118c2ecf20Sopenharmony_ci{
14128c2ecf20Sopenharmony_ci	struct wl1273_device *radio = to_radio(ctrl);
14138c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
14148c2ecf20Sopenharmony_ci	int r = 0;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	switch (ctrl->id) {
14198c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
14208c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&core->lock))
14218c2ecf20Sopenharmony_ci			return -EINTR;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci		if (core->mode == WL1273_MODE_RX && ctrl->val)
14248c2ecf20Sopenharmony_ci			r = core->write(core,
14258c2ecf20Sopenharmony_ci					WL1273_MUTE_STATUS_SET,
14268c2ecf20Sopenharmony_ci					WL1273_MUTE_HARD_LEFT |
14278c2ecf20Sopenharmony_ci					WL1273_MUTE_HARD_RIGHT);
14288c2ecf20Sopenharmony_ci		else if (core->mode == WL1273_MODE_RX)
14298c2ecf20Sopenharmony_ci			r = core->write(core,
14308c2ecf20Sopenharmony_ci					WL1273_MUTE_STATUS_SET, 0x0);
14318c2ecf20Sopenharmony_ci		else if (core->mode == WL1273_MODE_TX && ctrl->val)
14328c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_MUTE, 1);
14338c2ecf20Sopenharmony_ci		else if (core->mode == WL1273_MODE_TX)
14348c2ecf20Sopenharmony_ci			r = core->write(core, WL1273_MUTE, 0);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci		mutex_unlock(&core->lock);
14378c2ecf20Sopenharmony_ci		break;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
14408c2ecf20Sopenharmony_ci		if (ctrl->val == 0)
14418c2ecf20Sopenharmony_ci			r = wl1273_fm_set_mode(radio, WL1273_MODE_OFF);
14428c2ecf20Sopenharmony_ci		else
14438c2ecf20Sopenharmony_ci			r =  core->set_volume(core, core->volume);
14448c2ecf20Sopenharmony_ci		break;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	case V4L2_CID_TUNE_PREEMPHASIS:
14478c2ecf20Sopenharmony_ci		r = wl1273_fm_set_preemphasis(radio, ctrl->val);
14488c2ecf20Sopenharmony_ci		break;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	case V4L2_CID_TUNE_POWER_LEVEL:
14518c2ecf20Sopenharmony_ci		r = wl1273_fm_set_tx_power(radio, ctrl->val);
14528c2ecf20Sopenharmony_ci		break;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	default:
14558c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "%s: Unknown IOCTL: %d\n",
14568c2ecf20Sopenharmony_ci			 __func__, ctrl->id);
14578c2ecf20Sopenharmony_ci		break;
14588c2ecf20Sopenharmony_ci	}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
14618c2ecf20Sopenharmony_ci	return r;
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_g_audio(struct file *file, void *priv,
14658c2ecf20Sopenharmony_ci				    struct v4l2_audio *audio)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (audio->index > 1)
14728c2ecf20Sopenharmony_ci		return -EINVAL;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	strscpy(audio->name, "Radio", sizeof(audio->name));
14758c2ecf20Sopenharmony_ci	audio->capability = V4L2_AUDCAP_STEREO;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	return 0;
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_audio(struct file *file, void *priv,
14818c2ecf20Sopenharmony_ci				    const struct v4l2_audio *audio)
14828c2ecf20Sopenharmony_ci{
14838c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	if (audio->index != 0)
14888c2ecf20Sopenharmony_ci		return -EINVAL;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	return 0;
14918c2ecf20Sopenharmony_ci}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci#define WL1273_RDS_NOT_SYNCHRONIZED 0
14948c2ecf20Sopenharmony_ci#define WL1273_RDS_SYNCHRONIZED 1
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv,
14978c2ecf20Sopenharmony_ci				    struct v4l2_tuner *tuner)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
15008c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
15018c2ecf20Sopenharmony_ci	u16 val;
15028c2ecf20Sopenharmony_ci	int r;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	if (tuner->index > 0)
15078c2ecf20Sopenharmony_ci		return -EINVAL;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	strscpy(tuner->name, WL1273_FM_DRIVER_NAME, sizeof(tuner->name));
15108c2ecf20Sopenharmony_ci	tuner->type = V4L2_TUNER_RADIO;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	tuner->rangelow	= WL1273_FREQ(WL1273_BAND_JAPAN_LOW);
15138c2ecf20Sopenharmony_ci	tuner->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH);
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS |
15168c2ecf20Sopenharmony_ci		V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO |
15178c2ecf20Sopenharmony_ci		V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (radio->stereo)
15208c2ecf20Sopenharmony_ci		tuner->audmode = V4L2_TUNER_MODE_STEREO;
15218c2ecf20Sopenharmony_ci	else
15228c2ecf20Sopenharmony_ci		tuner->audmode = V4L2_TUNER_MODE_MONO;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (core->mode != WL1273_MODE_RX)
15258c2ecf20Sopenharmony_ci		return 0;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
15288c2ecf20Sopenharmony_ci		return -EINTR;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_STEREO_GET, &val);
15318c2ecf20Sopenharmony_ci	if (r)
15328c2ecf20Sopenharmony_ci		goto out;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	if (val == 1)
15358c2ecf20Sopenharmony_ci		tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
15368c2ecf20Sopenharmony_ci	else
15378c2ecf20Sopenharmony_ci		tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_RSSI_LVL_GET, &val);
15408c2ecf20Sopenharmony_ci	if (r)
15418c2ecf20Sopenharmony_ci		goto out;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	tuner->signal = (s16) val;
15448c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "Signal: %d\n", tuner->signal);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	tuner->afc = 0;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_RDS_SYNC_GET, &val);
15498c2ecf20Sopenharmony_ci	if (r)
15508c2ecf20Sopenharmony_ci		goto out;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (val == WL1273_RDS_SYNCHRONIZED)
15538c2ecf20Sopenharmony_ci		tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
15548c2ecf20Sopenharmony_ciout:
15558c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	return r;
15588c2ecf20Sopenharmony_ci}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv,
15618c2ecf20Sopenharmony_ci				    const struct v4l2_tuner *tuner)
15628c2ecf20Sopenharmony_ci{
15638c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
15648c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
15658c2ecf20Sopenharmony_ci	int r = 0;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
15688c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "tuner->index: %d\n", tuner->index);
15698c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "tuner->name: %s\n", tuner->name);
15708c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "tuner->capability: 0x%04x\n", tuner->capability);
15718c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "tuner->rxsubchans: 0x%04x\n", tuner->rxsubchans);
15728c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "tuner->rangelow: %d\n", tuner->rangelow);
15738c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "tuner->rangehigh: %d\n", tuner->rangehigh);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	if (tuner->index > 0)
15768c2ecf20Sopenharmony_ci		return -EINVAL;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
15798c2ecf20Sopenharmony_ci		return -EINTR;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	r = wl1273_fm_set_mode(radio, WL1273_MODE_RX);
15828c2ecf20Sopenharmony_ci	if (r)
15838c2ecf20Sopenharmony_ci		goto out;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	if (tuner->rxsubchans & V4L2_TUNER_SUB_RDS)
15868c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rds(radio, WL1273_RDS_ON);
15878c2ecf20Sopenharmony_ci	else
15888c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF);
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	if (r)
15918c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "%s: RDS fails: %d\n", __func__, r);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
15948c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO);
15958c2ecf20Sopenharmony_ci		if (r < 0) {
15968c2ecf20Sopenharmony_ci			dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n",
15978c2ecf20Sopenharmony_ci				 __func__, r);
15988c2ecf20Sopenharmony_ci			goto out;
15998c2ecf20Sopenharmony_ci		}
16008c2ecf20Sopenharmony_ci		radio->stereo = false;
16018c2ecf20Sopenharmony_ci	} else if (tuner->audmode == V4L2_TUNER_MODE_STEREO) {
16028c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO);
16038c2ecf20Sopenharmony_ci		if (r < 0) {
16048c2ecf20Sopenharmony_ci			dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n",
16058c2ecf20Sopenharmony_ci				 __func__, r);
16068c2ecf20Sopenharmony_ci			goto out;
16078c2ecf20Sopenharmony_ci		}
16088c2ecf20Sopenharmony_ci		radio->stereo = true;
16098c2ecf20Sopenharmony_ci	} else {
16108c2ecf20Sopenharmony_ci		dev_err(radio->dev, "%s: tuner->audmode: %d\n",
16118c2ecf20Sopenharmony_ci			 __func__, tuner->audmode);
16128c2ecf20Sopenharmony_ci		r = -EINVAL;
16138c2ecf20Sopenharmony_ci		goto out;
16148c2ecf20Sopenharmony_ci	}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ciout:
16178c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	return r;
16208c2ecf20Sopenharmony_ci}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_g_frequency(struct file *file, void *priv,
16238c2ecf20Sopenharmony_ci					struct v4l2_frequency *freq)
16248c2ecf20Sopenharmony_ci{
16258c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
16268c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
16318c2ecf20Sopenharmony_ci		return -EINTR;
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	freq->type = V4L2_TUNER_RADIO;
16348c2ecf20Sopenharmony_ci	freq->frequency = WL1273_FREQ(wl1273_fm_get_freq(radio));
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	return 0;
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_frequency(struct file *file, void *priv,
16428c2ecf20Sopenharmony_ci					const struct v4l2_frequency *freq)
16438c2ecf20Sopenharmony_ci{
16448c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
16458c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
16468c2ecf20Sopenharmony_ci	int r;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s: %d\n", __func__, freq->frequency);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	if (freq->type != V4L2_TUNER_RADIO) {
16518c2ecf20Sopenharmony_ci		dev_dbg(radio->dev,
16528c2ecf20Sopenharmony_ci			"freq->type != V4L2_TUNER_RADIO: %d\n", freq->type);
16538c2ecf20Sopenharmony_ci		return -EINVAL;
16548c2ecf20Sopenharmony_ci	}
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
16578c2ecf20Sopenharmony_ci		return -EINTR;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_RX) {
16608c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "freq: %d\n", freq->frequency);
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rx_freq(radio,
16638c2ecf20Sopenharmony_ci					  WL1273_INV_FREQ(freq->frequency));
16648c2ecf20Sopenharmony_ci		if (r)
16658c2ecf20Sopenharmony_ci			dev_warn(radio->dev, WL1273_FM_DRIVER_NAME
16668c2ecf20Sopenharmony_ci				 ": set frequency failed with %d\n", r);
16678c2ecf20Sopenharmony_ci	} else {
16688c2ecf20Sopenharmony_ci		r = wl1273_fm_set_tx_freq(radio,
16698c2ecf20Sopenharmony_ci					  WL1273_INV_FREQ(freq->frequency));
16708c2ecf20Sopenharmony_ci		if (r)
16718c2ecf20Sopenharmony_ci			dev_warn(radio->dev, WL1273_FM_DRIVER_NAME
16728c2ecf20Sopenharmony_ci				 ": set frequency failed with %d\n", r);
16738c2ecf20Sopenharmony_ci	}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "wl1273_vidioc_s_frequency: DONE\n");
16788c2ecf20Sopenharmony_ci	return r;
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci#define WL1273_DEFAULT_SEEK_LEVEL	7
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv,
16848c2ecf20Sopenharmony_ci					   const struct v4l2_hw_freq_seek *seek)
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
16878c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
16888c2ecf20Sopenharmony_ci	int r;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	if (seek->tuner != 0 || seek->type != V4L2_TUNER_RADIO)
16938c2ecf20Sopenharmony_ci		return -EINVAL;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	if (file->f_flags & O_NONBLOCK)
16968c2ecf20Sopenharmony_ci		return -EWOULDBLOCK;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
16998c2ecf20Sopenharmony_ci		return -EINTR;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	r = wl1273_fm_set_mode(radio, WL1273_MODE_RX);
17028c2ecf20Sopenharmony_ci	if (r)
17038c2ecf20Sopenharmony_ci		goto out;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	r = wl1273_fm_tx_set_spacing(radio, seek->spacing);
17068c2ecf20Sopenharmony_ci	if (r)
17078c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "HW seek failed: %d\n", r);
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	r = wl1273_fm_set_seek(radio, seek->wrap_around, seek->seek_upward,
17108c2ecf20Sopenharmony_ci			       WL1273_DEFAULT_SEEK_LEVEL);
17118c2ecf20Sopenharmony_ci	if (r)
17128c2ecf20Sopenharmony_ci		dev_warn(radio->dev, "HW seek failed: %d\n", r);
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ciout:
17158c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
17168c2ecf20Sopenharmony_ci	return r;
17178c2ecf20Sopenharmony_ci}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_s_modulator(struct file *file, void *priv,
17208c2ecf20Sopenharmony_ci					const struct v4l2_modulator *modulator)
17218c2ecf20Sopenharmony_ci{
17228c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
17238c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
17248c2ecf20Sopenharmony_ci	int r = 0;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	if (modulator->index > 0)
17298c2ecf20Sopenharmony_ci		return -EINVAL;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
17328c2ecf20Sopenharmony_ci		return -EINTR;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	r = wl1273_fm_set_mode(radio, WL1273_MODE_TX);
17358c2ecf20Sopenharmony_ci	if (r)
17368c2ecf20Sopenharmony_ci		goto out;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	if (modulator->txsubchans & V4L2_TUNER_SUB_RDS)
17398c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rds(radio, WL1273_RDS_ON);
17408c2ecf20Sopenharmony_ci	else
17418c2ecf20Sopenharmony_ci		r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (modulator->txsubchans & V4L2_TUNER_SUB_MONO)
17448c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO);
17458c2ecf20Sopenharmony_ci	else
17468c2ecf20Sopenharmony_ci		r = core->write(core, WL1273_MONO_SET,
17478c2ecf20Sopenharmony_ci				WL1273_RX_STEREO);
17488c2ecf20Sopenharmony_ci	if (r < 0)
17498c2ecf20Sopenharmony_ci		dev_warn(radio->dev, WL1273_FM_DRIVER_NAME
17508c2ecf20Sopenharmony_ci			 "MONO_SET fails: %d\n", r);
17518c2ecf20Sopenharmony_ciout:
17528c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	return r;
17558c2ecf20Sopenharmony_ci}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_g_modulator(struct file *file, void *priv,
17588c2ecf20Sopenharmony_ci					struct v4l2_modulator *modulator)
17598c2ecf20Sopenharmony_ci{
17608c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
17618c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
17628c2ecf20Sopenharmony_ci	u16 val;
17638c2ecf20Sopenharmony_ci	int r;
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	dev_dbg(radio->dev, "%s\n", __func__);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	strscpy(modulator->name, WL1273_FM_DRIVER_NAME,
17688c2ecf20Sopenharmony_ci		sizeof(modulator->name));
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	modulator->rangelow = WL1273_FREQ(WL1273_BAND_JAPAN_LOW);
17718c2ecf20Sopenharmony_ci	modulator->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH);
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	modulator->capability =  V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS |
17748c2ecf20Sopenharmony_ci		V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	if (core->mode != WL1273_MODE_TX)
17778c2ecf20Sopenharmony_ci		return 0;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&core->lock))
17808c2ecf20Sopenharmony_ci		return -EINTR;
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_MONO_SET, &val);
17838c2ecf20Sopenharmony_ci	if (r)
17848c2ecf20Sopenharmony_ci		goto out;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	if (val == WL1273_TX_STEREO)
17878c2ecf20Sopenharmony_ci		modulator->txsubchans = V4L2_TUNER_SUB_STEREO;
17888c2ecf20Sopenharmony_ci	else
17898c2ecf20Sopenharmony_ci		modulator->txsubchans = V4L2_TUNER_SUB_MONO;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	if (radio->rds_on)
17928c2ecf20Sopenharmony_ci		modulator->txsubchans |= V4L2_TUNER_SUB_RDS;
17938c2ecf20Sopenharmony_ciout:
17948c2ecf20Sopenharmony_ci	mutex_unlock(&core->lock);
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	return 0;
17978c2ecf20Sopenharmony_ci}
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_cistatic int wl1273_fm_vidioc_log_status(struct file *file, void *priv)
18008c2ecf20Sopenharmony_ci{
18018c2ecf20Sopenharmony_ci	struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
18028c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
18038c2ecf20Sopenharmony_ci	struct device *dev = radio->dev;
18048c2ecf20Sopenharmony_ci	u16 val;
18058c2ecf20Sopenharmony_ci	int r;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	dev_info(dev, DRIVER_DESC);
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_OFF) {
18108c2ecf20Sopenharmony_ci		dev_info(dev, "Mode: Off\n");
18118c2ecf20Sopenharmony_ci		return 0;
18128c2ecf20Sopenharmony_ci	}
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_SUSPENDED) {
18158c2ecf20Sopenharmony_ci		dev_info(dev, "Mode: Suspended\n");
18168c2ecf20Sopenharmony_ci		return 0;
18178c2ecf20Sopenharmony_ci	}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_ASIC_ID_GET, &val);
18208c2ecf20Sopenharmony_ci	if (r)
18218c2ecf20Sopenharmony_ci		dev_err(dev, "%s: Get ASIC_ID fails.\n", __func__);
18228c2ecf20Sopenharmony_ci	else
18238c2ecf20Sopenharmony_ci		dev_info(dev, "ASIC_ID: 0x%04x\n", val);
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_ASIC_VER_GET, &val);
18268c2ecf20Sopenharmony_ci	if (r)
18278c2ecf20Sopenharmony_ci		dev_err(dev, "%s: Get ASIC_VER fails.\n", __func__);
18288c2ecf20Sopenharmony_ci	else
18298c2ecf20Sopenharmony_ci		dev_info(dev, "ASIC Version: 0x%04x\n", val);
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_FIRM_VER_GET, &val);
18328c2ecf20Sopenharmony_ci	if (r)
18338c2ecf20Sopenharmony_ci		dev_err(dev, "%s: Get FIRM_VER fails.\n", __func__);
18348c2ecf20Sopenharmony_ci	else
18358c2ecf20Sopenharmony_ci		dev_info(dev, "FW version: %d(0x%04x)\n", val, val);
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	r = core->read(core, WL1273_BAND_SET, &val);
18388c2ecf20Sopenharmony_ci	if (r)
18398c2ecf20Sopenharmony_ci		dev_err(dev, "%s: Get BAND fails.\n", __func__);
18408c2ecf20Sopenharmony_ci	else
18418c2ecf20Sopenharmony_ci		dev_info(dev, "BAND: %d\n", val);
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	if (core->mode == WL1273_MODE_TX) {
18448c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_PUPD_SET, &val);
18458c2ecf20Sopenharmony_ci		if (r)
18468c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get PUPD fails.\n", __func__);
18478c2ecf20Sopenharmony_ci		else
18488c2ecf20Sopenharmony_ci			dev_info(dev, "PUPD: 0x%04x\n", val);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_CHANL_SET, &val);
18518c2ecf20Sopenharmony_ci		if (r)
18528c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get CHANL fails.\n", __func__);
18538c2ecf20Sopenharmony_ci		else
18548c2ecf20Sopenharmony_ci			dev_info(dev, "Tx frequency: %dkHz\n", val*10);
18558c2ecf20Sopenharmony_ci	} else if (core->mode == WL1273_MODE_RX) {
18568c2ecf20Sopenharmony_ci		int bf = radio->rangelow;
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_FREQ_SET, &val);
18598c2ecf20Sopenharmony_ci		if (r)
18608c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get FREQ fails.\n", __func__);
18618c2ecf20Sopenharmony_ci		else
18628c2ecf20Sopenharmony_ci			dev_info(dev, "RX Frequency: %dkHz\n", bf + val*50);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_MOST_MODE_SET, &val);
18658c2ecf20Sopenharmony_ci		if (r)
18668c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get MOST_MODE fails.\n",
18678c2ecf20Sopenharmony_ci				__func__);
18688c2ecf20Sopenharmony_ci		else if (val == 0)
18698c2ecf20Sopenharmony_ci			dev_info(dev, "MOST_MODE: Stereo according to blend\n");
18708c2ecf20Sopenharmony_ci		else if (val == 1)
18718c2ecf20Sopenharmony_ci			dev_info(dev, "MOST_MODE: Force mono output\n");
18728c2ecf20Sopenharmony_ci		else
18738c2ecf20Sopenharmony_ci			dev_info(dev, "MOST_MODE: Unexpected value: %d\n", val);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_MOST_BLEND_SET, &val);
18768c2ecf20Sopenharmony_ci		if (r)
18778c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get MOST_BLEND fails.\n", __func__);
18788c2ecf20Sopenharmony_ci		else if (val == 0)
18798c2ecf20Sopenharmony_ci			dev_info(dev,
18808c2ecf20Sopenharmony_ci				 "MOST_BLEND: Switched blend & hysteresis.\n");
18818c2ecf20Sopenharmony_ci		else if (val == 1)
18828c2ecf20Sopenharmony_ci			dev_info(dev, "MOST_BLEND: Soft blend.\n");
18838c2ecf20Sopenharmony_ci		else
18848c2ecf20Sopenharmony_ci			dev_info(dev, "MOST_BLEND: Unexpected val: %d\n", val);
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_STEREO_GET, &val);
18878c2ecf20Sopenharmony_ci		if (r)
18888c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get STEREO fails.\n", __func__);
18898c2ecf20Sopenharmony_ci		else if (val == 0)
18908c2ecf20Sopenharmony_ci			dev_info(dev, "STEREO: Not detected\n");
18918c2ecf20Sopenharmony_ci		else if (val == 1)
18928c2ecf20Sopenharmony_ci			dev_info(dev, "STEREO: Detected\n");
18938c2ecf20Sopenharmony_ci		else
18948c2ecf20Sopenharmony_ci			dev_info(dev, "STEREO: Unexpected value: %d\n", val);
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_RSSI_LVL_GET, &val);
18978c2ecf20Sopenharmony_ci		if (r)
18988c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get RSSI_LVL fails.\n", __func__);
18998c2ecf20Sopenharmony_ci		else
19008c2ecf20Sopenharmony_ci			dev_info(dev, "RX signal strength: %d\n", (s16) val);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_POWER_SET, &val);
19038c2ecf20Sopenharmony_ci		if (r)
19048c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get POWER fails.\n", __func__);
19058c2ecf20Sopenharmony_ci		else
19068c2ecf20Sopenharmony_ci			dev_info(dev, "POWER: 0x%04x\n", val);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_INT_MASK_SET, &val);
19098c2ecf20Sopenharmony_ci		if (r)
19108c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get INT_MASK fails.\n", __func__);
19118c2ecf20Sopenharmony_ci		else
19128c2ecf20Sopenharmony_ci			dev_info(dev, "INT_MASK: 0x%04x\n", val);
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_RDS_SYNC_GET, &val);
19158c2ecf20Sopenharmony_ci		if (r)
19168c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get RDS_SYNC fails.\n",
19178c2ecf20Sopenharmony_ci				__func__);
19188c2ecf20Sopenharmony_ci		else if (val == 0)
19198c2ecf20Sopenharmony_ci			dev_info(dev, "RDS_SYNC: Not synchronized\n");
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci		else if (val == 1)
19228c2ecf20Sopenharmony_ci			dev_info(dev, "RDS_SYNC: Synchronized\n");
19238c2ecf20Sopenharmony_ci		else
19248c2ecf20Sopenharmony_ci			dev_info(dev, "RDS_SYNC: Unexpected value: %d\n", val);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_I2S_MODE_CONFIG_SET, &val);
19278c2ecf20Sopenharmony_ci		if (r)
19288c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get I2S_MODE_CONFIG fails.\n",
19298c2ecf20Sopenharmony_ci				__func__);
19308c2ecf20Sopenharmony_ci		else
19318c2ecf20Sopenharmony_ci			dev_info(dev, "I2S_MODE_CONFIG: 0x%04x\n", val);
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci		r = core->read(core, WL1273_VOLUME_SET, &val);
19348c2ecf20Sopenharmony_ci		if (r)
19358c2ecf20Sopenharmony_ci			dev_err(dev, "%s: Get VOLUME fails.\n", __func__);
19368c2ecf20Sopenharmony_ci		else
19378c2ecf20Sopenharmony_ci			dev_info(dev, "VOLUME: 0x%04x\n", val);
19388c2ecf20Sopenharmony_ci	}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	return 0;
19418c2ecf20Sopenharmony_ci}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_cistatic void wl1273_vdev_release(struct video_device *dev)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops wl1273_ctrl_ops = {
19488c2ecf20Sopenharmony_ci	.s_ctrl = wl1273_fm_vidioc_s_ctrl,
19498c2ecf20Sopenharmony_ci	.g_volatile_ctrl = wl1273_fm_g_volatile_ctrl,
19508c2ecf20Sopenharmony_ci};
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops wl1273_ioctl_ops = {
19538c2ecf20Sopenharmony_ci	.vidioc_querycap	= wl1273_fm_vidioc_querycap,
19548c2ecf20Sopenharmony_ci	.vidioc_g_input		= wl1273_fm_vidioc_g_input,
19558c2ecf20Sopenharmony_ci	.vidioc_s_input		= wl1273_fm_vidioc_s_input,
19568c2ecf20Sopenharmony_ci	.vidioc_g_audio		= wl1273_fm_vidioc_g_audio,
19578c2ecf20Sopenharmony_ci	.vidioc_s_audio		= wl1273_fm_vidioc_s_audio,
19588c2ecf20Sopenharmony_ci	.vidioc_g_tuner		= wl1273_fm_vidioc_g_tuner,
19598c2ecf20Sopenharmony_ci	.vidioc_s_tuner		= wl1273_fm_vidioc_s_tuner,
19608c2ecf20Sopenharmony_ci	.vidioc_g_frequency	= wl1273_fm_vidioc_g_frequency,
19618c2ecf20Sopenharmony_ci	.vidioc_s_frequency	= wl1273_fm_vidioc_s_frequency,
19628c2ecf20Sopenharmony_ci	.vidioc_s_hw_freq_seek	= wl1273_fm_vidioc_s_hw_freq_seek,
19638c2ecf20Sopenharmony_ci	.vidioc_g_modulator	= wl1273_fm_vidioc_g_modulator,
19648c2ecf20Sopenharmony_ci	.vidioc_s_modulator	= wl1273_fm_vidioc_s_modulator,
19658c2ecf20Sopenharmony_ci	.vidioc_log_status      = wl1273_fm_vidioc_log_status,
19668c2ecf20Sopenharmony_ci};
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_cistatic const struct video_device wl1273_viddev_template = {
19698c2ecf20Sopenharmony_ci	.fops			= &wl1273_fops,
19708c2ecf20Sopenharmony_ci	.ioctl_ops		= &wl1273_ioctl_ops,
19718c2ecf20Sopenharmony_ci	.name			= WL1273_FM_DRIVER_NAME,
19728c2ecf20Sopenharmony_ci	.release		= wl1273_vdev_release,
19738c2ecf20Sopenharmony_ci	.vfl_dir		= VFL_DIR_TX,
19748c2ecf20Sopenharmony_ci	.device_caps		= V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
19758c2ecf20Sopenharmony_ci				  V4L2_CAP_RADIO | V4L2_CAP_AUDIO |
19768c2ecf20Sopenharmony_ci				  V4L2_CAP_RDS_CAPTURE | V4L2_CAP_MODULATOR |
19778c2ecf20Sopenharmony_ci				  V4L2_CAP_RDS_OUTPUT,
19788c2ecf20Sopenharmony_ci};
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_cistatic int wl1273_fm_radio_remove(struct platform_device *pdev)
19818c2ecf20Sopenharmony_ci{
19828c2ecf20Sopenharmony_ci	struct wl1273_device *radio = platform_get_drvdata(pdev);
19838c2ecf20Sopenharmony_ci	struct wl1273_core *core = radio->core;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "%s.\n", __func__);
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	free_irq(core->client->irq, radio);
19888c2ecf20Sopenharmony_ci	core->pdata->free_resources();
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&radio->ctrl_handler);
19918c2ecf20Sopenharmony_ci	video_unregister_device(&radio->videodev);
19928c2ecf20Sopenharmony_ci	v4l2_device_unregister(&radio->v4l2dev);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci	return 0;
19958c2ecf20Sopenharmony_ci}
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_cistatic int wl1273_fm_radio_probe(struct platform_device *pdev)
19988c2ecf20Sopenharmony_ci{
19998c2ecf20Sopenharmony_ci	struct wl1273_core **core = pdev->dev.platform_data;
20008c2ecf20Sopenharmony_ci	struct wl1273_device *radio;
20018c2ecf20Sopenharmony_ci	struct v4l2_ctrl *ctrl;
20028c2ecf20Sopenharmony_ci	int r = 0;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	if (!core) {
20078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "No platform data.\n");
20088c2ecf20Sopenharmony_ci		r = -EINVAL;
20098c2ecf20Sopenharmony_ci		goto pdata_err;
20108c2ecf20Sopenharmony_ci	}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL);
20138c2ecf20Sopenharmony_ci	if (!radio) {
20148c2ecf20Sopenharmony_ci		r = -ENOMEM;
20158c2ecf20Sopenharmony_ci		goto pdata_err;
20168c2ecf20Sopenharmony_ci	}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	/* RDS buffer allocation */
20198c2ecf20Sopenharmony_ci	radio->buf_size = rds_buf * RDS_BLOCK_SIZE;
20208c2ecf20Sopenharmony_ci	radio->buffer = devm_kzalloc(&pdev->dev, radio->buf_size, GFP_KERNEL);
20218c2ecf20Sopenharmony_ci	if (!radio->buffer) {
20228c2ecf20Sopenharmony_ci		pr_err("Cannot allocate memory for RDS buffer.\n");
20238c2ecf20Sopenharmony_ci		r = -ENOMEM;
20248c2ecf20Sopenharmony_ci		goto pdata_err;
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	radio->core = *core;
20288c2ecf20Sopenharmony_ci	radio->irq_flags = WL1273_IRQ_MASK;
20298c2ecf20Sopenharmony_ci	radio->dev = &radio->core->client->dev;
20308c2ecf20Sopenharmony_ci	radio->rds_on = false;
20318c2ecf20Sopenharmony_ci	radio->core->mode = WL1273_MODE_OFF;
20328c2ecf20Sopenharmony_ci	radio->tx_power = 118;
20338c2ecf20Sopenharmony_ci	radio->core->audio_mode = WL1273_AUDIO_ANALOG;
20348c2ecf20Sopenharmony_ci	radio->band = WL1273_BAND_OTHER;
20358c2ecf20Sopenharmony_ci	radio->core->i2s_mode = WL1273_I2S_DEF_MODE;
20368c2ecf20Sopenharmony_ci	radio->core->channel_number = 2;
20378c2ecf20Sopenharmony_ci	radio->core->volume = WL1273_DEFAULT_VOLUME;
20388c2ecf20Sopenharmony_ci	radio->rx_frequency = WL1273_BAND_OTHER_LOW;
20398c2ecf20Sopenharmony_ci	radio->tx_frequency = WL1273_BAND_OTHER_HIGH;
20408c2ecf20Sopenharmony_ci	radio->rangelow = WL1273_BAND_OTHER_LOW;
20418c2ecf20Sopenharmony_ci	radio->rangehigh = WL1273_BAND_OTHER_HIGH;
20428c2ecf20Sopenharmony_ci	radio->stereo = true;
20438c2ecf20Sopenharmony_ci	radio->bus_type = "I2C";
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	if (radio->core->pdata->request_resources) {
20468c2ecf20Sopenharmony_ci		r = radio->core->pdata->request_resources(radio->core->client);
20478c2ecf20Sopenharmony_ci		if (r) {
20488c2ecf20Sopenharmony_ci			dev_err(radio->dev, WL1273_FM_DRIVER_NAME
20498c2ecf20Sopenharmony_ci				": Cannot get platform data\n");
20508c2ecf20Sopenharmony_ci			goto pdata_err;
20518c2ecf20Sopenharmony_ci		}
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci		dev_dbg(radio->dev, "irq: %d\n", radio->core->client->irq);
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci		r = request_threaded_irq(radio->core->client->irq, NULL,
20568c2ecf20Sopenharmony_ci					 wl1273_fm_irq_thread_handler,
20578c2ecf20Sopenharmony_ci					 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
20588c2ecf20Sopenharmony_ci					 "wl1273-fm", radio);
20598c2ecf20Sopenharmony_ci		if (r < 0) {
20608c2ecf20Sopenharmony_ci			dev_err(radio->dev, WL1273_FM_DRIVER_NAME
20618c2ecf20Sopenharmony_ci				": Unable to register IRQ handler: %d\n", r);
20628c2ecf20Sopenharmony_ci			goto err_request_irq;
20638c2ecf20Sopenharmony_ci		}
20648c2ecf20Sopenharmony_ci	} else {
20658c2ecf20Sopenharmony_ci		dev_err(radio->dev, WL1273_FM_DRIVER_NAME ": Core WL1273 IRQ not configured");
20668c2ecf20Sopenharmony_ci		r = -EINVAL;
20678c2ecf20Sopenharmony_ci		goto pdata_err;
20688c2ecf20Sopenharmony_ci	}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	init_completion(&radio->busy);
20718c2ecf20Sopenharmony_ci	init_waitqueue_head(&radio->read_queue);
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	radio->write_buf = devm_kzalloc(&pdev->dev, 256, GFP_KERNEL);
20748c2ecf20Sopenharmony_ci	if (!radio->write_buf) {
20758c2ecf20Sopenharmony_ci		r = -ENOMEM;
20768c2ecf20Sopenharmony_ci		goto write_buf_err;
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	radio->dev = &pdev->dev;
20808c2ecf20Sopenharmony_ci	radio->v4l2dev.ctrl_handler = &radio->ctrl_handler;
20818c2ecf20Sopenharmony_ci	radio->rds_users = 0;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	r = v4l2_device_register(&pdev->dev, &radio->v4l2dev);
20848c2ecf20Sopenharmony_ci	if (r) {
20858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cannot register v4l2_device.\n");
20868c2ecf20Sopenharmony_ci		goto write_buf_err;
20878c2ecf20Sopenharmony_ci	}
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	/* V4L2 configuration */
20908c2ecf20Sopenharmony_ci	radio->videodev = wl1273_viddev_template;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	radio->videodev.v4l2_dev = &radio->v4l2dev;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&radio->ctrl_handler, 6);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	/* add in ascending ID order */
20978c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops,
20988c2ecf20Sopenharmony_ci			  V4L2_CID_AUDIO_VOLUME, 0, WL1273_MAX_VOLUME, 1,
20998c2ecf20Sopenharmony_ci			  WL1273_DEFAULT_VOLUME);
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops,
21028c2ecf20Sopenharmony_ci			  V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std_menu(&radio->ctrl_handler, &wl1273_ctrl_ops,
21058c2ecf20Sopenharmony_ci			       V4L2_CID_TUNE_PREEMPHASIS,
21068c2ecf20Sopenharmony_ci			       V4L2_PREEMPHASIS_75_uS, 0x03,
21078c2ecf20Sopenharmony_ci			       V4L2_PREEMPHASIS_50_uS);
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops,
21108c2ecf20Sopenharmony_ci			  V4L2_CID_TUNE_POWER_LEVEL, 91, 122, 1, 118);
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops,
21138c2ecf20Sopenharmony_ci				 V4L2_CID_TUNE_ANTENNA_CAPACITOR,
21148c2ecf20Sopenharmony_ci				 0, 255, 1, 255);
21158c2ecf20Sopenharmony_ci	if (ctrl)
21168c2ecf20Sopenharmony_ci		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	if (radio->ctrl_handler.error) {
21198c2ecf20Sopenharmony_ci		r = radio->ctrl_handler.error;
21208c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Ctrl handler error: %d\n", r);
21218c2ecf20Sopenharmony_ci		goto handler_init_err;
21228c2ecf20Sopenharmony_ci	}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	video_set_drvdata(&radio->videodev, radio);
21258c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, radio);
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	/* register video device */
21288c2ecf20Sopenharmony_ci	r = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
21298c2ecf20Sopenharmony_ci	if (r) {
21308c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, WL1273_FM_DRIVER_NAME
21318c2ecf20Sopenharmony_ci			": Could not register video device\n");
21328c2ecf20Sopenharmony_ci		goto handler_init_err;
21338c2ecf20Sopenharmony_ci	}
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	return 0;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_cihandler_init_err:
21388c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&radio->ctrl_handler);
21398c2ecf20Sopenharmony_ci	v4l2_device_unregister(&radio->v4l2dev);
21408c2ecf20Sopenharmony_ciwrite_buf_err:
21418c2ecf20Sopenharmony_ci	free_irq(radio->core->client->irq, radio);
21428c2ecf20Sopenharmony_cierr_request_irq:
21438c2ecf20Sopenharmony_ci	radio->core->pdata->free_resources();
21448c2ecf20Sopenharmony_cipdata_err:
21458c2ecf20Sopenharmony_ci	return r;
21468c2ecf20Sopenharmony_ci}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_cistatic struct platform_driver wl1273_fm_radio_driver = {
21498c2ecf20Sopenharmony_ci	.probe		= wl1273_fm_radio_probe,
21508c2ecf20Sopenharmony_ci	.remove		= wl1273_fm_radio_remove,
21518c2ecf20Sopenharmony_ci	.driver		= {
21528c2ecf20Sopenharmony_ci		.name	= "wl1273_fm_radio",
21538c2ecf20Sopenharmony_ci	},
21548c2ecf20Sopenharmony_ci};
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_cimodule_platform_driver(wl1273_fm_radio_driver);
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
21598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
21608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
21618c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:wl1273_fm_radio");
2162