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