MT6620: add the new driver JB2 V1.0
[firefly-linux-kernel-4.4.55.git] / drivers / mtk_wcn_combo / drv_fm / core / fm_module.c
1 /* fm_module.c
2  *
3  * (C) Copyright 2011
4  * MediaTek <www.MediaTek.com>
5  * Hongcheng <hongcheng.xia@MediaTek.com>
6  *
7  * FM Radio Driver -- main functions
8  *
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.
13  *
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.
18  *
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
22  */
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>
28 #include <linux/fs.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()
35
36 #include "fm_config.h"
37 #include "fm_main.h"
38 #include "fm_ioctl.h"
39
40 #define FM_PROC_FILE            "fm"
41
42 fm_u32 g_dbg_level = 0xfffffff5;  // Debug level of FM
43
44 //fm main data structure
45 static struct fm *g_fm = NULL;
46 //proc file entry
47 static struct proc_dir_entry *g_fm_proc = NULL;
48
49 //char device interface
50 static fm_s32  fm_cdev_setup(struct fm *fm);
51 static fm_s32  fm_cdev_destroy(struct fm *fm);
52
53 static long  fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg);
54 static loff_t fm_ops_lseek(struct file *filp, loff_t off, fm_s32 whence);
55 static ssize_t fm_ops_read(struct file *filp, char *buf, size_t len, loff_t *off);
56 static fm_s32  fm_ops_open(struct inode *inode, struct file *filp);
57 static fm_s32  fm_ops_release(struct inode *inode, struct file *filp);
58 static fm_s32  fm_ops_flush(struct file *filp,fl_owner_t Id);
59 static struct file_operations fm_ops = {
60     .owner = THIS_MODULE,
61     .unlocked_ioctl = fm_ops_ioctl,
62     .llseek = fm_ops_lseek,
63     .read = fm_ops_read,
64     .open = fm_ops_open,
65     .release = fm_ops_release,
66     .flush = fm_ops_flush,
67 };
68
69 static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data);
70 static fm_s32 fm_proc_write(struct file *file, const char *buffer, unsigned long count, void *data);
71
72
73 static struct fm_scan_t parm = {
74     .sr_size = 0,
75     .sr.ch_rssi_buf = NULL,
76 };
77
78 static long fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg)
79 {
80     fm_s32 ret = 0;
81     struct fm_platform *plat = container_of(filp->f_dentry->d_inode->i_cdev, struct fm_platform, cdev);
82     struct fm *fm = container_of(plat, struct fm, platform);
83
84     WCN_DBG(FM_NTC | MAIN, "%s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current->comm, current->pid, cmd, (fm_u32)arg);
85
86     if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
87         WCN_DBG(FM_ALT | MAIN, "FM subsys is resetting, retry later\n");
88         ret = -FM_ESRST;
89         return ret;
90     }
91     
92     switch (cmd) {
93     case FM_IOCTL_POWERUP: {
94         struct fm_tune_parm parm;
95         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:0\n");
96
97         if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
98             ret = -EFAULT;
99             goto out;
100         }
101
102         ret = fm_powerup(fm, &parm);
103         if (ret < 0) goto out;
104         ret = fm_tune(fm, &parm);
105         if (ret < 0) goto out;
106
107         if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
108             ret = -EFAULT;
109             goto out;
110         }
111                 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:1\n");
112
113         break;
114     }
115
116     case FM_IOCTL_POWERDOWN: {
117         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:0\n");
118         ret = fm_powerdown(fm);
119                 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:1\n");
120         break;
121     }
122
123     case FM_IOCTL_TUNE: {
124         struct fm_tune_parm parm;
125         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:0\n");
126
127         if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
128             ret = -EFAULT;
129             goto out;
130         }
131
132         ret = fm_tune(fm, &parm);
133         if (ret < 0) {
134             goto out;
135         }
136
137         if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
138             ret = -EFAULT;
139             goto out;
140         }
141
142                 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:1\n");
143         break;
144     }
145
146         case FM_IOCTL_SOFT_MUTE_TUNE:
147         {
148         struct fm_softmute_tune_t parm;
149         fm_cqi_log();//cqi log tool
150         WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_SOFT_MUTE_TUNE......\n");
151         if(copy_from_user(&parm, (void*)arg, sizeof(struct fm_softmute_tune_t)))
152         {
153             ret = -EFAULT;
154             goto out;
155         }
156
157         ret = fm_soft_mute_tune(fm, &parm);
158         if (ret < 0) {
159             goto out;
160         }
161
162         if(copy_to_user((void*)arg, &parm, sizeof(struct fm_softmute_tune_t)))
163         {
164             ret = -EFAULT;
165             goto out;
166         }
167         break;
168         }    
169         case FM_IOCTL_SEEK: {
170         struct fm_seek_parm parm;
171         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:0\n");
172
173         if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_seek_parm))) {
174             ret = -EFAULT;
175             goto out;
176         }
177
178         ret = fm_seek(fm, &parm);
179         if (ret < 0) {
180             goto out;
181         }
182
183         if (copy_to_user((void*)arg, &parm, sizeof(struct fm_seek_parm))) {
184             ret = -EFAULT;
185             goto out;
186         }
187                 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:1\n");
188         break;
189     }
190
191     case FM_IOCTL_SCAN: {
192         struct fm_scan_parm parm;
193         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN start\n");
194
195         if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_scan_parm))) {
196             WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
197             ret = -EFAULT;
198             goto out;
199         }
200
201         ret = fm_scan(fm, &parm);
202         if (ret < 0) goto out;
203
204         if (copy_to_user((void*)arg, &parm, sizeof(struct fm_scan_parm))) {
205             ret = -EFAULT;
206             goto out;
207         }
208
209                 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN end\n");
210         break;
211     }
212
213     case FM_IOCTL_TUNE_NEW: {
214         struct fm_tune_t tune_tmp;
215     
216         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_NEW\n");
217     
218         if (copy_from_user(&tune_tmp, (void*)arg, sizeof(struct fm_tune_t))) {
219             WCN_DBG(FM_ERR | MAIN, "tune new copy_from_user error\n");
220             ret = -EFAULT;
221             goto out;
222         }
223     
224         ret = fm_tune_new(fm, &tune_tmp);
225         if (ret < 0) {
226             goto out;
227         }
228             
229         if (copy_to_user((void*)arg, &tune_tmp, sizeof(struct fm_tune_t))) {
230             WCN_DBG(FM_ERR | MAIN, "tune new copy_to_user error\n");
231             ret = -EFAULT;
232             goto out;
233         }
234             
235         break;        
236     }
237
238     case FM_IOCTL_SEEK_NEW: {
239         struct fm_seek_t seek_tmp;
240
241         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK_NEW\n");
242         
243         if (copy_from_user(&seek_tmp, (void*)arg, sizeof(struct fm_seek_t))) {
244             WCN_DBG(FM_ERR | MAIN, "seek new copy_from_user error\n");
245             ret = -EFAULT;
246             goto out;
247         }
248         
249         ret = fm_seek_new(fm, &seek_tmp);
250         if (ret < 0) {
251             goto out;
252         }
253
254         if (copy_to_user((void*)arg, &seek_tmp, sizeof(struct fm_seek_t))) {
255             WCN_DBG(FM_ERR | MAIN, "seek new copy_to_user error\n");
256             ret = -EFAULT;
257             goto out;
258         }
259         
260         break;
261     }
262     
263     case FM_IOCTL_SCAN_NEW: {
264         struct fm_scan_t tmp;
265         
266         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN_NEW\n");
267
268         if (copy_from_user(&tmp, (void*)arg, sizeof(struct fm_scan_t))) {
269             WCN_DBG(FM_ERR | MAIN, "copy_from_user error\n");
270             ret =  -EFAULT;
271             goto out;
272         }
273
274         switch (tmp.cmd) {
275             case FM_SCAN_CMD_START: 
276                 if ((tmp.upper > 10800) || (tmp.lower < 7600) || (tmp.space < 5) || (tmp.space > 20)) {
277                     WCN_DBG(FM_ERR | MAIN, "scan para error\n");
278                     ret = -EFAULT;
279                     goto out;
280                 }
281                 parm.cmd = tmp.cmd;
282                 parm.lower = tmp.lower;
283                 parm.upper = tmp.upper;
284                 parm.space = tmp.space;
285
286                 ret = fm_scan_new(fm, &parm);
287                 if (ret < 0) {
288                     goto out;
289                 }
290                 break;
291                 
292             case FM_SCAN_CMD_GET_CH_RSSI:
293                 if (parm.sr_size && parm.sr.ch_rssi_buf) {
294                     if (copy_to_user(tmp.sr.ch_rssi_buf, parm.sr.ch_rssi_buf, parm.num * sizeof(struct fm_ch_rssi))) {
295                         WCN_DBG(FM_ERR | MAIN, "scan copy_to_user err\n");
296                         ret = -EFAULT;
297                         goto out;
298                     }
299                 }
300                 break;
301                 
302             default:
303                 break;
304         }
305
306         tmp.num = parm.num;
307         if (copy_to_user((void*)arg, &tmp, sizeof(struct fm_scan_t))) {
308             WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
309             ret = -EFAULT;
310             goto out;
311         }
312         break;
313     }   
314     
315     case FM_IOCTL_CQI_GET: {
316         struct fm_cqi_req cqi_req;
317         fm_s8 *buf = NULL;
318         fm_s32 tmp;
319
320         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_CQI_GET\n");
321
322         if (copy_from_user(&cqi_req, (void*)arg, sizeof(struct fm_cqi_req))) {
323             WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
324             ret = -EFAULT;
325             goto out;
326         }
327
328         if ((cqi_req.ch_num*sizeof(struct fm_cqi) > cqi_req.buf_size) || !cqi_req.cqi_buf) {
329             ret = -FM_EPARA;
330             goto out;
331         }
332
333         tmp = cqi_req.ch_num / 16 + ((cqi_req.ch_num % 16) ? 1 : 0);
334         tmp = tmp * 16 * sizeof(struct fm_cqi);
335         buf = fm_zalloc(tmp);
336
337         if (!buf) {
338             ret = -FM_ENOMEM;
339             goto out;
340         }
341
342         ret = fm_cqi_get(fm, cqi_req.ch_num, buf, tmp);
343
344         if (ret) {
345             fm_free(buf);
346             WCN_DBG(FM_ALT | MAIN, "get cqi failed\n");
347             goto out;
348         }
349
350         if (copy_to_user((void*)cqi_req.cqi_buf, buf, cqi_req.ch_num*sizeof(struct fm_cqi))) {
351             fm_free(buf);
352             ret = -EFAULT;
353             goto out;
354         }
355
356         fm_free(buf);
357         break;
358     }
359
360     case FM_IOCTL_GET_HW_INFO: {
361         struct fm_hw_info info;
362
363         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_HW_INFO\n");
364
365         ret = fm_get_hw_info(fm, &info);
366
367         if (ret) {
368             WCN_DBG(FM_ALT | MAIN, "get hw info failed\n");
369             goto out;
370         }
371
372         if (copy_to_user((void*)arg, &info, sizeof(struct fm_hw_info))) {
373             ret = -EFAULT;
374             goto out;
375         }
376
377         break;
378     }
379
380     case FM_IOCTL_GET_I2S_INFO: {
381         struct fm_i2s_info i2sinfo;
382
383         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_I2S_INFO\n");
384
385         ret = fm_get_i2s_info(fm, &i2sinfo);
386
387         if (ret) {
388             WCN_DBG(FM_ALT | MAIN, "get i2s info failed\n");
389             goto out;
390         }
391
392         if (copy_to_user((void*)arg, &i2sinfo, sizeof(struct fm_i2s_info))) {
393             ret = -EFAULT;
394             goto out;
395         }
396
397         break;
398     }
399
400     case FM_IOCTL_SETVOL: {
401         fm_u32 vol;
402
403         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL start\n");
404         if (copy_from_user(&vol, (void*)arg, sizeof(fm_u32))) {
405             WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
406             ret = -EFAULT;
407             goto out;
408         }
409
410         ret = fm_setvol(fm, vol);
411         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL end:%d\n", vol);
412         break;
413     }
414     case FM_IOCTL_GETVOL: {
415         fm_u32 vol;
416         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL start\n");
417         ret = fm_getvol(fm, &vol);
418         if (ret < 0) goto out;
419
420         if (copy_to_user((void*)arg, &vol, sizeof(fm_u32))) {
421             ret = -EFAULT;
422             goto out;
423         }
424
425         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL end=%d\n",vol);
426         break;
427     }
428
429     case FM_IOCTL_MUTE: {
430         fm_u32 bmute;
431
432         WCN_DBG(FM_NTC| MAIN, "FM_IOCTL_MUTE start\n");
433         if (copy_from_user(&bmute, (void*)arg, sizeof(fm_u32))) {
434             ret = -EFAULT;
435             goto out;
436         }
437
438         ret = fm_mute(fm, bmute);
439         WCN_DBG(FM_NTC| MAIN, "FM_IOCTL_MUTE end-%d\n", bmute);
440         break;
441     }
442
443     case FM_IOCTL_GETRSSI: {
444         fm_s32 rssi = 0;
445
446         ret = fm_getrssi(fm, &rssi);
447         if (ret < 0) goto out;
448
449         if (copy_to_user((void*)arg, &rssi, sizeof(fm_s32))) {
450             ret = -EFAULT;
451             goto out;
452         }
453
454         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETRSSI:%d\n",rssi);
455         break;
456     }
457
458     case FM_IOCTL_RW_REG: {
459         struct fm_ctl_parm parm_ctl;
460         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RW_REG\n");
461
462         if (copy_from_user(&parm_ctl, (void*)arg, sizeof(struct fm_ctl_parm))) {
463             ret = -EFAULT;
464             goto out;
465         }
466
467         if (parm_ctl.rw_flag == 0) {
468             ret = fm_reg_write(fm, parm_ctl.addr, parm_ctl.val);
469         } else {
470             ret = fm_reg_read(fm, parm_ctl.addr, &parm_ctl.val);
471         }
472         if (ret < 0) goto out;
473
474         if ((parm_ctl.rw_flag == 0x01) && (!ret)) {
475             if (copy_to_user((void*)arg, &parm_ctl, sizeof(struct fm_ctl_parm))) {
476                 ret = -EFAULT;
477                 goto out;
478             }
479         }
480
481         break;
482     }
483
484     case FM_IOCTL_GETCHIPID: {
485         fm_u16 chipid;
486
487         ret = fm_chipid_get(fm, &chipid);
488         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCHIPID:%04x\n", chipid);
489         if (ret < 0) goto out;
490
491         if (copy_to_user((void*)arg, &chipid, sizeof(fm_u16))) {
492             ret = -EFAULT;
493             goto out;
494         }
495         break;
496     }
497
498     case FM_IOCTL_GETMONOSTERO: {
499         fm_u16 usStereoMono;
500
501         ret = fm_monostereo_get(fm, &usStereoMono);
502         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETMONOSTERO:%04x\n", usStereoMono);
503         if (ret < 0) goto out;
504
505         if (copy_to_user((void*)arg, &usStereoMono, sizeof(fm_u16))) {
506             ret = -EFAULT;
507             goto out;
508         }
509         break;
510     }
511
512     case FM_IOCTL_SETMONOSTERO: {
513         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SETMONOSTERO, %d\n", (fm_s32)arg);
514         ret = fm_monostereo_set(fm, (fm_s32)arg);
515         break;
516     }
517
518     case FM_IOCTL_GETCURPAMD: {
519         fm_u16 PamdLevl;
520
521         ret = fm_pamd_get(fm, &PamdLevl);
522         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCURPAMD:%d\n", PamdLevl);
523         if (ret < 0) goto out;
524
525         if (copy_to_user((void*)arg, &PamdLevl, sizeof(fm_u16)))
526             ret = -EFAULT;
527             goto out;
528
529         break;
530     }
531
532     case FM_IOCTL_GETCAPARRAY: {
533         fm_s32 ca;
534
535         ret = fm_caparray_get(fm, &ca);
536         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCAPARRAY:%d\n", ca);
537         if (ret < 0) goto out;
538
539         if (copy_to_user((void*)arg, &ca, sizeof(fm_s32))) {
540             ret = -EFAULT;
541             goto out;
542         }
543         break;
544     }
545
546     case FM_IOCTL_EM_TEST: {
547         struct fm_em_parm parm_em;
548
549         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_EM_TEST\n");
550
551         if (copy_from_user(&parm_em, (void*)arg, sizeof(struct fm_em_parm))) {
552             ret = -EFAULT;
553             goto out;
554         }
555         ret = fm_em_test(fm, parm_em.group_idx, parm_em.item_idx, parm_em.item_value);
556         break;
557     }
558
559     case FM_IOCTL_RDS_SUPPORT: {
560         fm_s32 support = FM_RDS_ENABLE;
561         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_SUPPORT\n");
562
563         if (copy_to_user((void*)arg, &support, sizeof(fm_s32))) {
564             ret = -EFAULT;
565             goto out;
566         }
567         break;
568     }
569
570     case FM_IOCTL_IS_FM_POWERED_UP: {
571         fm_u32 powerup;
572         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_FM_POWERED_UP");
573
574         if (fm->chipon && fm_pwr_state_get(fm)) {
575             powerup = 1;
576         } else {
577             powerup = 0;
578         }
579
580         if (copy_to_user((void*)arg, &powerup, sizeof(fm_u32))) {
581             ret = -EFAULT;
582             goto out;
583         }
584         break;
585     }
586
587     case FM_IOCTL_RDS_ONOFF: {
588         fm_u16 rdson_off = 0;
589         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF start\n");
590
591         if (copy_from_user(&rdson_off, (void*)arg, sizeof(fm_u16))) {
592             ret = -EFAULT;
593             goto out;
594         }
595         ret = fm_rds_onoff(fm, rdson_off);
596         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF end:%d\n",rdson_off);
597         break;
598     }
599
600     case FM_IOCTL_GETGOODBCNT: {
601         fm_u16 uGBLCNT = 0;
602
603         ret = fm_rds_good_bc_get(fm, &uGBLCNT);
604         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETGOODBCNT:%d\n", uGBLCNT);
605         if (ret < 0) goto out;
606
607         if (copy_to_user((void*)arg, &uGBLCNT, sizeof(fm_u16))) {
608             ret = -EFAULT;
609             goto out;
610         }
611         break;
612     }
613
614     case FM_IOCTL_GETBADBNT: {
615         fm_u16 uBadBLCNT = 0;
616
617         ret = fm_rds_bad_bc_get(fm, &uBadBLCNT);
618         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBADBNT:%d\n", uBadBLCNT);
619         if (ret < 0) goto out;
620
621         if (copy_to_user((void*)arg, &uBadBLCNT, sizeof(fm_u16))) {
622             ret = -EFAULT;
623             goto out;
624         }
625         break;
626     }
627
628     case FM_IOCTL_GETBLERRATIO: {
629         fm_u16 uBlerRatio = 0;
630
631         ret = fm_rds_bler_ratio_get(fm, &uBlerRatio);
632         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBLERRATIO:%d\n", uBlerRatio);
633         if (ret < 0) goto out;
634
635         if (copy_to_user((void*)arg, &uBlerRatio, sizeof(fm_u16))) {
636             ret = -EFAULT;
637             goto out;
638         }
639         break;
640     }
641
642     case FM_IOCTL_ANA_SWITCH: {
643         fm_s32 antenna = -1;
644         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_ANA_SWITCH\n");
645
646         if (copy_from_user(&antenna, (void*)arg, sizeof(fm_s32))) {
647             WCN_DBG(FM_ALT | MAIN, "copy from user error\n");
648             ret = -EFAULT;
649             goto out;
650         }
651
652         ret = fm_ana_switch(fm, antenna);
653         break;
654     }
655
656     case FM_IOCTL_RDS_GROUPCNT: {
657         struct rds_group_cnt_req_t gc_req;
658         WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GROUPCNT......\n");
659
660         if (copy_from_user(&gc_req, (void*)arg, sizeof(struct rds_group_cnt_req_t))) {
661             WCN_DBG(FM_ALT | MAIN, "copy_from_user error\n");
662             ret = -EFAULT;
663             goto out;
664         }
665
666         //handle group counter request
667         switch (gc_req.op) {
668         case RDS_GROUP_CNT_READ:
669             ret = fm_rds_group_cnt_get(fm, &gc_req.gc);
670             break;
671         case RDS_GROUP_CNT_WRITE:
672             break;
673         case RDS_GROUP_CNT_RESET:
674             ret = fm_rds_group_cnt_reset(fm);
675             break;
676         default:
677             break;
678         }
679
680         if (copy_to_user((void*)arg, &gc_req, sizeof(struct rds_group_cnt_req_t))) {
681             WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
682             ret = -EFAULT;
683             goto out;
684         }
685
686         break;
687     }
688
689     case FM_IOCTL_RDS_GET_LOG: {
690         struct rds_raw_t rds_log;
691         fm_s32 len;
692         WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GET_LOG......\n");
693         //fetch a record form RDS log buffer
694         ret = fm_rds_log_get(fm, (struct rds_rx_t*) & (rds_log.data), &len);
695         rds_log.dirty = TRUE;
696         rds_log.len = (len < sizeof(rds_log.data)) ? len : sizeof(rds_log.data);
697         if (ret < 0) goto out;
698
699         if (copy_to_user((void*)arg, &rds_log, rds_log.len + 2*sizeof(fm_s32))) {
700             WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
701             ret = -EFAULT;
702             goto out;
703         }
704
705         break;
706     }
707
708     case FM_IOCTL_RDS_BC_RST: {
709         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RDS_BC_RST\n");
710         ret = fm_rds_block_cnt_reset(fm);
711         break;
712     }
713
714     case FM_IOCTL_I2S_SETTING: {
715         struct fm_i2s_setting i2s_cfg;
716         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_I2S_SETTING\n");
717
718         if (copy_from_user(&i2s_cfg, (void*)arg, sizeof(struct fm_i2s_setting))) {
719             WCN_DBG(FM_ALT | MAIN, "i2s set, copy_from_user err\n");
720             ret = -EFAULT;
721             goto out;
722         }
723
724         ret = fm_i2s_set(fm, i2s_cfg.onoff, i2s_cfg.mode, i2s_cfg.sample);
725
726         if (ret) {
727             WCN_DBG(FM_ALT | MAIN, "Set i2s err\n");
728             goto out;
729         }
730
731         break;
732     }
733
734     case FM_IOCTL_IS_DESE_CHAN: {
735         fm_s32 tmp;
736         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");
737
738         if (copy_from_user(&tmp, (void*)arg, sizeof(fm_s32))) {
739             WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_from_user err\n");
740             ret = -EFAULT;
741             goto out;
742         }
743
744         tmp = fm_is_dese_chan(fm, (fm_u16)tmp);
745
746         if (copy_to_user((void*)arg, &tmp, sizeof(fm_s32))) {
747             WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_to_user err\n");
748             ret = -EFAULT;
749             goto out;
750         }
751
752         break;
753     }
754     case FM_IOCTL_DESENSE_CHECK: 
755     {
756         fm_desense_check_t tmp;
757         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");
758
759         if (copy_from_user(&tmp, (void*)arg, sizeof(fm_desense_check_t))) 
760         {
761             WCN_DBG(FM_ALT | MAIN, "desene check, copy_from_user err\n");
762             ret = -EFAULT;
763             goto out;
764         }
765         ret = fm_desense_check(fm, (fm_u16)tmp.freq,tmp.rssi);
766
767         /*if (copy_to_user((void*)arg, &tmp, sizeof(fm_desense_check_t))) {
768             WCN_DBG(FM_ALT | MAIN, "desene check, copy_to_user err\n");
769             ret = -EFAULT;
770             goto out;
771         }*/
772
773         break;
774     }
775         case FM_IOCTL_SCAN_GETRSSI:
776         {
777                 /*struct fm_rssi_req *req;
778         WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SCAN_GETRSSI\n");
779                 if(!(req = fm_vmalloc(sizeof(struct fm_rssi_req))))
780                 {
781             WCN_DBG(FM_ALT | MAIN, "fm_vmalloc err\n");
782             ret = -EFAULT;
783             goto out;
784                 }
785                 if(copy_from_user(req, (void*)arg, sizeof(struct fm_rssi_req)))
786                 {
787             WCN_DBG(FM_ALT | MAIN, "copy_from_user err\n");
788             ret = -EFAULT;
789                         fm_vfree(req);
790             goto out;
791                 }
792                 ret = fm_get_rssi_after_scan(fm, req);
793                 if(-ERR_FW_NORES == ret){
794             WCN_DBG(FM_ALT | MAIN, "fm_get_rssi_after_scan err\n");
795                 }
796                 if(copy_to_user((void*)arg, req, sizeof(struct fm_rssi_req)))
797                 {
798             WCN_DBG(FM_ALT | MAIN, "copy_to_user err\n");
799             ret = -EFAULT;
800                         fm_vfree(req);
801             goto out;
802                 }
803                 */
804         WCN_DBG(FM_ALT | MAIN, "FM_IOCTL_SCAN_GETRSSI:not support\n");
805                 break;
806         }
807
808         case FM_IOCTL_DUMP_REG:
809         {
810                 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_DUMP_REG......\n");
811
812                 ret = fm_dump_reg();
813                 if(ret)
814                 {
815                         WCN_DBG(FM_ALT | MAIN, "fm_dump_reg err\n");
816                 }
817                 break;
818         }
819         case FM_IOCTL_GPS_RTC_DRIFT:{
820                 struct fm_gps_rtc_info rtc_info;
821                 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_GPS_RTC_DRIFT......\n");
822         
823                 if (fm_false == fm->chipon){
824                         WCN_DBG(FM_ERR | MAIN,"ERROR, FM chip is OFF\n");
825             ret = -EFAULT;
826             goto out;
827                 }
828                 if(copy_from_user(&rtc_info, (void*)arg, sizeof(struct fm_gps_rtc_info))){
829                         WCN_DBG(FM_ERR | MAIN,"copy_from_user error\n");
830             ret = -EFAULT;
831             goto out;
832                 }
833                 
834                 ret = fm_get_gps_rtc_info(&rtc_info);
835                 if(ret){
836                         WCN_DBG(FM_ERR | MAIN,"fm_get_gps_rtc_info error\n");
837             goto out;
838                 }
839                 break;
840         }
841         case FM_IOCTL_OVER_BT_ENABLE:
842         {
843                 fm_s32 fm_via_bt = -1;
844                 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_OVER_BT_ENABLE......\n");
845                 
846                 if(copy_from_user(&fm_via_bt, (void*)arg, sizeof(int32_t))){
847                         WCN_DBG(FM_ERR | MAIN,"copy_from_user error\n");
848                         ret = -EFAULT;
849                         goto out;
850                 }
851         
852                 ret = fm_over_bt(fm, fm_via_bt);
853                 if(ret)
854                 {
855                         WCN_DBG(FM_ERR | MAIN, "fm_over_bt err\n");
856                 }
857                 break;
858         }
859         
860     /***************************FM Tx function************************************/
861         case FM_IOCTL_TX_SUPPORT:
862         {
863                 fm_s32 tx_support = -1;
864                 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_TX_SUPPORT......\n");
865
866                 ret = fm_tx_support(fm,&tx_support);
867                 if(ret)
868                 {
869                         WCN_DBG(FM_ERR | MAIN, "fm_tx_support err\n");
870                 }
871                 if (copy_to_user((void*)arg, &tx_support, sizeof(fm_s32)))
872                 {
873                         WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
874                         ret = -EFAULT;
875                         goto out;
876                 }
877                 break;
878         }
879         case FM_IOCTL_POWERUP_TX:
880         {
881                 struct fm_tune_parm parm;
882         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:0\n");
883         if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
884             ret = -EFAULT;
885             goto out;
886         }
887
888                 ret = fm_powerup_tx(fm, &parm);
889         if (ret < 0) {
890             goto out;
891         }
892         ret = fm_tune_tx(fm, &parm);
893         if (ret < 0) goto out;
894
895         if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
896             ret = -EFAULT;
897             goto out;
898         }
899         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:1\n");
900                 break;
901         }
902         
903         case FM_IOCTL_TUNE_TX:
904         {
905                 struct fm_tune_parm parm;
906         WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:0\n");
907
908         if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
909             ret = -EFAULT;
910             goto out;
911         }
912
913         ret = fm_tune_tx(fm, &parm);
914         if (ret < 0) {
915             goto out;
916         }
917
918         if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
919             ret = -EFAULT;
920             goto out;
921         }
922
923                 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:1\n");
924                 break;
925         }
926         case FM_IOCTL_RDSTX_SUPPORT:
927         {
928                 fm_s32 rds_tx_support = -1;
929                 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_RDSTX_SUPPORT......\n");
930
931                 ret = fm_rdstx_support(fm,&rds_tx_support);
932                 if(ret)
933                 {
934                         WCN_DBG(FM_ERR | MAIN, "fm_rdstx_support err\n");
935                 }
936                 if (copy_to_user((void*)arg, &rds_tx_support, sizeof(fm_s32)))
937                 {
938                         WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
939                         ret = -EFAULT;
940                         goto out;
941                 }
942                 break;
943         }       
944         
945         case FM_IOCTL_RDSTX_ENABLE:
946         {
947                 fm_s32 rds_tx_enable = -1;
948                 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_RDSTX_ENABLE......\n");
949
950                 ret = fm_rdstx_enable(fm,&rds_tx_enable);
951                 if(ret)
952                 {
953                         WCN_DBG(FM_ERR | MAIN, "fm_rdstx_enable err\n");
954                 }
955                 if (copy_to_user((void*)arg, &rds_tx_enable, sizeof(fm_s32)))
956                 {
957                         WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
958                         ret = -EFAULT;
959                         goto out;
960                 }
961                 break;
962         }       
963         
964         case FM_IOCTL_RDS_TX:
965         {
966                 struct fm_rds_tx_parm parm;
967                 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_RDS_TX......\n");
968
969                 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_rds_tx_parm)))
970                 {
971             WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_from_user err\n");
972                         ret = -EFAULT;
973                         goto out;
974                 }
975
976                 ret = fm_rds_tx(fm, &parm);
977                 if(ret)
978                 {
979             WCN_DBG(FM_ALT | MAIN, "fm_rds_tx err\n");
980                 }
981
982                 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_rds_tx_parm))){
983             WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_to_user err\n");
984                         ret = -EFAULT;
985                         goto out;
986                 }
987                 break;
988         }
989         
990         case FM_IOCTL_TX_SCAN:
991         {
992                 struct fm_tx_scan_parm parm;
993                 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_TX_SCAN......\n");
994
995                 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tx_scan_parm))){
996                         WCN_DBG(FM_ALT | MAIN,"copy_from_user error\n");
997                         ret = -EFAULT;
998                         goto out;
999                 }
1000                 ret = fm_tx_scan(fm, &parm);
1001                 if(ret < 0){
1002                         WCN_DBG(FM_ERR | MAIN,"FM_IOCTL_TX_SCAN failed\n");
1003                 }
1004                 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tx_scan_parm))){
1005                         WCN_DBG(FM_ALT | MAIN,"copy_to_user error\n");
1006                         ret = -EFAULT;
1007                         goto out;
1008                 }
1009                 break;
1010         }
1011
1012     default:
1013         ret = -EPERM;
1014     }
1015
1016 out:
1017     if (ret == -FM_EFW) {
1018         // subsystem reset
1019         fm_subsys_reset(fm);
1020     }
1021
1022     return ret;
1023 }
1024
1025 static loff_t fm_ops_lseek(struct file *filp, loff_t off, fm_s32 whence)
1026 {
1027     struct fm *fm = filp->private_data;
1028
1029     if (whence == SEEK_END) {
1030         fm_hwscan_stop(fm);
1031     } else if (whence == SEEK_SET) {
1032         FM_EVENT_SEND(fm->rds_event, FM_RDS_DATA_READY);
1033     }
1034
1035     return off;
1036 }
1037
1038 static ssize_t fm_ops_read(struct file *filp, char *buf, size_t len, loff_t *off)
1039 {
1040     struct fm *fm = filp->private_data;
1041     fm_s32 copy_len = 0;
1042
1043     if (!fm) {
1044         WCN_DBG(FM_ALT | MAIN, "fm_read invalid fm pointer\n");
1045         return 0;
1046     }
1047
1048     if (!buf || len < sizeof(rds_t)) {
1049         WCN_DBG(FM_DBG | MAIN, "fm_read invliad buf\n");
1050         return 0;
1051     }
1052
1053     //block when FM is resetting
1054     while (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
1055         msleep_interruptible(100);
1056     }
1057
1058     copy_len = sizeof(rds_t);
1059
1060     return fm_rds_read(fm, buf, copy_len);
1061 }
1062
1063 static fm_s32 fm_ops_open(struct inode *inode, struct file *filp)
1064 {
1065     fm_s32 ret = 0;
1066     struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev);
1067     struct fm *fm = container_of(plat, struct fm, platform);
1068
1069     WCN_DBG(FM_NTC | MAIN, "fm_ops_open:0\n");
1070     if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
1071         WCN_DBG(FM_ALT| MAIN, "FM subsys is resetting, retry later\n");
1072         ret = -FM_ESRST;
1073         return ret;
1074     }
1075     
1076     ret = fm_open(fm);
1077     filp->private_data = fm;
1078
1079     WCN_DBG(FM_NTC | MAIN, "fm_ops_open:1\n");
1080     return ret;
1081 }
1082
1083 static fm_s32 fm_ops_release(struct inode *inode, struct file *filp)
1084 {
1085 //    fm_s32 ret = 0;
1086 //    struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev);
1087 //    struct fm *fm = container_of(plat, struct fm, platform);
1088
1089 //    WCN_DBG(FM_NTC | MAIN, "fm_ops_release:0\n");
1090     //fm_close(fm);
1091     filp->private_data = NULL;
1092
1093     WCN_DBG(FM_NTC | MAIN, "fm_ops_release\n");
1094     return 0;
1095 }
1096
1097 static fm_s32 fm_ops_flush(struct file *filp,fl_owner_t Id)
1098 {
1099     fm_s32 ret = 0;
1100     struct fm *fm = filp->private_data;
1101
1102     WCN_DBG(FM_NTC | MAIN, "fm_ops_flush:0\n");
1103     fm_close(fm);
1104     filp->private_data = fm;
1105
1106     WCN_DBG(FM_NTC | MAIN, "fm_ops_flush:1\n");
1107     return ret;
1108 }
1109
1110 static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data)
1111 {
1112     fm_s32 cnt = 0;
1113     struct fm *fm  = g_fm;
1114
1115     WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");
1116
1117     if (off != 0)
1118         return 0;
1119
1120     if (!fm) {
1121         WCN_DBG(FM_ALT | MAIN, "para err\n");
1122         return 0;
1123     }
1124
1125     if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON)) 
1126         {
1127         cnt = sprintf(page, "1\n");
1128                 WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
1129     } 
1130         else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON)) 
1131         {
1132                 WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
1133         cnt = sprintf(page, "2\n");
1134     } 
1135         else 
1136         {
1137         cnt = sprintf(page, "0\n");
1138     }
1139
1140     *eof = 1;
1141     WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. cnt = %d\n", cnt);
1142     return cnt;
1143 }
1144
1145 static fm_s32 fm_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
1146 {
1147     fm_s8 tmp_buf[50] = {0};
1148     fm_u32 copysize;
1149
1150     copysize = (count < (sizeof(tmp_buf) - 1)) ? count : (sizeof(tmp_buf) - 1);
1151
1152     WCN_DBG(FM_NTC | MAIN, "fm_proc_write:0\n");
1153     if (copy_from_user(tmp_buf, buffer, copysize)) {
1154         WCN_DBG(FM_ERR | MAIN, "failed copy_from_user\n");
1155         return -EFAULT;
1156     }
1157
1158     if (strncmp(tmp_buf, "subsys reset", strlen("subsys reset")) == 0) {
1159         fm_subsys_reset(g_fm);
1160         return count;
1161     }
1162     
1163     if (!fm_cust_config_setup(tmp_buf)) {
1164         WCN_DBG(FM_NTC | MAIN, "get config form %s ok\n", tmp_buf);
1165         return count;
1166     }
1167
1168     if (sscanf(tmp_buf, "%x", &g_dbg_level) != 1) {
1169         WCN_DBG(FM_ERR | MAIN, "failed g_dbg_level = 0x%x\n", g_dbg_level);
1170         return -EFAULT;
1171     }
1172
1173     WCN_DBG(FM_NTC | MAIN, "fm_proc_write:1 g_dbg_level = 0x%x\n", g_dbg_level);
1174     return count;
1175 }
1176
1177 #define FM_DEV_STATIC_ALLOC
1178 #define FM_DEV_MAJOR    193
1179 static int FM_major = FM_DEV_MAJOR;      /* dynamic allocation */
1180
1181 static fm_s32 fm_cdev_setup(struct fm *fm)
1182 {
1183     fm_s32 ret = 0;
1184     struct fm_platform *plat = &fm->platform;
1185
1186 #ifdef FM_DEV_STATIC_ALLOC
1187     /*static allocate chrdev*/
1188     plat->dev_t = MKDEV(FM_major, 0);
1189     ret = register_chrdev_region(plat->dev_t, 1, FM_NAME);
1190
1191     if (ret) {
1192         WCN_DBG(FM_ERR | MAIN, "%s():fail to register chrdev\n", __func__);
1193         return ret;
1194     }
1195
1196 #endif
1197
1198 #ifndef FM_DEV_STATIC_ALLOC
1199     ret = alloc_chrdev_region(&plat->dev_t, 0, 1, FM_NAME);
1200
1201     if (ret) {
1202         WCN_DBG(FM_ALT | MAIN, "alloc dev_t failed\n");
1203         return ret;
1204     }
1205
1206 #endif
1207
1208     WCN_DBG(FM_NTC | MAIN, "alloc %s:%d:%d\n", FM_NAME, MAJOR(plat->dev_t), MINOR(plat->dev_t));
1209
1210     cdev_init(&plat->cdev, &fm_ops);
1211
1212     plat->cdev.owner = THIS_MODULE;
1213     plat->cdev.ops = &fm_ops;
1214
1215     ret = cdev_add(&plat->cdev, plat->dev_t, 1);
1216
1217     if (ret) {
1218         WCN_DBG(FM_ALT | MAIN, "add dev_t failed\n");
1219         return ret;
1220     }
1221
1222 #ifndef FM_DEV_STATIC_ALLOC
1223     plat->cls = class_create(THIS_MODULE, FM_NAME);
1224
1225     if (IS_ERR(plat->cls)) {
1226         ret = PTR_ERR(plat->cls);
1227         WCN_DBG(FM_ALT | MAIN, "class_create err:%d\n", ret);
1228         return ret;
1229     }
1230
1231     plat->dev = device_create(plat->cls, NULL, plat->dev_t, NULL, FM_NAME);
1232 #endif
1233
1234     return ret;
1235 }
1236
1237 static fm_s32  fm_cdev_destroy(struct fm *fm)
1238 {
1239     FMR_ASSERT(fm);
1240
1241     device_destroy(fm->platform.cls, fm->platform.dev_t);
1242     class_destroy(fm->platform.cls);
1243     cdev_del(&fm->platform.cdev);
1244     unregister_chrdev_region(fm->platform.dev_t, 1);
1245
1246     return 0;
1247 }
1248
1249 static fm_s32 fm_mod_init(fm_u32 arg)
1250 {
1251     fm_s32 ret = 0;
1252     struct fm *fm = NULL;
1253
1254     fm = fm_dev_init(0);
1255
1256     if (!fm) {
1257         ret = -ENOMEM;
1258         goto ERR_EXIT;
1259     }
1260
1261     if ((ret = fm_cdev_setup(fm))) {
1262         goto ERR_EXIT;
1263     }
1264
1265     //fm proc file create "/proc/fm"
1266     g_fm_proc = create_proc_entry(FM_PROC_FILE, 0444, NULL);
1267
1268     if (g_fm_proc == NULL) {
1269         WCN_DBG(FM_ALT | MAIN, "create_proc_entry failed\n");
1270         ret = -ENOMEM;
1271         goto ERR_EXIT;
1272     } else {
1273         g_fm_proc->read_proc = fm_proc_read;
1274         g_fm_proc->write_proc = fm_proc_write;
1275         WCN_DBG(FM_NTC | MAIN, "create_proc_entry success\n");
1276     }
1277
1278     g_fm = fm;
1279     return 0;
1280
1281 ERR_EXIT:
1282
1283     if (fm) {
1284         fm_cdev_destroy(fm);
1285         fm_dev_destroy(fm);
1286     }
1287
1288     remove_proc_entry(FM_PROC_FILE, NULL);
1289     return ret;
1290 }
1291
1292 static fm_s32 fm_mod_destroy(struct fm *fm)
1293 {
1294     fm_s32 ret = 0;
1295
1296     FMR_ASSERT(fm);
1297
1298     WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1299     remove_proc_entry(FM_PROC_FILE, NULL);
1300     fm_cdev_destroy(fm);
1301     fm_dev_destroy(fm);
1302
1303     return ret;
1304 }
1305
1306 static fm_s32 mt_fm_probe(struct platform_device *pdev)
1307 {
1308     fm_s32 ret = 0;
1309
1310     WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1311
1312     ret = fm_mod_init(0);
1313
1314     if (ret) {
1315         WCN_DBG(FM_ERR | MAIN, "fm mod init err\n");
1316         return -ENOMEM;
1317     }
1318
1319     return ret;
1320 }
1321
1322 static fm_s32 mt_fm_remove(struct platform_device *pdev)
1323 {
1324     WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1325
1326     fm_mod_destroy(g_fm);
1327     g_fm = NULL;
1328     return 0;
1329 }
1330
1331 static struct platform_device mt_fm_device = {
1332     .name       = FM_NAME,
1333     .id = -1,
1334 };
1335
1336 //platform driver entry
1337 static struct platform_driver mt_fm_dev_drv = {
1338     .probe   = mt_fm_probe,
1339     .remove  = mt_fm_remove,
1340     .driver = {
1341         .name   = FM_NAME,
1342         .owner  = THIS_MODULE,
1343     }
1344 };
1345
1346 static fm_s32 __init mt_fm_init(void)
1347 {
1348     fm_s32 ret = 0;
1349
1350     ret = fm_env_setup();
1351
1352     if (ret) {
1353         fm_env_destroy();
1354         return ret;
1355     }
1356
1357     //register fm device to platform bus
1358     ret = platform_device_register(&mt_fm_device);
1359
1360     if (ret) {
1361         return ret;
1362     }
1363
1364     //register fm driver to platform bus
1365     ret = platform_driver_register(&mt_fm_dev_drv);
1366
1367     if (ret) {
1368         return ret;
1369     }
1370
1371     WCN_DBG(FM_NTC | MAIN, "6. fm platform driver registered\n");
1372     return ret;
1373 }
1374
1375 static void __exit mt_fm_exit(void)
1376 {
1377     platform_driver_unregister(&mt_fm_dev_drv);
1378     fm_env_destroy();
1379 }
1380
1381
1382 EXPORT_SYMBOL(g_dbg_level);
1383 module_init(mt_fm_init);
1384 module_exit(mt_fm_exit);
1385
1386 MODULE_LICENSE("GPL");
1387 MODULE_DESCRIPTION("MediaTek FM Driver");
1388 MODULE_AUTHOR("Hongcheng <hongcheng.xia@MediaTek.com>");