1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * FM Driver for Connectivity chip of Texas Instruments.
4*4882a593Smuzhiyun * This sub-module of FM driver implements FM RX functionality.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2011 Texas Instruments
7*4882a593Smuzhiyun * Author: Raja Mani <raja_mani@ti.com>
8*4882a593Smuzhiyun * Author: Manjunatha Halli <manjunatha_halli@ti.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include "fmdrv.h"
12*4882a593Smuzhiyun #include "fmdrv_common.h"
13*4882a593Smuzhiyun #include "fmdrv_rx.h"
14*4882a593Smuzhiyun
fm_rx_reset_rds_cache(struct fmdev * fmdev)15*4882a593Smuzhiyun void fm_rx_reset_rds_cache(struct fmdev *fmdev)
16*4882a593Smuzhiyun {
17*4882a593Smuzhiyun fmdev->rx.rds.flag = FM_RDS_DISABLE;
18*4882a593Smuzhiyun fmdev->rx.rds.last_blk_idx = 0;
19*4882a593Smuzhiyun fmdev->rx.rds.wr_idx = 0;
20*4882a593Smuzhiyun fmdev->rx.rds.rd_idx = 0;
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
23*4882a593Smuzhiyun fmdev->irq_info.mask |= FM_LEV_EVENT;
24*4882a593Smuzhiyun }
25*4882a593Smuzhiyun
fm_rx_reset_station_info(struct fmdev * fmdev)26*4882a593Smuzhiyun void fm_rx_reset_station_info(struct fmdev *fmdev)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun fmdev->rx.stat_info.picode = FM_NO_PI_CODE;
29*4882a593Smuzhiyun fmdev->rx.stat_info.afcache_size = 0;
30*4882a593Smuzhiyun fmdev->rx.stat_info.af_list_max = 0;
31*4882a593Smuzhiyun }
32*4882a593Smuzhiyun
fm_rx_set_freq(struct fmdev * fmdev,u32 freq)33*4882a593Smuzhiyun int fm_rx_set_freq(struct fmdev *fmdev, u32 freq)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun unsigned long timeleft;
36*4882a593Smuzhiyun u16 payload, curr_frq, intr_flag;
37*4882a593Smuzhiyun u32 curr_frq_in_khz;
38*4882a593Smuzhiyun u32 resp_len;
39*4882a593Smuzhiyun int ret;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) {
42*4882a593Smuzhiyun fmerr("Invalid frequency %d\n", freq);
43*4882a593Smuzhiyun return -EINVAL;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* Set audio enable */
47*4882a593Smuzhiyun payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload,
50*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
51*4882a593Smuzhiyun if (ret < 0)
52*4882a593Smuzhiyun return ret;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* Set hilo to automatic selection */
55*4882a593Smuzhiyun payload = FM_RX_IFFREQ_HILO_AUTOMATIC;
56*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload,
57*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
58*4882a593Smuzhiyun if (ret < 0)
59*4882a593Smuzhiyun return ret;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun /* Calculate frequency index and set*/
62*4882a593Smuzhiyun payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
65*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
66*4882a593Smuzhiyun if (ret < 0)
67*4882a593Smuzhiyun return ret;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /* Read flags - just to clear any pending interrupts if we had */
70*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
71*4882a593Smuzhiyun if (ret < 0)
72*4882a593Smuzhiyun return ret;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun /* Enable FR, BL interrupts */
75*4882a593Smuzhiyun intr_flag = fmdev->irq_info.mask;
76*4882a593Smuzhiyun fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
77*4882a593Smuzhiyun payload = fmdev->irq_info.mask;
78*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
79*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
80*4882a593Smuzhiyun if (ret < 0)
81*4882a593Smuzhiyun return ret;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* Start tune */
84*4882a593Smuzhiyun payload = FM_TUNER_PRESET_MODE;
85*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
86*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
87*4882a593Smuzhiyun if (ret < 0)
88*4882a593Smuzhiyun goto exit;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun /* Wait for tune ended interrupt */
91*4882a593Smuzhiyun init_completion(&fmdev->maintask_comp);
92*4882a593Smuzhiyun timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
93*4882a593Smuzhiyun FM_DRV_TX_TIMEOUT);
94*4882a593Smuzhiyun if (!timeleft) {
95*4882a593Smuzhiyun fmerr("Timeout(%d sec),didn't get tune ended int\n",
96*4882a593Smuzhiyun jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
97*4882a593Smuzhiyun ret = -ETIMEDOUT;
98*4882a593Smuzhiyun goto exit;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /* Read freq back to confirm */
102*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len);
103*4882a593Smuzhiyun if (ret < 0)
104*4882a593Smuzhiyun goto exit;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun curr_frq = be16_to_cpu((__force __be16)curr_frq);
107*4882a593Smuzhiyun curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun if (curr_frq_in_khz != freq) {
110*4882a593Smuzhiyun pr_info("Frequency is set to (%d) but requested freq is (%d)\n",
111*4882a593Smuzhiyun curr_frq_in_khz, freq);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* Update local cache */
115*4882a593Smuzhiyun fmdev->rx.freq = curr_frq_in_khz;
116*4882a593Smuzhiyun exit:
117*4882a593Smuzhiyun /* Re-enable default FM interrupts */
118*4882a593Smuzhiyun fmdev->irq_info.mask = intr_flag;
119*4882a593Smuzhiyun payload = fmdev->irq_info.mask;
120*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
121*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
122*4882a593Smuzhiyun if (ret < 0)
123*4882a593Smuzhiyun return ret;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /* Reset RDS cache and current station pointers */
126*4882a593Smuzhiyun fm_rx_reset_rds_cache(fmdev);
127*4882a593Smuzhiyun fm_rx_reset_station_info(fmdev);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun return ret;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
fm_rx_set_channel_spacing(struct fmdev * fmdev,u32 spacing)132*4882a593Smuzhiyun static int fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun u16 payload;
135*4882a593Smuzhiyun int ret;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun if (spacing > 0 && spacing <= 50000)
138*4882a593Smuzhiyun spacing = FM_CHANNEL_SPACING_50KHZ;
139*4882a593Smuzhiyun else if (spacing > 50000 && spacing <= 100000)
140*4882a593Smuzhiyun spacing = FM_CHANNEL_SPACING_100KHZ;
141*4882a593Smuzhiyun else
142*4882a593Smuzhiyun spacing = FM_CHANNEL_SPACING_200KHZ;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* set channel spacing */
145*4882a593Smuzhiyun payload = spacing;
146*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload,
147*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
148*4882a593Smuzhiyun if (ret < 0)
149*4882a593Smuzhiyun return ret;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun return ret;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
fm_rx_seek(struct fmdev * fmdev,u32 seek_upward,u32 wrap_around,u32 spacing)156*4882a593Smuzhiyun int fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,
157*4882a593Smuzhiyun u32 wrap_around, u32 spacing)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun u32 resp_len;
160*4882a593Smuzhiyun u16 curr_frq, next_frq, last_frq;
161*4882a593Smuzhiyun u16 payload, int_reason, intr_flag;
162*4882a593Smuzhiyun u16 offset, space_idx;
163*4882a593Smuzhiyun unsigned long timeleft;
164*4882a593Smuzhiyun int ret;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun /* Set channel spacing */
167*4882a593Smuzhiyun ret = fm_rx_set_channel_spacing(fmdev, spacing);
168*4882a593Smuzhiyun if (ret < 0) {
169*4882a593Smuzhiyun fmerr("Failed to set channel spacing\n");
170*4882a593Smuzhiyun return ret;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun /* Read the current frequency from chip */
174*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL,
175*4882a593Smuzhiyun sizeof(curr_frq), &curr_frq, &resp_len);
176*4882a593Smuzhiyun if (ret < 0)
177*4882a593Smuzhiyun return ret;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun curr_frq = be16_to_cpu((__force __be16)curr_frq);
180*4882a593Smuzhiyun last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun /* Check the offset in order to be aligned to the channel spacing*/
183*4882a593Smuzhiyun space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL;
184*4882a593Smuzhiyun offset = curr_frq % space_idx;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ :
187*4882a593Smuzhiyun curr_frq - space_idx /* Seek Down */ ;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /*
190*4882a593Smuzhiyun * Add or subtract offset in order to stay aligned to the channel
191*4882a593Smuzhiyun * spacing.
192*4882a593Smuzhiyun */
193*4882a593Smuzhiyun if ((short)next_frq < 0)
194*4882a593Smuzhiyun next_frq = last_frq - offset;
195*4882a593Smuzhiyun else if (next_frq > last_frq)
196*4882a593Smuzhiyun next_frq = 0 + offset;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun again:
199*4882a593Smuzhiyun /* Set calculated next frequency to perform seek */
200*4882a593Smuzhiyun payload = next_frq;
201*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
202*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
203*4882a593Smuzhiyun if (ret < 0)
204*4882a593Smuzhiyun return ret;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* Set search direction (0:Seek Down, 1:Seek Up) */
207*4882a593Smuzhiyun payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN);
208*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload,
209*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
210*4882a593Smuzhiyun if (ret < 0)
211*4882a593Smuzhiyun return ret;
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun /* Read flags - just to clear any pending interrupts if we had */
214*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
215*4882a593Smuzhiyun if (ret < 0)
216*4882a593Smuzhiyun return ret;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun /* Enable FR, BL interrupts */
219*4882a593Smuzhiyun intr_flag = fmdev->irq_info.mask;
220*4882a593Smuzhiyun fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
221*4882a593Smuzhiyun payload = fmdev->irq_info.mask;
222*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
223*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
224*4882a593Smuzhiyun if (ret < 0)
225*4882a593Smuzhiyun return ret;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun /* Start seek */
228*4882a593Smuzhiyun payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
229*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
230*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
231*4882a593Smuzhiyun if (ret < 0)
232*4882a593Smuzhiyun return ret;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* Wait for tune ended/band limit reached interrupt */
235*4882a593Smuzhiyun init_completion(&fmdev->maintask_comp);
236*4882a593Smuzhiyun timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
237*4882a593Smuzhiyun FM_DRV_RX_SEEK_TIMEOUT);
238*4882a593Smuzhiyun if (!timeleft) {
239*4882a593Smuzhiyun fmerr("Timeout(%d sec),didn't get tune ended int\n",
240*4882a593Smuzhiyun jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
241*4882a593Smuzhiyun return -ENODATA;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun /* Re-enable default FM interrupts */
247*4882a593Smuzhiyun fmdev->irq_info.mask = intr_flag;
248*4882a593Smuzhiyun payload = fmdev->irq_info.mask;
249*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
250*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
251*4882a593Smuzhiyun if (ret < 0)
252*4882a593Smuzhiyun return ret;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (int_reason & FM_BL_EVENT) {
255*4882a593Smuzhiyun if (wrap_around == 0) {
256*4882a593Smuzhiyun fmdev->rx.freq = seek_upward ?
257*4882a593Smuzhiyun fmdev->rx.region.top_freq :
258*4882a593Smuzhiyun fmdev->rx.region.bot_freq;
259*4882a593Smuzhiyun } else {
260*4882a593Smuzhiyun fmdev->rx.freq = seek_upward ?
261*4882a593Smuzhiyun fmdev->rx.region.bot_freq :
262*4882a593Smuzhiyun fmdev->rx.region.top_freq;
263*4882a593Smuzhiyun /* Calculate frequency index to write */
264*4882a593Smuzhiyun next_frq = (fmdev->rx.freq -
265*4882a593Smuzhiyun fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
266*4882a593Smuzhiyun goto again;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun } else {
269*4882a593Smuzhiyun /* Read freq to know where operation tune operation stopped */
270*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2,
271*4882a593Smuzhiyun &curr_frq, &resp_len);
272*4882a593Smuzhiyun if (ret < 0)
273*4882a593Smuzhiyun return ret;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun curr_frq = be16_to_cpu((__force __be16)curr_frq);
276*4882a593Smuzhiyun fmdev->rx.freq = (fmdev->rx.region.bot_freq +
277*4882a593Smuzhiyun ((u32)curr_frq * FM_FREQ_MUL));
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun /* Reset RDS cache and current station pointers */
281*4882a593Smuzhiyun fm_rx_reset_rds_cache(fmdev);
282*4882a593Smuzhiyun fm_rx_reset_station_info(fmdev);
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun return ret;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
fm_rx_set_volume(struct fmdev * fmdev,u16 vol_to_set)287*4882a593Smuzhiyun int fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun u16 payload;
290*4882a593Smuzhiyun int ret;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
293*4882a593Smuzhiyun return -EPERM;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun if (vol_to_set > FM_RX_VOLUME_MAX) {
296*4882a593Smuzhiyun fmerr("Volume is not within(%d-%d) range\n",
297*4882a593Smuzhiyun FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);
298*4882a593Smuzhiyun return -EINVAL;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun vol_to_set *= FM_RX_VOLUME_GAIN_STEP;
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun payload = vol_to_set;
303*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload,
304*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
305*4882a593Smuzhiyun if (ret < 0)
306*4882a593Smuzhiyun return ret;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun fmdev->rx.volume = vol_to_set;
309*4882a593Smuzhiyun return ret;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /* Get volume */
fm_rx_get_volume(struct fmdev * fmdev,u16 * curr_vol)313*4882a593Smuzhiyun int fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
316*4882a593Smuzhiyun return -EPERM;
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun if (curr_vol == NULL) {
319*4882a593Smuzhiyun fmerr("Invalid memory\n");
320*4882a593Smuzhiyun return -ENOMEM;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP;
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun return 0;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun /* To get current band's bottom and top frequency */
fm_rx_get_band_freq_range(struct fmdev * fmdev,u32 * bot_freq,u32 * top_freq)329*4882a593Smuzhiyun int fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun if (bot_freq != NULL)
332*4882a593Smuzhiyun *bot_freq = fmdev->rx.region.bot_freq;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun if (top_freq != NULL)
335*4882a593Smuzhiyun *top_freq = fmdev->rx.region.top_freq;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun return 0;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun /* Returns current band index (0-Europe/US; 1-Japan) */
fm_rx_get_region(struct fmdev * fmdev,u8 * region)341*4882a593Smuzhiyun void fm_rx_get_region(struct fmdev *fmdev, u8 *region)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun *region = fmdev->rx.region.fm_band;
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun /* Sets band (0-Europe/US; 1-Japan) */
fm_rx_set_region(struct fmdev * fmdev,u8 region_to_set)347*4882a593Smuzhiyun int fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun u16 payload;
350*4882a593Smuzhiyun u32 new_frq = 0;
351*4882a593Smuzhiyun int ret;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (region_to_set != FM_BAND_EUROPE_US &&
354*4882a593Smuzhiyun region_to_set != FM_BAND_JAPAN) {
355*4882a593Smuzhiyun fmerr("Invalid band\n");
356*4882a593Smuzhiyun return -EINVAL;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun if (fmdev->rx.region.fm_band == region_to_set) {
360*4882a593Smuzhiyun fmerr("Requested band is already configured\n");
361*4882a593Smuzhiyun return 0;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* Send cmd to set the band */
365*4882a593Smuzhiyun payload = (u16)region_to_set;
366*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload,
367*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
368*4882a593Smuzhiyun if (ret < 0)
369*4882a593Smuzhiyun return ret;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun fmc_update_region_info(fmdev, region_to_set);
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun /* Check whether current RX frequency is within band boundary */
374*4882a593Smuzhiyun if (fmdev->rx.freq < fmdev->rx.region.bot_freq)
375*4882a593Smuzhiyun new_frq = fmdev->rx.region.bot_freq;
376*4882a593Smuzhiyun else if (fmdev->rx.freq > fmdev->rx.region.top_freq)
377*4882a593Smuzhiyun new_frq = fmdev->rx.region.top_freq;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun if (new_frq) {
380*4882a593Smuzhiyun fmdbg("Current freq is not within band limit boundary,switching to %d KHz\n",
381*4882a593Smuzhiyun new_frq);
382*4882a593Smuzhiyun /* Current RX frequency is not in range. So, update it */
383*4882a593Smuzhiyun ret = fm_rx_set_freq(fmdev, new_frq);
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun return ret;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun /* Reads current mute mode (Mute Off/On/Attenuate)*/
fm_rx_get_mute_mode(struct fmdev * fmdev,u8 * curr_mute_mode)390*4882a593Smuzhiyun int fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
393*4882a593Smuzhiyun return -EPERM;
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun if (curr_mute_mode == NULL) {
396*4882a593Smuzhiyun fmerr("Invalid memory\n");
397*4882a593Smuzhiyun return -ENOMEM;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun *curr_mute_mode = fmdev->rx.mute_mode;
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun return 0;
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun
fm_config_rx_mute_reg(struct fmdev * fmdev)405*4882a593Smuzhiyun static int fm_config_rx_mute_reg(struct fmdev *fmdev)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun u16 payload, muteval;
408*4882a593Smuzhiyun int ret;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun muteval = 0;
411*4882a593Smuzhiyun switch (fmdev->rx.mute_mode) {
412*4882a593Smuzhiyun case FM_MUTE_ON:
413*4882a593Smuzhiyun muteval = FM_RX_AC_MUTE_MODE;
414*4882a593Smuzhiyun break;
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun case FM_MUTE_OFF:
417*4882a593Smuzhiyun muteval = FM_RX_UNMUTE_MODE;
418*4882a593Smuzhiyun break;
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun case FM_MUTE_ATTENUATE:
421*4882a593Smuzhiyun muteval = FM_RX_SOFT_MUTE_FORCE_MODE;
422*4882a593Smuzhiyun break;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON)
425*4882a593Smuzhiyun muteval |= FM_RX_RF_DEP_MODE;
426*4882a593Smuzhiyun else
427*4882a593Smuzhiyun muteval &= ~FM_RX_RF_DEP_MODE;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun payload = muteval;
430*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload,
431*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
432*4882a593Smuzhiyun if (ret < 0)
433*4882a593Smuzhiyun return ret;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun return 0;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun /* Configures mute mode (Mute Off/On/Attenuate) */
fm_rx_set_mute_mode(struct fmdev * fmdev,u8 mute_mode_toset)439*4882a593Smuzhiyun int fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun u8 org_state;
442*4882a593Smuzhiyun int ret;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun if (fmdev->rx.mute_mode == mute_mode_toset)
445*4882a593Smuzhiyun return 0;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun org_state = fmdev->rx.mute_mode;
448*4882a593Smuzhiyun fmdev->rx.mute_mode = mute_mode_toset;
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun ret = fm_config_rx_mute_reg(fmdev);
451*4882a593Smuzhiyun if (ret < 0) {
452*4882a593Smuzhiyun fmdev->rx.mute_mode = org_state;
453*4882a593Smuzhiyun return ret;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun return 0;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun /* Gets RF dependent soft mute mode enable/disable status */
fm_rx_get_rfdepend_softmute(struct fmdev * fmdev,u8 * curr_mute_mode)460*4882a593Smuzhiyun int fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
463*4882a593Smuzhiyun return -EPERM;
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun if (curr_mute_mode == NULL) {
466*4882a593Smuzhiyun fmerr("Invalid memory\n");
467*4882a593Smuzhiyun return -ENOMEM;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun *curr_mute_mode = fmdev->rx.rf_depend_mute;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun return 0;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun /* Sets RF dependent soft mute mode */
fm_rx_set_rfdepend_softmute(struct fmdev * fmdev,u8 rfdepend_mute)476*4882a593Smuzhiyun int fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun u8 org_state;
479*4882a593Smuzhiyun int ret;
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
482*4882a593Smuzhiyun return -EPERM;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON &&
485*4882a593Smuzhiyun rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) {
486*4882a593Smuzhiyun fmerr("Invalid RF dependent soft mute\n");
487*4882a593Smuzhiyun return -EINVAL;
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun if (fmdev->rx.rf_depend_mute == rfdepend_mute)
490*4882a593Smuzhiyun return 0;
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun org_state = fmdev->rx.rf_depend_mute;
493*4882a593Smuzhiyun fmdev->rx.rf_depend_mute = rfdepend_mute;
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun ret = fm_config_rx_mute_reg(fmdev);
496*4882a593Smuzhiyun if (ret < 0) {
497*4882a593Smuzhiyun fmdev->rx.rf_depend_mute = org_state;
498*4882a593Smuzhiyun return ret;
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun return 0;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun /* Returns the signal strength level of current channel */
fm_rx_get_rssi_level(struct fmdev * fmdev,u16 * rssilvl)505*4882a593Smuzhiyun int fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)
506*4882a593Smuzhiyun {
507*4882a593Smuzhiyun __be16 curr_rssi_lel;
508*4882a593Smuzhiyun u32 resp_len;
509*4882a593Smuzhiyun int ret;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun if (rssilvl == NULL) {
512*4882a593Smuzhiyun fmerr("Invalid memory\n");
513*4882a593Smuzhiyun return -ENOMEM;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun /* Read current RSSI level */
516*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2,
517*4882a593Smuzhiyun &curr_rssi_lel, &resp_len);
518*4882a593Smuzhiyun if (ret < 0)
519*4882a593Smuzhiyun return ret;
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun *rssilvl = be16_to_cpu(curr_rssi_lel);
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun return 0;
524*4882a593Smuzhiyun }
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun /*
527*4882a593Smuzhiyun * Sets the signal strength level that once reached
528*4882a593Smuzhiyun * will stop the auto search process
529*4882a593Smuzhiyun */
fm_rx_set_rssi_threshold(struct fmdev * fmdev,short rssi_lvl_toset)530*4882a593Smuzhiyun int fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset)
531*4882a593Smuzhiyun {
532*4882a593Smuzhiyun u16 payload;
533*4882a593Smuzhiyun int ret;
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN ||
536*4882a593Smuzhiyun rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) {
537*4882a593Smuzhiyun fmerr("Invalid RSSI threshold level\n");
538*4882a593Smuzhiyun return -EINVAL;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun payload = (u16)rssi_lvl_toset;
541*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload,
542*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
543*4882a593Smuzhiyun if (ret < 0)
544*4882a593Smuzhiyun return ret;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun fmdev->rx.rssi_threshold = rssi_lvl_toset;
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun return 0;
549*4882a593Smuzhiyun }
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun /* Returns current RX RSSI threshold value */
fm_rx_get_rssi_threshold(struct fmdev * fmdev,short * curr_rssi_lvl)552*4882a593Smuzhiyun int fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl)
553*4882a593Smuzhiyun {
554*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
555*4882a593Smuzhiyun return -EPERM;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun if (curr_rssi_lvl == NULL) {
558*4882a593Smuzhiyun fmerr("Invalid memory\n");
559*4882a593Smuzhiyun return -ENOMEM;
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun *curr_rssi_lvl = fmdev->rx.rssi_threshold;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun return 0;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun /* Sets RX stereo/mono modes */
fm_rx_set_stereo_mono(struct fmdev * fmdev,u16 mode)568*4882a593Smuzhiyun int fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun u16 payload;
571*4882a593Smuzhiyun int ret;
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) {
574*4882a593Smuzhiyun fmerr("Invalid mode\n");
575*4882a593Smuzhiyun return -EINVAL;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun /* Set stereo/mono mode */
579*4882a593Smuzhiyun payload = (u16)mode;
580*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload,
581*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
582*4882a593Smuzhiyun if (ret < 0)
583*4882a593Smuzhiyun return ret;
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun /* Set stereo blending mode */
586*4882a593Smuzhiyun payload = FM_STEREO_SOFT_BLEND;
587*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload,
588*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
589*4882a593Smuzhiyun if (ret < 0)
590*4882a593Smuzhiyun return ret;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun return 0;
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun /* Gets current RX stereo/mono mode */
fm_rx_get_stereo_mono(struct fmdev * fmdev,u16 * mode)596*4882a593Smuzhiyun int fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)
597*4882a593Smuzhiyun {
598*4882a593Smuzhiyun __be16 curr_mode;
599*4882a593Smuzhiyun u32 resp_len;
600*4882a593Smuzhiyun int ret;
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun if (mode == NULL) {
603*4882a593Smuzhiyun fmerr("Invalid memory\n");
604*4882a593Smuzhiyun return -ENOMEM;
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2,
608*4882a593Smuzhiyun &curr_mode, &resp_len);
609*4882a593Smuzhiyun if (ret < 0)
610*4882a593Smuzhiyun return ret;
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun *mode = be16_to_cpu(curr_mode);
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun return 0;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun /* Choose RX de-emphasis filter mode (50us/75us) */
fm_rx_set_deemphasis_mode(struct fmdev * fmdev,u16 mode)618*4882a593Smuzhiyun int fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode)
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun u16 payload;
621*4882a593Smuzhiyun int ret;
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
624*4882a593Smuzhiyun return -EPERM;
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun if (mode != FM_RX_EMPHASIS_FILTER_50_USEC &&
627*4882a593Smuzhiyun mode != FM_RX_EMPHASIS_FILTER_75_USEC) {
628*4882a593Smuzhiyun fmerr("Invalid rx de-emphasis mode (%d)\n", mode);
629*4882a593Smuzhiyun return -EINVAL;
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun payload = mode;
633*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload,
634*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
635*4882a593Smuzhiyun if (ret < 0)
636*4882a593Smuzhiyun return ret;
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun fmdev->rx.deemphasis_mode = mode;
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun return 0;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun /* Gets current RX de-emphasis filter mode */
fm_rx_get_deemph_mode(struct fmdev * fmdev,u16 * curr_deemphasis_mode)644*4882a593Smuzhiyun int fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
647*4882a593Smuzhiyun return -EPERM;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun if (curr_deemphasis_mode == NULL) {
650*4882a593Smuzhiyun fmerr("Invalid memory\n");
651*4882a593Smuzhiyun return -ENOMEM;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun *curr_deemphasis_mode = fmdev->rx.deemphasis_mode;
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun return 0;
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun /* Enable/Disable RX RDS */
fm_rx_set_rds_mode(struct fmdev * fmdev,u8 rds_en_dis)660*4882a593Smuzhiyun int fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
661*4882a593Smuzhiyun {
662*4882a593Smuzhiyun u16 payload;
663*4882a593Smuzhiyun int ret;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {
666*4882a593Smuzhiyun fmerr("Invalid rds option\n");
667*4882a593Smuzhiyun return -EINVAL;
668*4882a593Smuzhiyun }
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun if (rds_en_dis == FM_RDS_ENABLE
671*4882a593Smuzhiyun && fmdev->rx.rds.flag == FM_RDS_DISABLE) {
672*4882a593Smuzhiyun /* Turn on RX RDS and RDS circuit */
673*4882a593Smuzhiyun payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON;
674*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
675*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
676*4882a593Smuzhiyun if (ret < 0)
677*4882a593Smuzhiyun return ret;
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun /* Clear and reset RDS FIFO */
680*4882a593Smuzhiyun payload = FM_RX_RDS_FLUSH_FIFO;
681*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload,
682*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
683*4882a593Smuzhiyun if (ret < 0)
684*4882a593Smuzhiyun return ret;
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun /* Read flags - just to clear any pending interrupts. */
687*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2,
688*4882a593Smuzhiyun NULL, NULL);
689*4882a593Smuzhiyun if (ret < 0)
690*4882a593Smuzhiyun return ret;
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun /* Set RDS FIFO threshold value */
693*4882a593Smuzhiyun payload = FM_RX_RDS_FIFO_THRESHOLD;
694*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload,
695*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
696*4882a593Smuzhiyun if (ret < 0)
697*4882a593Smuzhiyun return ret;
698*4882a593Smuzhiyun
699*4882a593Smuzhiyun /* Enable RDS interrupt */
700*4882a593Smuzhiyun fmdev->irq_info.mask |= FM_RDS_EVENT;
701*4882a593Smuzhiyun payload = fmdev->irq_info.mask;
702*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
703*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
704*4882a593Smuzhiyun if (ret < 0) {
705*4882a593Smuzhiyun fmdev->irq_info.mask &= ~FM_RDS_EVENT;
706*4882a593Smuzhiyun return ret;
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun /* Update our local flag */
710*4882a593Smuzhiyun fmdev->rx.rds.flag = FM_RDS_ENABLE;
711*4882a593Smuzhiyun } else if (rds_en_dis == FM_RDS_DISABLE
712*4882a593Smuzhiyun && fmdev->rx.rds.flag == FM_RDS_ENABLE) {
713*4882a593Smuzhiyun /* Turn off RX RDS */
714*4882a593Smuzhiyun payload = FM_RX_PWR_SET_FM_ON_RDS_OFF;
715*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
716*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
717*4882a593Smuzhiyun if (ret < 0)
718*4882a593Smuzhiyun return ret;
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun /* Reset RDS pointers */
721*4882a593Smuzhiyun fmdev->rx.rds.last_blk_idx = 0;
722*4882a593Smuzhiyun fmdev->rx.rds.wr_idx = 0;
723*4882a593Smuzhiyun fmdev->rx.rds.rd_idx = 0;
724*4882a593Smuzhiyun fm_rx_reset_station_info(fmdev);
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun /* Update RDS local cache */
727*4882a593Smuzhiyun fmdev->irq_info.mask &= ~(FM_RDS_EVENT);
728*4882a593Smuzhiyun fmdev->rx.rds.flag = FM_RDS_DISABLE;
729*4882a593Smuzhiyun }
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun return 0;
732*4882a593Smuzhiyun }
733*4882a593Smuzhiyun
734*4882a593Smuzhiyun /* Returns current RX RDS enable/disable status */
fm_rx_get_rds_mode(struct fmdev * fmdev,u8 * curr_rds_en_dis)735*4882a593Smuzhiyun int fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis)
736*4882a593Smuzhiyun {
737*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
738*4882a593Smuzhiyun return -EPERM;
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun if (curr_rds_en_dis == NULL) {
741*4882a593Smuzhiyun fmerr("Invalid memory\n");
742*4882a593Smuzhiyun return -ENOMEM;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun *curr_rds_en_dis = fmdev->rx.rds.flag;
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun return 0;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun /* Sets RDS operation mode (RDS/RDBS) */
fm_rx_set_rds_system(struct fmdev * fmdev,u8 rds_mode)751*4882a593Smuzhiyun int fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun u16 payload;
754*4882a593Smuzhiyun int ret;
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
757*4882a593Smuzhiyun return -EPERM;
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) {
760*4882a593Smuzhiyun fmerr("Invalid rds mode\n");
761*4882a593Smuzhiyun return -EINVAL;
762*4882a593Smuzhiyun }
763*4882a593Smuzhiyun /* Set RDS operation mode */
764*4882a593Smuzhiyun payload = (u16)rds_mode;
765*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload,
766*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
767*4882a593Smuzhiyun if (ret < 0)
768*4882a593Smuzhiyun return ret;
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun fmdev->rx.rds_mode = rds_mode;
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun return 0;
773*4882a593Smuzhiyun }
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun /* Configures Alternate Frequency switch mode */
fm_rx_set_af_switch(struct fmdev * fmdev,u8 af_mode)776*4882a593Smuzhiyun int fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)
777*4882a593Smuzhiyun {
778*4882a593Smuzhiyun u16 payload;
779*4882a593Smuzhiyun int ret;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
782*4882a593Smuzhiyun return -EPERM;
783*4882a593Smuzhiyun
784*4882a593Smuzhiyun if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON &&
785*4882a593Smuzhiyun af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) {
786*4882a593Smuzhiyun fmerr("Invalid af mode\n");
787*4882a593Smuzhiyun return -EINVAL;
788*4882a593Smuzhiyun }
789*4882a593Smuzhiyun /* Enable/disable low RSSI interrupt based on af_mode */
790*4882a593Smuzhiyun if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
791*4882a593Smuzhiyun fmdev->irq_info.mask |= FM_LEV_EVENT;
792*4882a593Smuzhiyun else
793*4882a593Smuzhiyun fmdev->irq_info.mask &= ~FM_LEV_EVENT;
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun payload = fmdev->irq_info.mask;
796*4882a593Smuzhiyun ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
797*4882a593Smuzhiyun sizeof(payload), NULL, NULL);
798*4882a593Smuzhiyun if (ret < 0)
799*4882a593Smuzhiyun return ret;
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun fmdev->rx.af_mode = af_mode;
802*4882a593Smuzhiyun
803*4882a593Smuzhiyun return 0;
804*4882a593Smuzhiyun }
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun /* Returns Alternate Frequency switch status */
fm_rx_get_af_switch(struct fmdev * fmdev,u8 * af_mode)807*4882a593Smuzhiyun int fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode)
808*4882a593Smuzhiyun {
809*4882a593Smuzhiyun if (fmdev->curr_fmmode != FM_MODE_RX)
810*4882a593Smuzhiyun return -EPERM;
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun if (af_mode == NULL) {
813*4882a593Smuzhiyun fmerr("Invalid memory\n");
814*4882a593Smuzhiyun return -ENOMEM;
815*4882a593Smuzhiyun }
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun *af_mode = fmdev->rx.af_mode;
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun return 0;
820*4882a593Smuzhiyun }
821