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