/* drivers/media/radio/rtc6226/radio-rtc6226-common.c * * Driver for Richwave RTC6226 FM Tuner * * Copyright (c) 2009 Tobias Lorenz * Copyright (c) 2012 Hans de Goede * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * 2008-01-12 Tobias Lorenz * Version 1.0.0 * - First working version * 2008-01-13 Tobias Lorenz * Version 1.0.1 * - Improved error handling, every function now returns errno * - Improved multi user access (start/mute/stop) * - Channel doesn't get lost anymore after start/mute/stop * - RDS support added (polling mode via interrupt EP 1) * - marked default module parameters with *value* * - switched from bit structs to bit masks * - header file cleaned and integrated * 2008-01-14 Tobias Lorenz * Version 1.0.2 * - hex values are now lower case * - commented USB ID for ADS/Tech moved on todo list * - blacklisted in hid-quirks.c * - rds buffer handling functions integrated into *_work, *_read * - rds_command exchanged against simple retval * - check for firmware version 15 * - code order and prototypes still remain the same * - spacing and bottom of band codes remain the same * 2008-01-16 Tobias Lorenz * Version 1.0.3 * - code reordered to avoid function prototypes * - switch/case defaults are now more user-friendly * - unified comment style * - applied all checkpatch.pl v1.12 suggestions * except the warning about the too long lines with bit comments * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) * 2008-01-22 Tobias Lorenz * Version 1.0.4 * - avoid poss. locking when doing copy_to_user which may sleep * - RDS is automatically activated on read now * - code cleaned of unnecessary rds_commands * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified * (thanks to Guillaume RAMOUSSE) * 2008-01-27 Tobias Lorenz * Version 1.0.5 * - number of seek_retries changed to tune_timeout * - fixed problem with incomplete tune operations by own buffers * - optimization of variables and printf types * - improved error logging * 2008-01-31 Tobias Lorenz * Oliver Neukum * Version 1.0.6 * - fixed coverity checker warnings in *_usb_driver_disconnect * - probe()/open() race by correct ordering in probe() * - DMA coherency rules by separate allocation of all buffers * - use of endianness macros * - abuse of spinlock, replaced by mutex * - racy handling of timer in disconnect, * replaced by delayed_work * - racy interruptible_sleep_on(), * replaced with wait_event_interruptible() * - handle signals in read() * 2008-02-08 Tobias Lorenz * Oliver Neukum * Version 1.0.7 * - usb autosuspend support * - unplugging fixed * 2008-05-07 Tobias Lorenz * Version 1.0.8 * - hardware frequency seek support * - afc indication * - more safety checks, get_freq return errno * - vidioc behavior corrected according to v4l2 spec * 2008-10-20 Alexey Klimov * - add support for KWorld USB FM Radio FM700 * - blacklisted KWorld radio in hid-core.c and hid-ids.h * 2008-12-03 Mark Lord * - add support for DealExtreme USB Radio * 2009-01-31 Bob Ross * - correction of stereo detection/setting * - correction of signal strength indicator scaling * 2009-01-31 Rick Bronson * Tobias Lorenz * - add LED status output * - get HW/SW version from scratchpad * 2009-06-16 Edouard Lafargue * Version 1.0.10 * - add support for interrupt mode for RDS endpoint, * instead of polling. * Improves RDS reception significantly * 2018-02-01 LG Electronics, Inc. * 2018-08-19 Richwave Technology Co.Ltd * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. */ /* kernel includes */ #include #include #include "radio-rtc6226.h" /************************************************************************** * Module Parameters **************************************************************************/ /* Bottom of Band (MHz) */ /* 0: 87.5 - 108 MHz (USA, Europe)*/ /* 1: 76 - 108 MHz (Japan wide band) */ /* 2: 76 - 90 MHz (Japan) */ /* De-emphasis */ /* 0: 75 us (USA) */ /* 1: 50 us (Europe, Australia, Japan) */ static unsigned short de; wait_queue_head_t rtc6226_wq; int rtc6226_wq_flag = NO_WAIT; #ifdef New_VolumeControl unsigned short global_volume; #endif void rtc6226_q_event(struct rtc6226_device *radio, enum rtc6226_evt_t event) { struct kfifo *data_b; unsigned char evt = event; data_b = &radio->data_buf[RTC6226_FM_BUF_EVENTS]; FMDBG("%s updating event_q with event %x\n", __func__, event); if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[RTC6226_FM_BUF_EVENTS])) wake_up_interruptible(&radio->event_queue); } /* * rtc6226_set_chan - set the channel */ static int rtc6226_set_chan(struct rtc6226_device *radio, unsigned short chan) { int retval; unsigned short current_chan = radio->registers[CHANNEL] & CHANNEL_CSR0_CH; FMDBG("%s CHAN=%d chan=%d\n", __func__, radio->registers[CHANNEL], chan); /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CSR0_CH; radio->registers[CHANNEL] |= CHANNEL_CSR0_TUNE | chan; retval = rtc6226_set_register(radio, CHANNEL); if (retval < 0) { radio->registers[CHANNEL] = current_chan; goto done; } done: FMDBG("%s exit %d\n", __func__, retval); return retval; } /* * rtc6226_get_freq - get the frequency */ static int rtc6226_get_freq(struct rtc6226_device *radio, unsigned int *freq) { unsigned short chan; unsigned short rssi = 0; int retval; FMDBG("%s enter\n", __func__); /* read channel */ retval = rtc6226_get_register(radio, CHANNEL1); if (retval < 0) { FMDBG("%s fail to get register\n", __func__); goto end; } chan = radio->registers[CHANNEL1] & STATUS_READCH; retval = rtc6226_get_register(radio, RSSI); rssi = radio->registers[RSSI] & RSSI_RSSI; FMDBG("%s chan %d\n", __func__, chan); *freq = chan * TUNE_STEP_SIZE; FMDBG("FMRICHWAVE, freq= %d, rssi= %d dBuV\n", *freq, rssi); if (rssi < radio->rssi_th) rtc6226_q_event(radio, RTC6226_EVT_BELOW_TH); else rtc6226_q_event(radio, RTC6226_EVT_ABOVE_TH); end: return retval; } /* * rtc6226_set_freq - set the frequency */ int rtc6226_set_freq(struct rtc6226_device *radio, unsigned int freq) { unsigned int band_bottom; unsigned short chan; unsigned char i; int retval = 0; FMDBG("%s enter freq:%d\n", __func__, freq); band_bottom = (radio->registers[RADIOSEEKCFG2] & CHANNEL_CSR0_FREQ_BOT) * TUNE_STEP_SIZE; if (freq < band_bottom) freq = band_bottom; /* Chan = Freq (Mhz) / 10 */ chan = (u16)(freq / TUNE_STEP_SIZE); FMDBG("%s chan:%d freq:%d band_bottom:%d\n", __func__, chan, freq, band_bottom); retval = rtc6226_set_chan(radio, chan); if (retval < 0) { FMDBG("%s fail to set chan\n", __func__); goto end; } for (i = 0x12; i < RADIO_REGISTER_NUM; i++) { retval = rtc6226_get_register(radio, i); if (retval < 0) { FMDBG("%s fail to get register\n", __func__); goto end; } } end: return retval; } /* * rtc6226_set_seek - set seek */ static int rtc6226_set_seek(struct rtc6226_device *radio, unsigned int seek_up, unsigned int seek_wrap) { int retval = 0; unsigned short seekcfg1_val = radio->registers[SEEKCFG1]; FMDBG("%s enter up:%d wrap:%d, th:%d\n", __func__, seek_up, seek_wrap, seekcfg1_val); if (seek_wrap) radio->registers[SEEKCFG1] &= ~SEEKCFG1_CSR0_SKMODE; else radio->registers[SEEKCFG1] |= SEEKCFG1_CSR0_SKMODE; if (seek_up) radio->registers[SEEKCFG1] |= SEEKCFG1_CSR0_SEEKUP; else radio->registers[SEEKCFG1] &= ~SEEKCFG1_CSR0_SEEKUP; radio->registers[SEEKCFG1] &= ~SEEKCFG1_CSR0_SEEK; retval = rtc6226_set_register(radio, SEEKCFG1); if (retval < 0) { radio->registers[SEEKCFG1] = seekcfg1_val; goto done; } /* start seeking */ radio->registers[SEEKCFG1] |= SEEKCFG1_CSR0_SEEK; retval = rtc6226_set_register(radio, SEEKCFG1); if (retval < 0) { radio->registers[SEEKCFG1] = seekcfg1_val; goto done; } done: FMDBG("%s exit %d\n", __func__, retval); return retval; } static void rtc6226_update_search_list(struct rtc6226_device *radio, int freq) { int temp_freq = freq; int index = radio->srch_list.num_stations_found; temp_freq = temp_freq - (radio->recv_conf.band_low_limit * TUNE_STEP_SIZE); temp_freq = temp_freq / 50; radio->srch_list.rel_freq[index].rel_freq_lsb = GET_LSB(temp_freq); radio->srch_list.rel_freq[index].rel_freq_msb = GET_MSB(temp_freq); radio->srch_list.num_stations_found++; } void rtc6226_scan(struct work_struct *work) { struct rtc6226_device *radio; int current_freq_khz = 0; struct kfifo *data_b; int len = 0; u32 next_freq_khz; int retval = 0; int i, rssi; FMDBG("%s enter\n", __func__); radio = container_of(work, struct rtc6226_device, work_scan.work); retval = rtc6226_get_freq(radio, ¤t_freq_khz); if (retval < 0) { FMDERR("%s fail to get freq\n", __func__); goto seek_tune_fail; } FMDBG("%s current freq %d\n", __func__, current_freq_khz); /* tune to lowest freq of the band */ radio->seek_tune_status = SCAN_PENDING; retval = rtc6226_set_freq(radio, radio->recv_conf.band_low_limit * TUNE_STEP_SIZE); if (retval < 0) goto seek_tune_fail; /* wait for tune to complete. */ if (!wait_for_completion_timeout(&radio->completion, msecs_to_jiffies(TUNE_TIMEOUT_MSEC))) { FMDERR("In %s, didn't receive STC for tune\n", __func__); rtc6226_q_event(radio, RTC6226_EVT_ERROR); return; } while (1) { if (radio->is_search_cancelled) { FMDERR("%s: scan cancelled\n", __func__); if (radio->g_search_mode == SCAN_FOR_STRONG) goto seek_tune_fail; else goto seek_cancelled; goto seek_cancelled; } else if (radio->mode != FM_RECV) { FMDERR("%s: FM is not in proper state\n", __func__); rtc6226_q_event(radio, RTC6226_EVT_ERROR); return; } retval = rtc6226_set_seek(radio, SRCH_UP, WRAP_DISABLE); if (retval < 0) { FMDERR("%s seek fail %d\n", __func__, retval); goto seek_tune_fail; } /* wait for seek to complete */ if (!wait_for_completion_timeout(&radio->completion, msecs_to_jiffies(SEEK_TIMEOUT_MSEC))) { FMDERR("%s:timeout didn't receive STC for seek\n", __func__); rtc6226_get_all_registers(radio); for (i = 0; i < 16; i++) FMDBG("%s registers[%d]:%x\n", __func__, i, radio->registers[i]); /* FM is not correct state or scan is cancelled */ rtc6226_q_event(radio, RTC6226_EVT_ERROR); return; } else FMDERR("%s: received STC for seek\n", __func__); retval = rtc6226_get_freq(radio, &next_freq_khz); if (retval < 0) { FMDERR("%s fail to get freq\n", __func__); goto seek_tune_fail; } FMDBG("%s next freq %d\n", __func__, next_freq_khz); retval = rtc6226_get_register(radio, RSSI); if (retval < 0) { FMDERR("%s read fail to RSSI\n", __func__); goto seek_tune_fail; } rssi = radio->registers[RSSI] & RSSI_RSSI; FMDBG("%s valid channel %d, rssi %d threshold rssi %d\n", __func__, next_freq_khz, rssi, radio->rssi_th); if (radio->g_search_mode == SCAN && rssi >= radio->rssi_th) rtc6226_q_event(radio, RTC6226_EVT_TUNE_SUCC); /* * If scan is cancelled or FM is not ON, break ASAP so that we * don't need to sleep for dwell time. */ if (radio->is_search_cancelled) { FMDERR("%s: scan cancelled\n", __func__); if (radio->g_search_mode == SCAN_FOR_STRONG) goto seek_tune_fail; else goto seek_cancelled; goto seek_cancelled; } else if (radio->mode != FM_RECV) { FMDERR("%s: FM is not in proper state\n", __func__); rtc6226_q_event(radio, RTC6226_EVT_ERROR); return; } FMDBG("%s update search list %d\n", __func__, next_freq_khz); if (radio->g_search_mode == SCAN && rssi >= radio->rssi_th) { /* sleep for dwell period */ msleep(radio->dwell_time_sec * 1000); /* need to queue the event when the seek completes */ FMDBG("%s frequency update list %d\n", __func__, next_freq_khz); rtc6226_q_event(radio, RTC6226_EVT_SCAN_NEXT); } else if (radio->g_search_mode == SCAN_FOR_STRONG && rssi >= radio->rssi_th) { rtc6226_update_search_list(radio, next_freq_khz); } FMDBG("%s : STATUS=0x%4.4hx\n", __func__, radio->registers[STATUS]); if (radio->registers[STATUS] & STATUS_SF || (radio->recv_conf.band_high_limit * TUNE_STEP_SIZE) == next_freq_khz) { FMDERR("%s Seek one more time if lower freq is valid\n", __func__); retval = rtc6226_set_seek(radio, SRCH_UP, WRAP_ENABLE); if (retval < 0) { FMDERR("%s seek fail %d\n", __func__, retval); goto seek_tune_fail; } if (!wait_for_completion_timeout(&radio->completion, msecs_to_jiffies(SEEK_TIMEOUT_MSEC))) { FMDERR("timeout didn't receive STC for seek\n"); rtc6226_q_event(radio, RTC6226_EVT_ERROR); return; } else { FMDERR("%s: received STC for seek\n", __func__); retval = rtc6226_get_freq(radio, &next_freq_khz); if (retval < 0) { FMDERR("%s getFreq failed\n", __func__); goto seek_tune_fail; } retval = rtc6226_get_register(radio, RSSI); if (retval < 0) { FMDERR("%s read fail to RSSI\n", __func__); goto seek_tune_fail; } rssi = radio->registers[RSSI] & RSSI_RSSI; FMDBG("%s freq %d, rssi %d rssi threshold %d\n", __func__, next_freq_khz, rssi, radio->rssi_th); if ((radio->recv_conf.band_low_limit * TUNE_STEP_SIZE) == next_freq_khz && rssi >= radio->rssi_th) { FMDERR("lower band freq is valid\n"); rtc6226_q_event(radio, RTC6226_EVT_TUNE_SUCC); /* sleep for dwell period */ msleep(radio->dwell_time_sec * 1000); rtc6226_q_event(radio, RTC6226_EVT_SCAN_NEXT); } } break; } } seek_tune_fail: if (radio->g_search_mode == SCAN_FOR_STRONG) { len = radio->srch_list.num_stations_found * 2 + sizeof(radio->srch_list.num_stations_found); data_b = &radio->data_buf[RTC6226_FM_BUF_SRCH_LIST]; kfifo_in_locked(data_b, &radio->srch_list, len, &radio->buf_lock[RTC6226_FM_BUF_SRCH_LIST]); rtc6226_q_event(radio, RTC6226_EVT_NEW_SRCH_LIST); } seek_cancelled: /* tune to original frequency */ retval = rtc6226_set_freq(radio, current_freq_khz); if (retval < 0) FMDERR("%s: Tune to orig freq failed with error %d\n", __func__, retval); else { if (!wait_for_completion_timeout(&radio->completion, msecs_to_jiffies(TUNE_TIMEOUT_MSEC))) FMDERR("%s: didn't receive STD for tune\n", __func__); else FMDERR("%s: received STD for tune\n", __func__); } /* Enable the RDS as it was disabled before scan */ rtc6226_rds_on(radio); rtc6226_q_event(radio, RTC6226_EVT_SEEK_COMPLETE); rtc6226_q_event(radio, RTC6226_EVT_TUNE_SUCC); radio->seek_tune_status = NO_SEEK_TUNE_PENDING; FMDERR("%s seek cancelled %d\n", __func__, retval); return; } int rtc6226_cancel_seek(struct rtc6226_device *radio) { int retval = 0; FMDBG("%s enter\n", __func__); mutex_lock(&radio->lock); /* stop seeking */ radio->registers[SEEKCFG1] &= ~SEEKCFG1_CSR0_SEEK; retval = rtc6226_set_register(radio, SEEKCFG1); complete(&radio->completion); mutex_unlock(&radio->lock); radio->is_search_cancelled = true; if (radio->g_search_mode == SEEK) rtc6226_q_event(radio, RTC6226_EVT_SEEK_COMPLETE); return retval; } void rtc6226_search(struct rtc6226_device *radio, bool on) { int current_freq_khz; current_freq_khz = radio->tuned_freq_khz; if (on) { FMDBG("%s: Queuing the work onto scan work q\n", __func__); queue_delayed_work(radio->wqueue_scan, &radio->work_scan, msecs_to_jiffies(10)); } else { rtc6226_cancel_seek(radio); } } /* * rtc6226_start - switch on radio */ int rtc6226_start(struct rtc6226_device *radio) { int retval; u8 i2c_error; u16 initbuf[] = {0x0000}; radio->registers[BANKCFG] = 0x0000; i2c_error = 0; /* Keep in case of any unpredicted control */ /* Set 0x16AA */ radio->registers[DEVICEID] = 0x16AA; /* released the I2C from unexpected I2C start condition */ retval = rtc6226_set_register(radio, DEVICEID); /* recheck TH : 10 */ while ((retval < 0) && (i2c_error < 10)) { retval = rtc6226_set_register(radio, DEVICEID); i2c_error++; } if (retval < 0) { FMDERR("%s set to fail retval = %d\n", __func__, retval); /* goto done;*/ } msleep(30); /* Don't read all between writing 0x16AA and 0x96AA */ i2c_error = 0; radio->registers[DEVICEID] = 0x96AA; retval = rtc6226_set_register(radio, DEVICEID); /* recheck TH : 10 */ while ((retval < 0) && (i2c_error < 10)) { retval = rtc6226_set_register(radio, DEVICEID); i2c_error++; } if (retval < 0) FMDERR("%s set to fail 0x96AA %d\n", __func__, retval); msleep(30); /* get device and chip versions */ rtc6226_get_register(radio, DEVICEID); rtc6226_get_register(radio, CHIPID); FMDBG("%s DeviceID=0x%x ChipID=0x%x Addr=0x%x\n", __func__, radio->registers[DEVICEID], radio->registers[CHIPID], radio->client->addr); /* Have to update shadow buf from all register */ retval = rtc6226_get_all_registers(radio); if (retval < 0) goto done; FMDBG("%s rtc6226_power_up1: DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", __func__, radio->registers[DEVICEID], radio->registers[CHIPID]); FMDBG("%s rtc6226_power_up2: Reg2=0x%4.4hx Reg3=0x%4.4hx\n", __func__, radio->registers[MPXCFG], radio->registers[CHANNEL]); FMDBG("%s rtc6226_power_up3: Reg4=0x%4.4hx Reg5=0x%4.4hx\n", __func__, radio->registers[SYSCFG], radio->registers[SEEKCFG1]); FMDBG("%s rtc6226_power_up4: Reg6=0x%4.4hx Reg7=0x%4.4hx\n", __func__, radio->registers[POWERCFG], radio->registers[PADCFG]); FMDBG("%s rtc6226_power_up5: Reg8=0x%4.4hx Reg9=0x%4.4hx\n", __func__, radio->registers[8], radio->registers[9]); FMDBG("%s rtc6226_power_up6: regA=0x%4.4hx RegB=0x%4.4hx\n", __func__, radio->registers[10], radio->registers[11]); FMDBG("%s rtc6226_power_up7: regC=0x%4.4hx RegD=0x%4.4hx\n", __func__, radio->registers[12], radio->registers[13]); FMDBG("%s rtc6226_power_up8: regE=0x%4.4hx RegF=0x%4.4hx\n", __func__, radio->registers[14], radio->registers[15]); FMDBG("%s DeviceID=0x%x ChipID=0x%x Addr=0x%x\n", __func__, radio->registers[DEVICEID], radio->registers[CHIPID], radio->client->addr); /* initial patch 01 */ initbuf[0] = 0x0038; retval = rtc6226_set_serial_registers(radio, initbuf, 0x40); if (retval < 0) goto done; /* initial patch 02 */ initbuf[0] = 0xC100; retval = rtc6226_set_serial_registers(radio, initbuf, 0x8E); if (retval < 0) goto done; done: return retval; } /* * rtc6226_stop - switch off radio */ int rtc6226_stop(struct rtc6226_device *radio) { int retval; /* sysconfig */ radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDS_EN; radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDSIRQEN; radio->registers[SYSCFG] &= ~SYSCFG_CSR0_STDIRQEN; retval = rtc6226_set_register(radio, SYSCFG); if (retval < 0) goto done; /* powerconfig */ radio->registers[MPXCFG] &= ~MPXCFG_CSR0_DIS_MUTE; retval = rtc6226_set_register(radio, MPXCFG); /* POWERCFG_ENABLE has to automatically go low */ radio->registers[POWERCFG] |= POWERCFG_CSR0_DISABLE; radio->registers[POWERCFG] &= ~POWERCFG_CSR0_ENABLE; retval = rtc6226_set_register(radio, POWERCFG); /* Set 0x16AA */ radio->registers[DEVICEID] = 0x16AA; retval = rtc6226_set_register(radio, DEVICEID); done: return retval; } static void rtc6226_get_rds(struct rtc6226_device *radio) { int retval = 0; mutex_lock(&radio->lock); retval = rtc6226_get_all_registers(radio); if (retval < 0) { FMDERR("%s read fail%d\n", __func__, retval); mutex_unlock(&radio->lock); return; } radio->block[0] = radio->registers[BA_DATA]; radio->block[1] = radio->registers[BB_DATA]; radio->block[2] = radio->registers[BC_DATA]; radio->block[3] = radio->registers[BD_DATA]; radio->bler[0] = (radio->registers[RSSI] & RSSI_RDS_BA_ERRS) >> 14; radio->bler[1] = (radio->registers[RSSI] & RSSI_RDS_BB_ERRS) >> 12; radio->bler[2] = (radio->registers[RSSI] & RSSI_RDS_BC_ERRS) >> 10; radio->bler[3] = (radio->registers[RSSI] & RSSI_RDS_BD_ERRS) >> 8; mutex_unlock(&radio->lock); } static void rtc6226_pi_check(struct rtc6226_device *radio, u16 current_pi) { if (radio->pi != current_pi) { FMDBG("%s current_pi %x , radio->pi %x\n" , __func__, current_pi, radio->pi); radio->pi = current_pi; } else { FMDBG("%s Received same PI code\n", __func__); } } static void rtc6226_pty_check(struct rtc6226_device *radio, u8 current_pty) { if (radio->pty != current_pty) { FMDBG("%s PTY code of radio->block[1] = %x\n", __func__, current_pty); radio->pty = current_pty; } else { FMDBG("%s PTY repeated\n", __func__); } } static bool is_new_freq(struct rtc6226_device *radio, u32 freq) { u8 i = 0; for (i = 0; i < radio->af_info2.size; i++) { if (freq == radio->af_info2.af_list[i]) return false; } return true; } static bool is_different_af_list(struct rtc6226_device *radio) { u8 i = 0, j = 0; u32 freq; if (radio->af_info1.orig_freq_khz != radio->af_info2.orig_freq_khz) return true; /* freq is same, check if the AFs are same. */ for (i = 0; i < radio->af_info1.size; i++) { freq = radio->af_info1.af_list[i]; for (j = 0; j < radio->af_info2.size; j++) { if (freq == radio->af_info2.af_list[j]) break; } /* freq is not there in list2 i.e list1, list2 are different.*/ if (j == radio->af_info2.size) return true; } return false; } static bool is_valid_freq(struct rtc6226_device *radio, u32 freq) { u32 band_low_limit; u32 band_high_limit; u8 spacing = 0; band_low_limit = radio->recv_conf.band_low_limit * TUNE_STEP_SIZE; band_high_limit = radio->recv_conf.band_high_limit * TUNE_STEP_SIZE; if (radio->space == 0) spacing = CH_SPACING_200; else if (radio->space == 1) spacing = CH_SPACING_100; else if (radio->space == 2) spacing = CH_SPACING_50; else return false; if ((freq >= band_low_limit) && (freq <= band_high_limit) && ((freq - band_low_limit) % spacing == 0)) return true; return false; } static void rtc6226_update_af_list(struct rtc6226_device *radio) { bool retval; u8 i = 0; u8 af_data = radio->block[2] >> 8; u32 af_freq_khz; u32 tuned_freq_khz; struct kfifo *buff; struct af_list_ev ev; spinlock_t lock = radio->buf_lock[RTC6226_FM_BUF_AF_LIST]; rtc6226_get_freq(radio, &tuned_freq_khz); for (; i < NO_OF_AF_IN_GRP; i++, af_data = radio->block[2] & 0xFF) { if (af_data >= MIN_AF_CNT_CODE && af_data <= MAX_AF_CNT_CODE) { FMDBG("%s: resetting af info, freq %u, pi %u\n", __func__, tuned_freq_khz, radio->pi); radio->af_info2.inval_freq_cnt = 0; radio->af_info2.cnt = 0; radio->af_info2.orig_freq_khz = 0; /* AF count. */ radio->af_info2.cnt = af_data - NO_AF_CNT_CODE; radio->af_info2.orig_freq_khz = tuned_freq_khz; radio->af_info2.pi = radio->pi; FMDBG("%s: current freq is %u, AF cnt is %u\n", __func__, tuned_freq_khz, radio->af_info2.cnt); } else if (af_data >= MIN_AF_FREQ_CODE && af_data <= MAX_AF_FREQ_CODE && radio->af_info2.orig_freq_khz != 0 && radio->af_info2.size < MAX_NO_OF_AF) { af_freq_khz = SCALE_AF_CODE_TO_FREQ_KHZ(af_data); retval = is_valid_freq(radio, af_freq_khz); if (!retval) { FMDBG("%s: Invalid AF\n", __func__); radio->af_info2.inval_freq_cnt++; continue; } retval = is_new_freq(radio, af_freq_khz); if (!retval) { FMDBG("%s: Duplicate AF\n", __func__); radio->af_info2.inval_freq_cnt++; continue; } /* update the AF list */ radio->af_info2.af_list[radio->af_info2.size++] = af_freq_khz; FMDBG("%s: AF is %u\n", __func__, af_freq_khz); if ((radio->af_info2.size + radio->af_info2.inval_freq_cnt == radio->af_info2.cnt) && is_different_af_list(radio)) { /* Copy the list to af_info1. */ radio->af_info1.cnt = radio->af_info2.cnt; radio->af_info1.size = radio->af_info2.size; radio->af_info1.pi = radio->af_info2.pi; radio->af_info1.orig_freq_khz = radio->af_info2.orig_freq_khz; memset(radio->af_info1.af_list, 0, sizeof(radio->af_info1.af_list)); memcpy(radio->af_info1.af_list, radio->af_info2.af_list, sizeof(radio->af_info2.af_list)); /* AF list changed, post it to user space */ memset(&ev, 0, sizeof(struct af_list_ev)); ev.tune_freq_khz = radio->af_info1.orig_freq_khz; ev.pi_code = radio->pi; ev.af_size = radio->af_info1.size; memcpy(&ev.af_list[0], radio->af_info1.af_list, GET_AF_LIST_LEN(ev.af_size)); buff = &radio->data_buf[RTC6226_FM_BUF_AF_LIST]; kfifo_in_locked(buff, (u8 *)&ev, GET_AF_EVT_LEN(ev.af_size), &lock); FMDBG("%s: posting AF list evt,currfreq %u\n", __func__, ev.tune_freq_khz); rtc6226_q_event(radio, RTC6226_EVT_NEW_AF_LIST); } } } } static void rtc6226_update_ps(struct rtc6226_device *radio, u8 addr, u8 ps) { u8 i; bool ps_txt_chg = false; bool ps_cmplt = true; u8 *data; struct kfifo *data_b; FMDBG("%s enter addr:%x ps:%x\n", __func__, addr, ps); if (radio->ps_tmp0[addr] == ps) { if (radio->ps_cnt[addr] < PS_VALIDATE_LIMIT) { radio->ps_cnt[addr]++; } else { radio->ps_cnt[addr] = PS_VALIDATE_LIMIT; radio->ps_tmp1[addr] = ps; } } else if (radio->ps_tmp1[addr] == ps) { if (radio->ps_cnt[addr] >= PS_VALIDATE_LIMIT) { ps_txt_chg = true; radio->ps_cnt[addr] = PS_VALIDATE_LIMIT + 1; } else { radio->ps_cnt[addr] = PS_VALIDATE_LIMIT; } radio->ps_tmp1[addr] = radio->ps_tmp0[addr]; radio->ps_tmp0[addr] = ps; } else if (!radio->ps_cnt[addr]) { radio->ps_tmp0[addr] = ps; radio->ps_cnt[addr] = 1; } else { radio->ps_tmp1[addr] = ps; } if (ps_txt_chg) { for (i = 0; i < MAX_PS_LEN; i++) { if (radio->ps_cnt[i] > 1) radio->ps_cnt[i]--; } } for (i = 0; i < MAX_PS_LEN; i++) { if (radio->ps_cnt[i] < PS_VALIDATE_LIMIT) { FMDBG("%s ps_cnt[%d] %d\n", __func__, i, radio->ps_cnt[i]); ps_cmplt = false; return; } } if (ps_cmplt) { for (i = 0; (i < MAX_PS_LEN) && (radio->ps_display[i] == radio->ps_tmp0[i]); i++) ; if (i == MAX_PS_LEN) { FMDBG("%s Same PS string repeated\n", __func__); return; } for (i = 0; i < MAX_PS_LEN; i++) radio->ps_display[i] = radio->ps_tmp0[i]; data = kmalloc(PS_EVT_DATA_LEN, GFP_ATOMIC); if (data != NULL) { data[0] = NO_OF_PS; data[1] = radio->pty; data[2] = (radio->pi >> 8) & 0xFF; data[3] = (radio->pi & 0xFF); data[4] = 0; memcpy(data + OFFSET_OF_PS, radio->ps_tmp0, MAX_PS_LEN); data_b = &radio->data_buf[RTC6226_FM_BUF_PS_RDS]; kfifo_in_locked(data_b, data, PS_EVT_DATA_LEN, &radio->buf_lock[RTC6226_FM_BUF_PS_RDS]); FMDBG("%s Q the PS event\n", __func__); rtc6226_q_event(radio, RTC6226_EVT_NEW_PS_RDS); kfree(data); } else { FMDERR("%s Memory allocation failed for PTY\n", __func__); } } } static void display_rt(struct rtc6226_device *radio) { u8 len = 0, i = 0; u8 *data; struct kfifo *data_b; bool rt_cmplt = true; FMDBG("%s enter\n", __func__); for (i = 0; i < MAX_RT_LEN; i++) { if (radio->rt_cnt[i] < RT_VALIDATE_LIMIT) { FMDBG("%s rt_cnt %d\n", __func__, radio->rt_cnt[i]); rt_cmplt = false; return; } if (radio->rt_tmp0[i] == END_OF_RT) break; } if (rt_cmplt) { while ((len < MAX_RT_LEN) && (radio->rt_tmp0[len] != END_OF_RT)) len++; for (i = 0; (i < len) && (radio->rt_display[i] == radio->rt_tmp0[i]); i++) ; if (i == len) { FMDBG("%s Same RT string repeated\n", __func__); return; } for (i = 0; i < len; i++) radio->rt_display[i] = radio->rt_tmp0[i]; data = kmalloc(len + OFFSET_OF_RT, GFP_ATOMIC); if (data != NULL) { data[0] = len; /* len of RT */ data[1] = radio->pty; data[2] = (radio->pi >> 8) & 0xFF; data[3] = (radio->pi & 0xFF); data[4] = radio->rt_flag; memcpy(data + OFFSET_OF_RT, radio->rt_display, len); data_b = &radio->data_buf[RTC6226_FM_BUF_RT_RDS]; kfifo_in_locked(data_b, data, OFFSET_OF_RT + len, &radio->buf_lock[RTC6226_FM_BUF_RT_RDS]); FMDBG("%s Q the RT event\n", __func__); rtc6226_q_event(radio, RTC6226_EVT_NEW_RT_RDS); kfree(data); } else { FMDERR("%s Memory allocation failed for PTY\n", __func__); } } } static void rt_handler(struct rtc6226_device *radio, u8 ab_flg, u8 cnt, u8 addr, u8 *rt) { u8 i, errcnt, blermax; bool rt_txt_chg = false; FMDBG("%s enter\n", __func__); if (ab_flg != radio->rt_flag && radio->valid_rt_flg) { for (i = 0; i < sizeof(radio->rt_cnt); i++) { if (!radio->rt_tmp0[i]) { radio->rt_tmp0[i] = ' '; radio->rt_cnt[i]++; } } memset(radio->rt_cnt, 0, sizeof(radio->rt_cnt)); memset(radio->rt_tmp0, 0, sizeof(radio->rt_tmp0)); memset(radio->rt_tmp1, 0, sizeof(radio->rt_tmp1)); } radio->rt_flag = ab_flg; radio->valid_rt_flg = true; for (i = 0; i < cnt; i++) { if ((i < 2) && (cnt > 2)) { errcnt = radio->bler[2]; blermax = CORRECTED_THREE_TO_FIVE; } else { errcnt = radio->bler[3]; blermax = CORRECTED_THREE_TO_FIVE; } if (errcnt <= blermax) { if (!rt[i]) rt[i] = ' '; if (radio->rt_tmp0[addr+i] == rt[i]) { if (radio->rt_cnt[addr+i] < RT_VALIDATE_LIMIT) { radio->rt_cnt[addr+i]++; } else { radio->rt_cnt[addr+i] = RT_VALIDATE_LIMIT; radio->rt_tmp1[addr+i] = rt[i]; } } else if (radio->rt_tmp1[addr+i] == rt[i]) { if (radio->rt_cnt[addr+i] >= RT_VALIDATE_LIMIT) { rt_txt_chg = true; radio->rt_cnt[addr+i] = RT_VALIDATE_LIMIT + 1; } else { radio->rt_cnt[addr+i] = RT_VALIDATE_LIMIT; } radio->rt_tmp1[addr+i] = radio->rt_tmp0[addr+i]; radio->rt_tmp0[addr+i] = rt[i]; } else if (!radio->rt_cnt[addr+i]) { radio->rt_tmp0[addr+i] = rt[i]; radio->rt_cnt[addr+i] = 1; } else { radio->rt_tmp1[addr+i] = rt[i]; } } } if (rt_txt_chg) { for (i = 0; i < MAX_RT_LEN; i++) { if (radio->rt_cnt[i] > 1) radio->rt_cnt[i]--; } } display_rt(radio); } static void rtc6226_raw_rds(struct rtc6226_device *radio) { u16 aid, app_grp_typ; aid = radio->block[3]; app_grp_typ = radio->block[1] & APP_GRP_typ_MASK; FMDBG("%s app_grp_typ = %x\n", __func__, app_grp_typ); FMDBG("%s AID = %x\n", __func__, aid); switch (aid) { case ERT_AID: radio->utf_8_flag = (radio->block[2] & 1); radio->formatting_dir = EXTRACT_BIT(radio->block[2], ERT_FORMAT_DIR_BIT); if (radio->ert_carrier != app_grp_typ) { rtc6226_q_event(radio, RTC6226_EVT_NEW_ODA); radio->ert_carrier = app_grp_typ; } break; case RT_PLUS_AID: /*Extract 5th bit of MSB (b7b6b5b4b3b2b1b0)*/ radio->rt_ert_flag = EXTRACT_BIT(radio->block[2], RT_ERT_FLAG_BIT); if (radio->rt_plus_carrier != app_grp_typ) { rtc6226_q_event(radio, RTC6226_EVT_NEW_ODA); radio->rt_plus_carrier = app_grp_typ; } break; default: FMDBG("%s Not handling the AID of %x\n", __func__, aid); break; } } static void rtc6226_ev_ert(struct rtc6226_device *radio) { u8 *data = NULL; struct kfifo *data_b; if (radio->ert_len <= 0) return; FMDBG("%s enter\n", __func__); data = kmalloc((radio->ert_len + ERT_OFFSET), GFP_ATOMIC); if (data != NULL) { data[0] = radio->ert_len; data[1] = radio->utf_8_flag; data[2] = radio->formatting_dir; memcpy((data + ERT_OFFSET), radio->ert_buf, radio->ert_len); data_b = &radio->data_buf[RTC6226_FM_BUF_ERT]; kfifo_in_locked(data_b, data, (radio->ert_len + ERT_OFFSET), &radio->buf_lock[RTC6226_FM_BUF_ERT]); rtc6226_q_event(radio, RTC6226_EVT_NEW_ERT); kfree(data); } } static void rtc6226_buff_ert(struct rtc6226_device *radio) { int i; u16 info_byte = 0; u8 byte_pair_index; byte_pair_index = radio->block[1] & APP_GRP_typ_MASK; if (byte_pair_index == 0) { radio->c_byt_pair_index = 0; radio->ert_len = 0; } if (radio->c_byt_pair_index == byte_pair_index) { for (i = 2; i <= 3; i++) { info_byte = radio->block[i]; FMDBG("%s info_byte = %x\n", __func__, info_byte); FMDBG("%s ert_len = %x\n", __func__, radio->ert_len); if (radio->ert_len > (MAX_ERT_LEN - 2)) return; radio->ert_buf[radio->ert_len] = radio->block[i] >> 8; radio->ert_buf[radio->ert_len + 1] = radio->block[i] & 0xFF; radio->ert_len += ERT_CNT_PER_BLK; FMDBG("%s utf_8_flag = %d\n", __func__, radio->utf_8_flag); if ((radio->utf_8_flag == 0) && (info_byte == END_OF_RT)) { radio->ert_len -= ERT_CNT_PER_BLK; break; } else if ((radio->utf_8_flag == 1) && (radio->block[i] >> 8 == END_OF_RT)) { info_byte = END_OF_RT; radio->ert_len -= ERT_CNT_PER_BLK; break; } else if ((radio->utf_8_flag == 1) && ((radio->block[i] & 0xFF) == END_OF_RT)) { info_byte = END_OF_RT; radio->ert_len--; break; } } if ((byte_pair_index == MAX_ERT_SEGMENT) || (info_byte == END_OF_RT)) { rtc6226_ev_ert(radio); radio->c_byt_pair_index = 0; radio->ert_len = 0; } radio->c_byt_pair_index++; } else { radio->ert_len = 0; radio->c_byt_pair_index = 0; } } static void rtc6226_rt_plus(struct rtc6226_device *radio) { u8 tag_type1, tag_type2; u8 *data = NULL; int len = 0; u16 grp_typ; struct kfifo *data_b; grp_typ = radio->block[1] & APP_GRP_typ_MASK; /* *right most 3 bits of Lsb of block 2 * and left most 3 bits of Msb of block 3 */ tag_type1 = (((grp_typ & TAG1_MSB_MASK) << TAG1_MSB_OFFSET) | (radio->block[2] >> TAG1_LSB_OFFSET)); /* *right most 1 bit of lsb of 3rd block * and left most 5 bits of Msb of 4th block */ tag_type2 = (((radio->block[2] & TAG2_MSB_MASK) << TAG2_MSB_OFFSET) | (radio->block[2] >> TAG2_LSB_OFFSET)); if (tag_type1 != DUMMY_CLASS) len += RT_PLUS_LEN_1_TAG; if (tag_type2 != DUMMY_CLASS) len += RT_PLUS_LEN_1_TAG; if (len != 0) { len += RT_PLUS_OFFSET; data = kmalloc(len, GFP_ATOMIC); } else { FMDERR("%s:Len is zero\n", __func__); return; } if (data != NULL) { data[0] = len; len = RT_ERT_FLAG_OFFSET; data[len++] = radio->rt_ert_flag; if (tag_type1 != DUMMY_CLASS) { data[len++] = tag_type1; /* *start position of tag1 *right most 5 bits of msb of 3rd block *and left most bit of lsb of 3rd block */ data[len++] = (radio->block[2] >> TAG1_POS_LSB_OFFSET) & TAG1_POS_MSB_MASK; /* *length of tag1 *left most 6 bits of lsb of 3rd block */ data[len++] = (radio->block[2] >> TAG1_LEN_OFFSET) & TAG1_LEN_MASK; } if (tag_type2 != DUMMY_CLASS) { data[len++] = tag_type2; /* *start position of tag2 *right most 3 bit of msb of 4th block *and left most 3 bits of lsb of 4th block */ data[len++] = (radio->block[3] >> TAG2_POS_LSB_OFFSET) & TAG2_POS_MSB_MASK; /* *length of tag2 *right most 5 bits of lsb of 4th block */ data[len++] = radio->block[3] & TAG2_LEN_MASK; } data_b = &radio->data_buf[RTC6226_FM_BUF_RT_PLUS]; kfifo_in_locked(data_b, data, len, &radio->buf_lock[RTC6226_FM_BUF_RT_PLUS]); rtc6226_q_event(radio, RTC6226_EVT_NEW_RT_PLUS); kfree(data); } else { FMDERR("%s:memory allocation failed\n", __func__); } } void rtc6226_rds_handler(struct work_struct *worker) { struct rtc6226_device *radio; u8 rt_blks[NO_OF_RDS_BLKS]; u8 grp_type, addr, ab_flg; radio = container_of(worker, struct rtc6226_device, rds_worker); if (!radio) { FMDERR("%s:radio is null\n", __func__); return; } FMDBG("%s enter\n", __func__); rtc6226_get_rds(radio); if (radio->bler[0] < CORRECTED_THREE_TO_FIVE) rtc6226_pi_check(radio, radio->block[0]); if (radio->bler[1] < CORRECTED_ONE_TO_TWO) { grp_type = radio->block[1] >> OFFSET_OF_GRP_TYP; FMDBG("%s grp_type = %d\n", __func__, grp_type); } else { /* invalid data case */ return; } if (grp_type & 0x01) rtc6226_pi_check(radio, radio->block[2]); rtc6226_pty_check(radio, (radio->block[1] >> OFFSET_OF_PTY) & PTY_MASK); switch (grp_type) { case RDS_TYPE_0A: if (radio->bler[2] <= CORRECTED_THREE_TO_FIVE) rtc6226_update_af_list(radio); /* fall through */ fallthrough; case RDS_TYPE_0B: addr = (radio->block[1] & PS_MASK) * NO_OF_CHARS_IN_EACH_ADD; FMDBG("%s RDS is PS\n", __func__); if (radio->bler[3] <= CORRECTED_THREE_TO_FIVE) { rtc6226_update_ps(radio, addr+0, radio->block[3] >> 8); rtc6226_update_ps(radio, addr+1, radio->block[3] & 0xff); } break; case RDS_TYPE_2A: FMDBG("%s RDS is RT 2A group\n", __func__); rt_blks[0] = (u8)(radio->block[2] >> 8); rt_blks[1] = (u8)(radio->block[2] & 0xFF); rt_blks[2] = (u8)(radio->block[3] >> 8); rt_blks[3] = (u8)(radio->block[3] & 0xFF); addr = (radio->block[1] & 0xf) * 4; ab_flg = (radio->block[1] & 0x0010) >> 4; rt_handler(radio, ab_flg, CNT_FOR_2A_GRP_RT, addr, rt_blks); break; case RDS_TYPE_2B: FMDBG("%s RDS is RT 2B group\n", __func__); rt_blks[0] = (u8)(radio->block[3] >> 8); rt_blks[1] = (u8)(radio->block[3] & 0xFF); rt_blks[2] = 0; rt_blks[3] = 0; addr = (radio->block[1] & 0xf) * 2; ab_flg = (radio->block[1] & 0x0010) >> 4; radio->rt_tmp0[MAX_LEN_2B_GRP_RT] = END_OF_RT; radio->rt_tmp1[MAX_LEN_2B_GRP_RT] = END_OF_RT; radio->rt_cnt[MAX_LEN_2B_GRP_RT] = RT_VALIDATE_LIMIT; rt_handler(radio, ab_flg, CNT_FOR_2B_GRP_RT, addr, rt_blks); break; case RDS_TYPE_3A: FMDBG("%s RDS is 3A group\n", __func__); rtc6226_raw_rds(radio); break; default: FMDERR("%s Not handling the group type %d\n", __func__, grp_type); break; } FMDBG("%s rt_plus_carrier = %x\n", __func__, radio->rt_plus_carrier); FMDBG("%s ert_carrier = %x\n", __func__, radio->ert_carrier); if (radio->rt_plus_carrier && (grp_type == radio->rt_plus_carrier)) rtc6226_rt_plus(radio); else if (radio->ert_carrier && (grp_type == radio->ert_carrier)) rtc6226_buff_ert(radio); } /* * rtc6226_rds_on - switch on rds reception */ int rtc6226_rds_on(struct rtc6226_device *radio) { int retval; FMDBG("%s enter\n", __func__); /* sysconfig */ radio->registers[SYSCFG] |= SYSCFG_CSR0_RDS_EN; retval = rtc6226_set_register(radio, SYSCFG); if (retval < 0) radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDS_EN; return retval; } int rtc6226_reset_rds_data(struct rtc6226_device *radio) { mutex_lock(&radio->lock); radio->pi = 0; /* reset PS bufferes */ memset(radio->ps_display, 0, sizeof(radio->ps_display)); memset(radio->ps_tmp0, 0, sizeof(radio->ps_tmp0)); memset(radio->ps_tmp1, 0, sizeof(radio->ps_tmp1)); memset(radio->ps_cnt, 0, sizeof(radio->ps_cnt)); memset(radio->rt_display, 0, sizeof(radio->rt_display)); memset(radio->rt_tmp0, 0, sizeof(radio->rt_tmp0)); memset(radio->rt_tmp1, 0, sizeof(radio->rt_tmp1)); memset(radio->rt_cnt, 0, sizeof(radio->rt_cnt)); radio->wr_index = 0; radio->rd_index = 0; memset(radio->buffer, 0, radio->buf_size); mutex_unlock(&radio->lock); return 0; } int rtc6226_set_rssi_threshold(struct rtc6226_device *radio, u16 rssi) { int retval = 0; /*csr_rssi_low_th = RSSI_threshold/4*/ rssi = rssi/4; if ((rssi < MIN_RSSI) && (rssi > MAX_RSSI)) return -EINVAL; radio->registers[SEEKCFG1] &= ~SEEKCFG1_CSR0_RSSI_LOW_TH; radio->registers[SEEKCFG1] |= rssi << 8; retval = rtc6226_set_register(radio, SEEKCFG1); radio->rssi_th = (u8)(rssi*4); return retval; } int rtc6226_power_down(struct rtc6226_device *radio) { int retval = 0; FMDBG("%s enter\n", __func__); mutex_lock(&radio->lock); /* stop radio */ retval = rtc6226_stop(radio); //rtc6226_disable_irq(radio); mutex_unlock(&radio->lock); FMDBG("%s exit %d\n", __func__, retval); return retval; } int rtc6226_power_up(struct rtc6226_device *radio) { int retval = 0; mutex_lock(&radio->lock); FMDBG("%s enter\n", __func__); /* start radio */ retval = rtc6226_start(radio); if (retval < 0) goto done; FMDBG("%s : after initialization\n", __func__); /* mpxconfig */ /* Disable Mute / De-emphasis / Volume 12 */ radio->registers[MPXCFG] = 0x000c | MPXCFG_CSR0_DIS_MUTE | ((de << 12) & MPXCFG_CSR0_DEEM); retval = rtc6226_set_register(radio, MPXCFG); if (retval < 0) goto done; /* enable RDS / STC interrupt */ radio->registers[SYSCFG] |= SYSCFG_CSR0_RDSIRQEN; radio->registers[SYSCFG] |= SYSCFG_CSR0_STDIRQEN; /*radio->registers[SYSCFG] |= SYSCFG_CSR0_RDS_EN;*/ retval = rtc6226_set_register(radio, SYSCFG); if (retval < 0) goto done; radio->registers[PADCFG] &= ~PADCFG_CSR0_GPIO; radio->registers[PADCFG] |= 0x1 << 2; retval = rtc6226_set_register(radio, PADCFG); if (retval < 0) goto done; /* I2S salve */ radio->registers[I2SCFG] = 0x2480; retval = rtc6226_set_register(radio, I2SCFG); if (retval < 0) goto done; /*set default rssi threshold*/ retval = rtc6226_set_rssi_threshold(radio, DEFAULT_RSSI_TH); if (retval < 0) FMDERR("%s fail to set rssi threshold\n", __func__); /* powerconfig */ /* Enable FM */ radio->registers[POWERCFG] = POWERCFG_CSR0_ENABLE; retval = rtc6226_set_register(radio, POWERCFG); if (retval < 0) goto done; /*wait for radio enable to complete*/ msleep(30); retval = rtc6226_get_all_registers(radio); if (retval < 0) goto done; FMDBG("%s : DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", __func__, radio->registers[DEVICEID], radio->registers[CHIPID]); FMDBG("%s : Reg2=0x%4.4hx Reg3=0x%4.4hx\n", __func__, radio->registers[MPXCFG], radio->registers[CHANNEL]); FMDBG("%s : Reg4=0x%4.4hx Reg5=0x%4.4hx\n", __func__, radio->registers[SYSCFG], radio->registers[SEEKCFG1]); FMDBG("%s : Reg6=0x%4.4hx Reg7=0x%4.4hx\n", __func__, radio->registers[POWERCFG], radio->registers[PADCFG]); FMDBG("%s : Reg8=0x%4.4hx Reg9=0x%4.4hx\n", __func__, radio->registers[8], radio->registers[9]); FMDBG("%s : regA=0x%4.4hx RegB=0x%4.4hx\n", __func__, radio->registers[10], radio->registers[11]); FMDBG("%s : regC=0x%4.4hx RegD=0x%4.4hx\n", __func__, radio->registers[12], radio->registers[13]); FMDBG("%s : regE=0x%4.4hx RegF=0x%4.4hx\n", __func__, radio->registers[14], radio->registers[15]); done: FMDBG("%s exit %d\n", __func__, retval); mutex_unlock(&radio->lock); return retval; } /************************************************************************** * File Operations Interface **************************************************************************/ /* * rtc6226_fops_read - read event data */ static ssize_t rtc6226_fops_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct rtc6226_device *radio = video_get_drvdata(video_devdata(file)); enum rtc6226_buf_t buf_type = -1; u8 buf_fifo[STD_BUF_SIZE] = {0}; struct kfifo *data_fifo = NULL; int len = 0, retval = -1; u32 bytesused = 0; if ((radio == NULL) || (buffer == NULL)) { FMDERR("%s radio/buffer is NULL\n", __func__); return -ENXIO; } buf_type = count; len = STD_BUF_SIZE; FMDBG("%s: requesting buffer %d\n", __func__, buf_type); if ((buf_type < RTC6226_FM_BUF_MAX) && (buf_type >= 0)) { data_fifo = &radio->data_buf[buf_type]; if (buf_type == RTC6226_FM_BUF_EVENTS) { if (wait_event_interruptible(radio->event_queue, kfifo_len(data_fifo)) < 0) { return -EINTR; } } } else { FMDERR("%s invalid buffer type\n", __func__); return -EINVAL; } if (len <= STD_BUF_SIZE) { bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0], len, &radio->buf_lock[buf_type]); } else { FMDERR("%s kfifo_out_locked can not use len more than 128\n", __func__); return -EINVAL; } retval = copy_to_user(buffer, &buf_fifo[0], bytesused); if (retval > 0) { FMDERR("%s Failed to copy %d bytes data\n", __func__, retval); return -EAGAIN; } retval = bytesused; return retval; } /* * rtc6226_fops_poll - poll RDS data */ static unsigned int rtc6226_fops_poll(struct file *file, struct poll_table_struct *pts) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; /* switch on rds reception */ mutex_lock(&radio->lock); if ((radio->registers[SYSCFG] & SYSCFG_CSR0_RDS_EN) == 0) rtc6226_rds_on(radio); mutex_unlock(&radio->lock); poll_wait(file, &radio->read_queue, pts); if (radio->rd_index != radio->wr_index) retval = POLLIN | POLLRDNORM; return retval; } /* static */ int rtc6226_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; FMDBG("%s enter, ctrl->id: %x, value:%d\n", __func__, ctrl->id, ctrl->value); mutex_lock(&radio->lock); switch (ctrl->id) { case V4L2_CID_PRIVATE_CSR0_ENABLE: FMDBG("V4L2_CID_PRIVATE_CSR0_ENABLE val=%d\n", ctrl->value); break; case V4L2_CID_PRIVATE_CSR0_DISABLE: FMDBG("V4L2_CID_PRIVATE_CSR0_DISABLE val=%d\n", ctrl->value); break; case V4L2_CID_PRIVATE_CSR0_VOLUME: case V4L2_CID_AUDIO_VOLUME: ctrl->value = radio->registers[MPXCFG] & MPXCFG_CSR0_VOLUME; break; case V4L2_CID_PRIVATE_CSR0_DIS_MUTE: case V4L2_CID_AUDIO_MUTE: ctrl->value = ((radio->registers[MPXCFG] & MPXCFG_CSR0_DIS_MUTE) == 0) ? 1 : 0; break; case V4L2_CID_PRIVATE_CSR0_DIS_SMUTE: ctrl->value = ((radio->registers[MPXCFG] & MPXCFG_CSR0_DIS_SMUTE) == 0) ? 1 : 0; break; case V4L2_CID_PRIVATE_CSR0_BAND: ctrl->value = radio->band; break; case V4L2_CID_PRIVATE_CSR0_SEEKRSSITH: ctrl->value = radio->registers[SEEKCFG1] & SEEKCFG1_CSR0_RSSI_LOW_TH; break; case V4L2_CID_PRIVATE_RSSI: rtc6226_get_all_registers(radio); ctrl->value = radio->registers[RSSI] & RSSI_RSSI; FMDBG("Get V4L2_CONTROL V4L2_CID_PRIVATE_RSSI: RSSI = %d\n", radio->registers[RSSI] & RSSI_RSSI); break; case V4L2_CID_PRIVATE_DEVICEID: ctrl->value = radio->registers[DEVICEID] & DEVICE_ID; FMDBG("Get V4L2_CID_PRIVATE_DEVICEID: DEVICEID=0x%4.4hx\n", radio->registers[DEVICEID]); break; case V4L2_CID_PRIVATE_RTC6226_RDSGROUP_PROC: break; case V4L2_CID_PRIVATE_RTC6226_SIGNAL_TH: /* intentional fallthrough */ case V4L2_CID_PRIVATE_RTC6226_RSSI_TH: ctrl->value = radio->rssi_th; break; default: FMDBG("%s in default id:%d\n", __func__, ctrl->id); retval = -EINVAL; } mutex_unlock(&radio->lock); return retval; } static int rtc6226_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buffer) { struct rtc6226_device *radio = video_get_drvdata(video_devdata(file)); enum rtc6226_buf_t buf_type = -1; u8 buf_fifo[STD_BUF_SIZE] = {0}; struct kfifo *data_fifo = NULL; u8 *buf = NULL; int len = 0, retval = -1; if ((radio == NULL) || (buffer == NULL)) { FMDERR("%s radio/buffer is NULL\n", __func__); return -ENXIO; } buf_type = buffer->index; buf = (u8 *)buffer->m.userptr; len = buffer->length; FMDBG("%s: requesting buffer %d\n", __func__, buf_type); if ((buf_type < RTC6226_FM_BUF_MAX) && (buf_type >= 0)) { data_fifo = &radio->data_buf[buf_type]; if (buf_type == RTC6226_FM_BUF_EVENTS) { if (wait_event_interruptible(radio->event_queue, kfifo_len(data_fifo)) < 0) { return -EINTR; } } } else { FMDERR("%s invalid buffer type\n", __func__); return -EINVAL; } if (len <= STD_BUF_SIZE) { buffer->bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0], len, &radio->buf_lock[buf_type]); } else { FMDERR("%s kfifo_out_locked can not use len more than 128\n", __func__); return -EINVAL; } retval = copy_to_user(buf, &buf_fifo[0], buffer->bytesused); if (retval > 0) { FMDERR("%s Failed to copy %d bytes data\n", __func__, retval); return -EAGAIN; } return retval; } static bool check_mode(struct rtc6226_device *radio) { bool retval = true; if (radio->mode == FM_OFF || radio->mode == FM_RECV) retval = false; return retval; } static int rtc6226_disable(struct rtc6226_device *radio) { int retval = 0; /* disable RDS/STC interrupt */ radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDS_EN; radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDSIRQEN; radio->registers[SYSCFG] &= ~SYSCFG_CSR0_STDIRQEN; retval = rtc6226_set_register(radio, SYSCFG); if (retval < 0) { FMDERR("%s fail to disable RDS/SCT interrupt\n", __func__); goto done; } retval = rtc6226_power_down(radio); if (retval < 0) { FMDERR("%s fail to turn off fmradio\n", __func__); goto done; } if (radio->mode == FM_TURNING_OFF || radio->mode == FM_RECV) { FMDBG("%s: posting RTC6226_EVT_RADIO_DISABLED event\n", __func__); rtc6226_q_event(radio, RTC6226_EVT_RADIO_DISABLED); radio->mode = FM_OFF; } /* flush_workqueue(radio->wqueue); */ done: return retval; } static int rtc6226_enable(struct rtc6226_device *radio) { int retval = 0; rtc6226_get_register(radio, POWERCFG); retval = rtc6226_power_up(radio); if (retval < 0) goto done; if ((radio->registers[SYSCFG] & SYSCFG_CSR0_STDIRQEN) == 0) { radio->registers[SYSCFG] |= SYSCFG_CSR0_RDSIRQEN; radio->registers[SYSCFG] |= SYSCFG_CSR0_STDIRQEN; retval = rtc6226_set_register(radio, SYSCFG); if (retval < 0) { FMDERR("%s set register fail\n", __func__); goto done; } else { rtc6226_q_event(radio, RTC6226_EVT_RADIO_READY); radio->mode = FM_RECV; } } else { rtc6226_q_event(radio, RTC6226_EVT_RADIO_READY); radio->mode = FM_RECV; } done: return retval; } bool rtc6226_is_valid_srch_mode(int srch_mode) { if ((srch_mode >= RTC6226_MIN_SRCH_MODE) && (srch_mode <= RTC6226_MAX_SRCH_MODE)) return true; else return false; } /* * rtc6226_vidioc_s_ctrl - set the value of a control */ int rtc6226_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; FMDBG("%s enter, ctrl->id: %x, value:%d\n", __func__, ctrl->id, ctrl->value); switch (ctrl->id) { case V4L2_CID_PRIVATE_RTC6226_STATE: if (ctrl->value == FM_RECV) { if (check_mode(radio)) { FMDERR("%s:fm is not in proper state\n", __func__); retval = -EINVAL; goto end; } radio->mode = FM_RECV_TURNING_ON; retval = rtc6226_enable(radio); if (retval < 0) { FMDERR( "%s Error while enabling RECV FM %d\n", __func__, retval); radio->mode = FM_OFF; goto end; } } else if (ctrl->value == FM_OFF) { radio->mode = FM_TURNING_OFF; retval = rtc6226_disable(radio); if (retval < 0) { FMDERR("Err on disable recv FM %d\n", retval); radio->mode = FM_RECV; goto end; } } break; case V4L2_CID_PRIVATE_RTC6226_SET_AUDIO_PATH: case V4L2_CID_PRIVATE_RTC6226_SRCH_ALGORITHM: case V4L2_CID_PRIVATE_RTC6226_REGION: retval = 0; break; case V4L2_CID_PRIVATE_RTC6226_EMPHASIS: if (ctrl->value == 1) radio->registers[MPXCFG] |= MPXCFG_CSR0_DEEM; else radio->registers[MPXCFG] &= ~MPXCFG_CSR0_DEEM; retval = rtc6226_set_register(radio, MPXCFG); break; case V4L2_CID_PRIVATE_RTC6226_RDS_STD: /* enable RDS / STC interrupt */ radio->registers[SYSCFG] |= SYSCFG_CSR0_RDSIRQEN; radio->registers[SYSCFG] |= SYSCFG_CSR0_STDIRQEN; /*radio->registers[SYSCFG] |= SYSCFG_CSR0_RDS_EN;*/ retval = rtc6226_set_register(radio, SYSCFG); break; case V4L2_CID_PRIVATE_RTC6226_SRCHON: rtc6226_search(radio, (bool)ctrl->value); break; case V4L2_CID_PRIVATE_RTC6226_LP_MODE: if (ctrl->value) { /* disable RDS interrupts */ radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDSIRQEN; retval = rtc6226_set_register(radio, SYSCFG); } else { /* enable RDS interrupts */ radio->registers[SYSCFG] |= SYSCFG_CSR0_RDSIRQEN; retval = rtc6226_set_register(radio, SYSCFG); } break; case V4L2_CID_PRIVATE_RTC6226_ANTENNA: case V4L2_CID_PRIVATE_RTC6226_AF_JUMP: case V4L2_CID_PRIVATE_RTC6226_SRCH_CNT: case V4L2_CID_PRIVATE_RTC6226_RXREPEATCOUNT: case V4L2_CID_PRIVATE_RTC6226_SINR_THRESHOLD: retval = 0; break; case V4L2_CID_PRIVATE_RTC6226_SIGNAL_TH: retval = rtc6226_set_rssi_threshold(radio, ctrl->value); if (retval < 0) FMDERR("%s fail to set rssi threshold\n", __func__); rtc6226_get_register(radio, SEEKCFG1); FMDBG("FMRICHWAVE RSSI_TH: Dec = %d , Hexa = %x\n", radio->registers[SEEKCFG1] & 0xFF, radio->registers[SEEKCFG1] & 0xFF); break; /* case V4L2_CID_PRIVATE_RTC6226_OFS_THRESHOLD: */ case V4L2_CID_PRIVATE_RTC6226_SPUR_FREQ_RMSSI: break; case V4L2_CID_PRIVATE_RTC6226_RDSD_BUF: case V4L2_CID_PRIVATE_RTC6226_RDSGROUP_MASK: case V4L2_CID_PRIVATE_RTC6226_RDSGROUP_PROC: if ((radio->registers[SYSCFG] & SYSCFG_CSR0_RDS_EN) == 0) rtc6226_rds_on(radio); retval = 0; break; case V4L2_CID_PRIVATE_RTC6226_SRCHMODE: if (rtc6226_is_valid_srch_mode(ctrl->value)) { radio->g_search_mode = ctrl->value; } else { FMDERR("%s:srch mode is not valid\n", __func__); retval = -EINVAL; goto end; } break; case V4L2_CID_PRIVATE_RTC6226_PSALL: break; case V4L2_CID_PRIVATE_RTC6226_SCANDWELL: if ((ctrl->value >= MIN_DWELL_TIME) && (ctrl->value <= MAX_DWELL_TIME)) { radio->dwell_time_sec = ctrl->value; } else { FMDERR( "%s:scandwell period is not valid\n", __func__); retval = -EINVAL; } break; case V4L2_CID_PRIVATE_CSR0_ENABLE: FMDBG("V4L2_CID_PRIVATE_CSR0_ENABLE val=%d\n", ctrl->value); retval = rtc6226_power_up(radio); /* must keep below line */ ctrl->value = 0; break; case V4L2_CID_PRIVATE_CSR0_DISABLE: FMDBG("V4L2_CID_PRIVATE_CSR0_DISABLE val=%d\n", ctrl->value); retval = rtc6226_power_down(radio); /* must keep below line */ ctrl->value = 0; break; case V4L2_CID_PRIVATE_DEVICEID: FMDBG("V4L2_CID_PRIVATE_DEVICEID val=%d\n", ctrl->value); break; case V4L2_CID_PRIVATE_CSR0_VOLUME: case V4L2_CID_AUDIO_VOLUME: FMDBG("MPXCFG=0x%4.4hx POWERCFG=0x%4.4hx\n", radio->registers[MPXCFG], radio->registers[POWERCFG]); radio->registers[MPXCFG] &= ~MPXCFG_CSR0_VOLUME; radio->registers[MPXCFG] |= (ctrl->value > 15) ? 8 : ctrl->value; FMDBG("MPXCFG=0x%4.4hx POWERCFG=0x%4.4hx\n", radio->registers[MPXCFG], radio->registers[POWERCFG]); retval = rtc6226_set_register(radio, MPXCFG); break; case V4L2_CID_PRIVATE_CSR0_DIS_MUTE: case V4L2_CID_AUDIO_MUTE: if (ctrl->value == 1) radio->registers[MPXCFG] &= ~MPXCFG_CSR0_DIS_MUTE; else radio->registers[MPXCFG] |= MPXCFG_CSR0_DIS_MUTE; retval = rtc6226_set_register(radio, MPXCFG); break; case V4L2_CID_PRIVATE_RTC6226_SOFT_MUTE: FMDBG("V4L2_CID_PRIVATE_RTC6226_SOFT_MUTE\n"); if (ctrl->value == 1) radio->registers[MPXCFG] &= ~MPXCFG_CSR0_DIS_SMUTE; else radio->registers[MPXCFG] |= MPXCFG_CSR0_DIS_SMUTE; retval = rtc6226_set_register(radio, MPXCFG); break; case V4L2_CID_PRIVATE_CSR0_DEEM: FMDBG("V4L2_CID_PRIVATE_CSR0_DEEM\n"); if (ctrl->value == 1) radio->registers[MPXCFG] |= MPXCFG_CSR0_DEEM; else radio->registers[MPXCFG] &= ~MPXCFG_CSR0_DEEM; retval = rtc6226_set_register(radio, MPXCFG); break; case V4L2_CID_PRIVATE_CSR0_BLNDADJUST: FMDBG("V4L2_CID_PRIVATE_CSR0_BLNDADJUST val=%d\n", ctrl->value); break; case V4L2_CID_PRIVATE_CSR0_BAND: FMDBG( "V4L2_CID_PRIVATE_CSR0_BAND : FREQ_TOP=%d FREQ_BOT=%d %d\n", radio->registers[RADIOSEEKCFG1], radio->registers[RADIOSEEKCFG2], ctrl->value); switch (ctrl->value) { case FMBAND_87_108_MHZ: radio->registers[RADIOSEEKCFG1] = 10800; radio->registers[RADIOSEEKCFG2] = 8750; break; case FMBAND_76_108_MHZ: radio->registers[RADIOSEEKCFG1] = 10800; radio->registers[RADIOSEEKCFG2] = 7600; break; case FMBAND_76_91_MHZ: radio->registers[RADIOSEEKCFG1] = 9100; radio->registers[RADIOSEEKCFG2] = 7600; break; case FMBAND_64_76_MHZ: radio->registers[RADIOSEEKCFG1] = 7600; radio->registers[RADIOSEEKCFG2] = 6400; break; default: retval = -EINVAL; break; } FMDBG( "V4L2_CID_PRIVATE_CSR0_BAND : FREQ_TOP=%d FREQ_BOT=%d %d\n", radio->registers[RADIOSEEKCFG1], radio->registers[RADIOSEEKCFG2], ctrl->value); radio->band = ctrl->value; retval = rtc6226_set_register(radio, RADIOSEEKCFG1); retval = rtc6226_set_register(radio, RADIOSEEKCFG2); break; case V4L2_CID_PRIVATE_RTC6226_SPACING: case V4L2_CID_PRIVATE_CSR0_CHSPACE: FMDBG("V4L2_CID_PRIVATE_CSR0_CHSPACE : FM_SPACE=%d %d\n", radio->registers[RADIOCFG], ctrl->value); radio->space = ctrl->value; radio->registers[RADIOCFG] &= ~CHANNEL_CSR0_CHSPACE; switch (ctrl->value) { case FMSPACE_200_KHZ: radio->registers[RADIOCFG] |= 0x1400; break; case FMSPACE_100_KHZ: radio->registers[RADIOCFG] |= 0x0A00; break; case FMSPACE_50_KHZ: radio->registers[RADIOCFG] |= 0x0500; break; default: retval = -EINVAL; break; } radio->space = ctrl->value; FMDBG("V4L2_CID_PRIVATE_CSR0_CHSPACE : FM_SPACE=%d %d\n", radio->registers[RADIOCFG], ctrl->value); retval = rtc6226_set_register(radio, RADIOCFG); break; case V4L2_CID_PRIVATE_CSR0_DIS_AGC: FMDBG("V4L2_CID_PRIVATE_CSR0_DIS_AGC val=%d\n", ctrl->value); break; case V4L2_CID_PRIVATE_RTC6226_RDSON: FMDBG( "V4L2_CSR0_RDS_EN:CHANNEL=0x%4.4hx SYSCFG=0x%4.4hx\n", radio->registers[CHANNEL], radio->registers[SYSCFG]); rtc6226_reset_rds_data(radio); radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDS_EN; radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDSIRQEN; radio->registers[SYSCFG] |= (ctrl->value << 15); radio->registers[SYSCFG] |= (ctrl->value << 12); FMDBG ("V4L2_CSR0_RDS_EN : CHANNEL=0x%4.4hx SYSCFG=0x%4.4hx\n", radio->registers[CHANNEL], radio->registers[SYSCFG]); retval = rtc6226_set_register(radio, SYSCFG); break; case V4L2_CID_PRIVATE_SEEK_CANCEL: rtc6226_search(radio, (bool)ctrl->value); break; case V4L2_CID_PRIVATE_CSR0_SEEKRSSITH: radio->registers[SEEKCFG1] &= ~SEEKCFG1_CSR0_RSSI_LOW_TH; radio->registers[SEEKCFG1] |= ctrl->value; retval = rtc6226_set_register(radio, SEEKCFG1); break; default: FMDBG("%s id: %x in default\n", __func__, ctrl->id); retval = -EINVAL; break; } end: FMDBG("%s exit id: %x , ret: %d\n", __func__, ctrl->id, retval); return retval; } /* * rtc6226_vidioc_g_audio - get audio attributes */ static int rtc6226_vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *audio) { /* driver constants */ audio->index = 0; strlcpy(audio->name, "Radio", sizeof(audio->name)); audio->capability = V4L2_AUDCAP_STEREO; audio->mode = 0; return 0; } /* * rtc6226_vidioc_g_tuner - get tuner attributes */ static int rtc6226_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; FMDBG("%s enter\n", __func__); if (tuner->index != 0) { retval = -EINVAL; goto done; } retval = rtc6226_get_register(radio, RSSI); if (retval < 0) goto done; /* driver constants */ strlcpy(tuner->name, "FM", sizeof(tuner->name)); tuner->type = V4L2_TUNER_RADIO; tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; tuner->rangehigh = (radio->registers[RADIOSEEKCFG1] & CHANNEL_CSR0_FREQ_TOP) * TUNE_STEP_SIZE * TUNE_PARAM; tuner->rangelow = (radio->registers[RADIOSEEKCFG2] & CHANNEL_CSR0_FREQ_BOT) * TUNE_STEP_SIZE * TUNE_PARAM; FMDBG("%s low:%d high:%d\n", __func__, tuner->rangelow, tuner->rangehigh); /* stereo indicator == stereo (instead of mono) */ if ((radio->registers[STATUS] & STATUS_SI) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; /* If there is a reliable method of detecting an RDS channel, * then this code should check for that before setting this * RDS subchannel. */ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; /* mono/stereo selector */ if ((radio->registers[MPXCFG] & MPXCFG_CSR0_MONO) == 0) { tuner->audmode = V4L2_TUNER_MODE_STEREO; rtc6226_q_event(radio, RTC6226_EVT_STEREO); } else { tuner->audmode = V4L2_TUNER_MODE_MONO; rtc6226_q_event(radio, RTC6226_EVT_MONO); } /* min is worst, max is best; rssi: 0..0xff */ tuner->signal = (radio->registers[RSSI] & RSSI_RSSI); done: FMDBG("%s exit %d\n", __func__, retval); return retval; } /* * rtc6226_vidioc_s_tuner - set tuner attributes */ static int rtc6226_vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *tuner) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; u16 bottom_freq; u16 top_freq; FMDBG("%s entry\n", __func__); if (tuner->index != 0) { FMDBG("%s index :%d\n", __func__, tuner->index); goto done; } /* mono/stereo selector */ switch (tuner->audmode) { case V4L2_TUNER_MODE_MONO: radio->registers[MPXCFG] |= MPXCFG_CSR0_MONO; /* force mono */ break; case V4L2_TUNER_MODE_STEREO: radio->registers[MPXCFG] &= ~MPXCFG_CSR0_MONO; /* try stereo */ break; default: FMDBG("%s audmode is not set\n", __func__); } retval = rtc6226_set_register(radio, MPXCFG); /* unit is 10kHz */ top_freq = (u16)((tuner->rangehigh / TUNE_PARAM) / TUNE_STEP_SIZE); bottom_freq = (u16)((tuner->rangelow / TUNE_PARAM) / TUNE_STEP_SIZE); FMDBG("%s low:%d high:%d\n", __func__, bottom_freq, top_freq); radio->registers[RADIOSEEKCFG1] = top_freq; radio->registers[RADIOSEEKCFG2] = bottom_freq; retval = rtc6226_set_register(radio, RADIOSEEKCFG1); if (retval < 0) FMDERR("In %s, error %d setting higher limit freq\n", __func__, retval); else radio->recv_conf.band_high_limit = top_freq; retval = rtc6226_set_register(radio, RADIOSEEKCFG2); if (retval < 0) FMDERR("In %s, error %d setting lower limit freq\n", __func__, retval); else radio->recv_conf.band_low_limit = bottom_freq; done: FMDBG("%s exit %d\n", __func__, retval); return retval; } /* * rtc6226_vidioc_g_frequency - get tuner or modulator radio frequency */ static int rtc6226_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; unsigned int frq; FMDBG("%s enter freq %d\n", __func__, freq->frequency); freq->type = V4L2_TUNER_RADIO; retval = rtc6226_get_freq(radio, &frq); freq->frequency = frq * TUNE_PARAM; radio->tuned_freq_khz = frq * TUNE_STEP_SIZE; FMDBG(" %s *freq=%d, ret %d\n", __func__, freq->frequency, retval); if (retval < 0) FMDERR(" %s get frequency failed with %d\n", __func__, retval); return retval; } /* * rtc6226_vidioc_s_frequency - set tuner or modulator radio frequency */ static int rtc6226_vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *freq) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; u32 f = 0; FMDBG("%s enter freq = %d\n", __func__, freq->frequency); if (unlikely(freq == NULL)) { FMDERR("%s:freq is null\n", __func__); return -EINVAL; } if (freq->type != V4L2_TUNER_RADIO) return -EINVAL; f = (freq->frequency)/TUNE_PARAM; radio->seek_tune_status = TUNE_PENDING; retval = rtc6226_set_freq(radio, f); if (retval < 0) FMDERR("%s set frequency failed with %d\n", __func__, retval); else radio->tuned_freq_khz = f; return retval; } /* * rtc6226_vidioc_s_hw_freq_seek - set hardware frequency seek */ static int rtc6226_vidioc_s_hw_freq_seek(struct file *file, void *priv, const struct v4l2_hw_freq_seek *seek) { struct rtc6226_device *radio = video_drvdata(file); int retval = 0; FMDBG("%s enter\n", __func__); if (file->f_flags & O_NONBLOCK) return -EWOULDBLOCK; radio->is_search_cancelled = false; /* Disable the rds before seek */ radio->registers[SYSCFG] &= ~SYSCFG_CSR0_RDS_EN; retval = rtc6226_set_register(radio, SYSCFG); if (retval < 0) { FMDERR("%s fail to disable RDS\n", __func__); return retval; } if (radio->g_search_mode == SEEK) { /* seek */ FMDBG("%s starting seek\n", __func__); radio->seek_tune_status = SEEK_PENDING; retval = rtc6226_set_seek(radio, seek->seek_upward, WRAP_ENABLE); } else if ((radio->g_search_mode == SCAN) || (radio->g_search_mode == SCAN_FOR_STRONG)) { /* scan */ if (radio->g_search_mode == SCAN_FOR_STRONG) { FMDBG("%s starting search list\n", __func__); memset(&radio->srch_list, 0, sizeof(struct rtc6226_srch_list_compl)); } else { FMDBG("%s starting scan\n", __func__); } rtc6226_search(radio, START_SCAN); } else { retval = -EINVAL; FMDERR("In %s, invalid search mode %d\n", __func__, radio->g_search_mode); } FMDBG("%s exit %d\n", __func__, retval); return retval; } static const struct v4l2_file_operations rtc6226_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl32 = v4l2_compat_ioctl32, #endif .read = rtc6226_fops_read, .poll = rtc6226_fops_poll, .open = rtc6226_fops_open, .release = rtc6226_fops_release, }; /* * rtc6226_ioctl_ops - video device ioctl operations */ /* static */ const struct v4l2_ioctl_ops rtc6226_ioctl_ops = { .vidioc_querycap = rtc6226_vidioc_querycap, .vidioc_g_audio = rtc6226_vidioc_g_audio, .vidioc_g_tuner = rtc6226_vidioc_g_tuner, .vidioc_s_tuner = rtc6226_vidioc_s_tuner, .vidioc_g_ctrl = rtc6226_vidioc_g_ctrl, .vidioc_s_ctrl = rtc6226_vidioc_s_ctrl, .vidioc_g_frequency = rtc6226_vidioc_g_frequency, .vidioc_s_frequency = rtc6226_vidioc_s_frequency, .vidioc_s_hw_freq_seek = rtc6226_vidioc_s_hw_freq_seek, .vidioc_dqbuf = rtc6226_vidioc_dqbuf, }; /* * rtc6226_viddev_template - video device interface */ struct video_device rtc6226_viddev_template = { .fops = &rtc6226_fops, .name = DRIVER_NAME, .release = video_device_release_empty, .ioctl_ops = &rtc6226_ioctl_ops, }; /************************************************************************** * Module Interface **************************************************************************/ /* * rtc6226_i2c_init - module init */ static __init int rtc6226_init(void) { FMDBG(DRIVER_DESC ", Version " DRIVER_VERSION "\n"); return rtc6226_i2c_init(); } /* * rtc6226_i2c_exit - module exit */ static void __exit rtc6226_exit(void) { i2c_del_driver(&rtc6226_i2c_driver); } module_init(rtc6226_init); module_exit(rtc6226_exit);