4 * MediaTek <www.MediaTek.com>
5 * Hongcheng <hongcheng.xia@MediaTek.com>
7 * FM Radio Driver -- main functions
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/device.h>
29 #include <linux/proc_fs.h>
30 #include <linux/cdev.h>
31 #include <linux/interrupt.h>
32 #include <asm/uaccess.h>
33 #include <linux/sched.h>
34 #include <linux/delay.h> /* udelay() */
36 #include "fm_config.h"
40 #define FM_PROC_FILE "fm"
42 fm_u32 g_dbg_level
= 0xfffffff5; /* Debug level of FM */
44 /* fm main data structure */
45 static struct fm
*g_fm
;
47 static struct proc_dir_entry
*g_fm_proc
;
49 /* char device interface */
50 static fm_s32
fm_cdev_setup(struct fm
*fm
);
51 static fm_s32
fm_cdev_destroy(struct fm
*fm
);
53 static long fm_ops_ioctl(struct file
*filp
, fm_u32 cmd
, unsigned long arg
);
55 static long fm_ops_compat_ioctl(struct file
* filp
,fm_u32 cmd
,unsigned long arg
);
57 static loff_t
fm_ops_lseek(struct file
*filp
, loff_t off
, fm_s32 whence
);
58 static ssize_t
fm_ops_read(struct file
*filp
, char *buf
, size_t len
, loff_t
*off
);
59 static fm_s32
fm_ops_open(struct inode
*inode
, struct file
*filp
);
60 static fm_s32
fm_ops_release(struct inode
*inode
, struct file
*filp
);
61 static fm_s32
fm_ops_flush(struct file
*filp
, fl_owner_t Id
);
62 static struct file_operations fm_ops
= {
64 .unlocked_ioctl
= fm_ops_ioctl
,
66 .compat_ioctl
= fm_ops_compat_ioctl
,
68 .llseek
= fm_ops_lseek
,
71 .release
= fm_ops_release
,
72 .flush
= fm_ops_flush
,
75 /* static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data); */
76 static ssize_t
fm_proc_read(struct file
*file
, char __user
*buf
, size_t count
, loff_t
*ppos
);
77 static ssize_t
fm_proc_write(struct file
*file
, const char *buffer
, size_t count
, loff_t
*ppos
);
79 static struct file_operations fm_proc_ops
= {
82 .write
= fm_proc_write
,
85 static struct fm_scan_t parm
= {
87 .sr
.ch_rssi_buf
= NULL
,
91 static long fm_ops_compat_ioctl(struct file
*filp
, fm_u32 cmd
, unsigned long arg
)
95 WCN_DBG(FM_NTC
| MAIN
, "COMPAT %s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current
->comm
,
96 current
->pid
, cmd
, (fm_u32
) arg
);
98 if(!filp
->f_op
|| !filp
->f_op
->unlocked_ioctl
)
101 ret
= filp
->f_op
->unlocked_ioctl(filp
, cmd
, arg
);
107 static long fm_ops_ioctl(struct file
*filp
, fm_u32 cmd
, unsigned long arg
)
110 struct fm_platform
*plat
=
111 container_of(filp
->f_dentry
->d_inode
->i_cdev
, struct fm_platform
, cdev
);
112 struct fm
*fm
= container_of(plat
, struct fm
, platform
);
114 WCN_DBG(FM_NTC
| MAIN
, "%s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current
->comm
,
115 current
->pid
, cmd
, (fm_u32
) arg
);
117 if (fm_sys_state_get(fm
) != FM_SUBSYS_RST_OFF
) {
118 WCN_DBG(FM_ALT
| MAIN
, "FM subsys is resetting, retry later\n");
124 case FM_IOCTL_POWERUP
:{
125 struct fm_tune_parm parm
;
126 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_POWERUP:0\n");
128 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_tune_parm
))) {
133 ret
= fm_powerup(fm
, &parm
);
136 ret
= fm_tune(fm
, &parm
);
140 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_tune_parm
))) {
144 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_POWERUP:1\n");
149 case FM_IOCTL_POWERDOWN
:{
150 int powerdwn_type
= 0;
151 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_POWERDOWN:0\n");
153 if (copy_from_user(&powerdwn_type
, (void *)arg
, sizeof(int))) {
158 ret
= fm_powerdown(fm
, powerdwn_type
); /* 0: RX 1: TX */
159 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_POWERDOWN:1\n");
164 struct fm_tune_parm parm
;
165 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_TUNE:0\n");
167 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_tune_parm
))) {
172 ret
= fm_tune(fm
, &parm
);
177 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_tune_parm
))) {
182 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_TUNE:1\n");
186 case FM_IOCTL_SOFT_MUTE_TUNE
:
188 struct fm_softmute_tune_t parm
;
189 fm_cqi_log(); /* cqi log tool */
190 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_SOFT_MUTE_TUNE......\n");
191 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_softmute_tune_t
))) {
196 ret
= fm_soft_mute_tune(fm
, &parm
);
201 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_softmute_tune_t
))) {
207 case FM_IOCTL_PRE_SEARCH
:
209 ret
= fm_pre_search(fm
);
212 case FM_IOCTL_RESTORE_SEARCH
:
214 ret
= fm_restore_search(fm
);
218 struct fm_seek_parm parm
;
219 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SEEK:0\n");
221 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_seek_parm
))) {
226 ret
= fm_seek(fm
, &parm
);
231 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_seek_parm
))) {
235 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SEEK:1\n");
240 struct fm_scan_parm parm
;
241 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SCAN start\n");
243 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_scan_parm
))) {
244 WCN_DBG(FM_ALT
| MAIN
, "copy_from_user failed\n");
249 ret
= fm_scan(fm
, &parm
);
253 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_scan_parm
))) {
258 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SCAN end\n");
262 case FM_IOCTL_TUNE_NEW
:{
263 struct fm_tune_t tune_tmp
;
265 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_TUNE_NEW\n");
267 if (copy_from_user(&tune_tmp
, (void *)arg
, sizeof(struct fm_tune_t
))) {
268 WCN_DBG(FM_ERR
| MAIN
, "tune new copy_from_user error\n");
273 ret
= fm_tune_new(fm
, &tune_tmp
);
278 if (copy_to_user((void *)arg
, &tune_tmp
, sizeof(struct fm_tune_t
))) {
279 WCN_DBG(FM_ERR
| MAIN
, "tune new copy_to_user error\n");
287 case FM_IOCTL_SEEK_NEW
:{
288 struct fm_seek_t seek_tmp
;
290 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SEEK_NEW\n");
292 if (copy_from_user(&seek_tmp
, (void *)arg
, sizeof(struct fm_seek_t
))) {
293 WCN_DBG(FM_ERR
| MAIN
, "seek new copy_from_user error\n");
298 ret
= fm_seek_new(fm
, &seek_tmp
);
303 if (copy_to_user((void *)arg
, &seek_tmp
, sizeof(struct fm_seek_t
))) {
304 WCN_DBG(FM_ERR
| MAIN
, "seek new copy_to_user error\n");
312 case FM_IOCTL_SCAN_NEW
:{
313 struct fm_scan_t tmp
;
315 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SCAN_NEW\n");
317 if (copy_from_user(&tmp
, (void *)arg
, sizeof(struct fm_scan_t
))) {
318 WCN_DBG(FM_ERR
| MAIN
, "copy_from_user error\n");
324 case FM_SCAN_CMD_START
:
325 if ((tmp
.upper
> 10800) || (tmp
.lower
< 7600) || (tmp
.space
< 5)
326 || (tmp
.space
> 20)) {
327 WCN_DBG(FM_ERR
| MAIN
, "scan para error\n");
332 parm
.lower
= tmp
.lower
;
333 parm
.upper
= tmp
.upper
;
334 parm
.space
= tmp
.space
;
336 ret
= fm_scan_new(fm
, &parm
);
342 case FM_SCAN_CMD_GET_CH_RSSI
:
343 if (parm
.sr_size
&& parm
.sr
.ch_rssi_buf
) {
345 (tmp
.sr
.ch_rssi_buf
, parm
.sr
.ch_rssi_buf
,
346 parm
.num
* sizeof(struct fm_ch_rssi
))) {
347 WCN_DBG(FM_ERR
| MAIN
, "scan copy_to_user err\n");
359 if (copy_to_user((void *)arg
, &tmp
, sizeof(struct fm_scan_t
))) {
360 WCN_DBG(FM_ERR
| MAIN
, "copy_to_user error\n");
367 case FM_IOCTL_CQI_GET
:{
368 struct fm_cqi_req cqi_req
;
372 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_CQI_GET\n");
374 if (copy_from_user(&cqi_req
, (void *)arg
, sizeof(struct fm_cqi_req
))) {
375 WCN_DBG(FM_ALT
| MAIN
, "copy_from_user failed\n");
380 if ((cqi_req
.ch_num
* sizeof(struct fm_cqi
) > cqi_req
.buf_size
)
381 || !cqi_req
.cqi_buf
) {
386 tmp
= cqi_req
.ch_num
/ 16 + ((cqi_req
.ch_num
% 16) ? 1 : 0);
387 tmp
= tmp
* 16 * sizeof(struct fm_cqi
);
388 buf
= fm_zalloc(tmp
);
395 ret
= fm_cqi_get(fm
, cqi_req
.ch_num
, buf
, tmp
);
399 WCN_DBG(FM_ALT
| MAIN
, "get cqi failed\n");
404 ((void *)cqi_req
.cqi_buf
, buf
,
405 cqi_req
.ch_num
* sizeof(struct fm_cqi
))) {
415 case FM_IOCTL_GET_HW_INFO
:{
416 struct fm_hw_info info
;
418 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_GET_HW_INFO\n");
420 ret
= fm_get_hw_info(fm
, &info
);
423 WCN_DBG(FM_ALT
| MAIN
, "get hw info failed\n");
427 if (copy_to_user((void *)arg
, &info
, sizeof(struct fm_hw_info
))) {
435 case FM_IOCTL_GET_I2S_INFO
:{
436 struct fm_i2s_info i2sinfo
;
438 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_GET_I2S_INFO\n");
440 ret
= fm_get_i2s_info(fm
, &i2sinfo
);
443 WCN_DBG(FM_ALT
| MAIN
, "get i2s info failed\n");
447 if (copy_to_user((void *)arg
, &i2sinfo
, sizeof(struct fm_i2s_info
))) {
455 case FM_IOCTL_SETVOL
:{
458 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SETVOL start\n");
459 if (copy_from_user(&vol
, (void *)arg
, sizeof(fm_u32
))) {
460 WCN_DBG(FM_ALT
| MAIN
, "copy_from_user failed\n");
465 ret
= fm_setvol(fm
, vol
);
466 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_SETVOL end:%d\n", vol
);
469 case FM_IOCTL_GETVOL
:{
471 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_GETVOL start\n");
472 ret
= fm_getvol(fm
, &vol
);
476 if (copy_to_user((void *)arg
, &vol
, sizeof(fm_u32
))) {
481 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_GETVOL end=%d\n", vol
);
488 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_MUTE start\n");
489 if (copy_from_user(&bmute
, (void *)arg
, sizeof(fm_u32
))) {
494 ret
= fm_mute(fm
, bmute
);
495 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_MUTE end-%d\n", bmute
);
499 case FM_IOCTL_GETRSSI
:{
502 ret
= fm_getrssi(fm
, &rssi
);
506 if (copy_to_user((void *)arg
, &rssi
, sizeof(fm_s32
))) {
511 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_GETRSSI:%d\n", rssi
);
515 case FM_IOCTL_RW_REG
:{
516 struct fm_ctl_parm parm_ctl
;
517 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_RW_REG\n");
519 if (copy_from_user(&parm_ctl
, (void *)arg
, sizeof(struct fm_ctl_parm
))) {
524 if (parm_ctl
.rw_flag
== 0) {
525 ret
= fm_reg_write(fm
, parm_ctl
.addr
, parm_ctl
.val
);
527 ret
= fm_reg_read(fm
, parm_ctl
.addr
, &parm_ctl
.val
);
532 if ((parm_ctl
.rw_flag
== 0x01) && (!ret
)) {
534 ((void *)arg
, &parm_ctl
, sizeof(struct fm_ctl_parm
))) {
542 case FM_IOCTL_TOP_RDWR
:
544 struct fm_top_rw_parm parm_ctl
;
545 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_TOP_RDWR\n");
547 if (copy_from_user(&parm_ctl
, (void *)arg
, sizeof(struct fm_top_rw_parm
))) {
552 if (parm_ctl
.rw_flag
== 0) {
553 ret
= fm_top_write(fm
, parm_ctl
.addr
, parm_ctl
.val
);
555 ret
= fm_top_read(fm
, parm_ctl
.addr
, &parm_ctl
.val
);
560 if ((parm_ctl
.rw_flag
== 0x01) && (!ret
)) {
562 ((void *)arg
, &parm_ctl
, sizeof(struct fm_top_rw_parm
))) {
570 case FM_IOCTL_HOST_RDWR
:
572 struct fm_host_rw_parm parm_ctl
;
573 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_TOP_RDWR\n");
575 if (copy_from_user(&parm_ctl
, (void *)arg
, sizeof(struct fm_host_rw_parm
))) {
580 if (parm_ctl
.rw_flag
== 0) {
581 ret
= fm_host_write(fm
, parm_ctl
.addr
, parm_ctl
.val
);
583 ret
= fm_host_read(fm
, parm_ctl
.addr
, &parm_ctl
.val
);
588 if ((parm_ctl
.rw_flag
== 0x01) && (!ret
)) {
590 ((void *)arg
, &parm_ctl
, sizeof(struct fm_host_rw_parm
))) {
599 case FM_IOCTL_GETCHIPID
:{
602 ret
= fm_chipid_get(fm
, &chipid
);
603 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETCHIPID:%04x\n", chipid
);
607 if (copy_to_user((void *)arg
, &chipid
, sizeof(fm_u16
))) {
614 case FM_IOCTL_GETMONOSTERO
:{
617 ret
= fm_monostereo_get(fm
, &usStereoMono
);
618 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETMONOSTERO:%04x\n", usStereoMono
);
622 if (copy_to_user((void *)arg
, &usStereoMono
, sizeof(fm_u16
))) {
629 case FM_IOCTL_SETMONOSTERO
:{
630 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_SETMONOSTERO, %d\n", (fm_s32
) arg
);
631 ret
= fm_monostereo_set(fm
, (fm_s32
) arg
);
635 case FM_IOCTL_GETCURPAMD
:{
638 ret
= fm_pamd_get(fm
, &PamdLevl
);
639 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETCURPAMD:%d\n", PamdLevl
);
643 if (copy_to_user((void *)arg
, &PamdLevl
, sizeof(fm_u16
)))
650 case FM_IOCTL_GETCAPARRAY
:{
653 ret
= fm_caparray_get(fm
, &ca
);
654 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETCAPARRAY:%d\n", ca
);
658 if (copy_to_user((void *)arg
, &ca
, sizeof(fm_s32
))) {
665 case FM_IOCTL_EM_TEST
:{
666 struct fm_em_parm parm_em
;
668 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_EM_TEST\n");
670 if (copy_from_user(&parm_em
, (void *)arg
, sizeof(struct fm_em_parm
))) {
675 fm_em_test(fm
, parm_em
.group_idx
, parm_em
.item_idx
, parm_em
.item_value
);
679 case FM_IOCTL_RDS_SUPPORT
:{
680 fm_s32 support
= FM_RDS_ENABLE
;
681 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_RDS_SUPPORT\n");
683 if (copy_to_user((void *)arg
, &support
, sizeof(fm_s32
))) {
690 case FM_IOCTL_IS_FM_POWERED_UP
:{
692 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_IS_FM_POWERED_UP");
694 if (fm
->chipon
&& fm_pwr_state_get(fm
)) {
700 if (copy_to_user((void *)arg
, &powerup
, sizeof(fm_u32
))) {
707 case FM_IOCTL_FM_SET_STATUS
: {
709 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_FM_SET_STATUS");
711 if (copy_from_user(&fm_stat
, (void*)arg
, sizeof(fm_status_t
))) {
716 fm_set_stat(fm
, fm_stat
.which
, fm_stat
.stat
);
721 case FM_IOCTL_FM_GET_STATUS
: {
723 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_FM_GET_STATUS");
725 if (copy_from_user(&fm_stat
, (void*)arg
, sizeof(fm_status_t
))) {
730 fm_get_stat(fm
, fm_stat
.which
, &fm_stat
.stat
);
732 if (copy_to_user((void*)arg
, &fm_stat
, sizeof(fm_status_t
))) {
740 case FM_IOCTL_RDS_ONOFF
:{
741 fm_u16 rdson_off
= 0;
742 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_RDS_ONOFF start\n");
744 if (copy_from_user(&rdson_off
, (void *)arg
, sizeof(fm_u16
))) {
748 ret
= fm_rds_onoff(fm
, rdson_off
);
749 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_RDS_ONOFF end:%d\n", rdson_off
);
753 case FM_IOCTL_GETGOODBCNT
:{
756 ret
= fm_rds_good_bc_get(fm
, &uGBLCNT
);
757 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETGOODBCNT:%d\n", uGBLCNT
);
761 if (copy_to_user((void *)arg
, &uGBLCNT
, sizeof(fm_u16
))) {
768 case FM_IOCTL_GETBADBNT
:{
769 fm_u16 uBadBLCNT
= 0;
771 ret
= fm_rds_bad_bc_get(fm
, &uBadBLCNT
);
772 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETBADBNT:%d\n", uBadBLCNT
);
776 if (copy_to_user((void *)arg
, &uBadBLCNT
, sizeof(fm_u16
))) {
783 case FM_IOCTL_GETBLERRATIO
:{
784 fm_u16 uBlerRatio
= 0;
786 ret
= fm_rds_bler_ratio_get(fm
, &uBlerRatio
);
787 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_GETBLERRATIO:%d\n", uBlerRatio
);
791 if (copy_to_user((void *)arg
, &uBlerRatio
, sizeof(fm_u16
))) {
798 case FM_IOCTL_ANA_SWITCH
:{
800 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_ANA_SWITCH\n");
802 if (copy_from_user(&antenna
, (void *)arg
, sizeof(fm_s32
))) {
803 WCN_DBG(FM_ALT
| MAIN
, "copy from user error\n");
808 ret
= fm_ana_switch(fm
, antenna
);
812 case FM_IOCTL_RDS_GROUPCNT
:{
813 struct rds_group_cnt_req_t gc_req
;
814 WCN_DBG(FM_DBG
| MAIN
, "......FM_IOCTL_RDS_GROUPCNT......\n");
817 (&gc_req
, (void *)arg
, sizeof(struct rds_group_cnt_req_t
))) {
818 WCN_DBG(FM_ALT
| MAIN
, "copy_from_user error\n");
822 /* handle group counter request */
824 case RDS_GROUP_CNT_READ
:
825 ret
= fm_rds_group_cnt_get(fm
, &gc_req
.gc
);
827 case RDS_GROUP_CNT_WRITE
:
829 case RDS_GROUP_CNT_RESET
:
830 ret
= fm_rds_group_cnt_reset(fm
);
836 if (copy_to_user((void *)arg
, &gc_req
, sizeof(struct rds_group_cnt_req_t
))) {
837 WCN_DBG(FM_ALT
| MAIN
, "copy_to_user error\n");
845 case FM_IOCTL_RDS_GET_LOG
:{
846 struct rds_raw_t rds_log
;
848 WCN_DBG(FM_DBG
| MAIN
, "......FM_IOCTL_RDS_GET_LOG......\n");
849 /* fetch a record form RDS log buffer */
850 ret
= fm_rds_log_get(fm
, (struct rds_rx_t
*)&(rds_log
.data
), &len
);
851 rds_log
.dirty
= TRUE
;
852 rds_log
.len
= (len
< sizeof(rds_log
.data
)) ? len
: sizeof(rds_log
.data
);
856 if (copy_to_user((void *)arg
, &rds_log
, rds_log
.len
+ 2 * sizeof(fm_s32
))) {
857 WCN_DBG(FM_ALT
| MAIN
, "copy_to_user error\n");
865 case FM_IOCTL_RDS_BC_RST
:{
866 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_RDS_BC_RST\n");
867 ret
= fm_rds_block_cnt_reset(fm
);
871 case FM_IOCTL_I2S_SETTING
:{
872 struct fm_i2s_setting i2s_cfg
;
873 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_I2S_SETTING\n");
875 if (copy_from_user(&i2s_cfg
, (void *)arg
, sizeof(struct fm_i2s_setting
))) {
876 WCN_DBG(FM_ALT
| MAIN
, "i2s set, copy_from_user err\n");
881 ret
= fm_i2s_set(fm
, i2s_cfg
.onoff
, i2s_cfg
.mode
, i2s_cfg
.sample
);
884 WCN_DBG(FM_ALT
| MAIN
, "Set i2s err\n");
891 case FM_IOCTL_IS_DESE_CHAN
:{
893 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_IS_DESE_CHAN\n");
895 if (copy_from_user(&tmp
, (void *)arg
, sizeof(fm_s32
))) {
896 WCN_DBG(FM_ALT
| MAIN
, "is dese chan, copy_from_user err\n");
901 tmp
= fm_is_dese_chan(fm
, (fm_u16
) tmp
);
903 if (copy_to_user((void *)arg
, &tmp
, sizeof(fm_s32
))) {
904 WCN_DBG(FM_ALT
| MAIN
, "is dese chan, copy_to_user err\n");
911 case FM_IOCTL_DESENSE_CHECK
:
913 fm_desense_check_t tmp
;
914 WCN_DBG(FM_DBG
| MAIN
, "FM_IOCTL_IS_DESE_CHAN\n");
916 if (copy_from_user(&tmp
, (void *)arg
, sizeof(fm_desense_check_t
))) {
917 WCN_DBG(FM_ALT
| MAIN
, "desene check, copy_from_user err\n");
921 ret
= fm_desense_check(fm
, (fm_u16
) tmp
.freq
, tmp
.rssi
);
923 /*if (copy_to_user((void*)arg, &tmp, sizeof(fm_desense_check_t))) {
924 WCN_DBG(FM_ALT | MAIN, "desene check, copy_to_user err\n");
931 case FM_IOCTL_SCAN_GETRSSI
:
933 /*struct fm_rssi_req *req;
934 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SCAN_GETRSSI\n");
935 if(!(req = fm_vmalloc(sizeof(struct fm_rssi_req))))
937 WCN_DBG(FM_ALT | MAIN, "fm_vmalloc err\n");
941 if(copy_from_user(req, (void*)arg, sizeof(struct fm_rssi_req)))
943 WCN_DBG(FM_ALT | MAIN, "copy_from_user err\n");
948 ret = fm_get_rssi_after_scan(fm, req);
949 if(-ERR_FW_NORES == ret){
950 WCN_DBG(FM_ALT | MAIN, "fm_get_rssi_after_scan err\n");
952 if(copy_to_user((void*)arg, req, sizeof(struct fm_rssi_req)))
954 WCN_DBG(FM_ALT | MAIN, "copy_to_user err\n");
960 WCN_DBG(FM_ALT
| MAIN
, "FM_IOCTL_SCAN_GETRSSI:not support\n");
964 case FM_IOCTL_DUMP_REG
:
966 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_DUMP_REG......\n");
970 WCN_DBG(FM_ALT
| MAIN
, "fm_dump_reg err\n");
974 case FM_IOCTL_GPS_RTC_DRIFT
:{
975 struct fm_gps_rtc_info rtc_info
;
976 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_GPS_RTC_DRIFT......\n");
978 if (fm_false
== fm
->chipon
) {
979 WCN_DBG(FM_ERR
| MAIN
, "ERROR, FM chip is OFF\n");
983 if (copy_from_user(&rtc_info
, (void *)arg
, sizeof(struct fm_gps_rtc_info
))) {
984 WCN_DBG(FM_ERR
| MAIN
, "copy_from_user error\n");
989 ret
= fm_get_gps_rtc_info(&rtc_info
);
991 WCN_DBG(FM_ERR
| MAIN
, "fm_get_gps_rtc_info error\n");
996 case FM_IOCTL_OVER_BT_ENABLE
:
998 fm_s32 fm_via_bt
= -1;
999 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_OVER_BT_ENABLE......\n");
1001 if (copy_from_user(&fm_via_bt
, (void *)arg
, sizeof(int32_t))) {
1002 WCN_DBG(FM_ERR
| MAIN
, "copy_from_user error\n");
1007 ret
= fm_over_bt(fm
, fm_via_bt
);
1009 WCN_DBG(FM_ERR
| MAIN
, "fm_over_bt err\n");
1014 case FM_IOCTL_SET_SEARCH_THRESHOLD
:
1016 struct fm_search_threshold_t parm
;
1017 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_SET_SEARCH_THRESHOLD......\n");
1020 (&parm
, (void *)arg
, sizeof(struct fm_search_threshold_t
))) {
1021 WCN_DBG(FM_ALT
| MAIN
, "copy_from_user error\n");
1025 ret
= fm_set_search_th(fm
, parm
);
1027 WCN_DBG(FM_ERR
| MAIN
,
1028 "FM_IOCTL_SET_SEARCH_THRESHOLD not supported\n");
1032 case FM_IOCTL_GET_AUDIO_INFO
:
1034 fm_audio_info_t aud_data
;
1036 ret
= fm_get_aud_info(&aud_data
);
1038 WCN_DBG(FM_ERR
| MAIN
, "fm_get_aud_info err\n");
1040 if (copy_to_user((void *)arg
, &aud_data
, sizeof(fm_audio_info_t
))) {
1041 WCN_DBG(FM_ERR
| MAIN
, "copy_to_user error\n");
1046 WCN_DBG(FM_DBG
| MAIN
, "fm_get_aud_info ret=%d\n", ret
);
1049 /***************************FM Tx function************************************/
1050 case FM_IOCTL_TX_SUPPORT
:
1052 fm_s32 tx_support
= -1;
1053 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_TX_SUPPORT......\n");
1055 ret
= fm_tx_support(fm
, &tx_support
);
1057 WCN_DBG(FM_ERR
| MAIN
, "fm_tx_support err\n");
1059 if (copy_to_user((void *)arg
, &tx_support
, sizeof(fm_s32
))) {
1060 WCN_DBG(FM_ERR
| MAIN
, "copy_to_user error\n");
1066 case FM_IOCTL_POWERUP_TX
:
1068 struct fm_tune_parm parm
;
1069 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_POWERUP_TX:0\n");
1070 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_tune_parm
))) {
1075 ret
= fm_powerup_tx(fm
, &parm
);
1079 ret
= fm_tune_tx(fm
, &parm
);
1083 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_tune_parm
))) {
1087 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_POWERUP_TX:1\n");
1091 case FM_IOCTL_TUNE_TX
:
1093 struct fm_tune_parm parm
;
1094 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_TUNE_TX:0\n");
1096 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_tune_parm
))) {
1101 ret
= fm_tune_tx(fm
, &parm
);
1106 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_tune_parm
))) {
1111 WCN_DBG(FM_NTC
| MAIN
, "FM_IOCTL_TUNE_TX:1\n");
1114 case FM_IOCTL_RDSTX_SUPPORT
:
1116 fm_s32 rds_tx_support
= -1;
1117 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_RDSTX_SUPPORT......\n");
1119 ret
= fm_rdstx_support(fm
, &rds_tx_support
);
1121 WCN_DBG(FM_ERR
| MAIN
, "fm_rdstx_support err\n");
1123 if (copy_to_user((void *)arg
, &rds_tx_support
, sizeof(fm_s32
))) {
1124 WCN_DBG(FM_ERR
| MAIN
, "copy_to_user error\n");
1131 case FM_IOCTL_RDSTX_ENABLE
:
1134 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_RDSTX_ENABLE......\n");
1136 if (copy_from_user(&onoff
, (void *)arg
, sizeof(fm_s32
))) {
1137 WCN_DBG(FM_ALT
| MAIN
,
1138 "FM_IOCTL_RDSTX_ENABLE, copy_from_user err\n");
1143 ret
= fm_rdstx_enable(fm
, onoff
);
1145 WCN_DBG(FM_ERR
| MAIN
, "fm_rdstx_enable err\n");
1151 case FM_IOCTL_RDS_TX
:
1153 struct fm_rds_tx_parm parm
;
1154 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_RDS_TX......\n");
1156 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_rds_tx_parm
))) {
1157 WCN_DBG(FM_ALT
| MAIN
, "RDS Tx, copy_from_user err\n");
1162 ret
= fm_rds_tx(fm
, &parm
);
1164 WCN_DBG(FM_ALT
| MAIN
, "fm_rds_tx err\n");
1167 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_rds_tx_parm
))) {
1168 WCN_DBG(FM_ALT
| MAIN
, "RDS Tx, copy_to_user err\n");
1175 case FM_IOCTL_TX_SCAN
:
1177 struct fm_tx_scan_parm parm
;
1178 WCN_DBG(FM_NTC
| MAIN
, "......FM_IOCTL_TX_SCAN......\n");
1180 if (copy_from_user(&parm
, (void *)arg
, sizeof(struct fm_tx_scan_parm
))) {
1181 WCN_DBG(FM_ALT
| MAIN
, "copy_from_user error\n");
1185 ret
= fm_tx_scan(fm
, &parm
);
1187 WCN_DBG(FM_ERR
| MAIN
, "FM_IOCTL_TX_SCAN failed\n");
1189 if (copy_to_user((void *)arg
, &parm
, sizeof(struct fm_tx_scan_parm
))) {
1190 WCN_DBG(FM_ALT
| MAIN
, "copy_to_user error\n");
1202 if (ret
== -FM_EFW
) {
1203 if (fm_sys_state_get(fm
) == FM_SUBSYS_RST_OFF
) {
1204 fm
->wholechiprst
= fm_false
;
1205 /* subsystem reset */
1206 fm_subsys_reset(fm
);
1213 static loff_t
fm_ops_lseek(struct file
*filp
, loff_t off
, fm_s32 whence
)
1215 struct fm
*fm
= filp
->private_data
;
1217 if (whence
== SEEK_END
) {
1219 } else if (whence
== SEEK_SET
) {
1220 FM_EVENT_SEND(fm
->rds_event
, FM_RDS_DATA_READY
);
1226 static ssize_t
fm_ops_read(struct file
*filp
, char *buf
, size_t len
, loff_t
*off
)
1228 struct fm
*fm
= filp
->private_data
;
1229 fm_s32 copy_len
= 0;
1232 WCN_DBG(FM_ALT
| MAIN
, "fm_read invalid fm pointer\n");
1236 WCN_DBG(FM_DBG
| MAIN
, "rds buf len=%zu\n", len
);
1237 WCN_DBG(FM_DBG
| MAIN
, "sizeof(rds_t)=%zu\n", sizeof(rds_t
));
1239 if (!buf
|| len
< sizeof(rds_t
)) {
1240 WCN_DBG(FM_DBG
| MAIN
, "fm_read invliad buf\n");
1243 /* return if FM is resetting */
1244 if (fm_sys_state_get(fm
) != FM_SUBSYS_RST_OFF
) {
1245 WCN_DBG(FM_ALT
| MAIN
, "fm subsys underring reset\n");
1249 copy_len
= sizeof(rds_t
);
1251 return fm_rds_read(fm
, buf
, copy_len
);
1254 static fm_s32
fm_ops_open(struct inode
*inode
, struct file
*filp
)
1257 struct fm_platform
*plat
= container_of(inode
->i_cdev
, struct fm_platform
, cdev
);
1258 struct fm
*fm
= container_of(plat
, struct fm
, platform
);
1260 WCN_DBG(FM_NTC
| MAIN
, "fm_ops_open:0\n");
1261 if (fm_sys_state_get(fm
) != FM_SUBSYS_RST_OFF
) {
1262 WCN_DBG(FM_ALT
| MAIN
, "FM subsys is resetting, retry later\n");
1268 filp
->private_data
= fm
;
1270 WCN_DBG(FM_NTC
| MAIN
, "fm_ops_open:1\n");
1274 static fm_s32
fm_ops_release(struct inode
*inode
, struct file
*filp
)
1276 /* fm_s32 ret = 0; */
1277 /* struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev); */
1278 /* struct fm *fm = container_of(plat, struct fm, platform); */
1280 /* WCN_DBG(FM_NTC | MAIN, "fm_ops_release:0\n"); */
1282 filp
->private_data
= NULL
;
1284 WCN_DBG(FM_NTC
| MAIN
, "fm_ops_release\n");
1288 static fm_s32
fm_ops_flush(struct file
*filp
, fl_owner_t Id
)
1291 struct fm
*fm
= filp
->private_data
;
1293 WCN_DBG(FM_NTC
| MAIN
, "fm_ops_flush:0\n");
1295 filp
->private_data
= fm
;
1297 WCN_DBG(FM_NTC
| MAIN
, "fm_ops_flush:1\n");
1301 static ssize_t
fm_proc_read(struct file
*file
, char __user
*buf
, size_t count
, loff_t
*ppos
)
1303 struct fm
*fm
= g_fm
;
1306 unsigned long pos
= *ppos
;
1308 WCN_DBG(FM_NTC
| MAIN
, "Enter fm_proc_read.\n");
1309 /* WCN_DBG(FM_NTC | MAIN, "count = %d\n", count); */
1310 /* WCN_DBG(FM_NTC | MAIN, "ppos = %d\n", pos); */
1316 WCN_DBG(FM_ALT
| MAIN
, "para err\n");
1320 if (fm
->chipon
&& (fm_pwr_state_get(fm
) == FM_PWR_RX_ON
)) {
1321 length
= sprintf(tmpbuf
, "1\n");
1322 WCN_DBG(FM_NTC
| MAIN
, " FM_PWR_RX_ON\n");
1323 } else if (fm
->chipon
&& (fm_pwr_state_get(fm
) == FM_PWR_TX_ON
)) {
1324 length
= sprintf(tmpbuf
, "2\n");
1325 WCN_DBG(FM_NTC
| MAIN
, " FM_PWR_TX_ON\n");
1327 length
= sprintf(tmpbuf
, "0\n");
1328 WCN_DBG(FM_NTC
| MAIN
, " FM POWER OFF\n");
1331 if (copy_to_user(buf
, tmpbuf
, length
)) {
1332 WCN_DBG(FM_NTC
| MAIN
, " Read FM status fail!\n");
1338 WCN_DBG(FM_NTC
| MAIN
, "Leave fm_proc_read. length = %zu\n", length
);
1344 static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data)
1347 struct fm *fm = g_fm;
1349 WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");
1355 WCN_DBG(FM_ALT | MAIN, "para err\n");
1359 if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON))
1361 cnt = sprintf(page, "1\n");
1362 WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
1364 else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON))
1366 WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
1367 cnt = sprintf(page, "2\n");
1371 cnt = sprintf(page, "0\n");
1375 WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. cnt = %d\n", cnt);
1379 static ssize_t
fm_proc_write(struct file
*file
, const char *buffer
, size_t count
, loff_t
*ppos
)
1381 fm_s8 tmp_buf
[50] = { 0 };
1384 copysize
= (count
< (sizeof(tmp_buf
) - 1)) ? count
: (sizeof(tmp_buf
) - 1);
1386 WCN_DBG(FM_NTC
| MAIN
, "fm_proc_write:0\n");
1387 if (copy_from_user(tmp_buf
, buffer
, copysize
)) {
1388 WCN_DBG(FM_ERR
| MAIN
, "failed copy_from_user\n");
1392 if (strncmp(tmp_buf
, "subsys reset", strlen("subsys reset")) == 0) {
1393 fm_subsys_reset(g_fm
);
1397 if (!fm_cust_config_setup(tmp_buf
)) {
1398 WCN_DBG(FM_NTC
| MAIN
, "get config form %s ok\n", tmp_buf
);
1402 if (sscanf(tmp_buf
, "%x", &g_dbg_level
) != 1) {
1403 WCN_DBG(FM_ERR
| MAIN
, "failed g_dbg_level = 0x%x\n", g_dbg_level
);
1407 WCN_DBG(FM_NTC
| MAIN
, "fm_proc_write:1 g_dbg_level = 0x%x\n", g_dbg_level
);
1411 //#define FM_DEV_STATIC_ALLOC
1412 /* #define FM_DEV_MAJOR 193 */
1413 /*static int FM_major = FM_DEV_MAJOR; */ /* dynamic allocation */
1415 static fm_s32
fm_cdev_setup(struct fm
*fm
)
1418 struct fm_platform
*plat
= &fm
->platform
;
1420 #ifdef FM_DEV_STATIC_ALLOC
1421 /*static allocate chrdev */
1422 plat
->dev_t
= MKDEV(FM_major
, 0);
1423 ret
= register_chrdev_region(plat
->dev_t
, 1, FM_NAME
);
1426 WCN_DBG(FM_ERR
| MAIN
, "%s():fail to register chrdev\n", __func__
);
1431 #ifndef FM_DEV_STATIC_ALLOC
1432 ret
= alloc_chrdev_region(&plat
->dev_t
, 0, 1, FM_NAME
);
1435 WCN_DBG(FM_ALT
| MAIN
, "alloc dev_t failed\n");
1440 WCN_DBG(FM_NTC
| MAIN
, "alloc %s:%d:%d\n", FM_NAME
, MAJOR(plat
->dev_t
), MINOR(plat
->dev_t
));
1442 cdev_init(&plat
->cdev
, &fm_ops
);
1444 plat
->cdev
.owner
= THIS_MODULE
;
1445 plat
->cdev
.ops
= &fm_ops
;
1447 ret
= cdev_add(&plat
->cdev
, plat
->dev_t
, 1);
1450 WCN_DBG(FM_ALT
| MAIN
, "add dev_t failed\n");
1453 #ifndef FM_DEV_STATIC_ALLOC
1454 plat
->cls
= class_create(THIS_MODULE
, FM_NAME
);
1456 if (IS_ERR(plat
->cls
)) {
1457 ret
= PTR_ERR(plat
->cls
);
1458 WCN_DBG(FM_ALT
| MAIN
, "class_create err:%d\n", ret
);
1462 plat
->dev
= device_create(plat
->cls
, NULL
, plat
->dev_t
, NULL
, FM_NAME
);
1468 static fm_s32
fm_cdev_destroy(struct fm
*fm
)
1472 device_destroy(fm
->platform
.cls
, fm
->platform
.dev_t
);
1473 class_destroy(fm
->platform
.cls
);
1474 cdev_del(&fm
->platform
.cdev
);
1475 unregister_chrdev_region(fm
->platform
.dev_t
, 1);
1480 static fm_s32
fm_mod_init(fm_u32 arg
)
1483 struct fm
*fm
= NULL
;
1485 fm
= fm_dev_init(0);
1492 if ((ret
= fm_cdev_setup(fm
))) {
1495 /* fm proc file create "/proc/fm" */
1496 g_fm_proc
= proc_create(FM_PROC_FILE
, 0444, NULL
, &fm_proc_ops
);
1498 if (g_fm_proc
== NULL
) {
1499 WCN_DBG(FM_ALT
| MAIN
, "create_proc_entry failed\n");
1503 WCN_DBG(FM_NTC
| MAIN
, "create_proc_entry success\n");
1512 fm_cdev_destroy(fm
);
1516 remove_proc_entry(FM_PROC_FILE
, NULL
);
1520 static fm_s32
fm_mod_destroy(struct fm
*fm
)
1526 WCN_DBG(FM_NTC
| MAIN
, "%s\n", __func__
);
1527 remove_proc_entry(FM_PROC_FILE
, NULL
);
1528 fm_cdev_destroy(fm
);
1534 static fm_s32
mt_fm_probe(struct platform_device
*pdev
)
1538 WCN_DBG(FM_NTC
| MAIN
, "%s\n", __func__
);
1540 ret
= fm_mod_init(0);
1543 WCN_DBG(FM_ERR
| MAIN
, "fm mod init err\n");
1550 static fm_s32
mt_fm_remove(struct platform_device
*pdev
)
1552 WCN_DBG(FM_NTC
| MAIN
, "%s\n", __func__
);
1554 fm_mod_destroy(g_fm
);
1559 static struct platform_device mt_fm_device
= {
1564 /* platform driver entry */
1565 static struct platform_driver mt_fm_dev_drv
= {
1566 .probe
= mt_fm_probe
,
1567 .remove
= mt_fm_remove
,
1570 .owner
= THIS_MODULE
,
1574 static fm_s32
mt_fm_init(void)
1578 ret
= fm_env_setup();
1584 /* register fm device to platform bus */
1585 ret
= platform_device_register(&mt_fm_device
);
1590 /* register fm driver to platform bus */
1591 ret
= platform_driver_register(&mt_fm_dev_drv
);
1597 WCN_DBG(FM_NTC
| MAIN
, "6. fm platform driver registered\n");
1601 static void mt_fm_exit(void)
1603 platform_driver_unregister(&mt_fm_dev_drv
);
1607 #ifdef MTK_WCN_REMOVE_KERNEL_MODULE
1608 int mtk_wcn_fm_init(void)
1610 return mt_fm_init();
1613 void mtk_wcn_fm_exit(void)
1617 EXPORT_SYMBOL(mtk_wcn_fm_init
);
1618 EXPORT_SYMBOL(mtk_wcn_fm_exit
);
1620 module_init(mt_fm_init
);
1621 module_exit(mt_fm_exit
);
1623 EXPORT_SYMBOL(g_dbg_level
);
1624 MODULE_LICENSE("GPL");
1625 MODULE_DESCRIPTION("MediaTek FM Driver");
1626 MODULE_AUTHOR("Hongcheng <hongcheng.xia@MediaTek.com>");