4 * MediaTek <www.MediaTek.com>
5 * hongcheng <hongcheng.xia@MediaTek.com>
7 * mt6626 FM Radio Driver
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 "fm_typedef.h"
26 #include "fm_interface.h"
27 #include "fm_stdlib.h"
30 #include "mt6626_fm_reg.h"
33 static fm_bool bRDS_FirstIn
= fm_false
;
34 static fm_u32 gBLER_CHK_INTERVAL
= 5000;
35 static fm_u16 GOOD_BLK_CNT
= 0, BAD_BLK_CNT
;
36 static fm_u8 BAD_BLK_RATIO
;
38 static struct fm_callback
*fm_cb
;
39 static struct fm_basic_interface
*fm_bi
;
42 static fm_bool
mt6626_RDS_support(void);
43 static fm_s32
mt6626_RDS_enable(void);
44 static fm_s32
mt6626_RDS_disable(void);
45 static fm_u16
mt6626_RDS_Get_GoodBlock_Counter(void);
46 static fm_u16
mt6626_RDS_Get_BadBlock_Counter(void);
47 static fm_u8
mt6626_RDS_Get_BadBlock_Ratio(void);
48 static fm_u32
mt6626_RDS_Get_BlerCheck_Interval(void);
49 static void mt6626_RDS_GetData(fm_u16
*data
, fm_u16 datalen
);
50 static void mt6626_RDS_Init_Data(rds_t
*pstRDSData
);
54 static fm_bool
mt6626_RDS_support(void)
59 static fm_s32
mt6626_RDS_enable(void)
63 WCN_DBG(FM_DBG
| RDSC
, "rds enable\n");
64 fm_bi
->read(FM_RDS_CFG0
, &dataRead
);
65 fm_bi
->write(FM_RDS_CFG0
, 6); /* set buf_start_th */
66 fm_bi
->read(FM_MAIN_CTRL
, &dataRead
);
67 fm_bi
->write(FM_MAIN_CTRL
, dataRead
| (RDS_MASK
));
72 static fm_s32
mt6626_RDS_disable(void)
76 WCN_DBG(FM_DBG
| RDSC
, "rds disable\n");
77 fm_bi
->read(FM_MAIN_CTRL
, &dataRead
);
78 fm_bi
->write(FM_MAIN_CTRL
, dataRead
& (~RDS_MASK
));
83 static fm_u16
mt6626_RDS_Get_GoodBlock_Counter(void)
87 fm_bi
->read(FM_RDS_GOODBK_CNT
, &tmp_reg
);
88 GOOD_BLK_CNT
= tmp_reg
;
89 WCN_DBG(FM_DBG
| RDSC
, "get good block cnt:%d\n", (fm_s32
) tmp_reg
);
94 static fm_u16
mt6626_RDS_Get_BadBlock_Counter(void)
98 fm_bi
->read(FM_RDS_BADBK_CNT
, &tmp_reg
);
99 BAD_BLK_CNT
= tmp_reg
;
100 WCN_DBG(FM_DBG
| RDSC
, "get bad block cnt:%d\n", (fm_s32
) tmp_reg
);
105 static fm_u8
mt6626_RDS_Get_BadBlock_Ratio(void)
111 gbc
= mt6626_RDS_Get_GoodBlock_Counter();
112 bbc
= mt6626_RDS_Get_BadBlock_Counter();
114 if ((gbc
+ bbc
) > 0) {
115 tmp_reg
= (fm_u8
) (bbc
* 100 / (gbc
+ bbc
));
120 BAD_BLK_RATIO
= tmp_reg
;
121 WCN_DBG(FM_DBG
| RDSC
, "get badblock ratio:%d\n", (fm_s32
) tmp_reg
);
126 static fm_s32
mt6626_RDS_BlockCounter_Reset(void)
128 mt6626_RDS_disable();
134 static fm_u32
mt6626_RDS_Get_BlerCheck_Interval(void)
136 return gBLER_CHK_INTERVAL
;
139 static fm_s32
mt6626_RDS_BlerCheck(rds_t
*dst
)
144 static void RDS_Recovery_Handler(void)
149 fm_bi
->read(FM_RDS_DATA_REG
, &tempData
);
150 fm_bi
->read(FM_RDS_POINTER
, &tempData
);
151 } while (tempData
& 0x3);
154 static void mt6626_RDS_GetData(fm_u16
*data
, fm_u16 datalen
)
156 #define RDS_GROUP_DIFF_OFS 0x007C
157 #define RDS_FIFO_DIFF 0x007F
158 #define RDS_CRC_BLK_ADJ 0x0020
159 #define RDS_CRC_CORR_CNT 0x001E
160 #define RDS_CRC_INFO 0x0001
162 fm_u16 CRC
= 0, i
= 0, RDS_adj
= 0, RDSDataCount
= 0, FM_WARorrCnt
= 0;
163 fm_u16 temp
= 0, OutputPofm_s32
= 0;
165 WCN_DBG(FM_DBG
| RDSC
, "get data\n");
166 fm_bi
->read(FM_RDS_FIFO_STATUS0
, &temp
);
167 RDSDataCount
= ((RDS_GROUP_DIFF_OFS
& temp
) << 2);
169 if ((temp
& RDS_FIFO_DIFF
) >= 4) {
170 /* block A data and info handling */
171 fm_bi
->read(FM_RDS_INFO
, &temp
);
172 RDS_adj
|= (temp
& RDS_CRC_BLK_ADJ
) << 10;
173 CRC
|= (temp
& RDS_CRC_INFO
) << 3;
174 FM_WARorrCnt
|= ((temp
& RDS_CRC_CORR_CNT
) << 11);
175 fm_bi
->read(FM_RDS_DATA_REG
, &data
[0]);
177 /* block B data and info handling */
178 fm_bi
->read(FM_RDS_INFO
, &temp
);
179 RDS_adj
|= (temp
& RDS_CRC_BLK_ADJ
) << 9;
180 CRC
|= (temp
& RDS_CRC_INFO
) << 2;
181 FM_WARorrCnt
|= ((temp
& RDS_CRC_CORR_CNT
) << 7);
182 fm_bi
->read(FM_RDS_DATA_REG
, &data
[1]);
184 /* block C data and info handling */
185 fm_bi
->read(FM_RDS_INFO
, &temp
);
186 RDS_adj
|= (temp
& RDS_CRC_BLK_ADJ
) << 8;
187 CRC
|= (temp
& RDS_CRC_INFO
) << 1;
188 FM_WARorrCnt
|= ((temp
& RDS_CRC_CORR_CNT
) << 3);
189 fm_bi
->read(FM_RDS_DATA_REG
, &data
[2]);
191 /* block D data and info handling */
192 fm_bi
->read(FM_RDS_INFO
, &temp
);
193 RDS_adj
|= (temp
& RDS_CRC_BLK_ADJ
) << 7;
194 CRC
|= (temp
& RDS_CRC_INFO
);
195 FM_WARorrCnt
|= ((temp
& RDS_CRC_CORR_CNT
) >> 1);
196 fm_bi
->read(FM_RDS_DATA_REG
, &data
[3]);
198 data
[4] = FM_WARorrCnt
; /* CBC */
199 data
[5] = (CRC
| RDS_adj
| RDSDataCount
); /* CRC */
201 fm_bi
->read(FM_RDS_PWDI
, &data
[6]);
202 fm_bi
->read(FM_RDS_PWDQ
, &data
[7]);
204 fm_bi
->read(FM_RDS_POINTER
, &OutputPofm_s32
);
206 /* Go fm_s32o RDS recovery handler while RDS output pofm_s32 doesn't align to 4 in numeric */
207 if (OutputPofm_s32
& 0x3) {
208 RDS_Recovery_Handler();
217 static void mt6626_RDS_Init_Data(rds_t
*pstRDSData
)
219 fm_memset(pstRDSData
, 0, sizeof(rds_t
));
220 bRDS_FirstIn
= fm_true
;
222 fm_memset(pstRDSData
->RT_Data
.TextData
, 0x20, sizeof(pstRDSData
->RT_Data
.TextData
));
223 fm_memset(pstRDSData
->PS_Data
.PS
, '\0', sizeof(pstRDSData
->PS_Data
.PS
));
224 fm_memset(pstRDSData
->PS_ON
, 0x20, sizeof(pstRDSData
->PS_ON
));
227 fm_bool
mt6626_RDS_OnOff(rds_t
*dst
, fm_bool bFlag
)
229 if (mt6626_RDS_support() == fm_false
) {
230 WCN_DBG(FM_ALT
| RDSC
, "mt6626_RDS_OnOff failed, RDS not support\n");
235 mt6626_RDS_Init_Data(dst
);
238 mt6626_RDS_disable();
244 DEFINE_RDSLOG(rds_log
);
246 /* mt6626_RDS_Efm_s32_Handler - response FM RDS interrupt
247 * @fm - main data structure of FM driver
248 * This function first get RDS raw data, then call RDS spec parser
250 static fm_s32
mt6626_rds_parser(rds_t
*rds_dst
, struct rds_rx_t
*rds_raw
, fm_s32 rds_size
,
251 fm_u16(*getfreq
) (void))
257 mt6626_RDS_GetData(&raw
.data
[0].blkA
, sizeof(rds_packet_t
) + 2);
258 fifo_offset
= (raw
.data
[0].crc
& FM_RDS_DCO_FIFO_OFST
) >> 5; /* FM_RDS_DATA_CRC_FFOST */
259 WCN_DBG(FM_DBG
| RDSC
, "RDS fifo_offset:%d\n", fifo_offset
);
260 rds_log
.log_in(&rds_log
, &raw
, sizeof(rds_packet_t
) + 2 * sizeof(fm_u16
));
261 rds_parser(rds_dst
, &raw
, sizeof(rds_packet_t
) + 2 * sizeof(fm_u16
), getfreq
);
262 } while (fifo_offset
> 1);
267 static fm_s32
mt6626_rds_log_get(struct rds_rx_t
*dst
, fm_s32
*dst_len
)
269 return rds_log
.log_out(&rds_log
, dst
, dst_len
);
272 static fm_s32
mt6626_rds_gc_get(struct rds_group_cnt_t
*dst
, rds_t
*rdsp
)
274 return rds_grp_counter_get(dst
, &rdsp
->gc
);
277 static fm_s32
mt6626_rds_gc_reset(rds_t
*rdsp
)
279 return rds_grp_counter_reset(&rdsp
->gc
);
282 fm_s32
fm_rds_ops_register(struct fm_lowlevel_ops
*ops
)
287 FMR_ASSERT(ops
->bi
.write
);
288 FMR_ASSERT(ops
->bi
.read
);
289 FMR_ASSERT(ops
->bi
.setbits
);
290 FMR_ASSERT(ops
->bi
.usdelay
);
293 FMR_ASSERT(ops
->cb
.cur_freq_get
);
294 FMR_ASSERT(ops
->cb
.cur_freq_set
);
297 ops
->ri
.rds_blercheck
= mt6626_RDS_BlerCheck
;
298 ops
->ri
.rds_onoff
= mt6626_RDS_OnOff
;
299 ops
->ri
.rds_parser
= mt6626_rds_parser
;
300 ops
->ri
.rds_gbc_get
= mt6626_RDS_Get_GoodBlock_Counter
;
301 ops
->ri
.rds_bbc_get
= mt6626_RDS_Get_BadBlock_Counter
;
302 ops
->ri
.rds_bbr_get
= mt6626_RDS_Get_BadBlock_Ratio
;
303 ops
->ri
.rds_bc_reset
= mt6626_RDS_BlockCounter_Reset
;
304 ops
->ri
.rds_bci_get
= mt6626_RDS_Get_BlerCheck_Interval
;
305 ops
->ri
.rds_log_get
= mt6626_rds_log_get
;
306 ops
->ri
.rds_gc_get
= mt6626_rds_gc_get
;
307 ops
->ri
.rds_gc_reset
= mt6626_rds_gc_reset
;
311 fm_s32
fm_rds_ops_unregister(struct fm_lowlevel_ops
*ops
)
318 fm_memset(&ops
->ri
, 0, sizeof(struct fm_rds_interface
));