162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Texas Instruments WL1273 FM radio. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Nokia Corporation 662306a36Sopenharmony_ci * Author: Matti J. Aaltonen <matti.j.aaltonen@nokia.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/firmware.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/mfd/wl1273-core.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <media/v4l2-common.h> 1662306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 1762306a36Sopenharmony_ci#include <media/v4l2-device.h> 1862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define DRIVER_DESC "Wl1273 FM Radio" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define WL1273_POWER_SET_OFF 0 2362306a36Sopenharmony_ci#define WL1273_POWER_SET_FM BIT(0) 2462306a36Sopenharmony_ci#define WL1273_POWER_SET_RDS BIT(1) 2562306a36Sopenharmony_ci#define WL1273_POWER_SET_RETENTION BIT(4) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define WL1273_PUPD_SET_OFF 0x00 2862306a36Sopenharmony_ci#define WL1273_PUPD_SET_ON 0x01 2962306a36Sopenharmony_ci#define WL1273_PUPD_SET_RETENTION 0x10 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define WL1273_FREQ(x) (x * 10000 / 625) 3262306a36Sopenharmony_ci#define WL1273_INV_FREQ(x) (x * 625 / 10000) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * static int radio_nr - The number of the radio device 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * The default is 0. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic int radio_nr; 4062306a36Sopenharmony_cimodule_param(radio_nr, int, 0); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "The number of the radio device. Default = 0"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct wl1273_device { 4462306a36Sopenharmony_ci char *bus_type; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci u8 forbidden; 4762306a36Sopenharmony_ci unsigned int preemphasis; 4862306a36Sopenharmony_ci unsigned int spacing; 4962306a36Sopenharmony_ci unsigned int tx_power; 5062306a36Sopenharmony_ci unsigned int rx_frequency; 5162306a36Sopenharmony_ci unsigned int tx_frequency; 5262306a36Sopenharmony_ci unsigned int rangelow; 5362306a36Sopenharmony_ci unsigned int rangehigh; 5462306a36Sopenharmony_ci unsigned int band; 5562306a36Sopenharmony_ci bool stereo; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* RDS */ 5862306a36Sopenharmony_ci unsigned int rds_on; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci wait_queue_head_t read_queue; 6162306a36Sopenharmony_ci struct mutex lock; /* for serializing fm radio operations */ 6262306a36Sopenharmony_ci struct completion busy; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci unsigned char *buffer; 6562306a36Sopenharmony_ci unsigned int buf_size; 6662306a36Sopenharmony_ci unsigned int rd_index; 6762306a36Sopenharmony_ci unsigned int wr_index; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Selected interrupts */ 7062306a36Sopenharmony_ci u16 irq_flags; 7162306a36Sopenharmony_ci u16 irq_received; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci struct v4l2_ctrl_handler ctrl_handler; 7462306a36Sopenharmony_ci struct v4l2_device v4l2dev; 7562306a36Sopenharmony_ci struct video_device videodev; 7662306a36Sopenharmony_ci struct device *dev; 7762306a36Sopenharmony_ci struct wl1273_core *core; 7862306a36Sopenharmony_ci struct file *owner; 7962306a36Sopenharmony_ci char *write_buf; 8062306a36Sopenharmony_ci unsigned int rds_users; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define WL1273_IRQ_MASK (WL1273_FR_EVENT | \ 8462306a36Sopenharmony_ci WL1273_POW_ENB_EVENT) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * static unsigned int rds_buf - the number of RDS buffer blocks used. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * The default number is 100. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic unsigned int rds_buf = 100; 9262306a36Sopenharmony_cimodule_param(rds_buf, uint, 0); 9362306a36Sopenharmony_ciMODULE_PARM_DESC(rds_buf, "Number of RDS buffer entries. Default = 100"); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int wl1273_fm_write_fw(struct wl1273_core *core, 9662306a36Sopenharmony_ci __u8 *fw, int len) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct i2c_client *client = core->client; 9962306a36Sopenharmony_ci struct i2c_msg msg; 10062306a36Sopenharmony_ci int i, r = 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci msg.addr = client->addr; 10362306a36Sopenharmony_ci msg.flags = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci for (i = 0; i <= len; i++) { 10662306a36Sopenharmony_ci msg.len = fw[0]; 10762306a36Sopenharmony_ci msg.buf = fw + 1; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci fw += msg.len + 1; 11062306a36Sopenharmony_ci dev_dbg(&client->dev, "%s:len[%d]: %d\n", __func__, i, msg.len); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci r = i2c_transfer(client->adapter, &msg, 1); 11362306a36Sopenharmony_ci if (r < 0 && i < len + 1) 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci dev_dbg(&client->dev, "%s: i: %d\n", __func__, i); 11862306a36Sopenharmony_ci dev_dbg(&client->dev, "%s: len + 1: %d\n", __func__, len + 1); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Last transfer always fails. */ 12162306a36Sopenharmony_ci if (i == len || r == 1) 12262306a36Sopenharmony_ci r = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return r; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define WL1273_FIFO_HAS_DATA(status) (1 << 5 & status) 12862306a36Sopenharmony_ci#define WL1273_RDS_CORRECTABLE_ERROR (1 << 3) 12962306a36Sopenharmony_ci#define WL1273_RDS_UNCORRECTABLE_ERROR (1 << 4) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int wl1273_fm_rds(struct wl1273_device *radio) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 13462306a36Sopenharmony_ci struct i2c_client *client = core->client; 13562306a36Sopenharmony_ci u16 val; 13662306a36Sopenharmony_ci u8 b0 = WL1273_RDS_DATA_GET, status; 13762306a36Sopenharmony_ci struct v4l2_rds_data rds = { 0, 0, 0 }; 13862306a36Sopenharmony_ci struct i2c_msg msg[] = { 13962306a36Sopenharmony_ci { 14062306a36Sopenharmony_ci .addr = client->addr, 14162306a36Sopenharmony_ci .flags = 0, 14262306a36Sopenharmony_ci .buf = &b0, 14362306a36Sopenharmony_ci .len = 1, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci { 14662306a36Sopenharmony_ci .addr = client->addr, 14762306a36Sopenharmony_ci .flags = I2C_M_RD, 14862306a36Sopenharmony_ci .buf = (u8 *) &rds, 14962306a36Sopenharmony_ci .len = sizeof(rds), 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci }; 15262306a36Sopenharmony_ci int r; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (core->mode != WL1273_MODE_RX) 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci r = core->read(core, WL1273_RDS_SYNC_GET, &val); 15862306a36Sopenharmony_ci if (r) 15962306a36Sopenharmony_ci return r; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if ((val & 0x01) == 0) { 16262306a36Sopenharmony_ci /* RDS decoder not synchronized */ 16362306a36Sopenharmony_ci return -EAGAIN; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* copy all four RDS blocks to internal buffer */ 16762306a36Sopenharmony_ci do { 16862306a36Sopenharmony_ci r = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 16962306a36Sopenharmony_ci if (r != ARRAY_SIZE(msg)) { 17062306a36Sopenharmony_ci dev_err(radio->dev, WL1273_FM_DRIVER_NAME 17162306a36Sopenharmony_ci ": %s: read_rds error r == %i)\n", 17262306a36Sopenharmony_ci __func__, r); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci status = rds.block; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (!WL1273_FIFO_HAS_DATA(status)) 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* copy bits 0-2 (the block ID) to bits 3-5 */ 18162306a36Sopenharmony_ci rds.block = V4L2_RDS_BLOCK_MSK & status; 18262306a36Sopenharmony_ci rds.block |= rds.block << 3; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* copy the error bits to standard positions */ 18562306a36Sopenharmony_ci if (WL1273_RDS_UNCORRECTABLE_ERROR & status) { 18662306a36Sopenharmony_ci rds.block |= V4L2_RDS_BLOCK_ERROR; 18762306a36Sopenharmony_ci rds.block &= ~V4L2_RDS_BLOCK_CORRECTED; 18862306a36Sopenharmony_ci } else if (WL1273_RDS_CORRECTABLE_ERROR & status) { 18962306a36Sopenharmony_ci rds.block &= ~V4L2_RDS_BLOCK_ERROR; 19062306a36Sopenharmony_ci rds.block |= V4L2_RDS_BLOCK_CORRECTED; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* copy RDS block to internal buffer */ 19462306a36Sopenharmony_ci memcpy(&radio->buffer[radio->wr_index], &rds, RDS_BLOCK_SIZE); 19562306a36Sopenharmony_ci radio->wr_index += 3; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* wrap write pointer */ 19862306a36Sopenharmony_ci if (radio->wr_index >= radio->buf_size) 19962306a36Sopenharmony_ci radio->wr_index = 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* check for overflow & start over */ 20262306a36Sopenharmony_ci if (radio->wr_index == radio->rd_index) { 20362306a36Sopenharmony_ci dev_dbg(radio->dev, "RDS OVERFLOW"); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci radio->rd_index = 0; 20662306a36Sopenharmony_ci radio->wr_index = 0; 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } while (WL1273_FIFO_HAS_DATA(status)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* wake up read queue */ 21262306a36Sopenharmony_ci if (radio->wr_index != radio->rd_index) 21362306a36Sopenharmony_ci wake_up_interruptible(&radio->read_queue); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic irqreturn_t wl1273_fm_irq_thread_handler(int irq, void *dev_id) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct wl1273_device *radio = dev_id; 22162306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 22262306a36Sopenharmony_ci u16 flags; 22362306a36Sopenharmony_ci int r; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci r = core->read(core, WL1273_FLAG_GET, &flags); 22662306a36Sopenharmony_ci if (r) 22762306a36Sopenharmony_ci goto out; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (flags & WL1273_BL_EVENT) { 23062306a36Sopenharmony_ci radio->irq_received = flags; 23162306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: BL\n"); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (flags & WL1273_RDS_EVENT) { 23562306a36Sopenharmony_ci msleep(200); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci wl1273_fm_rds(radio); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (flags & WL1273_BBLK_EVENT) 24162306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: BBLK\n"); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (flags & WL1273_LSYNC_EVENT) 24462306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: LSYNC\n"); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (flags & WL1273_LEV_EVENT) { 24762306a36Sopenharmony_ci u16 level; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci r = core->read(core, WL1273_RSSI_LVL_GET, &level); 25062306a36Sopenharmony_ci if (r) 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (level > 14) 25462306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: LEV: 0x%x04\n", level); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (flags & WL1273_IFFR_EVENT) 25862306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: IFFR\n"); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (flags & WL1273_PI_EVENT) 26162306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: PI\n"); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (flags & WL1273_PD_EVENT) 26462306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: PD\n"); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (flags & WL1273_STIC_EVENT) 26762306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: STIC\n"); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (flags & WL1273_MAL_EVENT) 27062306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: MAL\n"); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (flags & WL1273_POW_ENB_EVENT) { 27362306a36Sopenharmony_ci complete(&radio->busy); 27462306a36Sopenharmony_ci dev_dbg(radio->dev, "NOT BUSY\n"); 27562306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: POW_ENB\n"); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (flags & WL1273_SCAN_OVER_EVENT) 27962306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: SCAN_OVER\n"); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (flags & WL1273_ERROR_EVENT) 28262306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: ERROR\n"); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (flags & WL1273_FR_EVENT) { 28562306a36Sopenharmony_ci u16 freq; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dev_dbg(radio->dev, "IRQ: FR:\n"); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) { 29062306a36Sopenharmony_ci r = core->write(core, WL1273_TUNER_MODE_SET, 29162306a36Sopenharmony_ci TUNER_MODE_STOP_SEARCH); 29262306a36Sopenharmony_ci if (r) { 29362306a36Sopenharmony_ci dev_err(radio->dev, 29462306a36Sopenharmony_ci "%s: TUNER_MODE_SET fails: %d\n", 29562306a36Sopenharmony_ci __func__, r); 29662306a36Sopenharmony_ci goto out; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci r = core->read(core, WL1273_FREQ_SET, &freq); 30062306a36Sopenharmony_ci if (r) 30162306a36Sopenharmony_ci goto out; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (radio->band == WL1273_BAND_JAPAN) 30462306a36Sopenharmony_ci radio->rx_frequency = WL1273_BAND_JAPAN_LOW + 30562306a36Sopenharmony_ci freq * 50; 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci radio->rx_frequency = WL1273_BAND_OTHER_LOW + 30862306a36Sopenharmony_ci freq * 50; 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * The driver works better with this msleep, 31162306a36Sopenharmony_ci * the documentation doesn't mention it. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci usleep_range(10000, 15000); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dev_dbg(radio->dev, "%dkHz\n", radio->rx_frequency); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci r = core->read(core, WL1273_CHANL_SET, &freq); 31962306a36Sopenharmony_ci if (r) 32062306a36Sopenharmony_ci goto out; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dev_dbg(radio->dev, "%dkHz\n", freq); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: NOT BUSY\n", __func__); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciout: 32862306a36Sopenharmony_ci core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); 32962306a36Sopenharmony_ci complete(&radio->busy); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return IRQ_HANDLED; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int wl1273_fm_set_tx_freq(struct wl1273_device *radio, unsigned int freq) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 33762306a36Sopenharmony_ci int r = 0; 33862306a36Sopenharmony_ci unsigned long t; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (freq < WL1273_BAND_TX_LOW) { 34162306a36Sopenharmony_ci dev_err(radio->dev, 34262306a36Sopenharmony_ci "Frequency out of range: %d < %d\n", freq, 34362306a36Sopenharmony_ci WL1273_BAND_TX_LOW); 34462306a36Sopenharmony_ci return -ERANGE; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (freq > WL1273_BAND_TX_HIGH) { 34862306a36Sopenharmony_ci dev_err(radio->dev, 34962306a36Sopenharmony_ci "Frequency out of range: %d > %d\n", freq, 35062306a36Sopenharmony_ci WL1273_BAND_TX_HIGH); 35162306a36Sopenharmony_ci return -ERANGE; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * The driver works better with this sleep, 35662306a36Sopenharmony_ci * the documentation doesn't mention it. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci usleep_range(5000, 10000); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: freq: %d kHz\n", __func__, freq); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Set the current tx channel */ 36362306a36Sopenharmony_ci r = core->write(core, WL1273_CHANL_SET, freq / 10); 36462306a36Sopenharmony_ci if (r) 36562306a36Sopenharmony_ci return r; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci reinit_completion(&radio->busy); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* wait for the FR IRQ */ 37062306a36Sopenharmony_ci t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000)); 37162306a36Sopenharmony_ci if (!t) 37262306a36Sopenharmony_ci return -ETIMEDOUT; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci dev_dbg(radio->dev, "WL1273_CHANL_SET: %lu\n", t); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Enable the output power */ 37762306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_ENB_SET, 1); 37862306a36Sopenharmony_ci if (r) 37962306a36Sopenharmony_ci return r; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci reinit_completion(&radio->busy); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* wait for the POWER_ENB IRQ */ 38462306a36Sopenharmony_ci t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000)); 38562306a36Sopenharmony_ci if (!t) 38662306a36Sopenharmony_ci return -ETIMEDOUT; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci radio->tx_frequency = freq; 38962306a36Sopenharmony_ci dev_dbg(radio->dev, "WL1273_POWER_ENB_SET: %lu\n", t); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int wl1273_fm_set_rx_freq(struct wl1273_device *radio, unsigned int freq) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 39762306a36Sopenharmony_ci int r, f; 39862306a36Sopenharmony_ci unsigned long t; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (freq < radio->rangelow) { 40162306a36Sopenharmony_ci dev_err(radio->dev, 40262306a36Sopenharmony_ci "Frequency out of range: %d < %d\n", freq, 40362306a36Sopenharmony_ci radio->rangelow); 40462306a36Sopenharmony_ci r = -ERANGE; 40562306a36Sopenharmony_ci goto err; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (freq > radio->rangehigh) { 40962306a36Sopenharmony_ci dev_err(radio->dev, 41062306a36Sopenharmony_ci "Frequency out of range: %d > %d\n", freq, 41162306a36Sopenharmony_ci radio->rangehigh); 41262306a36Sopenharmony_ci r = -ERANGE; 41362306a36Sopenharmony_ci goto err; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: %dkHz\n", __func__, freq); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (radio->band == WL1273_BAND_JAPAN) 42162306a36Sopenharmony_ci f = (freq - WL1273_BAND_JAPAN_LOW) / 50; 42262306a36Sopenharmony_ci else 42362306a36Sopenharmony_ci f = (freq - WL1273_BAND_OTHER_LOW) / 50; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci r = core->write(core, WL1273_FREQ_SET, f); 42662306a36Sopenharmony_ci if (r) { 42762306a36Sopenharmony_ci dev_err(radio->dev, "FREQ_SET fails\n"); 42862306a36Sopenharmony_ci goto err; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_PRESET); 43262306a36Sopenharmony_ci if (r) { 43362306a36Sopenharmony_ci dev_err(radio->dev, "TUNER_MODE_SET fails\n"); 43462306a36Sopenharmony_ci goto err; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci reinit_completion(&radio->busy); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci t = wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(2000)); 44062306a36Sopenharmony_ci if (!t) { 44162306a36Sopenharmony_ci dev_err(radio->dev, "%s: TIMEOUT\n", __func__); 44262306a36Sopenharmony_ci return -ETIMEDOUT; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci radio->rd_index = 0; 44662306a36Sopenharmony_ci radio->wr_index = 0; 44762306a36Sopenharmony_ci radio->rx_frequency = freq; 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_cierr: 45062306a36Sopenharmony_ci return r; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int wl1273_fm_get_freq(struct wl1273_device *radio) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 45662306a36Sopenharmony_ci unsigned int freq; 45762306a36Sopenharmony_ci u16 f; 45862306a36Sopenharmony_ci int r; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) { 46162306a36Sopenharmony_ci r = core->read(core, WL1273_FREQ_SET, &f); 46262306a36Sopenharmony_ci if (r) 46362306a36Sopenharmony_ci return r; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci dev_dbg(radio->dev, "Freq get: 0x%04x\n", f); 46662306a36Sopenharmony_ci if (radio->band == WL1273_BAND_JAPAN) 46762306a36Sopenharmony_ci freq = WL1273_BAND_JAPAN_LOW + 50 * f; 46862306a36Sopenharmony_ci else 46962306a36Sopenharmony_ci freq = WL1273_BAND_OTHER_LOW + 50 * f; 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci r = core->read(core, WL1273_CHANL_SET, &f); 47262306a36Sopenharmony_ci if (r) 47362306a36Sopenharmony_ci return r; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci freq = f * 10; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return freq; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/** 48262306a36Sopenharmony_ci * wl1273_fm_upload_firmware_patch() - Upload the firmware. 48362306a36Sopenharmony_ci * @radio: A pointer to the device struct. 48462306a36Sopenharmony_ci * 48562306a36Sopenharmony_ci * The firmware file consists of arrays of bytes where the first byte 48662306a36Sopenharmony_ci * gives the array length. The first byte in the file gives the 48762306a36Sopenharmony_ci * number of these arrays. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_cistatic int wl1273_fm_upload_firmware_patch(struct wl1273_device *radio) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 49262306a36Sopenharmony_ci unsigned int packet_num; 49362306a36Sopenharmony_ci const struct firmware *fw_p; 49462306a36Sopenharmony_ci const char *fw_name = "radio-wl1273-fw.bin"; 49562306a36Sopenharmony_ci struct device *dev = radio->dev; 49662306a36Sopenharmony_ci __u8 *ptr; 49762306a36Sopenharmony_ci int r; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci dev_dbg(dev, "%s:\n", __func__); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* 50262306a36Sopenharmony_ci * Uploading the firmware patch is not always necessary, 50362306a36Sopenharmony_ci * so we only print an info message. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci if (request_firmware(&fw_p, fw_name, dev)) { 50662306a36Sopenharmony_ci dev_info(dev, "%s - %s not found\n", __func__, fw_name); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ptr = (__u8 *) fw_p->data; 51262306a36Sopenharmony_ci packet_num = ptr[0]; 51362306a36Sopenharmony_ci dev_dbg(dev, "%s: packets: %d\n", __func__, packet_num); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci r = wl1273_fm_write_fw(core, ptr + 1, packet_num); 51662306a36Sopenharmony_ci if (r) { 51762306a36Sopenharmony_ci dev_err(dev, "FW upload error: %d\n", r); 51862306a36Sopenharmony_ci goto out; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* ignore possible error here */ 52262306a36Sopenharmony_ci core->write(core, WL1273_RESET, 0); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci dev_dbg(dev, "%s - download OK, r: %d\n", __func__, r); 52562306a36Sopenharmony_ciout: 52662306a36Sopenharmony_ci release_firmware(fw_p); 52762306a36Sopenharmony_ci return r; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int wl1273_fm_stop(struct wl1273_device *radio) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) { 53562306a36Sopenharmony_ci int r = core->write(core, WL1273_POWER_SET, 53662306a36Sopenharmony_ci WL1273_POWER_SET_OFF); 53762306a36Sopenharmony_ci if (r) 53862306a36Sopenharmony_ci dev_err(radio->dev, "%s: POWER_SET fails: %d\n", 53962306a36Sopenharmony_ci __func__, r); 54062306a36Sopenharmony_ci } else if (core->mode == WL1273_MODE_TX) { 54162306a36Sopenharmony_ci int r = core->write(core, WL1273_PUPD_SET, 54262306a36Sopenharmony_ci WL1273_PUPD_SET_OFF); 54362306a36Sopenharmony_ci if (r) 54462306a36Sopenharmony_ci dev_err(radio->dev, 54562306a36Sopenharmony_ci "%s: PUPD_SET fails: %d\n", __func__, r); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (core->pdata->disable) { 54962306a36Sopenharmony_ci core->pdata->disable(); 55062306a36Sopenharmony_ci dev_dbg(radio->dev, "Back to reset\n"); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic int wl1273_fm_start(struct wl1273_device *radio, int new_mode) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 55962306a36Sopenharmony_ci struct wl1273_fm_platform_data *pdata = core->pdata; 56062306a36Sopenharmony_ci struct device *dev = radio->dev; 56162306a36Sopenharmony_ci int r = -EINVAL; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (pdata->enable && core->mode == WL1273_MODE_OFF) { 56462306a36Sopenharmony_ci dev_dbg(radio->dev, "Out of reset\n"); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci pdata->enable(); 56762306a36Sopenharmony_ci msleep(250); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (new_mode == WL1273_MODE_RX) { 57162306a36Sopenharmony_ci u16 val = WL1273_POWER_SET_FM; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (radio->rds_on) 57462306a36Sopenharmony_ci val |= WL1273_POWER_SET_RDS; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* If this fails try again */ 57762306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_SET, val); 57862306a36Sopenharmony_ci if (r) { 57962306a36Sopenharmony_ci msleep(100); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_SET, val); 58262306a36Sopenharmony_ci if (r) { 58362306a36Sopenharmony_ci dev_err(dev, "%s: POWER_SET fails\n", __func__); 58462306a36Sopenharmony_ci goto fail; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* rds buffer configuration */ 58962306a36Sopenharmony_ci radio->wr_index = 0; 59062306a36Sopenharmony_ci radio->rd_index = 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci } else if (new_mode == WL1273_MODE_TX) { 59362306a36Sopenharmony_ci /* If this fails try again once */ 59462306a36Sopenharmony_ci r = core->write(core, WL1273_PUPD_SET, WL1273_PUPD_SET_ON); 59562306a36Sopenharmony_ci if (r) { 59662306a36Sopenharmony_ci msleep(100); 59762306a36Sopenharmony_ci r = core->write(core, WL1273_PUPD_SET, 59862306a36Sopenharmony_ci WL1273_PUPD_SET_ON); 59962306a36Sopenharmony_ci if (r) { 60062306a36Sopenharmony_ci dev_err(dev, "%s: PUPD_SET fails\n", __func__); 60162306a36Sopenharmony_ci goto fail; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (radio->rds_on) { 60662306a36Sopenharmony_ci r = core->write(core, WL1273_RDS_DATA_ENB, 1); 60762306a36Sopenharmony_ci if (r) { 60862306a36Sopenharmony_ci dev_err(dev, "%s: RDS_DATA_ENB ON fails\n", 60962306a36Sopenharmony_ci __func__); 61062306a36Sopenharmony_ci goto fail; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci } else { 61362306a36Sopenharmony_ci r = core->write(core, WL1273_RDS_DATA_ENB, 0); 61462306a36Sopenharmony_ci if (r) { 61562306a36Sopenharmony_ci dev_err(dev, "%s: RDS_DATA_ENB OFF fails\n", 61662306a36Sopenharmony_ci __func__); 61762306a36Sopenharmony_ci goto fail; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci } else { 62162306a36Sopenharmony_ci dev_warn(dev, "%s: Illegal mode.\n", __func__); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (core->mode == WL1273_MODE_OFF) { 62562306a36Sopenharmony_ci r = wl1273_fm_upload_firmware_patch(radio); 62662306a36Sopenharmony_ci if (r) 62762306a36Sopenharmony_ci dev_warn(dev, "Firmware upload failed.\n"); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* 63062306a36Sopenharmony_ci * Sometimes the chip is in a wrong power state at this point. 63162306a36Sopenharmony_ci * So we set the power once again. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci if (new_mode == WL1273_MODE_RX) { 63462306a36Sopenharmony_ci u16 val = WL1273_POWER_SET_FM; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (radio->rds_on) 63762306a36Sopenharmony_ci val |= WL1273_POWER_SET_RDS; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_SET, val); 64062306a36Sopenharmony_ci if (r) { 64162306a36Sopenharmony_ci dev_err(dev, "%s: POWER_SET fails\n", __func__); 64262306a36Sopenharmony_ci goto fail; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } else if (new_mode == WL1273_MODE_TX) { 64562306a36Sopenharmony_ci r = core->write(core, WL1273_PUPD_SET, 64662306a36Sopenharmony_ci WL1273_PUPD_SET_ON); 64762306a36Sopenharmony_ci if (r) { 64862306a36Sopenharmony_ci dev_err(dev, "%s: PUPD_SET fails\n", __func__); 64962306a36Sopenharmony_ci goto fail; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_cifail: 65662306a36Sopenharmony_ci if (pdata->disable) 65762306a36Sopenharmony_ci pdata->disable(); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci dev_dbg(dev, "%s: return: %d\n", __func__, r); 66062306a36Sopenharmony_ci return r; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int wl1273_fm_suspend(struct wl1273_device *radio) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 66662306a36Sopenharmony_ci int r; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Cannot go from OFF to SUSPENDED */ 66962306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) 67062306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_SET, 67162306a36Sopenharmony_ci WL1273_POWER_SET_RETENTION); 67262306a36Sopenharmony_ci else if (core->mode == WL1273_MODE_TX) 67362306a36Sopenharmony_ci r = core->write(core, WL1273_PUPD_SET, 67462306a36Sopenharmony_ci WL1273_PUPD_SET_RETENTION); 67562306a36Sopenharmony_ci else 67662306a36Sopenharmony_ci r = -EINVAL; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (r) { 67962306a36Sopenharmony_ci dev_err(radio->dev, "%s: POWER_SET fails: %d\n", __func__, r); 68062306a36Sopenharmony_ci goto out; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ciout: 68462306a36Sopenharmony_ci return r; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int wl1273_fm_set_mode(struct wl1273_device *radio, int mode) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 69062306a36Sopenharmony_ci struct device *dev = radio->dev; 69162306a36Sopenharmony_ci int old_mode; 69262306a36Sopenharmony_ci int r; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 69562306a36Sopenharmony_ci dev_dbg(dev, "Forbidden modes: 0x%02x\n", radio->forbidden); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci old_mode = core->mode; 69862306a36Sopenharmony_ci if (mode & radio->forbidden) { 69962306a36Sopenharmony_ci r = -EPERM; 70062306a36Sopenharmony_ci goto out; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci switch (mode) { 70462306a36Sopenharmony_ci case WL1273_MODE_RX: 70562306a36Sopenharmony_ci case WL1273_MODE_TX: 70662306a36Sopenharmony_ci r = wl1273_fm_start(radio, mode); 70762306a36Sopenharmony_ci if (r) { 70862306a36Sopenharmony_ci dev_err(dev, "%s: Cannot start.\n", __func__); 70962306a36Sopenharmony_ci wl1273_fm_stop(radio); 71062306a36Sopenharmony_ci goto out; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci core->mode = mode; 71462306a36Sopenharmony_ci r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); 71562306a36Sopenharmony_ci if (r) { 71662306a36Sopenharmony_ci dev_err(dev, "INT_MASK_SET fails.\n"); 71762306a36Sopenharmony_ci goto out; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* remember previous settings */ 72162306a36Sopenharmony_ci if (mode == WL1273_MODE_RX) { 72262306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, radio->rx_frequency); 72362306a36Sopenharmony_ci if (r) { 72462306a36Sopenharmony_ci dev_err(dev, "set freq fails: %d.\n", r); 72562306a36Sopenharmony_ci goto out; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci r = core->set_volume(core, core->volume); 72962306a36Sopenharmony_ci if (r) { 73062306a36Sopenharmony_ci dev_err(dev, "set volume fails: %d.\n", r); 73162306a36Sopenharmony_ci goto out; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci dev_dbg(dev, "%s: Set vol: %d.\n", __func__, 73562306a36Sopenharmony_ci core->volume); 73662306a36Sopenharmony_ci } else { 73762306a36Sopenharmony_ci r = wl1273_fm_set_tx_freq(radio, radio->tx_frequency); 73862306a36Sopenharmony_ci if (r) { 73962306a36Sopenharmony_ci dev_err(dev, "set freq fails: %d.\n", r); 74062306a36Sopenharmony_ci goto out; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: Set audio mode.\n", __func__); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci r = core->set_audio(core, core->audio_mode); 74762306a36Sopenharmony_ci if (r) 74862306a36Sopenharmony_ci dev_err(dev, "Cannot set audio mode.\n"); 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci case WL1273_MODE_OFF: 75262306a36Sopenharmony_ci r = wl1273_fm_stop(radio); 75362306a36Sopenharmony_ci if (r) 75462306a36Sopenharmony_ci dev_err(dev, "%s: Off fails: %d\n", __func__, r); 75562306a36Sopenharmony_ci else 75662306a36Sopenharmony_ci core->mode = WL1273_MODE_OFF; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci case WL1273_MODE_SUSPENDED: 76162306a36Sopenharmony_ci r = wl1273_fm_suspend(radio); 76262306a36Sopenharmony_ci if (r) 76362306a36Sopenharmony_ci dev_err(dev, "%s: Suspend fails: %d\n", __func__, r); 76462306a36Sopenharmony_ci else 76562306a36Sopenharmony_ci core->mode = WL1273_MODE_SUSPENDED; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci default: 77062306a36Sopenharmony_ci dev_err(dev, "%s: Unknown mode: %d\n", __func__, mode); 77162306a36Sopenharmony_ci r = -EINVAL; 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ciout: 77562306a36Sopenharmony_ci if (r) 77662306a36Sopenharmony_ci core->mode = old_mode; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return r; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int wl1273_fm_set_seek(struct wl1273_device *radio, 78262306a36Sopenharmony_ci unsigned int wrap_around, 78362306a36Sopenharmony_ci unsigned int seek_upward, 78462306a36Sopenharmony_ci int level) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 78762306a36Sopenharmony_ci int r = 0; 78862306a36Sopenharmony_ci unsigned int dir = (seek_upward == 0) ? 0 : 1; 78962306a36Sopenharmony_ci unsigned int f; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci f = radio->rx_frequency; 79262306a36Sopenharmony_ci dev_dbg(radio->dev, "rx_frequency: %d\n", f); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (dir && f + radio->spacing <= radio->rangehigh) 79562306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, f + radio->spacing); 79662306a36Sopenharmony_ci else if (dir && wrap_around) 79762306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, radio->rangelow); 79862306a36Sopenharmony_ci else if (f - radio->spacing >= radio->rangelow) 79962306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, f - radio->spacing); 80062306a36Sopenharmony_ci else if (wrap_around) 80162306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, radio->rangehigh); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (r) 80462306a36Sopenharmony_ci goto out; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (level < SCHAR_MIN || level > SCHAR_MAX) 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci reinit_completion(&radio->busy); 81062306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: BUSY\n", __func__); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); 81362306a36Sopenharmony_ci if (r) 81462306a36Sopenharmony_ci goto out; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci r = core->write(core, WL1273_SEARCH_LVL_SET, level); 81962306a36Sopenharmony_ci if (r) 82062306a36Sopenharmony_ci goto out; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci r = core->write(core, WL1273_SEARCH_DIR_SET, dir); 82362306a36Sopenharmony_ci if (r) 82462306a36Sopenharmony_ci goto out; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK); 82762306a36Sopenharmony_ci if (r) 82862306a36Sopenharmony_ci goto out; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* wait for the FR IRQ */ 83162306a36Sopenharmony_ci wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000)); 83262306a36Sopenharmony_ci if (!(radio->irq_received & WL1273_BL_EVENT)) { 83362306a36Sopenharmony_ci r = -ETIMEDOUT; 83462306a36Sopenharmony_ci goto out; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci radio->irq_received &= ~WL1273_BL_EVENT; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (!wrap_around) 84062306a36Sopenharmony_ci goto out; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* Wrap around */ 84362306a36Sopenharmony_ci dev_dbg(radio->dev, "Wrap around in HW seek.\n"); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (seek_upward) 84662306a36Sopenharmony_ci f = radio->rangelow; 84762306a36Sopenharmony_ci else 84862306a36Sopenharmony_ci f = radio->rangehigh; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, f); 85162306a36Sopenharmony_ci if (r) 85262306a36Sopenharmony_ci goto out; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci reinit_completion(&radio->busy); 85562306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: BUSY\n", __func__); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci r = core->write(core, WL1273_TUNER_MODE_SET, TUNER_MODE_AUTO_SEEK); 85862306a36Sopenharmony_ci if (r) 85962306a36Sopenharmony_ci goto out; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* wait for the FR IRQ */ 86262306a36Sopenharmony_ci if (!wait_for_completion_timeout(&radio->busy, msecs_to_jiffies(1000))) 86362306a36Sopenharmony_ci r = -ETIMEDOUT; 86462306a36Sopenharmony_ciout: 86562306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: Err: %d\n", __func__, r); 86662306a36Sopenharmony_ci return r; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/** 87062306a36Sopenharmony_ci * wl1273_fm_get_tx_ctune() - Get the TX tuning capacitor value. 87162306a36Sopenharmony_ci * @radio: A pointer to the device struct. 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_cistatic unsigned int wl1273_fm_get_tx_ctune(struct wl1273_device *radio) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 87662306a36Sopenharmony_ci struct device *dev = radio->dev; 87762306a36Sopenharmony_ci u16 val; 87862306a36Sopenharmony_ci int r; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (core->mode == WL1273_MODE_OFF || 88162306a36Sopenharmony_ci core->mode == WL1273_MODE_SUSPENDED) 88262306a36Sopenharmony_ci return -EPERM; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci r = core->read(core, WL1273_READ_FMANT_TUNE_VALUE, &val); 88562306a36Sopenharmony_ci if (r) { 88662306a36Sopenharmony_ci dev_err(dev, "%s: read error: %d\n", __func__, r); 88762306a36Sopenharmony_ci goto out; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ciout: 89162306a36Sopenharmony_ci return val; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/** 89562306a36Sopenharmony_ci * wl1273_fm_set_preemphasis() - Set the TX pre-emphasis value. 89662306a36Sopenharmony_ci * @radio: A pointer to the device struct. 89762306a36Sopenharmony_ci * @preemphasis: The new pre-amphasis value. 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * Possible pre-emphasis values are: V4L2_PREEMPHASIS_DISABLED, 90062306a36Sopenharmony_ci * V4L2_PREEMPHASIS_50_uS and V4L2_PREEMPHASIS_75_uS. 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_cistatic int wl1273_fm_set_preemphasis(struct wl1273_device *radio, 90362306a36Sopenharmony_ci unsigned int preemphasis) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 90662306a36Sopenharmony_ci int r; 90762306a36Sopenharmony_ci u16 em; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (core->mode == WL1273_MODE_OFF || 91062306a36Sopenharmony_ci core->mode == WL1273_MODE_SUSPENDED) 91162306a36Sopenharmony_ci return -EPERM; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci mutex_lock(&core->lock); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci switch (preemphasis) { 91662306a36Sopenharmony_ci case V4L2_PREEMPHASIS_DISABLED: 91762306a36Sopenharmony_ci em = 1; 91862306a36Sopenharmony_ci break; 91962306a36Sopenharmony_ci case V4L2_PREEMPHASIS_50_uS: 92062306a36Sopenharmony_ci em = 0; 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci case V4L2_PREEMPHASIS_75_uS: 92362306a36Sopenharmony_ci em = 2; 92462306a36Sopenharmony_ci break; 92562306a36Sopenharmony_ci default: 92662306a36Sopenharmony_ci r = -EINVAL; 92762306a36Sopenharmony_ci goto out; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci r = core->write(core, WL1273_PREMPH_SET, em); 93162306a36Sopenharmony_ci if (r) 93262306a36Sopenharmony_ci goto out; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci radio->preemphasis = preemphasis; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ciout: 93762306a36Sopenharmony_ci mutex_unlock(&core->lock); 93862306a36Sopenharmony_ci return r; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int wl1273_fm_rds_on(struct wl1273_device *radio) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 94462306a36Sopenharmony_ci int r; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 94762306a36Sopenharmony_ci if (radio->rds_on) 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_SET, 95162306a36Sopenharmony_ci WL1273_POWER_SET_FM | WL1273_POWER_SET_RDS); 95262306a36Sopenharmony_ci if (r) 95362306a36Sopenharmony_ci goto out; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, radio->rx_frequency); 95662306a36Sopenharmony_ci if (r) 95762306a36Sopenharmony_ci dev_err(radio->dev, "set freq fails: %d.\n", r); 95862306a36Sopenharmony_ciout: 95962306a36Sopenharmony_ci return r; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic int wl1273_fm_rds_off(struct wl1273_device *radio) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 96562306a36Sopenharmony_ci int r; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (!radio->rds_on) 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci radio->irq_flags &= ~WL1273_RDS_EVENT; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci r = core->write(core, WL1273_INT_MASK_SET, radio->irq_flags); 97362306a36Sopenharmony_ci if (r) 97462306a36Sopenharmony_ci goto out; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Service pending read */ 97762306a36Sopenharmony_ci wake_up_interruptible(&radio->read_queue); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_SET, WL1273_POWER_SET_FM); 98262306a36Sopenharmony_ci if (r) 98362306a36Sopenharmony_ci goto out; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, radio->rx_frequency); 98662306a36Sopenharmony_ci if (r) 98762306a36Sopenharmony_ci dev_err(radio->dev, "set freq fails: %d.\n", r); 98862306a36Sopenharmony_ciout: 98962306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: exiting...\n", __func__); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci return r; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int wl1273_fm_set_rds(struct wl1273_device *radio, unsigned int new_mode) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci int r = 0; 99762306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (core->mode == WL1273_MODE_OFF || 100062306a36Sopenharmony_ci core->mode == WL1273_MODE_SUSPENDED) 100162306a36Sopenharmony_ci return -EPERM; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (new_mode == WL1273_RDS_RESET) { 100462306a36Sopenharmony_ci r = core->write(core, WL1273_RDS_CNTRL_SET, 1); 100562306a36Sopenharmony_ci return r; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_OFF) { 100962306a36Sopenharmony_ci r = core->write(core, WL1273_RDS_DATA_ENB, 0); 101062306a36Sopenharmony_ci } else if (core->mode == WL1273_MODE_TX && new_mode == WL1273_RDS_ON) { 101162306a36Sopenharmony_ci r = core->write(core, WL1273_RDS_DATA_ENB, 1); 101262306a36Sopenharmony_ci } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_OFF) { 101362306a36Sopenharmony_ci r = wl1273_fm_rds_off(radio); 101462306a36Sopenharmony_ci } else if (core->mode == WL1273_MODE_RX && new_mode == WL1273_RDS_ON) { 101562306a36Sopenharmony_ci r = wl1273_fm_rds_on(radio); 101662306a36Sopenharmony_ci } else { 101762306a36Sopenharmony_ci dev_err(radio->dev, "%s: Unknown mode: %d\n", 101862306a36Sopenharmony_ci __func__, new_mode); 101962306a36Sopenharmony_ci r = -EINVAL; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (!r) 102362306a36Sopenharmony_ci radio->rds_on = (new_mode == WL1273_RDS_ON) ? true : false; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return r; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic ssize_t wl1273_fm_fops_write(struct file *file, const char __user *buf, 102962306a36Sopenharmony_ci size_t count, loff_t *ppos) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 103262306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 103362306a36Sopenharmony_ci u16 val; 103462306a36Sopenharmony_ci int r; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (core->mode != WL1273_MODE_TX) 103962306a36Sopenharmony_ci return count; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (radio->rds_users == 0) { 104262306a36Sopenharmony_ci dev_warn(radio->dev, "%s: RDS not on.\n", __func__); 104362306a36Sopenharmony_ci return 0; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 104762306a36Sopenharmony_ci return -EINTR; 104862306a36Sopenharmony_ci /* 104962306a36Sopenharmony_ci * Multiple processes can open the device, but only 105062306a36Sopenharmony_ci * one gets to write to it. 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_ci if (radio->owner && radio->owner != file) { 105362306a36Sopenharmony_ci r = -EBUSY; 105462306a36Sopenharmony_ci goto out; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci radio->owner = file; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* Manual Mode */ 105962306a36Sopenharmony_ci if (count > 255) 106062306a36Sopenharmony_ci val = 255; 106162306a36Sopenharmony_ci else 106262306a36Sopenharmony_ci val = count; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci core->write(core, WL1273_RDS_CONFIG_DATA_SET, val); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (copy_from_user(radio->write_buf + 1, buf, val)) { 106762306a36Sopenharmony_ci r = -EFAULT; 106862306a36Sopenharmony_ci goto out; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci dev_dbg(radio->dev, "Count: %d\n", val); 107262306a36Sopenharmony_ci dev_dbg(radio->dev, "From user: \"%s\"\n", radio->write_buf); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci radio->write_buf[0] = WL1273_RDS_DATA_SET; 107562306a36Sopenharmony_ci core->write_data(core, radio->write_buf, val + 1); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci r = val; 107862306a36Sopenharmony_ciout: 107962306a36Sopenharmony_ci mutex_unlock(&core->lock); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return r; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic __poll_t wl1273_fm_fops_poll(struct file *file, 108562306a36Sopenharmony_ci struct poll_table_struct *pts) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 108862306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (radio->owner && radio->owner != file) 109162306a36Sopenharmony_ci return EPOLLERR; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci radio->owner = file; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) { 109662306a36Sopenharmony_ci poll_wait(file, &radio->read_queue, pts); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (radio->rd_index != radio->wr_index) 109962306a36Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci } else if (core->mode == WL1273_MODE_TX) { 110262306a36Sopenharmony_ci return EPOLLOUT | EPOLLWRNORM; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci return 0; 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic int wl1273_fm_fops_open(struct file *file) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 111162306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 111262306a36Sopenharmony_ci int r = 0; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX && radio->rds_on && 111762306a36Sopenharmony_ci !radio->rds_users) { 111862306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: Mode: %d\n", __func__, core->mode); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 112162306a36Sopenharmony_ci return -EINTR; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci radio->irq_flags |= WL1273_RDS_EVENT; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci r = core->write(core, WL1273_INT_MASK_SET, 112662306a36Sopenharmony_ci radio->irq_flags); 112762306a36Sopenharmony_ci if (r) { 112862306a36Sopenharmony_ci mutex_unlock(&core->lock); 112962306a36Sopenharmony_ci goto out; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci radio->rds_users++; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci mutex_unlock(&core->lock); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ciout: 113762306a36Sopenharmony_ci return r; 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic int wl1273_fm_fops_release(struct file *file) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 114362306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 114462306a36Sopenharmony_ci int r = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (radio->rds_users > 0) { 114962306a36Sopenharmony_ci radio->rds_users--; 115062306a36Sopenharmony_ci if (radio->rds_users == 0) { 115162306a36Sopenharmony_ci mutex_lock(&core->lock); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci radio->irq_flags &= ~WL1273_RDS_EVENT; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) { 115662306a36Sopenharmony_ci r = core->write(core, 115762306a36Sopenharmony_ci WL1273_INT_MASK_SET, 115862306a36Sopenharmony_ci radio->irq_flags); 115962306a36Sopenharmony_ci if (r) { 116062306a36Sopenharmony_ci mutex_unlock(&core->lock); 116162306a36Sopenharmony_ci goto out; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci mutex_unlock(&core->lock); 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (file == radio->owner) 116962306a36Sopenharmony_ci radio->owner = NULL; 117062306a36Sopenharmony_ciout: 117162306a36Sopenharmony_ci return r; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cistatic ssize_t wl1273_fm_fops_read(struct file *file, char __user *buf, 117562306a36Sopenharmony_ci size_t count, loff_t *ppos) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci int r = 0; 117862306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 117962306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 118062306a36Sopenharmony_ci unsigned int block_count = 0; 118162306a36Sopenharmony_ci u16 val; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (core->mode != WL1273_MODE_RX) 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (radio->rds_users == 0) { 118962306a36Sopenharmony_ci dev_warn(radio->dev, "%s: RDS not on.\n", __func__); 119062306a36Sopenharmony_ci return 0; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 119462306a36Sopenharmony_ci return -EINTR; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* 119762306a36Sopenharmony_ci * Multiple processes can open the device, but only 119862306a36Sopenharmony_ci * one at a time gets read access. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci if (radio->owner && radio->owner != file) { 120162306a36Sopenharmony_ci r = -EBUSY; 120262306a36Sopenharmony_ci goto out; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci radio->owner = file; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci r = core->read(core, WL1273_RDS_SYNC_GET, &val); 120762306a36Sopenharmony_ci if (r) { 120862306a36Sopenharmony_ci dev_err(radio->dev, "%s: Get RDS_SYNC fails.\n", __func__); 120962306a36Sopenharmony_ci goto out; 121062306a36Sopenharmony_ci } else if (val == 0) { 121162306a36Sopenharmony_ci dev_info(radio->dev, "RDS_SYNC: Not synchronized\n"); 121262306a36Sopenharmony_ci r = -ENODATA; 121362306a36Sopenharmony_ci goto out; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* block if no new data available */ 121762306a36Sopenharmony_ci while (radio->wr_index == radio->rd_index) { 121862306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 121962306a36Sopenharmony_ci r = -EWOULDBLOCK; 122062306a36Sopenharmony_ci goto out; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: Wait for RDS data.\n", __func__); 122462306a36Sopenharmony_ci if (wait_event_interruptible(radio->read_queue, 122562306a36Sopenharmony_ci radio->wr_index != 122662306a36Sopenharmony_ci radio->rd_index) < 0) { 122762306a36Sopenharmony_ci r = -EINTR; 122862306a36Sopenharmony_ci goto out; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* calculate block count from byte count */ 123362306a36Sopenharmony_ci count /= RDS_BLOCK_SIZE; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* copy RDS blocks from the internal buffer and to user buffer */ 123662306a36Sopenharmony_ci while (block_count < count) { 123762306a36Sopenharmony_ci if (radio->rd_index == radio->wr_index) 123862306a36Sopenharmony_ci break; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* always transfer complete RDS blocks */ 124162306a36Sopenharmony_ci if (copy_to_user(buf, &radio->buffer[radio->rd_index], 124262306a36Sopenharmony_ci RDS_BLOCK_SIZE)) 124362306a36Sopenharmony_ci break; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* increment and wrap the read pointer */ 124662306a36Sopenharmony_ci radio->rd_index += RDS_BLOCK_SIZE; 124762306a36Sopenharmony_ci if (radio->rd_index >= radio->buf_size) 124862306a36Sopenharmony_ci radio->rd_index = 0; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* increment counters */ 125162306a36Sopenharmony_ci block_count++; 125262306a36Sopenharmony_ci buf += RDS_BLOCK_SIZE; 125362306a36Sopenharmony_ci r += RDS_BLOCK_SIZE; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ciout: 125762306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: exit\n", __func__); 125862306a36Sopenharmony_ci mutex_unlock(&core->lock); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci return r; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistatic const struct v4l2_file_operations wl1273_fops = { 126462306a36Sopenharmony_ci .owner = THIS_MODULE, 126562306a36Sopenharmony_ci .read = wl1273_fm_fops_read, 126662306a36Sopenharmony_ci .write = wl1273_fm_fops_write, 126762306a36Sopenharmony_ci .poll = wl1273_fm_fops_poll, 126862306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 126962306a36Sopenharmony_ci .open = wl1273_fm_fops_open, 127062306a36Sopenharmony_ci .release = wl1273_fm_fops_release, 127162306a36Sopenharmony_ci}; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic int wl1273_fm_vidioc_querycap(struct file *file, void *priv, 127462306a36Sopenharmony_ci struct v4l2_capability *capability) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci strscpy(capability->driver, WL1273_FM_DRIVER_NAME, 128162306a36Sopenharmony_ci sizeof(capability->driver)); 128262306a36Sopenharmony_ci strscpy(capability->card, "TI Wl1273 FM Radio", 128362306a36Sopenharmony_ci sizeof(capability->card)); 128462306a36Sopenharmony_ci strscpy(capability->bus_info, radio->bus_type, 128562306a36Sopenharmony_ci sizeof(capability->bus_info)); 128662306a36Sopenharmony_ci return 0; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic int wl1273_fm_vidioc_g_input(struct file *file, void *priv, 129062306a36Sopenharmony_ci unsigned int *i) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci *i = 0; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci return 0; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_input(struct file *file, void *priv, 130262306a36Sopenharmony_ci unsigned int i) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci if (i != 0) 130962306a36Sopenharmony_ci return -EINVAL; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci return 0; 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci/** 131562306a36Sopenharmony_ci * wl1273_fm_set_tx_power() - Set the transmission power value. 131662306a36Sopenharmony_ci * @radio: A pointer to the device struct. 131762306a36Sopenharmony_ci * @power: The new power value. 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_cistatic int wl1273_fm_set_tx_power(struct wl1273_device *radio, u16 power) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 132262306a36Sopenharmony_ci int r; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (core->mode == WL1273_MODE_OFF || 132562306a36Sopenharmony_ci core->mode == WL1273_MODE_SUSPENDED) 132662306a36Sopenharmony_ci return -EPERM; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci mutex_lock(&core->lock); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* Convert the dBuV value to chip presentation */ 133162306a36Sopenharmony_ci r = core->write(core, WL1273_POWER_LEV_SET, 122 - power); 133262306a36Sopenharmony_ci if (r) 133362306a36Sopenharmony_ci goto out; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci radio->tx_power = power; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ciout: 133862306a36Sopenharmony_ci mutex_unlock(&core->lock); 133962306a36Sopenharmony_ci return r; 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci#define WL1273_SPACING_50kHz 1 134362306a36Sopenharmony_ci#define WL1273_SPACING_100kHz 2 134462306a36Sopenharmony_ci#define WL1273_SPACING_200kHz 4 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int wl1273_fm_tx_set_spacing(struct wl1273_device *radio, 134762306a36Sopenharmony_ci unsigned int spacing) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 135062306a36Sopenharmony_ci int r; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (spacing == 0) { 135362306a36Sopenharmony_ci r = core->write(core, WL1273_SCAN_SPACING_SET, 135462306a36Sopenharmony_ci WL1273_SPACING_100kHz); 135562306a36Sopenharmony_ci radio->spacing = 100; 135662306a36Sopenharmony_ci } else if (spacing - 50000 < 25000) { 135762306a36Sopenharmony_ci r = core->write(core, WL1273_SCAN_SPACING_SET, 135862306a36Sopenharmony_ci WL1273_SPACING_50kHz); 135962306a36Sopenharmony_ci radio->spacing = 50; 136062306a36Sopenharmony_ci } else if (spacing - 100000 < 50000) { 136162306a36Sopenharmony_ci r = core->write(core, WL1273_SCAN_SPACING_SET, 136262306a36Sopenharmony_ci WL1273_SPACING_100kHz); 136362306a36Sopenharmony_ci radio->spacing = 100; 136462306a36Sopenharmony_ci } else { 136562306a36Sopenharmony_ci r = core->write(core, WL1273_SCAN_SPACING_SET, 136662306a36Sopenharmony_ci WL1273_SPACING_200kHz); 136762306a36Sopenharmony_ci radio->spacing = 200; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci return r; 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci struct wl1273_device *radio = ctrl->priv; 137662306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 138162306a36Sopenharmony_ci return -EINTR; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci switch (ctrl->id) { 138462306a36Sopenharmony_ci case V4L2_CID_TUNE_ANTENNA_CAPACITOR: 138562306a36Sopenharmony_ci ctrl->val = wl1273_fm_get_tx_ctune(radio); 138662306a36Sopenharmony_ci break; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci default: 138962306a36Sopenharmony_ci dev_warn(radio->dev, "%s: Unknown IOCTL: %d\n", 139062306a36Sopenharmony_ci __func__, ctrl->id); 139162306a36Sopenharmony_ci break; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci mutex_unlock(&core->lock); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci return 0; 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci#define WL1273_MUTE_SOFT_ENABLE (1 << 0) 140062306a36Sopenharmony_ci#define WL1273_MUTE_AC (1 << 1) 140162306a36Sopenharmony_ci#define WL1273_MUTE_HARD_LEFT (1 << 2) 140262306a36Sopenharmony_ci#define WL1273_MUTE_HARD_RIGHT (1 << 3) 140362306a36Sopenharmony_ci#define WL1273_MUTE_SOFT_FORCE (1 << 4) 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic inline struct wl1273_device *to_radio(struct v4l2_ctrl *ctrl) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci return container_of(ctrl->handler, struct wl1273_device, ctrl_handler); 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_ctrl(struct v4l2_ctrl *ctrl) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci struct wl1273_device *radio = to_radio(ctrl); 141362306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 141462306a36Sopenharmony_ci int r = 0; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci switch (ctrl->id) { 141962306a36Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 142062306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 142162306a36Sopenharmony_ci return -EINTR; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX && ctrl->val) 142462306a36Sopenharmony_ci r = core->write(core, 142562306a36Sopenharmony_ci WL1273_MUTE_STATUS_SET, 142662306a36Sopenharmony_ci WL1273_MUTE_HARD_LEFT | 142762306a36Sopenharmony_ci WL1273_MUTE_HARD_RIGHT); 142862306a36Sopenharmony_ci else if (core->mode == WL1273_MODE_RX) 142962306a36Sopenharmony_ci r = core->write(core, 143062306a36Sopenharmony_ci WL1273_MUTE_STATUS_SET, 0x0); 143162306a36Sopenharmony_ci else if (core->mode == WL1273_MODE_TX && ctrl->val) 143262306a36Sopenharmony_ci r = core->write(core, WL1273_MUTE, 1); 143362306a36Sopenharmony_ci else if (core->mode == WL1273_MODE_TX) 143462306a36Sopenharmony_ci r = core->write(core, WL1273_MUTE, 0); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci mutex_unlock(&core->lock); 143762306a36Sopenharmony_ci break; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: 144062306a36Sopenharmony_ci if (ctrl->val == 0) 144162306a36Sopenharmony_ci r = wl1273_fm_set_mode(radio, WL1273_MODE_OFF); 144262306a36Sopenharmony_ci else 144362306a36Sopenharmony_ci r = core->set_volume(core, core->volume); 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci case V4L2_CID_TUNE_PREEMPHASIS: 144762306a36Sopenharmony_ci r = wl1273_fm_set_preemphasis(radio, ctrl->val); 144862306a36Sopenharmony_ci break; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci case V4L2_CID_TUNE_POWER_LEVEL: 145162306a36Sopenharmony_ci r = wl1273_fm_set_tx_power(radio, ctrl->val); 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci default: 145562306a36Sopenharmony_ci dev_warn(radio->dev, "%s: Unknown IOCTL: %d\n", 145662306a36Sopenharmony_ci __func__, ctrl->id); 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 146162306a36Sopenharmony_ci return r; 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic int wl1273_fm_vidioc_g_audio(struct file *file, void *priv, 146562306a36Sopenharmony_ci struct v4l2_audio *audio) 146662306a36Sopenharmony_ci{ 146762306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if (audio->index > 1) 147262306a36Sopenharmony_ci return -EINVAL; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci strscpy(audio->name, "Radio", sizeof(audio->name)); 147562306a36Sopenharmony_ci audio->capability = V4L2_AUDCAP_STEREO; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci return 0; 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_audio(struct file *file, void *priv, 148162306a36Sopenharmony_ci const struct v4l2_audio *audio) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (audio->index != 0) 148862306a36Sopenharmony_ci return -EINVAL; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci return 0; 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci#define WL1273_RDS_NOT_SYNCHRONIZED 0 149462306a36Sopenharmony_ci#define WL1273_RDS_SYNCHRONIZED 1 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv, 149762306a36Sopenharmony_ci struct v4l2_tuner *tuner) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 150062306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 150162306a36Sopenharmony_ci u16 val; 150262306a36Sopenharmony_ci int r; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (tuner->index > 0) 150762306a36Sopenharmony_ci return -EINVAL; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci strscpy(tuner->name, WL1273_FM_DRIVER_NAME, sizeof(tuner->name)); 151062306a36Sopenharmony_ci tuner->type = V4L2_TUNER_RADIO; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci tuner->rangelow = WL1273_FREQ(WL1273_BAND_JAPAN_LOW); 151362306a36Sopenharmony_ci tuner->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS | 151662306a36Sopenharmony_ci V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO | 151762306a36Sopenharmony_ci V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (radio->stereo) 152062306a36Sopenharmony_ci tuner->audmode = V4L2_TUNER_MODE_STEREO; 152162306a36Sopenharmony_ci else 152262306a36Sopenharmony_ci tuner->audmode = V4L2_TUNER_MODE_MONO; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (core->mode != WL1273_MODE_RX) 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 152862306a36Sopenharmony_ci return -EINTR; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci r = core->read(core, WL1273_STEREO_GET, &val); 153162306a36Sopenharmony_ci if (r) 153262306a36Sopenharmony_ci goto out; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci if (val == 1) 153562306a36Sopenharmony_ci tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; 153662306a36Sopenharmony_ci else 153762306a36Sopenharmony_ci tuner->rxsubchans = V4L2_TUNER_SUB_MONO; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci r = core->read(core, WL1273_RSSI_LVL_GET, &val); 154062306a36Sopenharmony_ci if (r) 154162306a36Sopenharmony_ci goto out; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci tuner->signal = (s16) val; 154462306a36Sopenharmony_ci dev_dbg(radio->dev, "Signal: %d\n", tuner->signal); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci tuner->afc = 0; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci r = core->read(core, WL1273_RDS_SYNC_GET, &val); 154962306a36Sopenharmony_ci if (r) 155062306a36Sopenharmony_ci goto out; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (val == WL1273_RDS_SYNCHRONIZED) 155362306a36Sopenharmony_ci tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; 155462306a36Sopenharmony_ciout: 155562306a36Sopenharmony_ci mutex_unlock(&core->lock); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci return r; 155862306a36Sopenharmony_ci} 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_tuner(struct file *file, void *priv, 156162306a36Sopenharmony_ci const struct v4l2_tuner *tuner) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 156462306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 156562306a36Sopenharmony_ci int r = 0; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 156862306a36Sopenharmony_ci dev_dbg(radio->dev, "tuner->index: %d\n", tuner->index); 156962306a36Sopenharmony_ci dev_dbg(radio->dev, "tuner->name: %s\n", tuner->name); 157062306a36Sopenharmony_ci dev_dbg(radio->dev, "tuner->capability: 0x%04x\n", tuner->capability); 157162306a36Sopenharmony_ci dev_dbg(radio->dev, "tuner->rxsubchans: 0x%04x\n", tuner->rxsubchans); 157262306a36Sopenharmony_ci dev_dbg(radio->dev, "tuner->rangelow: %d\n", tuner->rangelow); 157362306a36Sopenharmony_ci dev_dbg(radio->dev, "tuner->rangehigh: %d\n", tuner->rangehigh); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci if (tuner->index > 0) 157662306a36Sopenharmony_ci return -EINVAL; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 157962306a36Sopenharmony_ci return -EINTR; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci r = wl1273_fm_set_mode(radio, WL1273_MODE_RX); 158262306a36Sopenharmony_ci if (r) 158362306a36Sopenharmony_ci goto out; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) 158662306a36Sopenharmony_ci r = wl1273_fm_set_rds(radio, WL1273_RDS_ON); 158762306a36Sopenharmony_ci else 158862306a36Sopenharmony_ci r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (r) 159162306a36Sopenharmony_ci dev_warn(radio->dev, "%s: RDS fails: %d\n", __func__, r); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (tuner->audmode == V4L2_TUNER_MODE_MONO) { 159462306a36Sopenharmony_ci r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); 159562306a36Sopenharmony_ci if (r < 0) { 159662306a36Sopenharmony_ci dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", 159762306a36Sopenharmony_ci __func__, r); 159862306a36Sopenharmony_ci goto out; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci radio->stereo = false; 160162306a36Sopenharmony_ci } else if (tuner->audmode == V4L2_TUNER_MODE_STEREO) { 160262306a36Sopenharmony_ci r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); 160362306a36Sopenharmony_ci if (r < 0) { 160462306a36Sopenharmony_ci dev_warn(radio->dev, "%s: MOST_MODE fails: %d\n", 160562306a36Sopenharmony_ci __func__, r); 160662306a36Sopenharmony_ci goto out; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci radio->stereo = true; 160962306a36Sopenharmony_ci } else { 161062306a36Sopenharmony_ci dev_err(radio->dev, "%s: tuner->audmode: %d\n", 161162306a36Sopenharmony_ci __func__, tuner->audmode); 161262306a36Sopenharmony_ci r = -EINVAL; 161362306a36Sopenharmony_ci goto out; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ciout: 161762306a36Sopenharmony_ci mutex_unlock(&core->lock); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci return r; 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cistatic int wl1273_fm_vidioc_g_frequency(struct file *file, void *priv, 162362306a36Sopenharmony_ci struct v4l2_frequency *freq) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 162662306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 163162306a36Sopenharmony_ci return -EINTR; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci freq->type = V4L2_TUNER_RADIO; 163462306a36Sopenharmony_ci freq->frequency = WL1273_FREQ(wl1273_fm_get_freq(radio)); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci mutex_unlock(&core->lock); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci return 0; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_frequency(struct file *file, void *priv, 164262306a36Sopenharmony_ci const struct v4l2_frequency *freq) 164362306a36Sopenharmony_ci{ 164462306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 164562306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 164662306a36Sopenharmony_ci int r; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci dev_dbg(radio->dev, "%s: %d\n", __func__, freq->frequency); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (freq->type != V4L2_TUNER_RADIO) { 165162306a36Sopenharmony_ci dev_dbg(radio->dev, 165262306a36Sopenharmony_ci "freq->type != V4L2_TUNER_RADIO: %d\n", freq->type); 165362306a36Sopenharmony_ci return -EINVAL; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 165762306a36Sopenharmony_ci return -EINTR; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci if (core->mode == WL1273_MODE_RX) { 166062306a36Sopenharmony_ci dev_dbg(radio->dev, "freq: %d\n", freq->frequency); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci r = wl1273_fm_set_rx_freq(radio, 166362306a36Sopenharmony_ci WL1273_INV_FREQ(freq->frequency)); 166462306a36Sopenharmony_ci if (r) 166562306a36Sopenharmony_ci dev_warn(radio->dev, WL1273_FM_DRIVER_NAME 166662306a36Sopenharmony_ci ": set frequency failed with %d\n", r); 166762306a36Sopenharmony_ci } else { 166862306a36Sopenharmony_ci r = wl1273_fm_set_tx_freq(radio, 166962306a36Sopenharmony_ci WL1273_INV_FREQ(freq->frequency)); 167062306a36Sopenharmony_ci if (r) 167162306a36Sopenharmony_ci dev_warn(radio->dev, WL1273_FM_DRIVER_NAME 167262306a36Sopenharmony_ci ": set frequency failed with %d\n", r); 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci mutex_unlock(&core->lock); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci dev_dbg(radio->dev, "wl1273_vidioc_s_frequency: DONE\n"); 167862306a36Sopenharmony_ci return r; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci#define WL1273_DEFAULT_SEEK_LEVEL 7 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv, 168462306a36Sopenharmony_ci const struct v4l2_hw_freq_seek *seek) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 168762306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 168862306a36Sopenharmony_ci int r; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (seek->tuner != 0 || seek->type != V4L2_TUNER_RADIO) 169362306a36Sopenharmony_ci return -EINVAL; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 169662306a36Sopenharmony_ci return -EWOULDBLOCK; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 169962306a36Sopenharmony_ci return -EINTR; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci r = wl1273_fm_set_mode(radio, WL1273_MODE_RX); 170262306a36Sopenharmony_ci if (r) 170362306a36Sopenharmony_ci goto out; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci r = wl1273_fm_tx_set_spacing(radio, seek->spacing); 170662306a36Sopenharmony_ci if (r) 170762306a36Sopenharmony_ci dev_warn(radio->dev, "HW seek failed: %d\n", r); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci r = wl1273_fm_set_seek(radio, seek->wrap_around, seek->seek_upward, 171062306a36Sopenharmony_ci WL1273_DEFAULT_SEEK_LEVEL); 171162306a36Sopenharmony_ci if (r) 171262306a36Sopenharmony_ci dev_warn(radio->dev, "HW seek failed: %d\n", r); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ciout: 171562306a36Sopenharmony_ci mutex_unlock(&core->lock); 171662306a36Sopenharmony_ci return r; 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic int wl1273_fm_vidioc_s_modulator(struct file *file, void *priv, 172062306a36Sopenharmony_ci const struct v4l2_modulator *modulator) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 172362306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 172462306a36Sopenharmony_ci int r = 0; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (modulator->index > 0) 172962306a36Sopenharmony_ci return -EINVAL; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 173262306a36Sopenharmony_ci return -EINTR; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci r = wl1273_fm_set_mode(radio, WL1273_MODE_TX); 173562306a36Sopenharmony_ci if (r) 173662306a36Sopenharmony_ci goto out; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (modulator->txsubchans & V4L2_TUNER_SUB_RDS) 173962306a36Sopenharmony_ci r = wl1273_fm_set_rds(radio, WL1273_RDS_ON); 174062306a36Sopenharmony_ci else 174162306a36Sopenharmony_ci r = wl1273_fm_set_rds(radio, WL1273_RDS_OFF); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (modulator->txsubchans & V4L2_TUNER_SUB_MONO) 174462306a36Sopenharmony_ci r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); 174562306a36Sopenharmony_ci else 174662306a36Sopenharmony_ci r = core->write(core, WL1273_MONO_SET, 174762306a36Sopenharmony_ci WL1273_RX_STEREO); 174862306a36Sopenharmony_ci if (r < 0) 174962306a36Sopenharmony_ci dev_warn(radio->dev, WL1273_FM_DRIVER_NAME 175062306a36Sopenharmony_ci "MONO_SET fails: %d\n", r); 175162306a36Sopenharmony_ciout: 175262306a36Sopenharmony_ci mutex_unlock(&core->lock); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci return r; 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_cistatic int wl1273_fm_vidioc_g_modulator(struct file *file, void *priv, 175862306a36Sopenharmony_ci struct v4l2_modulator *modulator) 175962306a36Sopenharmony_ci{ 176062306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 176162306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 176262306a36Sopenharmony_ci u16 val; 176362306a36Sopenharmony_ci int r; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci dev_dbg(radio->dev, "%s\n", __func__); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci strscpy(modulator->name, WL1273_FM_DRIVER_NAME, 176862306a36Sopenharmony_ci sizeof(modulator->name)); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci modulator->rangelow = WL1273_FREQ(WL1273_BAND_JAPAN_LOW); 177162306a36Sopenharmony_ci modulator->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci modulator->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS | 177462306a36Sopenharmony_ci V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci if (core->mode != WL1273_MODE_TX) 177762306a36Sopenharmony_ci return 0; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci if (mutex_lock_interruptible(&core->lock)) 178062306a36Sopenharmony_ci return -EINTR; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci r = core->read(core, WL1273_MONO_SET, &val); 178362306a36Sopenharmony_ci if (r) 178462306a36Sopenharmony_ci goto out; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci if (val == WL1273_TX_STEREO) 178762306a36Sopenharmony_ci modulator->txsubchans = V4L2_TUNER_SUB_STEREO; 178862306a36Sopenharmony_ci else 178962306a36Sopenharmony_ci modulator->txsubchans = V4L2_TUNER_SUB_MONO; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci if (radio->rds_on) 179262306a36Sopenharmony_ci modulator->txsubchans |= V4L2_TUNER_SUB_RDS; 179362306a36Sopenharmony_ciout: 179462306a36Sopenharmony_ci mutex_unlock(&core->lock); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci return 0; 179762306a36Sopenharmony_ci} 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_cistatic int wl1273_fm_vidioc_log_status(struct file *file, void *priv) 180062306a36Sopenharmony_ci{ 180162306a36Sopenharmony_ci struct wl1273_device *radio = video_get_drvdata(video_devdata(file)); 180262306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 180362306a36Sopenharmony_ci struct device *dev = radio->dev; 180462306a36Sopenharmony_ci u16 val; 180562306a36Sopenharmony_ci int r; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci dev_info(dev, DRIVER_DESC); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (core->mode == WL1273_MODE_OFF) { 181062306a36Sopenharmony_ci dev_info(dev, "Mode: Off\n"); 181162306a36Sopenharmony_ci return 0; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (core->mode == WL1273_MODE_SUSPENDED) { 181562306a36Sopenharmony_ci dev_info(dev, "Mode: Suspended\n"); 181662306a36Sopenharmony_ci return 0; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci r = core->read(core, WL1273_ASIC_ID_GET, &val); 182062306a36Sopenharmony_ci if (r) 182162306a36Sopenharmony_ci dev_err(dev, "%s: Get ASIC_ID fails.\n", __func__); 182262306a36Sopenharmony_ci else 182362306a36Sopenharmony_ci dev_info(dev, "ASIC_ID: 0x%04x\n", val); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci r = core->read(core, WL1273_ASIC_VER_GET, &val); 182662306a36Sopenharmony_ci if (r) 182762306a36Sopenharmony_ci dev_err(dev, "%s: Get ASIC_VER fails.\n", __func__); 182862306a36Sopenharmony_ci else 182962306a36Sopenharmony_ci dev_info(dev, "ASIC Version: 0x%04x\n", val); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci r = core->read(core, WL1273_FIRM_VER_GET, &val); 183262306a36Sopenharmony_ci if (r) 183362306a36Sopenharmony_ci dev_err(dev, "%s: Get FIRM_VER fails.\n", __func__); 183462306a36Sopenharmony_ci else 183562306a36Sopenharmony_ci dev_info(dev, "FW version: %d(0x%04x)\n", val, val); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci r = core->read(core, WL1273_BAND_SET, &val); 183862306a36Sopenharmony_ci if (r) 183962306a36Sopenharmony_ci dev_err(dev, "%s: Get BAND fails.\n", __func__); 184062306a36Sopenharmony_ci else 184162306a36Sopenharmony_ci dev_info(dev, "BAND: %d\n", val); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (core->mode == WL1273_MODE_TX) { 184462306a36Sopenharmony_ci r = core->read(core, WL1273_PUPD_SET, &val); 184562306a36Sopenharmony_ci if (r) 184662306a36Sopenharmony_ci dev_err(dev, "%s: Get PUPD fails.\n", __func__); 184762306a36Sopenharmony_ci else 184862306a36Sopenharmony_ci dev_info(dev, "PUPD: 0x%04x\n", val); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci r = core->read(core, WL1273_CHANL_SET, &val); 185162306a36Sopenharmony_ci if (r) 185262306a36Sopenharmony_ci dev_err(dev, "%s: Get CHANL fails.\n", __func__); 185362306a36Sopenharmony_ci else 185462306a36Sopenharmony_ci dev_info(dev, "Tx frequency: %dkHz\n", val*10); 185562306a36Sopenharmony_ci } else if (core->mode == WL1273_MODE_RX) { 185662306a36Sopenharmony_ci int bf = radio->rangelow; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci r = core->read(core, WL1273_FREQ_SET, &val); 185962306a36Sopenharmony_ci if (r) 186062306a36Sopenharmony_ci dev_err(dev, "%s: Get FREQ fails.\n", __func__); 186162306a36Sopenharmony_ci else 186262306a36Sopenharmony_ci dev_info(dev, "RX Frequency: %dkHz\n", bf + val*50); 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci r = core->read(core, WL1273_MOST_MODE_SET, &val); 186562306a36Sopenharmony_ci if (r) 186662306a36Sopenharmony_ci dev_err(dev, "%s: Get MOST_MODE fails.\n", 186762306a36Sopenharmony_ci __func__); 186862306a36Sopenharmony_ci else if (val == 0) 186962306a36Sopenharmony_ci dev_info(dev, "MOST_MODE: Stereo according to blend\n"); 187062306a36Sopenharmony_ci else if (val == 1) 187162306a36Sopenharmony_ci dev_info(dev, "MOST_MODE: Force mono output\n"); 187262306a36Sopenharmony_ci else 187362306a36Sopenharmony_ci dev_info(dev, "MOST_MODE: Unexpected value: %d\n", val); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci r = core->read(core, WL1273_MOST_BLEND_SET, &val); 187662306a36Sopenharmony_ci if (r) 187762306a36Sopenharmony_ci dev_err(dev, "%s: Get MOST_BLEND fails.\n", __func__); 187862306a36Sopenharmony_ci else if (val == 0) 187962306a36Sopenharmony_ci dev_info(dev, 188062306a36Sopenharmony_ci "MOST_BLEND: Switched blend & hysteresis.\n"); 188162306a36Sopenharmony_ci else if (val == 1) 188262306a36Sopenharmony_ci dev_info(dev, "MOST_BLEND: Soft blend.\n"); 188362306a36Sopenharmony_ci else 188462306a36Sopenharmony_ci dev_info(dev, "MOST_BLEND: Unexpected val: %d\n", val); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci r = core->read(core, WL1273_STEREO_GET, &val); 188762306a36Sopenharmony_ci if (r) 188862306a36Sopenharmony_ci dev_err(dev, "%s: Get STEREO fails.\n", __func__); 188962306a36Sopenharmony_ci else if (val == 0) 189062306a36Sopenharmony_ci dev_info(dev, "STEREO: Not detected\n"); 189162306a36Sopenharmony_ci else if (val == 1) 189262306a36Sopenharmony_ci dev_info(dev, "STEREO: Detected\n"); 189362306a36Sopenharmony_ci else 189462306a36Sopenharmony_ci dev_info(dev, "STEREO: Unexpected value: %d\n", val); 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci r = core->read(core, WL1273_RSSI_LVL_GET, &val); 189762306a36Sopenharmony_ci if (r) 189862306a36Sopenharmony_ci dev_err(dev, "%s: Get RSSI_LVL fails.\n", __func__); 189962306a36Sopenharmony_ci else 190062306a36Sopenharmony_ci dev_info(dev, "RX signal strength: %d\n", (s16) val); 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci r = core->read(core, WL1273_POWER_SET, &val); 190362306a36Sopenharmony_ci if (r) 190462306a36Sopenharmony_ci dev_err(dev, "%s: Get POWER fails.\n", __func__); 190562306a36Sopenharmony_ci else 190662306a36Sopenharmony_ci dev_info(dev, "POWER: 0x%04x\n", val); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci r = core->read(core, WL1273_INT_MASK_SET, &val); 190962306a36Sopenharmony_ci if (r) 191062306a36Sopenharmony_ci dev_err(dev, "%s: Get INT_MASK fails.\n", __func__); 191162306a36Sopenharmony_ci else 191262306a36Sopenharmony_ci dev_info(dev, "INT_MASK: 0x%04x\n", val); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci r = core->read(core, WL1273_RDS_SYNC_GET, &val); 191562306a36Sopenharmony_ci if (r) 191662306a36Sopenharmony_ci dev_err(dev, "%s: Get RDS_SYNC fails.\n", 191762306a36Sopenharmony_ci __func__); 191862306a36Sopenharmony_ci else if (val == 0) 191962306a36Sopenharmony_ci dev_info(dev, "RDS_SYNC: Not synchronized\n"); 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci else if (val == 1) 192262306a36Sopenharmony_ci dev_info(dev, "RDS_SYNC: Synchronized\n"); 192362306a36Sopenharmony_ci else 192462306a36Sopenharmony_ci dev_info(dev, "RDS_SYNC: Unexpected value: %d\n", val); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci r = core->read(core, WL1273_I2S_MODE_CONFIG_SET, &val); 192762306a36Sopenharmony_ci if (r) 192862306a36Sopenharmony_ci dev_err(dev, "%s: Get I2S_MODE_CONFIG fails.\n", 192962306a36Sopenharmony_ci __func__); 193062306a36Sopenharmony_ci else 193162306a36Sopenharmony_ci dev_info(dev, "I2S_MODE_CONFIG: 0x%04x\n", val); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci r = core->read(core, WL1273_VOLUME_SET, &val); 193462306a36Sopenharmony_ci if (r) 193562306a36Sopenharmony_ci dev_err(dev, "%s: Get VOLUME fails.\n", __func__); 193662306a36Sopenharmony_ci else 193762306a36Sopenharmony_ci dev_info(dev, "VOLUME: 0x%04x\n", val); 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci return 0; 194162306a36Sopenharmony_ci} 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_cistatic void wl1273_vdev_release(struct video_device *dev) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci} 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops wl1273_ctrl_ops = { 194862306a36Sopenharmony_ci .s_ctrl = wl1273_fm_vidioc_s_ctrl, 194962306a36Sopenharmony_ci .g_volatile_ctrl = wl1273_fm_g_volatile_ctrl, 195062306a36Sopenharmony_ci}; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops wl1273_ioctl_ops = { 195362306a36Sopenharmony_ci .vidioc_querycap = wl1273_fm_vidioc_querycap, 195462306a36Sopenharmony_ci .vidioc_g_input = wl1273_fm_vidioc_g_input, 195562306a36Sopenharmony_ci .vidioc_s_input = wl1273_fm_vidioc_s_input, 195662306a36Sopenharmony_ci .vidioc_g_audio = wl1273_fm_vidioc_g_audio, 195762306a36Sopenharmony_ci .vidioc_s_audio = wl1273_fm_vidioc_s_audio, 195862306a36Sopenharmony_ci .vidioc_g_tuner = wl1273_fm_vidioc_g_tuner, 195962306a36Sopenharmony_ci .vidioc_s_tuner = wl1273_fm_vidioc_s_tuner, 196062306a36Sopenharmony_ci .vidioc_g_frequency = wl1273_fm_vidioc_g_frequency, 196162306a36Sopenharmony_ci .vidioc_s_frequency = wl1273_fm_vidioc_s_frequency, 196262306a36Sopenharmony_ci .vidioc_s_hw_freq_seek = wl1273_fm_vidioc_s_hw_freq_seek, 196362306a36Sopenharmony_ci .vidioc_g_modulator = wl1273_fm_vidioc_g_modulator, 196462306a36Sopenharmony_ci .vidioc_s_modulator = wl1273_fm_vidioc_s_modulator, 196562306a36Sopenharmony_ci .vidioc_log_status = wl1273_fm_vidioc_log_status, 196662306a36Sopenharmony_ci}; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_cistatic const struct video_device wl1273_viddev_template = { 196962306a36Sopenharmony_ci .fops = &wl1273_fops, 197062306a36Sopenharmony_ci .ioctl_ops = &wl1273_ioctl_ops, 197162306a36Sopenharmony_ci .name = WL1273_FM_DRIVER_NAME, 197262306a36Sopenharmony_ci .release = wl1273_vdev_release, 197362306a36Sopenharmony_ci .vfl_dir = VFL_DIR_TX, 197462306a36Sopenharmony_ci .device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | 197562306a36Sopenharmony_ci V4L2_CAP_RADIO | V4L2_CAP_AUDIO | 197662306a36Sopenharmony_ci V4L2_CAP_RDS_CAPTURE | V4L2_CAP_MODULATOR | 197762306a36Sopenharmony_ci V4L2_CAP_RDS_OUTPUT, 197862306a36Sopenharmony_ci}; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_cistatic void wl1273_fm_radio_remove(struct platform_device *pdev) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci struct wl1273_device *radio = platform_get_drvdata(pdev); 198362306a36Sopenharmony_ci struct wl1273_core *core = radio->core; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci dev_info(&pdev->dev, "%s.\n", __func__); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci free_irq(core->client->irq, radio); 198862306a36Sopenharmony_ci core->pdata->free_resources(); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci v4l2_ctrl_handler_free(&radio->ctrl_handler); 199162306a36Sopenharmony_ci video_unregister_device(&radio->videodev); 199262306a36Sopenharmony_ci v4l2_device_unregister(&radio->v4l2dev); 199362306a36Sopenharmony_ci} 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_cistatic int wl1273_fm_radio_probe(struct platform_device *pdev) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci struct wl1273_core **core = pdev->dev.platform_data; 199862306a36Sopenharmony_ci struct wl1273_device *radio; 199962306a36Sopenharmony_ci struct v4l2_ctrl *ctrl; 200062306a36Sopenharmony_ci int r = 0; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci pr_debug("%s\n", __func__); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci if (!core) { 200562306a36Sopenharmony_ci dev_err(&pdev->dev, "No platform data.\n"); 200662306a36Sopenharmony_ci r = -EINVAL; 200762306a36Sopenharmony_ci goto pdata_err; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); 201162306a36Sopenharmony_ci if (!radio) { 201262306a36Sopenharmony_ci r = -ENOMEM; 201362306a36Sopenharmony_ci goto pdata_err; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci /* RDS buffer allocation */ 201762306a36Sopenharmony_ci radio->buf_size = rds_buf * RDS_BLOCK_SIZE; 201862306a36Sopenharmony_ci radio->buffer = devm_kzalloc(&pdev->dev, radio->buf_size, GFP_KERNEL); 201962306a36Sopenharmony_ci if (!radio->buffer) { 202062306a36Sopenharmony_ci pr_err("Cannot allocate memory for RDS buffer.\n"); 202162306a36Sopenharmony_ci r = -ENOMEM; 202262306a36Sopenharmony_ci goto pdata_err; 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci radio->core = *core; 202662306a36Sopenharmony_ci radio->irq_flags = WL1273_IRQ_MASK; 202762306a36Sopenharmony_ci radio->dev = &radio->core->client->dev; 202862306a36Sopenharmony_ci radio->rds_on = false; 202962306a36Sopenharmony_ci radio->core->mode = WL1273_MODE_OFF; 203062306a36Sopenharmony_ci radio->tx_power = 118; 203162306a36Sopenharmony_ci radio->core->audio_mode = WL1273_AUDIO_ANALOG; 203262306a36Sopenharmony_ci radio->band = WL1273_BAND_OTHER; 203362306a36Sopenharmony_ci radio->core->i2s_mode = WL1273_I2S_DEF_MODE; 203462306a36Sopenharmony_ci radio->core->channel_number = 2; 203562306a36Sopenharmony_ci radio->core->volume = WL1273_DEFAULT_VOLUME; 203662306a36Sopenharmony_ci radio->rx_frequency = WL1273_BAND_OTHER_LOW; 203762306a36Sopenharmony_ci radio->tx_frequency = WL1273_BAND_OTHER_HIGH; 203862306a36Sopenharmony_ci radio->rangelow = WL1273_BAND_OTHER_LOW; 203962306a36Sopenharmony_ci radio->rangehigh = WL1273_BAND_OTHER_HIGH; 204062306a36Sopenharmony_ci radio->stereo = true; 204162306a36Sopenharmony_ci radio->bus_type = "I2C"; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci if (radio->core->pdata->request_resources) { 204462306a36Sopenharmony_ci r = radio->core->pdata->request_resources(radio->core->client); 204562306a36Sopenharmony_ci if (r) { 204662306a36Sopenharmony_ci dev_err(radio->dev, WL1273_FM_DRIVER_NAME 204762306a36Sopenharmony_ci ": Cannot get platform data\n"); 204862306a36Sopenharmony_ci goto pdata_err; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci dev_dbg(radio->dev, "irq: %d\n", radio->core->client->irq); 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci r = request_threaded_irq(radio->core->client->irq, NULL, 205462306a36Sopenharmony_ci wl1273_fm_irq_thread_handler, 205562306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 205662306a36Sopenharmony_ci "wl1273-fm", radio); 205762306a36Sopenharmony_ci if (r < 0) { 205862306a36Sopenharmony_ci dev_err(radio->dev, WL1273_FM_DRIVER_NAME 205962306a36Sopenharmony_ci ": Unable to register IRQ handler: %d\n", r); 206062306a36Sopenharmony_ci goto err_request_irq; 206162306a36Sopenharmony_ci } 206262306a36Sopenharmony_ci } else { 206362306a36Sopenharmony_ci dev_err(radio->dev, WL1273_FM_DRIVER_NAME ": Core WL1273 IRQ not configured"); 206462306a36Sopenharmony_ci r = -EINVAL; 206562306a36Sopenharmony_ci goto pdata_err; 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci init_completion(&radio->busy); 206962306a36Sopenharmony_ci init_waitqueue_head(&radio->read_queue); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci radio->write_buf = devm_kzalloc(&pdev->dev, 256, GFP_KERNEL); 207262306a36Sopenharmony_ci if (!radio->write_buf) { 207362306a36Sopenharmony_ci r = -ENOMEM; 207462306a36Sopenharmony_ci goto write_buf_err; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci radio->dev = &pdev->dev; 207862306a36Sopenharmony_ci radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; 207962306a36Sopenharmony_ci radio->rds_users = 0; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci r = v4l2_device_register(&pdev->dev, &radio->v4l2dev); 208262306a36Sopenharmony_ci if (r) { 208362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register v4l2_device.\n"); 208462306a36Sopenharmony_ci goto write_buf_err; 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* V4L2 configuration */ 208862306a36Sopenharmony_ci radio->videodev = wl1273_viddev_template; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci radio->videodev.v4l2_dev = &radio->v4l2dev; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci v4l2_ctrl_handler_init(&radio->ctrl_handler, 6); 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci /* add in ascending ID order */ 209562306a36Sopenharmony_ci v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops, 209662306a36Sopenharmony_ci V4L2_CID_AUDIO_VOLUME, 0, WL1273_MAX_VOLUME, 1, 209762306a36Sopenharmony_ci WL1273_DEFAULT_VOLUME); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops, 210062306a36Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&radio->ctrl_handler, &wl1273_ctrl_ops, 210362306a36Sopenharmony_ci V4L2_CID_TUNE_PREEMPHASIS, 210462306a36Sopenharmony_ci V4L2_PREEMPHASIS_75_uS, 0x03, 210562306a36Sopenharmony_ci V4L2_PREEMPHASIS_50_uS); 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops, 210862306a36Sopenharmony_ci V4L2_CID_TUNE_POWER_LEVEL, 91, 122, 1, 118); 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &wl1273_ctrl_ops, 211162306a36Sopenharmony_ci V4L2_CID_TUNE_ANTENNA_CAPACITOR, 211262306a36Sopenharmony_ci 0, 255, 1, 255); 211362306a36Sopenharmony_ci if (ctrl) 211462306a36Sopenharmony_ci ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci if (radio->ctrl_handler.error) { 211762306a36Sopenharmony_ci r = radio->ctrl_handler.error; 211862306a36Sopenharmony_ci dev_err(&pdev->dev, "Ctrl handler error: %d\n", r); 211962306a36Sopenharmony_ci goto handler_init_err; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci video_set_drvdata(&radio->videodev, radio); 212362306a36Sopenharmony_ci platform_set_drvdata(pdev, radio); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci /* register video device */ 212662306a36Sopenharmony_ci r = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); 212762306a36Sopenharmony_ci if (r) { 212862306a36Sopenharmony_ci dev_err(&pdev->dev, WL1273_FM_DRIVER_NAME 212962306a36Sopenharmony_ci ": Could not register video device\n"); 213062306a36Sopenharmony_ci goto handler_init_err; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci return 0; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cihandler_init_err: 213662306a36Sopenharmony_ci v4l2_ctrl_handler_free(&radio->ctrl_handler); 213762306a36Sopenharmony_ci v4l2_device_unregister(&radio->v4l2dev); 213862306a36Sopenharmony_ciwrite_buf_err: 213962306a36Sopenharmony_ci free_irq(radio->core->client->irq, radio); 214062306a36Sopenharmony_cierr_request_irq: 214162306a36Sopenharmony_ci radio->core->pdata->free_resources(); 214262306a36Sopenharmony_cipdata_err: 214362306a36Sopenharmony_ci return r; 214462306a36Sopenharmony_ci} 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_cistatic struct platform_driver wl1273_fm_radio_driver = { 214762306a36Sopenharmony_ci .probe = wl1273_fm_radio_probe, 214862306a36Sopenharmony_ci .remove_new = wl1273_fm_radio_remove, 214962306a36Sopenharmony_ci .driver = { 215062306a36Sopenharmony_ci .name = "wl1273_fm_radio", 215162306a36Sopenharmony_ci }, 215262306a36Sopenharmony_ci}; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_cimodule_platform_driver(wl1273_fm_radio_driver); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ciMODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>"); 215762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 215862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 215962306a36Sopenharmony_ciMODULE_ALIAS("platform:wl1273_fm_radio"); 2160