4 * MediaTek <www.MediaTek.com>
5 * Hongcheng <hongcheng.xia@MediaTek.com>
7 * MT6628 FM Radio Driver -- setup data link
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
24 #include <linux/slab.h>
25 #include <linux/interrupt.h>
30 #include "fm_typedef.h"
33 #include "fm_stdlib.h"
35 #include "mt6628_fm.h"
36 #include "mt6628_fm_link.h"
38 static struct fm_link_event *link_event;
40 static struct fm_trace_fifo_t *cmd_fifo;
42 static struct fm_trace_fifo_t *evt_fifo;
44 static fm_s32 (*reset)(fm_s32 sta) = NULL;
46 static void mt6628_fm_wholechip_rst_cb(ENUM_WMTDRV_TYPE_T src,
47 ENUM_WMTDRV_TYPE_T dst,
48 ENUM_WMTMSG_TYPE_T type,
52 //To handle reset procedure please
53 ENUM_WMTRSTMSG_TYPE_T rst_msg;
55 if (sz <= sizeof(ENUM_WMTRSTMSG_TYPE_T)) {
56 memcpy((char *)&rst_msg, (char *)buf, sz);
57 WCN_DBG(FM_WAR | LINK, "[src=%d], [dst=%d], [type=%d], [buf=0x%x], [sz=%d], [max=%d]\n", src, dst, type, rst_msg, sz, WMTRSTMSG_RESET_MAX);
59 if ((src == WMTDRV_TYPE_WMT) && (dst == WMTDRV_TYPE_FM) && (type == WMTMSG_TYPE_RESET)) {
60 if (rst_msg == WMTRSTMSG_RESET_START) {
61 WCN_DBG(FM_WAR | LINK, "FM restart start!\n");
66 } else if (rst_msg == WMTRSTMSG_RESET_END) {
67 WCN_DBG(FM_WAR | LINK, "FM restart end!\n");
74 /*message format invalid*/
75 WCN_DBG(FM_WAR | LINK, "message format invalid!\n");
80 fm_s32 fm_link_setup(void* data)
84 if (!(link_event = fm_zalloc(sizeof(struct fm_link_event)))) {
85 WCN_DBG(FM_ALT | LINK, "fm_zalloc(fm_link_event) -ENOMEM\n");
89 link_event->ln_event = fm_flag_event_create("ln_evt");
91 if (!link_event->ln_event) {
92 WCN_DBG(FM_ALT | LINK, "create mt6628_ln_event failed\n");
97 fm_flag_event_get(link_event->ln_event);
100 WCN_DBG(FM_NTC | LINK, "fm link setup\n");
102 cmd_fifo = fm_trace_fifo_create("cmd_fifo");
104 WCN_DBG(FM_ALT | LINK, "create cmd_fifo failed\n");
109 evt_fifo = fm_trace_fifo_create("evt_fifo");
111 WCN_DBG(FM_ALT | LINK, "create evt_fifo failed\n");
116 reset = data; // get whole chip reset cb
117 mtk_wcn_wmt_msgcb_reg(WMTDRV_TYPE_FM, mt6628_fm_wholechip_rst_cb);
121 fm_trace_fifo_release(evt_fifo);
122 fm_trace_fifo_release(cmd_fifo);
123 fm_flag_event_put(link_event->ln_event);
131 fm_s32 fm_link_release(void)
134 fm_trace_fifo_release(evt_fifo);
135 fm_trace_fifo_release(cmd_fifo);
136 fm_flag_event_put(link_event->ln_event);
141 WCN_DBG(FM_NTC | LINK, "fm link release\n");
147 * the low level func to read a rigister
148 * @addr - rigister address
149 * @val - the pointer of target buf
150 * If success, return 0; else error code
152 fm_s32 fm_ctrl_rx(fm_u8 addr, fm_u16 *val)
159 * the low level func to write a rigister
160 * @addr - rigister address
161 * @val - value will be writed in the rigister
162 * If success, return 0; else error code
164 fm_s32 fm_ctrl_tx(fm_u8 addr, fm_u16 val)
170 * fm_cmd_tx() - send cmd to FM firmware and wait event
172 * @len - the length of cmd
173 * @mask - the event flag mask
174 * @ cnt - the retry conter
175 * @timeout - timeout per cmd
176 * Return 0, if success; error code, if failed
178 fm_s32 fm_cmd_tx(fm_u8* buf, fm_u16 len, fm_s32 mask, fm_s32 cnt, fm_s32 timeout, fm_s32(*callback)(struct fm_res_ctx* result))
181 struct task_struct *task = current;
182 struct fm_trace_t trace;
184 if ((NULL == buf) || (len < 0) || (0 == mask)
185 || (cnt > SW_RETRY_CNT_MAX) || (timeout > SW_WAIT_TIMEOUT_MAX)) {
186 WCN_DBG(FM_ERR | LINK, "cmd tx, invalid para\n");
190 FM_EVENT_CLR(link_event->ln_event, mask);
192 #ifdef FM_TRACE_ENABLE
194 trace.opcode = buf[1];
196 trace.tid = (fm_s32)task->pid;
197 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
198 fm_memcpy(trace.pkt, &buf[4], (trace.len > FM_TRACE_PKT_SIZE) ? FM_TRACE_PKT_SIZE : trace.len);
203 #ifdef FM_TRACE_ENABLE
204 if (fm_true == FM_TRACE_FULL(cmd_fifo)) {
205 FM_TRACE_OUT(cmd_fifo, NULL);
207 FM_TRACE_IN(cmd_fifo, &trace);
210 //send cmd to FM firmware
211 if (mtk_wcn_stp_send_data(buf, len, FM_TASK_INDX) == 0) {
212 WCN_DBG(FM_EMG | LINK, "send data over stp failed\n");
216 //wait the response form FM firmware
217 ret_time = FM_EVENT_WAIT_TIMEOUT(link_event->ln_event, mask, timeout);
221 WCN_DBG(FM_WAR | LINK, "wait even timeout, [retry_cnt=%d], pid=%d\n", cnt, task->pid);
225 goto sw_retry; //retry if timeout and retry cnt > 0
227 WCN_DBG(FM_ALT | LINK, "fatal error, SW retry failed, reset HW\n");
232 FM_EVENT_CLR(link_event->ln_event, mask);
235 callback(&link_event->result);
241 fm_s32 fm_event_parser(fm_s32(*rds_parser)(struct rds_rx_t*, fm_s32))
248 fm_u8 rx_buf[RX_BUF_SIZE + 10] = {0}; //the 10 bytes are protect gaps
249 static volatile fm_task_parser_state state = FM_TASK_RX_PARSER_PKT_TYPE;
250 struct fm_trace_t trace;
251 struct task_struct *task = current;
253 len = mtk_wcn_stp_receive_data(rx_buf, RX_BUF_SIZE, FM_TASK_INDX);
254 WCN_DBG(FM_DBG | LINK, "[len=%d],[CMD=0x%02x 0x%02x 0x%02x 0x%02x]\n", len, rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
260 case FM_TASK_RX_PARSER_PKT_TYPE:
262 if (ch == FM_TASK_EVENT_PKT_TYPE) {
263 if ((i + 5) < RX_BUF_SIZE) {
264 WCN_DBG(FM_DBG | LINK, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \n", rx_buf[i], rx_buf[i+1], rx_buf[i+2], rx_buf[i+3], rx_buf[i+4], rx_buf[i+5]);
266 WCN_DBG(FM_DBG | LINK, "0x%02x 0x%02x\n", rx_buf[i], rx_buf[i+1]);
269 state = FM_TASK_RX_PARSER_OPCODE;
271 WCN_DBG(FM_ALT | LINK, "event pkt type error (rx_buf[%d] = 0x%02x)\n", i, ch);
277 case FM_TASK_RX_PARSER_OPCODE:
280 state = FM_TASK_RX_PARSER_PKT_LEN_1;
283 case FM_TASK_RX_PARSER_PKT_LEN_1:
286 state = FM_TASK_RX_PARSER_PKT_LEN_2;
289 case FM_TASK_RX_PARSER_PKT_LEN_2:
291 length |= (fm_u16)(ch << 0x8);
293 #ifdef FM_TRACE_ENABLE
294 trace.type = FM_TASK_EVENT_PKT_TYPE;
295 trace.opcode = opcode;
297 trace.tid = (fm_s32)task->pid;
298 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
299 fm_memcpy(trace.pkt, &rx_buf[i], (length > FM_TRACE_PKT_SIZE) ? FM_TRACE_PKT_SIZE : length);
301 if (fm_true == FM_TRACE_FULL(cmd_fifo)) {
302 FM_TRACE_OUT(cmd_fifo, NULL);
304 FM_TRACE_IN(cmd_fifo, &trace);
307 state = FM_TASK_RX_PARSER_PKT_PAYLOAD;
309 state = FM_TASK_RX_PARSER_PKT_TYPE;
310 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
315 case FM_TASK_RX_PARSER_PKT_PAYLOAD:
320 if ((length == 1) && (rx_buf[i] == 1)) {
321 FM_EVENT_SEND(link_event->ln_event, FLAG_TUNE_DONE);
326 case FM_SOFT_MUTE_TUNE_OPCODE:
329 fm_memcpy(link_event->result.cqi, &rx_buf[i], (length > FM_CQI_BUF_SIZE) ? FM_CQI_BUF_SIZE : length);
330 FM_EVENT_SEND(link_event->ln_event, FLAG_SM_TUNE);
336 if ((i + 1) < RX_BUF_SIZE) {
337 link_event->result.seek_result = rx_buf[i] + (rx_buf[i+1] << 8); // 8760 means 87.60Mhz
340 FM_EVENT_SEND(link_event->ln_event, FLAG_SEEK_DONE);
345 //check if the result data is long enough
346 if ((RX_BUF_SIZE - i) < (sizeof(fm_u16) * FM_SCANTBL_SIZE)) {
347 WCN_DBG(FM_ALT | LINK, "FM_SCAN_OPCODE err, [tblsize=%d],[bufsize=%d]\n", (sizeof(fm_u16) * FM_SCANTBL_SIZE), (RX_BUF_SIZE - i));
348 FM_EVENT_SEND(link_event->ln_event, FLAG_SCAN_DONE);
350 } else if ((length >= FM_CQI_BUF_SIZE) && ((RX_BUF_SIZE - i) >= FM_CQI_BUF_SIZE)) {
351 fm_memcpy(link_event->result.cqi, &rx_buf[i], FM_CQI_BUF_SIZE);
352 FM_EVENT_SEND(link_event->ln_event, FLAG_CQI_DONE);
354 fm_memcpy(link_event->result.scan_result, &rx_buf[i], sizeof(fm_u16) * FM_SCANTBL_SIZE);
355 FM_EVENT_SEND(link_event->ln_event, FLAG_SCAN_DONE);
360 case FSPI_READ_OPCODE:
362 if ((i + 1) < RX_BUF_SIZE) {
363 link_event->result.fspi_rd = (rx_buf[i] + (rx_buf[i+1] << 8));
366 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
369 case RDS_RX_DATA_OPCODE:
371 //check if the rds data is long enough
372 if ((RX_BUF_SIZE - i) < length) {
373 WCN_DBG(FM_ALT | LINK, "RDS RX err, [rxlen=%d],[bufsize=%d]\n", (fm_s32)length, (RX_BUF_SIZE - i));
374 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
378 //copy rds data to rds buf
379 fm_memcpy(&link_event->result.rds_rx_result, &rx_buf[i], length);
381 /*Handle the RDS data that we get*/
383 rds_parser(&link_event->result.rds_rx_result, length);
385 WCN_DBG(FM_WAR | LINK, "no method to parse RDS data\n");
388 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
392 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
396 state = FM_TASK_RX_PARSER_PKT_TYPE;
408 fm_bool fm_wait_stc_done(fm_u32 sec)
413 fm_s32 fm_force_active_event(fm_u32 mask)
417 flag = FM_EVENT_GET(link_event->ln_event);
418 WCN_DBG(FM_WAR | LINK, "before force active event, [flag=0x%08x]\n", flag);
419 flag = FM_EVENT_SEND(link_event->ln_event, mask);
420 WCN_DBG(FM_WAR | LINK, "after force active event, [flag=0x%08x]\n", flag);
426 extern fm_s32 fm_print_cmd_fifo(void)
428 #ifdef FM_TRACE_ENABLE
429 struct fm_trace_t trace;
432 while (fm_false == FM_TRACE_EMPTY(cmd_fifo)) {
433 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
434 FM_TRACE_OUT(cmd_fifo, &trace);
435 WCN_DBG(FM_ALT | LINK, "trace, type %d, op %d, len %d, tid %d, time %d\n", trace.type, trace.opcode, trace.len, trace.tid, jiffies_to_msecs(abs(trace.time)));
437 while ((trace.len > 0) && (i < trace.len) && (i < (FM_TRACE_PKT_SIZE-8))) {
438 WCN_DBG(FM_ALT | LINK, "trace, %02x %02x %02x %02x %02x %02x %02x %02x\n", \
439 trace.pkt[i], trace.pkt[i+1], trace.pkt[i+2], trace.pkt[i+3], trace.pkt[i+4], trace.pkt[i+5], trace.pkt[i+6], trace.pkt[i+7]);
442 WCN_DBG(FM_ALT | LINK, "trace\n");
449 extern fm_s32 fm_print_evt_fifo(void)
451 #ifdef FM_TRACE_ENABLE
452 struct fm_trace_t trace;
455 while (fm_false == FM_TRACE_EMPTY(evt_fifo)) {
456 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
457 FM_TRACE_OUT(evt_fifo, &trace);
458 WCN_DBG(FM_ALT | LINK, "%s: op %d, len %d, %d\n", evt_fifo->name, trace.opcode, trace.len, jiffies_to_msecs(abs(trace.time)));
460 while ((trace.len > 0) && (i < trace.len) && (i < (FM_TRACE_PKT_SIZE-8))) {
461 WCN_DBG(FM_ALT | LINK, "%s: %02x %02x %02x %02x %02x %02x %02x %02x\n", \
462 evt_fifo->name, trace.pkt[i], trace.pkt[i+1], trace.pkt[i+2], trace.pkt[i+3], trace.pkt[i+4], trace.pkt[i+5], trace.pkt[i+6], trace.pkt[i+7]);
465 WCN_DBG(FM_ALT | LINK, "%s\n", evt_fifo->name);