162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  FM Driver for Connectivity chip of Texas Instruments.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  This sub-module of FM driver is common for FM RX and TX
662306a36Sopenharmony_ci *  functionality. This module is responsible for:
762306a36Sopenharmony_ci *  1) Forming group of Channel-8 commands to perform particular
862306a36Sopenharmony_ci *     functionality (eg., frequency set require more than
962306a36Sopenharmony_ci *     one Channel-8 command to be sent to the chip).
1062306a36Sopenharmony_ci *  2) Sending each Channel-8 command to the chip and reading
1162306a36Sopenharmony_ci *     response back over Shared Transport.
1262306a36Sopenharmony_ci *  3) Managing TX and RX Queues and Tasklets.
1362306a36Sopenharmony_ci *  4) Handling FM Interrupt packet and taking appropriate action.
1462306a36Sopenharmony_ci *  5) Loading FM firmware to the chip (common, FM TX, and FM RX
1562306a36Sopenharmony_ci *     firmware files based on mode selection)
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *  Copyright (C) 2011 Texas Instruments
1862306a36Sopenharmony_ci *  Author: Raja Mani <raja_mani@ti.com>
1962306a36Sopenharmony_ci *  Author: Manjunatha Halli <manjunatha_halli@ti.com>
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci#include <linux/firmware.h>
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/nospec.h>
2662306a36Sopenharmony_ci#include <linux/jiffies.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "fmdrv.h"
2962306a36Sopenharmony_ci#include "fmdrv_v4l2.h"
3062306a36Sopenharmony_ci#include "fmdrv_common.h"
3162306a36Sopenharmony_ci#include <linux/ti_wilink_st.h>
3262306a36Sopenharmony_ci#include "fmdrv_rx.h"
3362306a36Sopenharmony_ci#include "fmdrv_tx.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* Region info */
3662306a36Sopenharmony_cistatic struct region_info region_configs[] = {
3762306a36Sopenharmony_ci	/* Europe/US */
3862306a36Sopenharmony_ci	{
3962306a36Sopenharmony_ci	 .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
4062306a36Sopenharmony_ci	 .bot_freq = 87500,	/* 87.5 MHz */
4162306a36Sopenharmony_ci	 .top_freq = 108000,	/* 108 MHz */
4262306a36Sopenharmony_ci	 .fm_band = 0,
4362306a36Sopenharmony_ci	 },
4462306a36Sopenharmony_ci	/* Japan */
4562306a36Sopenharmony_ci	{
4662306a36Sopenharmony_ci	 .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
4762306a36Sopenharmony_ci	 .bot_freq = 76000,	/* 76 MHz */
4862306a36Sopenharmony_ci	 .top_freq = 90000,	/* 90 MHz */
4962306a36Sopenharmony_ci	 .fm_band = 1,
5062306a36Sopenharmony_ci	 },
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Band selection */
5462306a36Sopenharmony_cistatic u8 default_radio_region;	/* Europe/US */
5562306a36Sopenharmony_cimodule_param(default_radio_region, byte, 0);
5662306a36Sopenharmony_ciMODULE_PARM_DESC(default_radio_region, "Region: 0=Europe/US, 1=Japan");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* RDS buffer blocks */
5962306a36Sopenharmony_cistatic u32 default_rds_buf = 300;
6062306a36Sopenharmony_cimodule_param(default_rds_buf, uint, 0444);
6162306a36Sopenharmony_ciMODULE_PARM_DESC(default_rds_buf, "RDS buffer entries");
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Radio Nr */
6462306a36Sopenharmony_cistatic u32 radio_nr = -1;
6562306a36Sopenharmony_cimodule_param(radio_nr, int, 0444);
6662306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio Nr");
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* FM irq handlers forward declaration */
6962306a36Sopenharmony_cistatic void fm_irq_send_flag_getcmd(struct fmdev *);
7062306a36Sopenharmony_cistatic void fm_irq_handle_flag_getcmd_resp(struct fmdev *);
7162306a36Sopenharmony_cistatic void fm_irq_handle_hw_malfunction(struct fmdev *);
7262306a36Sopenharmony_cistatic void fm_irq_handle_rds_start(struct fmdev *);
7362306a36Sopenharmony_cistatic void fm_irq_send_rdsdata_getcmd(struct fmdev *);
7462306a36Sopenharmony_cistatic void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *);
7562306a36Sopenharmony_cistatic void fm_irq_handle_rds_finish(struct fmdev *);
7662306a36Sopenharmony_cistatic void fm_irq_handle_tune_op_ended(struct fmdev *);
7762306a36Sopenharmony_cistatic void fm_irq_handle_power_enb(struct fmdev *);
7862306a36Sopenharmony_cistatic void fm_irq_handle_low_rssi_start(struct fmdev *);
7962306a36Sopenharmony_cistatic void fm_irq_afjump_set_pi(struct fmdev *);
8062306a36Sopenharmony_cistatic void fm_irq_handle_set_pi_resp(struct fmdev *);
8162306a36Sopenharmony_cistatic void fm_irq_afjump_set_pimask(struct fmdev *);
8262306a36Sopenharmony_cistatic void fm_irq_handle_set_pimask_resp(struct fmdev *);
8362306a36Sopenharmony_cistatic void fm_irq_afjump_setfreq(struct fmdev *);
8462306a36Sopenharmony_cistatic void fm_irq_handle_setfreq_resp(struct fmdev *);
8562306a36Sopenharmony_cistatic void fm_irq_afjump_enableint(struct fmdev *);
8662306a36Sopenharmony_cistatic void fm_irq_afjump_enableint_resp(struct fmdev *);
8762306a36Sopenharmony_cistatic void fm_irq_start_afjump(struct fmdev *);
8862306a36Sopenharmony_cistatic void fm_irq_handle_start_afjump_resp(struct fmdev *);
8962306a36Sopenharmony_cistatic void fm_irq_afjump_rd_freq(struct fmdev *);
9062306a36Sopenharmony_cistatic void fm_irq_afjump_rd_freq_resp(struct fmdev *);
9162306a36Sopenharmony_cistatic void fm_irq_handle_low_rssi_finish(struct fmdev *);
9262306a36Sopenharmony_cistatic void fm_irq_send_intmsk_cmd(struct fmdev *);
9362306a36Sopenharmony_cistatic void fm_irq_handle_intmsk_cmd_resp(struct fmdev *);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * When FM common module receives interrupt packet, following handlers
9762306a36Sopenharmony_ci * will be executed one after another to service the interrupt(s)
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cienum fmc_irq_handler_index {
10062306a36Sopenharmony_ci	FM_SEND_FLAG_GETCMD_IDX,
10162306a36Sopenharmony_ci	FM_HANDLE_FLAG_GETCMD_RESP_IDX,
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* HW malfunction irq handler */
10462306a36Sopenharmony_ci	FM_HW_MAL_FUNC_IDX,
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* RDS threshold reached irq handler */
10762306a36Sopenharmony_ci	FM_RDS_START_IDX,
10862306a36Sopenharmony_ci	FM_RDS_SEND_RDS_GETCMD_IDX,
10962306a36Sopenharmony_ci	FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX,
11062306a36Sopenharmony_ci	FM_RDS_FINISH_IDX,
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Tune operation ended irq handler */
11362306a36Sopenharmony_ci	FM_HW_TUNE_OP_ENDED_IDX,
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* TX power enable irq handler */
11662306a36Sopenharmony_ci	FM_HW_POWER_ENB_IDX,
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Low RSSI irq handler */
11962306a36Sopenharmony_ci	FM_LOW_RSSI_START_IDX,
12062306a36Sopenharmony_ci	FM_AF_JUMP_SETPI_IDX,
12162306a36Sopenharmony_ci	FM_AF_JUMP_HANDLE_SETPI_RESP_IDX,
12262306a36Sopenharmony_ci	FM_AF_JUMP_SETPI_MASK_IDX,
12362306a36Sopenharmony_ci	FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX,
12462306a36Sopenharmony_ci	FM_AF_JUMP_SET_AF_FREQ_IDX,
12562306a36Sopenharmony_ci	FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX,
12662306a36Sopenharmony_ci	FM_AF_JUMP_ENABLE_INT_IDX,
12762306a36Sopenharmony_ci	FM_AF_JUMP_ENABLE_INT_RESP_IDX,
12862306a36Sopenharmony_ci	FM_AF_JUMP_START_AFJUMP_IDX,
12962306a36Sopenharmony_ci	FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX,
13062306a36Sopenharmony_ci	FM_AF_JUMP_RD_FREQ_IDX,
13162306a36Sopenharmony_ci	FM_AF_JUMP_RD_FREQ_RESP_IDX,
13262306a36Sopenharmony_ci	FM_LOW_RSSI_FINISH_IDX,
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Interrupt process post action */
13562306a36Sopenharmony_ci	FM_SEND_INTMSK_CMD_IDX,
13662306a36Sopenharmony_ci	FM_HANDLE_INTMSK_CMD_RESP_IDX,
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* FM interrupt handler table */
14062306a36Sopenharmony_cistatic int_handler_prototype int_handler_table[] = {
14162306a36Sopenharmony_ci	fm_irq_send_flag_getcmd,
14262306a36Sopenharmony_ci	fm_irq_handle_flag_getcmd_resp,
14362306a36Sopenharmony_ci	fm_irq_handle_hw_malfunction,
14462306a36Sopenharmony_ci	fm_irq_handle_rds_start, /* RDS threshold reached irq handler */
14562306a36Sopenharmony_ci	fm_irq_send_rdsdata_getcmd,
14662306a36Sopenharmony_ci	fm_irq_handle_rdsdata_getcmd_resp,
14762306a36Sopenharmony_ci	fm_irq_handle_rds_finish,
14862306a36Sopenharmony_ci	fm_irq_handle_tune_op_ended,
14962306a36Sopenharmony_ci	fm_irq_handle_power_enb, /* TX power enable irq handler */
15062306a36Sopenharmony_ci	fm_irq_handle_low_rssi_start,
15162306a36Sopenharmony_ci	fm_irq_afjump_set_pi,
15262306a36Sopenharmony_ci	fm_irq_handle_set_pi_resp,
15362306a36Sopenharmony_ci	fm_irq_afjump_set_pimask,
15462306a36Sopenharmony_ci	fm_irq_handle_set_pimask_resp,
15562306a36Sopenharmony_ci	fm_irq_afjump_setfreq,
15662306a36Sopenharmony_ci	fm_irq_handle_setfreq_resp,
15762306a36Sopenharmony_ci	fm_irq_afjump_enableint,
15862306a36Sopenharmony_ci	fm_irq_afjump_enableint_resp,
15962306a36Sopenharmony_ci	fm_irq_start_afjump,
16062306a36Sopenharmony_ci	fm_irq_handle_start_afjump_resp,
16162306a36Sopenharmony_ci	fm_irq_afjump_rd_freq,
16262306a36Sopenharmony_ci	fm_irq_afjump_rd_freq_resp,
16362306a36Sopenharmony_ci	fm_irq_handle_low_rssi_finish,
16462306a36Sopenharmony_ci	fm_irq_send_intmsk_cmd, /* Interrupt process post action */
16562306a36Sopenharmony_ci	fm_irq_handle_intmsk_cmd_resp
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic long (*g_st_write) (struct sk_buff *skb);
16962306a36Sopenharmony_cistatic struct completion wait_for_fmdrv_reg_comp;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic inline void fm_irq_call(struct fmdev *fmdev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/* Continue next function in interrupt handler table */
17762306a36Sopenharmony_cistatic inline void fm_irq_call_stage(struct fmdev *fmdev, u8 stage)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	fmdev->irq_info.stage = stage;
18062306a36Sopenharmony_ci	fm_irq_call(fmdev);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic inline void fm_irq_timeout_stage(struct fmdev *fmdev, u8 stage)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	fmdev->irq_info.stage = stage;
18662306a36Sopenharmony_ci	mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#ifdef FM_DUMP_TXRX_PKT
19062306a36Sopenharmony_ci /* To dump outgoing FM Channel-8 packets */
19162306a36Sopenharmony_ciinline void dump_tx_skb_data(struct sk_buff *skb)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	int len, len_org;
19462306a36Sopenharmony_ci	u8 index;
19562306a36Sopenharmony_ci	struct fm_cmd_msg_hdr *cmd_hdr;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	cmd_hdr = (struct fm_cmd_msg_hdr *)skb->data;
19862306a36Sopenharmony_ci	printk(KERN_INFO "<<%shdr:%02x len:%02x opcode:%02x type:%s dlen:%02x",
19962306a36Sopenharmony_ci	       fm_cb(skb)->completion ? " " : "*", cmd_hdr->hdr,
20062306a36Sopenharmony_ci	       cmd_hdr->len, cmd_hdr->op,
20162306a36Sopenharmony_ci	       cmd_hdr->rd_wr ? "RD" : "WR", cmd_hdr->dlen);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	len_org = skb->len - FM_CMD_MSG_HDR_SIZE;
20462306a36Sopenharmony_ci	if (len_org > 0) {
20562306a36Sopenharmony_ci		printk(KERN_CONT "\n   data(%d): ", cmd_hdr->dlen);
20662306a36Sopenharmony_ci		len = min(len_org, 14);
20762306a36Sopenharmony_ci		for (index = 0; index < len; index++)
20862306a36Sopenharmony_ci			printk(KERN_CONT "%x ",
20962306a36Sopenharmony_ci			       skb->data[FM_CMD_MSG_HDR_SIZE + index]);
21062306a36Sopenharmony_ci		printk(KERN_CONT "%s", (len_org > 14) ? ".." : "");
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	printk(KERN_CONT "\n");
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci /* To dump incoming FM Channel-8 packets */
21662306a36Sopenharmony_ciinline void dump_rx_skb_data(struct sk_buff *skb)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	int len, len_org;
21962306a36Sopenharmony_ci	u8 index;
22062306a36Sopenharmony_ci	struct fm_event_msg_hdr *evt_hdr;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	evt_hdr = (struct fm_event_msg_hdr *)skb->data;
22362306a36Sopenharmony_ci	printk(KERN_INFO ">> hdr:%02x len:%02x sts:%02x numhci:%02x opcode:%02x type:%s dlen:%02x",
22462306a36Sopenharmony_ci	       evt_hdr->hdr, evt_hdr->len,
22562306a36Sopenharmony_ci	       evt_hdr->status, evt_hdr->num_fm_hci_cmds, evt_hdr->op,
22662306a36Sopenharmony_ci	       (evt_hdr->rd_wr) ? "RD" : "WR", evt_hdr->dlen);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	len_org = skb->len - FM_EVT_MSG_HDR_SIZE;
22962306a36Sopenharmony_ci	if (len_org > 0) {
23062306a36Sopenharmony_ci		printk(KERN_CONT "\n   data(%d): ", evt_hdr->dlen);
23162306a36Sopenharmony_ci		len = min(len_org, 14);
23262306a36Sopenharmony_ci		for (index = 0; index < len; index++)
23362306a36Sopenharmony_ci			printk(KERN_CONT "%x ",
23462306a36Sopenharmony_ci			       skb->data[FM_EVT_MSG_HDR_SIZE + index]);
23562306a36Sopenharmony_ci		printk(KERN_CONT "%s", (len_org > 14) ? ".." : "");
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	printk(KERN_CONT "\n");
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci#endif
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	fmdev->rx.region = region_configs[region_to_set];
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/*
24762306a36Sopenharmony_ci * FM common sub-module will schedule this tasklet whenever it receives
24862306a36Sopenharmony_ci * FM packet from ST driver.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_cistatic void recv_tasklet(struct tasklet_struct *t)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct fmdev *fmdev;
25362306a36Sopenharmony_ci	struct fm_irq *irq_info;
25462306a36Sopenharmony_ci	struct fm_event_msg_hdr *evt_hdr;
25562306a36Sopenharmony_ci	struct sk_buff *skb;
25662306a36Sopenharmony_ci	u8 num_fm_hci_cmds;
25762306a36Sopenharmony_ci	unsigned long flags;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	fmdev = from_tasklet(fmdev, t, tx_task);
26062306a36Sopenharmony_ci	irq_info = &fmdev->irq_info;
26162306a36Sopenharmony_ci	/* Process all packets in the RX queue */
26262306a36Sopenharmony_ci	while ((skb = skb_dequeue(&fmdev->rx_q))) {
26362306a36Sopenharmony_ci		if (skb->len < sizeof(struct fm_event_msg_hdr)) {
26462306a36Sopenharmony_ci			fmerr("skb(%p) has only %d bytes, at least need %zu bytes to decode\n",
26562306a36Sopenharmony_ci			      skb,
26662306a36Sopenharmony_ci			      skb->len, sizeof(struct fm_event_msg_hdr));
26762306a36Sopenharmony_ci			kfree_skb(skb);
26862306a36Sopenharmony_ci			continue;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		evt_hdr = (void *)skb->data;
27262306a36Sopenharmony_ci		num_fm_hci_cmds = evt_hdr->num_fm_hci_cmds;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		/* FM interrupt packet? */
27562306a36Sopenharmony_ci		if (evt_hdr->op == FM_INTERRUPT) {
27662306a36Sopenharmony_ci			/* FM interrupt handler started already? */
27762306a36Sopenharmony_ci			if (!test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
27862306a36Sopenharmony_ci				set_bit(FM_INTTASK_RUNNING, &fmdev->flag);
27962306a36Sopenharmony_ci				if (irq_info->stage != 0) {
28062306a36Sopenharmony_ci					fmerr("Inval stage resetting to zero\n");
28162306a36Sopenharmony_ci					irq_info->stage = 0;
28262306a36Sopenharmony_ci				}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci				/*
28562306a36Sopenharmony_ci				 * Execute first function in interrupt handler
28662306a36Sopenharmony_ci				 * table.
28762306a36Sopenharmony_ci				 */
28862306a36Sopenharmony_ci				irq_info->handlers[irq_info->stage](fmdev);
28962306a36Sopenharmony_ci			} else {
29062306a36Sopenharmony_ci				set_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag);
29162306a36Sopenharmony_ci			}
29262306a36Sopenharmony_ci			kfree_skb(skb);
29362306a36Sopenharmony_ci		}
29462306a36Sopenharmony_ci		/* Anyone waiting for this with completion handler? */
29562306a36Sopenharmony_ci		else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp != NULL) {
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
29862306a36Sopenharmony_ci			fmdev->resp_skb = skb;
29962306a36Sopenharmony_ci			spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
30062306a36Sopenharmony_ci			complete(fmdev->resp_comp);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci			fmdev->resp_comp = NULL;
30362306a36Sopenharmony_ci			atomic_set(&fmdev->tx_cnt, 1);
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci		/* Is this for interrupt handler? */
30662306a36Sopenharmony_ci		else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp == NULL) {
30762306a36Sopenharmony_ci			if (fmdev->resp_skb != NULL)
30862306a36Sopenharmony_ci				fmerr("Response SKB ptr not NULL\n");
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci			spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
31162306a36Sopenharmony_ci			fmdev->resp_skb = skb;
31262306a36Sopenharmony_ci			spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			/* Execute interrupt handler where state index points */
31562306a36Sopenharmony_ci			irq_info->handlers[irq_info->stage](fmdev);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci			kfree_skb(skb);
31862306a36Sopenharmony_ci			atomic_set(&fmdev->tx_cnt, 1);
31962306a36Sopenharmony_ci		} else {
32062306a36Sopenharmony_ci			fmerr("Nobody claimed SKB(%p),purging\n", skb);
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		/*
32462306a36Sopenharmony_ci		 * Check flow control field. If Num_FM_HCI_Commands field is
32562306a36Sopenharmony_ci		 * not zero, schedule FM TX tasklet.
32662306a36Sopenharmony_ci		 */
32762306a36Sopenharmony_ci		if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt))
32862306a36Sopenharmony_ci			if (!skb_queue_empty(&fmdev->tx_q))
32962306a36Sopenharmony_ci				tasklet_schedule(&fmdev->tx_task);
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/* FM send tasklet: is scheduled when FM packet has to be sent to chip */
33462306a36Sopenharmony_cistatic void send_tasklet(struct tasklet_struct *t)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct fmdev *fmdev;
33762306a36Sopenharmony_ci	struct sk_buff *skb;
33862306a36Sopenharmony_ci	int len;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	fmdev = from_tasklet(fmdev, t, tx_task);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (!atomic_read(&fmdev->tx_cnt))
34362306a36Sopenharmony_ci		return;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* Check, is there any timeout happened to last transmitted packet */
34662306a36Sopenharmony_ci	if (time_is_before_jiffies(fmdev->last_tx_jiffies + FM_DRV_TX_TIMEOUT)) {
34762306a36Sopenharmony_ci		fmerr("TX timeout occurred\n");
34862306a36Sopenharmony_ci		atomic_set(&fmdev->tx_cnt, 1);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Send queued FM TX packets */
35262306a36Sopenharmony_ci	skb = skb_dequeue(&fmdev->tx_q);
35362306a36Sopenharmony_ci	if (!skb)
35462306a36Sopenharmony_ci		return;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	atomic_dec(&fmdev->tx_cnt);
35762306a36Sopenharmony_ci	fmdev->pre_op = fm_cb(skb)->fm_op;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (fmdev->resp_comp != NULL)
36062306a36Sopenharmony_ci		fmerr("Response completion handler is not NULL\n");
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	fmdev->resp_comp = fm_cb(skb)->completion;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Write FM packet to ST driver */
36562306a36Sopenharmony_ci	len = g_st_write(skb);
36662306a36Sopenharmony_ci	if (len < 0) {
36762306a36Sopenharmony_ci		kfree_skb(skb);
36862306a36Sopenharmony_ci		fmdev->resp_comp = NULL;
36962306a36Sopenharmony_ci		fmerr("TX tasklet failed to send skb(%p)\n", skb);
37062306a36Sopenharmony_ci		atomic_set(&fmdev->tx_cnt, 1);
37162306a36Sopenharmony_ci	} else {
37262306a36Sopenharmony_ci		fmdev->last_tx_jiffies = jiffies;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci/*
37762306a36Sopenharmony_ci * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for
37862306a36Sopenharmony_ci * transmission
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_cistatic int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type,	void *payload,
38162306a36Sopenharmony_ci		int payload_len, struct completion *wait_completion)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct sk_buff *skb;
38462306a36Sopenharmony_ci	struct fm_cmd_msg_hdr *hdr;
38562306a36Sopenharmony_ci	int size;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (fm_op >= FM_INTERRUPT) {
38862306a36Sopenharmony_ci		fmerr("Invalid fm opcode - %d\n", fm_op);
38962306a36Sopenharmony_ci		return -EINVAL;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	if (test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) && payload == NULL) {
39262306a36Sopenharmony_ci		fmerr("Payload data is NULL during fw download\n");
39362306a36Sopenharmony_ci		return -EINVAL;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag))
39662306a36Sopenharmony_ci		size =
39762306a36Sopenharmony_ci		    FM_CMD_MSG_HDR_SIZE + ((payload == NULL) ? 0 : payload_len);
39862306a36Sopenharmony_ci	else
39962306a36Sopenharmony_ci		size = payload_len;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	skb = alloc_skb(size, GFP_ATOMIC);
40262306a36Sopenharmony_ci	if (!skb) {
40362306a36Sopenharmony_ci		fmerr("No memory to create new SKB\n");
40462306a36Sopenharmony_ci		return -ENOMEM;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	/*
40762306a36Sopenharmony_ci	 * Don't fill FM header info for the commands which come from
40862306a36Sopenharmony_ci	 * FM firmware file.
40962306a36Sopenharmony_ci	 */
41062306a36Sopenharmony_ci	if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) ||
41162306a36Sopenharmony_ci			test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
41262306a36Sopenharmony_ci		/* Fill command header info */
41362306a36Sopenharmony_ci		hdr = skb_put(skb, FM_CMD_MSG_HDR_SIZE);
41462306a36Sopenharmony_ci		hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER;	/* 0x08 */
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		/* 3 (fm_opcode,rd_wr,dlen) + payload len) */
41762306a36Sopenharmony_ci		hdr->len = ((payload == NULL) ? 0 : payload_len) + 3;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		/* FM opcode */
42062306a36Sopenharmony_ci		hdr->op = fm_op;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		/* read/write type */
42362306a36Sopenharmony_ci		hdr->rd_wr = type;
42462306a36Sopenharmony_ci		hdr->dlen = payload_len;
42562306a36Sopenharmony_ci		fm_cb(skb)->fm_op = fm_op;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		/*
42862306a36Sopenharmony_ci		 * If firmware download has finished and the command is
42962306a36Sopenharmony_ci		 * not a read command then payload is != NULL - a write
43062306a36Sopenharmony_ci		 * command with u16 payload - convert to be16
43162306a36Sopenharmony_ci		 */
43262306a36Sopenharmony_ci		if (payload != NULL)
43362306a36Sopenharmony_ci			*(__be16 *)payload = cpu_to_be16(*(u16 *)payload);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	} else if (payload != NULL) {
43662306a36Sopenharmony_ci		fm_cb(skb)->fm_op = *((u8 *)payload + 2);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	if (payload != NULL)
43962306a36Sopenharmony_ci		skb_put_data(skb, payload, payload_len);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	fm_cb(skb)->completion = wait_completion;
44262306a36Sopenharmony_ci	skb_queue_tail(&fmdev->tx_q, skb);
44362306a36Sopenharmony_ci	tasklet_schedule(&fmdev->tx_task);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/* Sends FM Channel-8 command to the chip and waits for the response */
44962306a36Sopenharmony_ciint fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
45062306a36Sopenharmony_ci		unsigned int payload_len, void *response, int *response_len)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct sk_buff *skb;
45362306a36Sopenharmony_ci	struct fm_event_msg_hdr *evt_hdr;
45462306a36Sopenharmony_ci	unsigned long flags;
45562306a36Sopenharmony_ci	int ret;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	init_completion(&fmdev->maintask_comp);
45862306a36Sopenharmony_ci	ret = fm_send_cmd(fmdev, fm_op, type, payload, payload_len,
45962306a36Sopenharmony_ci			    &fmdev->maintask_comp);
46062306a36Sopenharmony_ci	if (ret)
46162306a36Sopenharmony_ci		return ret;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&fmdev->maintask_comp,
46462306a36Sopenharmony_ci					 FM_DRV_TX_TIMEOUT)) {
46562306a36Sopenharmony_ci		fmerr("Timeout(%d sec),didn't get regcompletion signal from RX tasklet\n",
46662306a36Sopenharmony_ci			   jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
46762306a36Sopenharmony_ci		return -ETIMEDOUT;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	if (!fmdev->resp_skb) {
47062306a36Sopenharmony_ci		fmerr("Response SKB is missing\n");
47162306a36Sopenharmony_ci		return -EFAULT;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
47462306a36Sopenharmony_ci	skb = fmdev->resp_skb;
47562306a36Sopenharmony_ci	fmdev->resp_skb = NULL;
47662306a36Sopenharmony_ci	spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	evt_hdr = (void *)skb->data;
47962306a36Sopenharmony_ci	if (evt_hdr->status != 0) {
48062306a36Sopenharmony_ci		fmerr("Received event pkt status(%d) is not zero\n",
48162306a36Sopenharmony_ci			   evt_hdr->status);
48262306a36Sopenharmony_ci		kfree_skb(skb);
48362306a36Sopenharmony_ci		return -EIO;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	/* Send response data to caller */
48662306a36Sopenharmony_ci	if (response != NULL && response_len != NULL && evt_hdr->dlen &&
48762306a36Sopenharmony_ci	    evt_hdr->dlen <= payload_len) {
48862306a36Sopenharmony_ci		/* Skip header info and copy only response data */
48962306a36Sopenharmony_ci		skb_pull(skb, sizeof(struct fm_event_msg_hdr));
49062306a36Sopenharmony_ci		memcpy(response, skb->data, evt_hdr->dlen);
49162306a36Sopenharmony_ci		*response_len = evt_hdr->dlen;
49262306a36Sopenharmony_ci	} else if (response_len != NULL && evt_hdr->dlen == 0) {
49362306a36Sopenharmony_ci		*response_len = 0;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	kfree_skb(skb);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/* --- Helper functions used in FM interrupt handlers ---*/
50162306a36Sopenharmony_cistatic inline int check_cmdresp_status(struct fmdev *fmdev,
50262306a36Sopenharmony_ci		struct sk_buff **skb)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct fm_event_msg_hdr *fm_evt_hdr;
50562306a36Sopenharmony_ci	unsigned long flags;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	del_timer(&fmdev->irq_info.timer);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
51062306a36Sopenharmony_ci	*skb = fmdev->resp_skb;
51162306a36Sopenharmony_ci	fmdev->resp_skb = NULL;
51262306a36Sopenharmony_ci	spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	fm_evt_hdr = (void *)(*skb)->data;
51562306a36Sopenharmony_ci	if (fm_evt_hdr->status != 0) {
51662306a36Sopenharmony_ci		fmerr("irq: opcode %x response status is not zero Initiating irq recovery process\n",
51762306a36Sopenharmony_ci				fm_evt_hdr->op);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT);
52062306a36Sopenharmony_ci		return -1;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic inline void fm_irq_common_cmd_resp_helper(struct fmdev *fmdev, u8 stage)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct sk_buff *skb;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (!check_cmdresp_status(fmdev, &skb))
53162306a36Sopenharmony_ci		fm_irq_call_stage(fmdev, stage);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/*
53562306a36Sopenharmony_ci * Interrupt process timeout handler.
53662306a36Sopenharmony_ci * One of the irq handler did not get proper response from the chip. So take
53762306a36Sopenharmony_ci * recovery action here. FM interrupts are disabled in the beginning of
53862306a36Sopenharmony_ci * interrupt process. Therefore reset stage index to re-enable default
53962306a36Sopenharmony_ci * interrupts. So that next interrupt will be processed as usual.
54062306a36Sopenharmony_ci */
54162306a36Sopenharmony_cistatic void int_timeout_handler(struct timer_list *t)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct fmdev *fmdev;
54462306a36Sopenharmony_ci	struct fm_irq *fmirq;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	fmdbg("irq: timeout,trying to re-enable fm interrupts\n");
54762306a36Sopenharmony_ci	fmdev = from_timer(fmdev, t, irq_info.timer);
54862306a36Sopenharmony_ci	fmirq = &fmdev->irq_info;
54962306a36Sopenharmony_ci	fmirq->retry++;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (fmirq->retry > FM_IRQ_TIMEOUT_RETRY_MAX) {
55262306a36Sopenharmony_ci		/* Stop recovery action (interrupt reenable process) and
55362306a36Sopenharmony_ci		 * reset stage index & retry count values */
55462306a36Sopenharmony_ci		fmirq->stage = 0;
55562306a36Sopenharmony_ci		fmirq->retry = 0;
55662306a36Sopenharmony_ci		fmerr("Recovery action failed duringirq processing, max retry reached\n");
55762306a36Sopenharmony_ci		return;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci/* --------- FM interrupt handlers ------------*/
56362306a36Sopenharmony_cistatic void fm_irq_send_flag_getcmd(struct fmdev *fmdev)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	u16 flag;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Send FLAG_GET command , to know the source of interrupt */
56862306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, sizeof(flag), NULL))
56962306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_HANDLE_FLAG_GETCMD_RESP_IDX);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct sk_buff *skb;
57562306a36Sopenharmony_ci	struct fm_event_msg_hdr *fm_evt_hdr;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (check_cmdresp_status(fmdev, &skb))
57862306a36Sopenharmony_ci		return;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	fm_evt_hdr = (void *)skb->data;
58162306a36Sopenharmony_ci	if (fm_evt_hdr->dlen > sizeof(fmdev->irq_info.flag))
58262306a36Sopenharmony_ci		return;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* Skip header info and copy only response data */
58562306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct fm_event_msg_hdr));
58662306a36Sopenharmony_ci	memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	fmdev->irq_info.flag = be16_to_cpu((__force __be16)fmdev->irq_info.flag);
58962306a36Sopenharmony_ci	fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* Continue next function in interrupt handler table */
59262306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_HW_MAL_FUNC_IDX);
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void fm_irq_handle_hw_malfunction(struct fmdev *fmdev)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	if (fmdev->irq_info.flag & FM_MAL_EVENT & fmdev->irq_info.mask)
59862306a36Sopenharmony_ci		fmerr("irq: HW MAL int received - do nothing\n");
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Continue next function in interrupt handler table */
60162306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_RDS_START_IDX);
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void fm_irq_handle_rds_start(struct fmdev *fmdev)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	if (fmdev->irq_info.flag & FM_RDS_EVENT & fmdev->irq_info.mask) {
60762306a36Sopenharmony_ci		fmdbg("irq: rds threshold reached\n");
60862306a36Sopenharmony_ci		fmdev->irq_info.stage = FM_RDS_SEND_RDS_GETCMD_IDX;
60962306a36Sopenharmony_ci	} else {
61062306a36Sopenharmony_ci		/* Continue next function in interrupt handler table */
61162306a36Sopenharmony_ci		fmdev->irq_info.stage = FM_HW_TUNE_OP_ENDED_IDX;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	fm_irq_call(fmdev);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic void fm_irq_send_rdsdata_getcmd(struct fmdev *fmdev)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	/* Send the command to read RDS data from the chip */
62062306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, RDS_DATA_GET, REG_RD, NULL,
62162306a36Sopenharmony_ci			    (FM_RX_RDS_FIFO_THRESHOLD * 3), NULL))
62262306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/* Keeps track of current RX channel AF (Alternate Frequency) */
62662306a36Sopenharmony_cistatic void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct tuned_station_info *stat_info = &fmdev->rx.stat_info;
62962306a36Sopenharmony_ci	u8 reg_idx = fmdev->rx.region.fm_band;
63062306a36Sopenharmony_ci	u8 index;
63162306a36Sopenharmony_ci	u32 freq;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* First AF indicates the number of AF follows. Reset the list */
63462306a36Sopenharmony_ci	if ((af >= FM_RDS_1_AF_FOLLOWS) && (af <= FM_RDS_25_AF_FOLLOWS)) {
63562306a36Sopenharmony_ci		fmdev->rx.stat_info.af_list_max = (af - FM_RDS_1_AF_FOLLOWS + 1);
63662306a36Sopenharmony_ci		fmdev->rx.stat_info.afcache_size = 0;
63762306a36Sopenharmony_ci		fmdbg("No of expected AF : %d\n", fmdev->rx.stat_info.af_list_max);
63862306a36Sopenharmony_ci		return;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (af < FM_RDS_MIN_AF)
64262306a36Sopenharmony_ci		return;
64362306a36Sopenharmony_ci	if (reg_idx == FM_BAND_EUROPE_US && af > FM_RDS_MAX_AF)
64462306a36Sopenharmony_ci		return;
64562306a36Sopenharmony_ci	if (reg_idx == FM_BAND_JAPAN && af > FM_RDS_MAX_AF_JAPAN)
64662306a36Sopenharmony_ci		return;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	freq = fmdev->rx.region.bot_freq + (af * 100);
64962306a36Sopenharmony_ci	if (freq == fmdev->rx.freq) {
65062306a36Sopenharmony_ci		fmdbg("Current freq(%d) is matching with received AF(%d)\n",
65162306a36Sopenharmony_ci				fmdev->rx.freq, freq);
65262306a36Sopenharmony_ci		return;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci	/* Do check in AF cache */
65562306a36Sopenharmony_ci	for (index = 0; index < stat_info->afcache_size; index++) {
65662306a36Sopenharmony_ci		if (stat_info->af_cache[index] == freq)
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	/* Reached the limit of the list - ignore the next AF */
66062306a36Sopenharmony_ci	if (index == stat_info->af_list_max) {
66162306a36Sopenharmony_ci		fmdbg("AF cache is full\n");
66262306a36Sopenharmony_ci		return;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci	/*
66562306a36Sopenharmony_ci	 * If we reached the end of the list then this AF is not
66662306a36Sopenharmony_ci	 * in the list - add it.
66762306a36Sopenharmony_ci	 */
66862306a36Sopenharmony_ci	if (index == stat_info->afcache_size) {
66962306a36Sopenharmony_ci		fmdbg("Storing AF %d to cache index %d\n", freq, index);
67062306a36Sopenharmony_ci		stat_info->af_cache[index] = freq;
67162306a36Sopenharmony_ci		stat_info->afcache_size++;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci/*
67662306a36Sopenharmony_ci * Converts RDS buffer data from big endian format
67762306a36Sopenharmony_ci * to little endian format.
67862306a36Sopenharmony_ci */
67962306a36Sopenharmony_cistatic void fm_rdsparse_swapbytes(struct fmdev *fmdev,
68062306a36Sopenharmony_ci		struct fm_rdsdata_format *rds_format)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	u8 index = 0;
68362306a36Sopenharmony_ci	u8 *rds_buff;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/*
68662306a36Sopenharmony_ci	 * Since in Orca the 2 RDS Data bytes are in little endian and
68762306a36Sopenharmony_ci	 * in Dolphin they are in big endian, the parsing of the RDS data
68862306a36Sopenharmony_ci	 * is chip dependent
68962306a36Sopenharmony_ci	 */
69062306a36Sopenharmony_ci	if (fmdev->asci_id != 0x6350) {
69162306a36Sopenharmony_ci		rds_buff = &rds_format->data.groupdatabuff.buff[0];
69262306a36Sopenharmony_ci		while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) {
69362306a36Sopenharmony_ci			swap(rds_buff[index], rds_buff[index + 1]);
69462306a36Sopenharmony_ci			index += 2;
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct sk_buff *skb;
70262306a36Sopenharmony_ci	struct fm_rdsdata_format rds_fmt;
70362306a36Sopenharmony_ci	struct fm_rds *rds = &fmdev->rx.rds;
70462306a36Sopenharmony_ci	unsigned long group_idx, flags;
70562306a36Sopenharmony_ci	u8 *rds_data, meta_data, tmpbuf[FM_RDS_BLK_SIZE];
70662306a36Sopenharmony_ci	u8 type, blk_idx, idx;
70762306a36Sopenharmony_ci	u16 cur_picode;
70862306a36Sopenharmony_ci	u32 rds_len;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (check_cmdresp_status(fmdev, &skb))
71162306a36Sopenharmony_ci		return;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/* Skip header info */
71462306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct fm_event_msg_hdr));
71562306a36Sopenharmony_ci	rds_data = skb->data;
71662306a36Sopenharmony_ci	rds_len = skb->len;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Parse the RDS data */
71962306a36Sopenharmony_ci	while (rds_len >= FM_RDS_BLK_SIZE) {
72062306a36Sopenharmony_ci		meta_data = rds_data[2];
72162306a36Sopenharmony_ci		/* Get the type: 0=A, 1=B, 2=C, 3=C', 4=D, 5=E */
72262306a36Sopenharmony_ci		type = (meta_data & 0x07);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		/* Transform the blk type into index sequence (0, 1, 2, 3, 4) */
72562306a36Sopenharmony_ci		blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1));
72662306a36Sopenharmony_ci		fmdbg("Block index:%d(%s)\n", blk_idx,
72762306a36Sopenharmony_ci			   (meta_data & FM_RDS_STATUS_ERR_MASK) ? "Bad" : "Ok");
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0)
73062306a36Sopenharmony_ci			break;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		if (blk_idx > FM_RDS_BLK_IDX_D) {
73362306a36Sopenharmony_ci			fmdbg("Block sequence mismatch\n");
73462306a36Sopenharmony_ci			rds->last_blk_idx = -1;
73562306a36Sopenharmony_ci			break;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		/* Skip checkword (control) byte and copy only data byte */
73962306a36Sopenharmony_ci		idx = array_index_nospec(blk_idx * (FM_RDS_BLK_SIZE - 1),
74062306a36Sopenharmony_ci					 FM_RX_RDS_INFO_FIELD_MAX - (FM_RDS_BLK_SIZE - 1));
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		memcpy(&rds_fmt.data.groupdatabuff.buff[idx], rds_data,
74362306a36Sopenharmony_ci		       FM_RDS_BLK_SIZE - 1);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		rds->last_blk_idx = blk_idx;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		/* If completed a whole group then handle it */
74862306a36Sopenharmony_ci		if (blk_idx == FM_RDS_BLK_IDX_D) {
74962306a36Sopenharmony_ci			fmdbg("Good block received\n");
75062306a36Sopenharmony_ci			fm_rdsparse_swapbytes(fmdev, &rds_fmt);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci			/*
75362306a36Sopenharmony_ci			 * Extract PI code and store in local cache.
75462306a36Sopenharmony_ci			 * We need this during AF switch processing.
75562306a36Sopenharmony_ci			 */
75662306a36Sopenharmony_ci			cur_picode = be16_to_cpu((__force __be16)rds_fmt.data.groupgeneral.pidata);
75762306a36Sopenharmony_ci			if (fmdev->rx.stat_info.picode != cur_picode)
75862306a36Sopenharmony_ci				fmdev->rx.stat_info.picode = cur_picode;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci			fmdbg("picode:%d\n", cur_picode);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci			group_idx = (rds_fmt.data.groupgeneral.blk_b[0] >> 3);
76362306a36Sopenharmony_ci			fmdbg("(fmdrv):Group:%ld%s\n", group_idx/2,
76462306a36Sopenharmony_ci					(group_idx % 2) ? "B" : "A");
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci			group_idx = 1 << (rds_fmt.data.groupgeneral.blk_b[0] >> 3);
76762306a36Sopenharmony_ci			if (group_idx == FM_RDS_GROUP_TYPE_MASK_0A) {
76862306a36Sopenharmony_ci				fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[0]);
76962306a36Sopenharmony_ci				fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[1]);
77062306a36Sopenharmony_ci			}
77162306a36Sopenharmony_ci		}
77262306a36Sopenharmony_ci		rds_len -= FM_RDS_BLK_SIZE;
77362306a36Sopenharmony_ci		rds_data += FM_RDS_BLK_SIZE;
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* Copy raw rds data to internal rds buffer */
77762306a36Sopenharmony_ci	rds_data = skb->data;
77862306a36Sopenharmony_ci	rds_len = skb->len;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
78162306a36Sopenharmony_ci	while (rds_len > 0) {
78262306a36Sopenharmony_ci		/*
78362306a36Sopenharmony_ci		 * Fill RDS buffer as per V4L2 specification.
78462306a36Sopenharmony_ci		 * Store control byte
78562306a36Sopenharmony_ci		 */
78662306a36Sopenharmony_ci		type = (rds_data[2] & 0x07);
78762306a36Sopenharmony_ci		blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1));
78862306a36Sopenharmony_ci		tmpbuf[2] = blk_idx;	/* Offset name */
78962306a36Sopenharmony_ci		tmpbuf[2] |= blk_idx << 3;	/* Received offset */
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		/* Store data byte */
79262306a36Sopenharmony_ci		tmpbuf[0] = rds_data[0];
79362306a36Sopenharmony_ci		tmpbuf[1] = rds_data[1];
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		memcpy(&rds->buff[rds->wr_idx], &tmpbuf, FM_RDS_BLK_SIZE);
79662306a36Sopenharmony_ci		rds->wr_idx = (rds->wr_idx + FM_RDS_BLK_SIZE) % rds->buf_size;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		/* Check for overflow & start over */
79962306a36Sopenharmony_ci		if (rds->wr_idx == rds->rd_idx) {
80062306a36Sopenharmony_ci			fmdbg("RDS buffer overflow\n");
80162306a36Sopenharmony_ci			rds->wr_idx = 0;
80262306a36Sopenharmony_ci			rds->rd_idx = 0;
80362306a36Sopenharmony_ci			break;
80462306a36Sopenharmony_ci		}
80562306a36Sopenharmony_ci		rds_len -= FM_RDS_BLK_SIZE;
80662306a36Sopenharmony_ci		rds_data += FM_RDS_BLK_SIZE;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci	spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* Wakeup read queue */
81162306a36Sopenharmony_ci	if (rds->wr_idx != rds->rd_idx)
81262306a36Sopenharmony_ci		wake_up_interruptible(&rds->read_queue);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_RDS_FINISH_IDX);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic void fm_irq_handle_rds_finish(struct fmdev *fmdev)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_HW_TUNE_OP_ENDED_IDX);
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic void fm_irq_handle_tune_op_ended(struct fmdev *fmdev)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	if (fmdev->irq_info.flag & (FM_FR_EVENT | FM_BL_EVENT) & fmdev->
82562306a36Sopenharmony_ci	    irq_info.mask) {
82662306a36Sopenharmony_ci		fmdbg("irq: tune ended/bandlimit reached\n");
82762306a36Sopenharmony_ci		if (test_and_clear_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag)) {
82862306a36Sopenharmony_ci			fmdev->irq_info.stage = FM_AF_JUMP_RD_FREQ_IDX;
82962306a36Sopenharmony_ci		} else {
83062306a36Sopenharmony_ci			complete(&fmdev->maintask_comp);
83162306a36Sopenharmony_ci			fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX;
83262306a36Sopenharmony_ci		}
83362306a36Sopenharmony_ci	} else
83462306a36Sopenharmony_ci		fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	fm_irq_call(fmdev);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic void fm_irq_handle_power_enb(struct fmdev *fmdev)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	if (fmdev->irq_info.flag & FM_POW_ENB_EVENT) {
84262306a36Sopenharmony_ci		fmdbg("irq: Power Enabled/Disabled\n");
84362306a36Sopenharmony_ci		complete(&fmdev->maintask_comp);
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_LOW_RSSI_START_IDX);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic void fm_irq_handle_low_rssi_start(struct fmdev *fmdev)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	if ((fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) &&
85262306a36Sopenharmony_ci	    (fmdev->irq_info.flag & FM_LEV_EVENT & fmdev->irq_info.mask) &&
85362306a36Sopenharmony_ci	    (fmdev->rx.freq != FM_UNDEFINED_FREQ) &&
85462306a36Sopenharmony_ci	    (fmdev->rx.stat_info.afcache_size != 0)) {
85562306a36Sopenharmony_ci		fmdbg("irq: rssi level has fallen below threshold level\n");
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		/* Disable further low RSSI interrupts */
85862306a36Sopenharmony_ci		fmdev->irq_info.mask &= ~FM_LEV_EVENT;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		fmdev->rx.afjump_idx = 0;
86162306a36Sopenharmony_ci		fmdev->rx.freq_before_jump = fmdev->rx.freq;
86262306a36Sopenharmony_ci		fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX;
86362306a36Sopenharmony_ci	} else {
86462306a36Sopenharmony_ci		/* Continue next function in interrupt handler table */
86562306a36Sopenharmony_ci		fmdev->irq_info.stage = FM_SEND_INTMSK_CMD_IDX;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	fm_irq_call(fmdev);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic void fm_irq_afjump_set_pi(struct fmdev *fmdev)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	u16 payload;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	/* Set PI code - must be updated if the AF list is not empty */
87662306a36Sopenharmony_ci	payload = fmdev->rx.stat_info.picode;
87762306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, RDS_PI_SET, REG_WR, &payload, sizeof(payload), NULL))
87862306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_RESP_IDX);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic void fm_irq_handle_set_pi_resp(struct fmdev *fmdev)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SETPI_MASK_IDX);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/*
88762306a36Sopenharmony_ci * Set PI mask.
88862306a36Sopenharmony_ci * 0xFFFF = Enable PI code matching
88962306a36Sopenharmony_ci * 0x0000 = Disable PI code matching
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_cistatic void fm_irq_afjump_set_pimask(struct fmdev *fmdev)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	u16 payload;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	payload = 0x0000;
89662306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, RDS_PI_MASK_SET, REG_WR, &payload, sizeof(payload), NULL))
89762306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX);
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic void fm_irq_handle_set_pimask_resp(struct fmdev *fmdev)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SET_AF_FREQ_IDX);
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void fm_irq_afjump_setfreq(struct fmdev *fmdev)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	u16 frq_index;
90862306a36Sopenharmony_ci	u16 payload;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	fmdbg("Switch to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]);
91162306a36Sopenharmony_ci	frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] -
91262306a36Sopenharmony_ci	     fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	payload = frq_index;
91562306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, AF_FREQ_SET, REG_WR, &payload, sizeof(payload), NULL))
91662306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX);
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic void fm_irq_handle_setfreq_resp(struct fmdev *fmdev)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_ENABLE_INT_IDX);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic void fm_irq_afjump_enableint(struct fmdev *fmdev)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	u16 payload;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* Enable FR (tuning operation ended) interrupt */
92962306a36Sopenharmony_ci	payload = FM_FR_EVENT;
93062306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, sizeof(payload), NULL))
93162306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_AF_JUMP_ENABLE_INT_RESP_IDX);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic void fm_irq_afjump_enableint_resp(struct fmdev *fmdev)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_START_AFJUMP_IDX);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic void fm_irq_start_afjump(struct fmdev *fmdev)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	u16 payload;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	payload = FM_TUNER_AF_JUMP_MODE;
94462306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
94562306a36Sopenharmony_ci			sizeof(payload), NULL))
94662306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX);
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic void fm_irq_handle_start_afjump_resp(struct fmdev *fmdev)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct sk_buff *skb;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (check_cmdresp_status(fmdev, &skb))
95462306a36Sopenharmony_ci		return;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX;
95762306a36Sopenharmony_ci	set_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag);
95862306a36Sopenharmony_ci	clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic void fm_irq_afjump_rd_freq(struct fmdev *fmdev)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	u16 payload;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, sizeof(payload), NULL))
96662306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_AF_JUMP_RD_FREQ_RESP_IDX);
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct sk_buff *skb;
97262306a36Sopenharmony_ci	u16 read_freq;
97362306a36Sopenharmony_ci	u32 curr_freq, jumped_freq;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (check_cmdresp_status(fmdev, &skb))
97662306a36Sopenharmony_ci		return;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* Skip header info and copy only response data */
97962306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct fm_event_msg_hdr));
98062306a36Sopenharmony_ci	memcpy(&read_freq, skb->data, sizeof(read_freq));
98162306a36Sopenharmony_ci	read_freq = be16_to_cpu((__force __be16)read_freq);
98262306a36Sopenharmony_ci	curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx];
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	/* If the frequency was changed the jump succeeded */
98762306a36Sopenharmony_ci	if ((curr_freq != fmdev->rx.freq_before_jump) && (curr_freq == jumped_freq)) {
98862306a36Sopenharmony_ci		fmdbg("Successfully switched to alternate freq %d\n", curr_freq);
98962306a36Sopenharmony_ci		fmdev->rx.freq = curr_freq;
99062306a36Sopenharmony_ci		fm_rx_reset_rds_cache(fmdev);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		/* AF feature is on, enable low level RSSI interrupt */
99362306a36Sopenharmony_ci		if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
99462306a36Sopenharmony_ci			fmdev->irq_info.mask |= FM_LEV_EVENT;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci		fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX;
99762306a36Sopenharmony_ci	} else {		/* jump to the next freq in the AF list */
99862306a36Sopenharmony_ci		fmdev->rx.afjump_idx++;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		/* If we reached the end of the list - stop searching */
100162306a36Sopenharmony_ci		if (fmdev->rx.afjump_idx >= fmdev->rx.stat_info.afcache_size) {
100262306a36Sopenharmony_ci			fmdbg("AF switch processing failed\n");
100362306a36Sopenharmony_ci			fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX;
100462306a36Sopenharmony_ci		} else {	/* AF List is not over - try next one */
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci			fmdbg("Trying next freq in AF cache\n");
100762306a36Sopenharmony_ci			fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX;
100862306a36Sopenharmony_ci		}
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci	fm_irq_call(fmdev);
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic void fm_irq_handle_low_rssi_finish(struct fmdev *fmdev)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX);
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic void fm_irq_send_intmsk_cmd(struct fmdev *fmdev)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	u16 payload;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* Re-enable FM interrupts */
102362306a36Sopenharmony_ci	payload = fmdev->irq_info.mask;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
102662306a36Sopenharmony_ci			sizeof(payload), NULL))
102762306a36Sopenharmony_ci		fm_irq_timeout_stage(fmdev, FM_HANDLE_INTMSK_CMD_RESP_IDX);
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct sk_buff *skb;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (check_cmdresp_status(fmdev, &skb))
103562306a36Sopenharmony_ci		return;
103662306a36Sopenharmony_ci	/*
103762306a36Sopenharmony_ci	 * This is last function in interrupt table to be executed.
103862306a36Sopenharmony_ci	 * So, reset stage index to 0.
103962306a36Sopenharmony_ci	 */
104062306a36Sopenharmony_ci	fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* Start processing any pending interrupt */
104362306a36Sopenharmony_ci	if (test_and_clear_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag))
104462306a36Sopenharmony_ci		fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev);
104562306a36Sopenharmony_ci	else
104662306a36Sopenharmony_ci		clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci/* Returns availability of RDS data in internal buffer */
105062306a36Sopenharmony_ciint fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file,
105162306a36Sopenharmony_ci				struct poll_table_struct *pts)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	poll_wait(file, &fmdev->rx.rds.read_queue, pts);
105462306a36Sopenharmony_ci	if (fmdev->rx.rds.rd_idx != fmdev->rx.rds.wr_idx)
105562306a36Sopenharmony_ci		return 0;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	return -EAGAIN;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci/* Copies RDS data from internal buffer to user buffer */
106162306a36Sopenharmony_ciint fmc_transfer_rds_from_internal_buff(struct fmdev *fmdev, struct file *file,
106262306a36Sopenharmony_ci		u8 __user *buf, size_t count)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	u32 block_count;
106562306a36Sopenharmony_ci	u8 tmpbuf[FM_RDS_BLK_SIZE];
106662306a36Sopenharmony_ci	unsigned long flags;
106762306a36Sopenharmony_ci	int ret;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) {
107062306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
107162306a36Sopenharmony_ci			return -EWOULDBLOCK;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		ret = wait_event_interruptible(fmdev->rx.rds.read_queue,
107462306a36Sopenharmony_ci				(fmdev->rx.rds.wr_idx != fmdev->rx.rds.rd_idx));
107562306a36Sopenharmony_ci		if (ret)
107662306a36Sopenharmony_ci			return -EINTR;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/* Calculate block count from byte count */
108062306a36Sopenharmony_ci	count /= FM_RDS_BLK_SIZE;
108162306a36Sopenharmony_ci	block_count = 0;
108262306a36Sopenharmony_ci	ret = 0;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	while (block_count < count) {
108562306a36Sopenharmony_ci		spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) {
108862306a36Sopenharmony_ci			spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
108962306a36Sopenharmony_ci			break;
109062306a36Sopenharmony_ci		}
109162306a36Sopenharmony_ci		memcpy(tmpbuf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx],
109262306a36Sopenharmony_ci					FM_RDS_BLK_SIZE);
109362306a36Sopenharmony_ci		fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE;
109462306a36Sopenharmony_ci		if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size)
109562306a36Sopenharmony_ci			fmdev->rx.rds.rd_idx = 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		if (copy_to_user(buf, tmpbuf, FM_RDS_BLK_SIZE))
110062306a36Sopenharmony_ci			break;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci		block_count++;
110362306a36Sopenharmony_ci		buf += FM_RDS_BLK_SIZE;
110462306a36Sopenharmony_ci		ret += FM_RDS_BLK_SIZE;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci	return ret;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ciint fmc_set_freq(struct fmdev *fmdev, u32 freq_to_set)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	switch (fmdev->curr_fmmode) {
111262306a36Sopenharmony_ci	case FM_MODE_RX:
111362306a36Sopenharmony_ci		return fm_rx_set_freq(fmdev, freq_to_set);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	case FM_MODE_TX:
111662306a36Sopenharmony_ci		return fm_tx_set_freq(fmdev, freq_to_set);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	default:
111962306a36Sopenharmony_ci		return -EINVAL;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ciint fmc_get_freq(struct fmdev *fmdev, u32 *cur_tuned_frq)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	if (fmdev->rx.freq == FM_UNDEFINED_FREQ) {
112662306a36Sopenharmony_ci		fmerr("RX frequency is not set\n");
112762306a36Sopenharmony_ci		return -EPERM;
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci	if (cur_tuned_frq == NULL) {
113062306a36Sopenharmony_ci		fmerr("Invalid memory\n");
113162306a36Sopenharmony_ci		return -ENOMEM;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	switch (fmdev->curr_fmmode) {
113562306a36Sopenharmony_ci	case FM_MODE_RX:
113662306a36Sopenharmony_ci		*cur_tuned_frq = fmdev->rx.freq;
113762306a36Sopenharmony_ci		return 0;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	case FM_MODE_TX:
114062306a36Sopenharmony_ci		*cur_tuned_frq = 0;	/* TODO : Change this later */
114162306a36Sopenharmony_ci		return 0;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	default:
114462306a36Sopenharmony_ci		return -EINVAL;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ciint fmc_set_region(struct fmdev *fmdev, u8 region_to_set)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	switch (fmdev->curr_fmmode) {
115262306a36Sopenharmony_ci	case FM_MODE_RX:
115362306a36Sopenharmony_ci		return fm_rx_set_region(fmdev, region_to_set);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	case FM_MODE_TX:
115662306a36Sopenharmony_ci		return fm_tx_set_region(fmdev, region_to_set);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	default:
115962306a36Sopenharmony_ci		return -EINVAL;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ciint fmc_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	switch (fmdev->curr_fmmode) {
116662306a36Sopenharmony_ci	case FM_MODE_RX:
116762306a36Sopenharmony_ci		return fm_rx_set_mute_mode(fmdev, mute_mode_toset);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	case FM_MODE_TX:
117062306a36Sopenharmony_ci		return fm_tx_set_mute_mode(fmdev, mute_mode_toset);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	default:
117362306a36Sopenharmony_ci		return -EINVAL;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ciint fmc_set_stereo_mono(struct fmdev *fmdev, u16 mode)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	switch (fmdev->curr_fmmode) {
118062306a36Sopenharmony_ci	case FM_MODE_RX:
118162306a36Sopenharmony_ci		return fm_rx_set_stereo_mono(fmdev, mode);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	case FM_MODE_TX:
118462306a36Sopenharmony_ci		return fm_tx_set_stereo_mono(fmdev, mode);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	default:
118762306a36Sopenharmony_ci		return -EINVAL;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ciint fmc_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	switch (fmdev->curr_fmmode) {
119462306a36Sopenharmony_ci	case FM_MODE_RX:
119562306a36Sopenharmony_ci		return fm_rx_set_rds_mode(fmdev, rds_en_dis);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	case FM_MODE_TX:
119862306a36Sopenharmony_ci		return fm_tx_set_rds_mode(fmdev, rds_en_dis);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	default:
120162306a36Sopenharmony_ci		return -EINVAL;
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci/* Sends power off command to the chip */
120662306a36Sopenharmony_cistatic int fm_power_down(struct fmdev *fmdev)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	u16 payload;
120962306a36Sopenharmony_ci	int ret;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
121262306a36Sopenharmony_ci		fmerr("FM core is not ready\n");
121362306a36Sopenharmony_ci		return -EPERM;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci	if (fmdev->curr_fmmode == FM_MODE_OFF) {
121662306a36Sopenharmony_ci		fmdbg("FM chip is already in OFF state\n");
121762306a36Sopenharmony_ci		return 0;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	payload = 0x0;
122162306a36Sopenharmony_ci	ret = fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload,
122262306a36Sopenharmony_ci		sizeof(payload), NULL, NULL);
122362306a36Sopenharmony_ci	if (ret < 0)
122462306a36Sopenharmony_ci		return ret;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	return fmc_release(fmdev);
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci/* Reads init command from FM firmware file and loads to the chip */
123062306a36Sopenharmony_cistatic int fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	const struct firmware *fw_entry;
123362306a36Sopenharmony_ci	struct bts_header *fw_header;
123462306a36Sopenharmony_ci	struct bts_action *action;
123562306a36Sopenharmony_ci	struct bts_action_delay *delay;
123662306a36Sopenharmony_ci	u8 *fw_data;
123762306a36Sopenharmony_ci	int ret, fw_len;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	set_bit(FM_FW_DW_INPROGRESS, &fmdev->flag);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	ret = request_firmware(&fw_entry, fw_name,
124262306a36Sopenharmony_ci				&fmdev->radio_dev->dev);
124362306a36Sopenharmony_ci	if (ret < 0) {
124462306a36Sopenharmony_ci		fmerr("Unable to read firmware(%s) content\n", fw_name);
124562306a36Sopenharmony_ci		return ret;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci	fmdbg("Firmware(%s) length : %zu bytes\n", fw_name, fw_entry->size);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	fw_data = (void *)fw_entry->data;
125062306a36Sopenharmony_ci	fw_len = fw_entry->size;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	fw_header = (struct bts_header *)fw_data;
125362306a36Sopenharmony_ci	if (fw_header->magic != FM_FW_FILE_HEADER_MAGIC) {
125462306a36Sopenharmony_ci		fmerr("%s not a legal TI firmware file\n", fw_name);
125562306a36Sopenharmony_ci		ret = -EINVAL;
125662306a36Sopenharmony_ci		goto rel_fw;
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci	fmdbg("FW(%s) magic number : 0x%x\n", fw_name, fw_header->magic);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/* Skip file header info , we already verified it */
126162306a36Sopenharmony_ci	fw_data += sizeof(struct bts_header);
126262306a36Sopenharmony_ci	fw_len -= sizeof(struct bts_header);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	while (fw_data && fw_len > 0) {
126562306a36Sopenharmony_ci		action = (struct bts_action *)fw_data;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		switch (action->type) {
126862306a36Sopenharmony_ci		case ACTION_SEND_COMMAND:	/* Send */
126962306a36Sopenharmony_ci			ret = fmc_send_cmd(fmdev, 0, 0, action->data,
127062306a36Sopenharmony_ci					   action->size, NULL, NULL);
127162306a36Sopenharmony_ci			if (ret)
127262306a36Sopenharmony_ci				goto rel_fw;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci			break;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		case ACTION_DELAY:	/* Delay */
127762306a36Sopenharmony_ci			delay = (struct bts_action_delay *)action->data;
127862306a36Sopenharmony_ci			mdelay(delay->msec);
127962306a36Sopenharmony_ci			break;
128062306a36Sopenharmony_ci		}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci		fw_data += (sizeof(struct bts_action) + (action->size));
128362306a36Sopenharmony_ci		fw_len -= (sizeof(struct bts_action) + (action->size));
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci	fmdbg("Transferred only %d of %d bytes of the firmware to chip\n",
128662306a36Sopenharmony_ci	      fw_entry->size - fw_len, fw_entry->size);
128762306a36Sopenharmony_cirel_fw:
128862306a36Sopenharmony_ci	release_firmware(fw_entry);
128962306a36Sopenharmony_ci	clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return ret;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci/* Loads default RX configuration to the chip */
129562306a36Sopenharmony_cistatic int load_default_rx_configuration(struct fmdev *fmdev)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	int ret;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	ret = fm_rx_set_volume(fmdev, FM_DEFAULT_RX_VOLUME);
130062306a36Sopenharmony_ci	if (ret < 0)
130162306a36Sopenharmony_ci		return ret;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	return fm_rx_set_rssi_threshold(fmdev, FM_DEFAULT_RSSI_THRESHOLD);
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci/* Does FM power on sequence */
130762306a36Sopenharmony_cistatic int fm_power_up(struct fmdev *fmdev, u8 mode)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	u16 payload;
131062306a36Sopenharmony_ci	__be16 asic_id = 0, asic_ver = 0;
131162306a36Sopenharmony_ci	int resp_len, ret;
131262306a36Sopenharmony_ci	u8 fw_name[50];
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (mode >= FM_MODE_ENTRY_MAX) {
131562306a36Sopenharmony_ci		fmerr("Invalid firmware download option\n");
131662306a36Sopenharmony_ci		return -EINVAL;
131762306a36Sopenharmony_ci	}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/*
132062306a36Sopenharmony_ci	 * Initialize FM common module. FM GPIO toggling is
132162306a36Sopenharmony_ci	 * taken care in Shared Transport driver.
132262306a36Sopenharmony_ci	 */
132362306a36Sopenharmony_ci	ret = fmc_prepare(fmdev);
132462306a36Sopenharmony_ci	if (ret < 0) {
132562306a36Sopenharmony_ci		fmerr("Unable to prepare FM Common\n");
132662306a36Sopenharmony_ci		return ret;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	payload = FM_ENABLE;
133062306a36Sopenharmony_ci	if (fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload,
133162306a36Sopenharmony_ci			sizeof(payload), NULL, NULL))
133262306a36Sopenharmony_ci		goto rel;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* Allow the chip to settle down in Channel-8 mode */
133562306a36Sopenharmony_ci	msleep(20);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (fmc_send_cmd(fmdev, ASIC_ID_GET, REG_RD, NULL,
133862306a36Sopenharmony_ci			sizeof(asic_id), &asic_id, &resp_len))
133962306a36Sopenharmony_ci		goto rel;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if (fmc_send_cmd(fmdev, ASIC_VER_GET, REG_RD, NULL,
134262306a36Sopenharmony_ci			sizeof(asic_ver), &asic_ver, &resp_len))
134362306a36Sopenharmony_ci		goto rel;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	fmdbg("ASIC ID: 0x%x , ASIC Version: %d\n",
134662306a36Sopenharmony_ci		be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	sprintf(fw_name, "%s_%x.%d.bts", FM_FMC_FW_FILE_START,
134962306a36Sopenharmony_ci		be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	ret = fm_download_firmware(fmdev, fw_name);
135262306a36Sopenharmony_ci	if (ret < 0) {
135362306a36Sopenharmony_ci		fmdbg("Failed to download firmware file %s\n", fw_name);
135462306a36Sopenharmony_ci		goto rel;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci	sprintf(fw_name, "%s_%x.%d.bts", (mode == FM_MODE_RX) ?
135762306a36Sopenharmony_ci			FM_RX_FW_FILE_START : FM_TX_FW_FILE_START,
135862306a36Sopenharmony_ci			be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	ret = fm_download_firmware(fmdev, fw_name);
136162306a36Sopenharmony_ci	if (ret < 0) {
136262306a36Sopenharmony_ci		fmdbg("Failed to download firmware file %s\n", fw_name);
136362306a36Sopenharmony_ci		goto rel;
136462306a36Sopenharmony_ci	} else
136562306a36Sopenharmony_ci		return ret;
136662306a36Sopenharmony_cirel:
136762306a36Sopenharmony_ci	return fmc_release(fmdev);
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci/* Set FM Modes(TX, RX, OFF) */
137162306a36Sopenharmony_ciint fmc_set_mode(struct fmdev *fmdev, u8 fm_mode)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	int ret = 0;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	if (fm_mode >= FM_MODE_ENTRY_MAX) {
137662306a36Sopenharmony_ci		fmerr("Invalid FM mode\n");
137762306a36Sopenharmony_ci		return -EINVAL;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci	if (fmdev->curr_fmmode == fm_mode) {
138062306a36Sopenharmony_ci		fmdbg("Already fm is in mode(%d)\n", fm_mode);
138162306a36Sopenharmony_ci		return ret;
138262306a36Sopenharmony_ci	}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	switch (fm_mode) {
138562306a36Sopenharmony_ci	case FM_MODE_OFF:	/* OFF Mode */
138662306a36Sopenharmony_ci		ret = fm_power_down(fmdev);
138762306a36Sopenharmony_ci		if (ret < 0) {
138862306a36Sopenharmony_ci			fmerr("Failed to set OFF mode\n");
138962306a36Sopenharmony_ci			return ret;
139062306a36Sopenharmony_ci		}
139162306a36Sopenharmony_ci		break;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	case FM_MODE_TX:	/* TX Mode */
139462306a36Sopenharmony_ci	case FM_MODE_RX:	/* RX Mode */
139562306a36Sopenharmony_ci		/* Power down before switching to TX or RX mode */
139662306a36Sopenharmony_ci		if (fmdev->curr_fmmode != FM_MODE_OFF) {
139762306a36Sopenharmony_ci			ret = fm_power_down(fmdev);
139862306a36Sopenharmony_ci			if (ret < 0) {
139962306a36Sopenharmony_ci				fmerr("Failed to set OFF mode\n");
140062306a36Sopenharmony_ci				return ret;
140162306a36Sopenharmony_ci			}
140262306a36Sopenharmony_ci			msleep(30);
140362306a36Sopenharmony_ci		}
140462306a36Sopenharmony_ci		ret = fm_power_up(fmdev, fm_mode);
140562306a36Sopenharmony_ci		if (ret < 0) {
140662306a36Sopenharmony_ci			fmerr("Failed to load firmware\n");
140762306a36Sopenharmony_ci			return ret;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci	fmdev->curr_fmmode = fm_mode;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	/* Set default configuration */
141362306a36Sopenharmony_ci	if (fmdev->curr_fmmode == FM_MODE_RX) {
141462306a36Sopenharmony_ci		fmdbg("Loading default rx configuration..\n");
141562306a36Sopenharmony_ci		ret = load_default_rx_configuration(fmdev);
141662306a36Sopenharmony_ci		if (ret < 0)
141762306a36Sopenharmony_ci			fmerr("Failed to load default values\n");
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	return ret;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci/* Returns current FM mode (TX, RX, OFF) */
142462306a36Sopenharmony_ciint fmc_get_mode(struct fmdev *fmdev, u8 *fmmode)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
142762306a36Sopenharmony_ci		fmerr("FM core is not ready\n");
142862306a36Sopenharmony_ci		return -EPERM;
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci	if (fmmode == NULL) {
143162306a36Sopenharmony_ci		fmerr("Invalid memory\n");
143262306a36Sopenharmony_ci		return -ENOMEM;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	*fmmode = fmdev->curr_fmmode;
143662306a36Sopenharmony_ci	return 0;
143762306a36Sopenharmony_ci}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci/* Called by ST layer when FM packet is available */
144062306a36Sopenharmony_cistatic long fm_st_receive(void *arg, struct sk_buff *skb)
144162306a36Sopenharmony_ci{
144262306a36Sopenharmony_ci	struct fmdev *fmdev;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	fmdev = arg;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	if (skb == NULL) {
144762306a36Sopenharmony_ci		fmerr("Invalid SKB received from ST\n");
144862306a36Sopenharmony_ci		return -EFAULT;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) {
145262306a36Sopenharmony_ci		fmerr("Received SKB (%p) is not FM Channel 8 pkt\n", skb);
145362306a36Sopenharmony_ci		return -EINVAL;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	memcpy(skb_push(skb, 1), &skb->cb[0], 1);
145762306a36Sopenharmony_ci	skb_queue_tail(&fmdev->rx_q, skb);
145862306a36Sopenharmony_ci	tasklet_schedule(&fmdev->rx_task);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	return 0;
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci/*
146462306a36Sopenharmony_ci * Called by ST layer to indicate protocol registration completion
146562306a36Sopenharmony_ci * status.
146662306a36Sopenharmony_ci */
146762306a36Sopenharmony_cistatic void fm_st_reg_comp_cb(void *arg, int data)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct fmdev *fmdev;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	fmdev = (struct fmdev *)arg;
147262306a36Sopenharmony_ci	fmdev->streg_cbdata = data;
147362306a36Sopenharmony_ci	complete(&wait_for_fmdrv_reg_comp);
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci/*
147762306a36Sopenharmony_ci * This function will be called from FM V4L2 open function.
147862306a36Sopenharmony_ci * Register with ST driver and initialize driver data.
147962306a36Sopenharmony_ci */
148062306a36Sopenharmony_ciint fmc_prepare(struct fmdev *fmdev)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	static struct st_proto_s fm_st_proto;
148362306a36Sopenharmony_ci	int ret;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (test_bit(FM_CORE_READY, &fmdev->flag)) {
148662306a36Sopenharmony_ci		fmdbg("FM Core is already up\n");
148762306a36Sopenharmony_ci		return 0;
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	memset(&fm_st_proto, 0, sizeof(fm_st_proto));
149162306a36Sopenharmony_ci	fm_st_proto.recv = fm_st_receive;
149262306a36Sopenharmony_ci	fm_st_proto.match_packet = NULL;
149362306a36Sopenharmony_ci	fm_st_proto.reg_complete_cb = fm_st_reg_comp_cb;
149462306a36Sopenharmony_ci	fm_st_proto.write = NULL; /* TI ST driver will fill write pointer */
149562306a36Sopenharmony_ci	fm_st_proto.priv_data = fmdev;
149662306a36Sopenharmony_ci	fm_st_proto.chnl_id = 0x08;
149762306a36Sopenharmony_ci	fm_st_proto.max_frame_size = 0xff;
149862306a36Sopenharmony_ci	fm_st_proto.hdr_len = 1;
149962306a36Sopenharmony_ci	fm_st_proto.offset_len_in_hdr = 0;
150062306a36Sopenharmony_ci	fm_st_proto.len_size = 1;
150162306a36Sopenharmony_ci	fm_st_proto.reserve = 1;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	ret = st_register(&fm_st_proto);
150462306a36Sopenharmony_ci	if (ret == -EINPROGRESS) {
150562306a36Sopenharmony_ci		init_completion(&wait_for_fmdrv_reg_comp);
150662306a36Sopenharmony_ci		fmdev->streg_cbdata = -EINPROGRESS;
150762306a36Sopenharmony_ci		fmdbg("%s waiting for ST reg completion signal\n", __func__);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci		if (!wait_for_completion_timeout(&wait_for_fmdrv_reg_comp,
151062306a36Sopenharmony_ci						 FM_ST_REG_TIMEOUT)) {
151162306a36Sopenharmony_ci			fmerr("Timeout(%d sec), didn't get reg completion signal from ST\n",
151262306a36Sopenharmony_ci					jiffies_to_msecs(FM_ST_REG_TIMEOUT) / 1000);
151362306a36Sopenharmony_ci			return -ETIMEDOUT;
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci		if (fmdev->streg_cbdata != 0) {
151662306a36Sopenharmony_ci			fmerr("ST reg comp CB called with error status %d\n",
151762306a36Sopenharmony_ci			      fmdev->streg_cbdata);
151862306a36Sopenharmony_ci			return -EAGAIN;
151962306a36Sopenharmony_ci		}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		ret = 0;
152262306a36Sopenharmony_ci	} else if (ret < 0) {
152362306a36Sopenharmony_ci		fmerr("st_register failed %d\n", ret);
152462306a36Sopenharmony_ci		return -EAGAIN;
152562306a36Sopenharmony_ci	}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	if (fm_st_proto.write != NULL) {
152862306a36Sopenharmony_ci		g_st_write = fm_st_proto.write;
152962306a36Sopenharmony_ci	} else {
153062306a36Sopenharmony_ci		fmerr("Failed to get ST write func pointer\n");
153162306a36Sopenharmony_ci		ret = st_unregister(&fm_st_proto);
153262306a36Sopenharmony_ci		if (ret < 0)
153362306a36Sopenharmony_ci			fmerr("st_unregister failed %d\n", ret);
153462306a36Sopenharmony_ci		return -EAGAIN;
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	spin_lock_init(&fmdev->rds_buff_lock);
153862306a36Sopenharmony_ci	spin_lock_init(&fmdev->resp_skb_lock);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	/* Initialize TX queue and TX tasklet */
154162306a36Sopenharmony_ci	skb_queue_head_init(&fmdev->tx_q);
154262306a36Sopenharmony_ci	tasklet_setup(&fmdev->tx_task, send_tasklet);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	/* Initialize RX Queue and RX tasklet */
154562306a36Sopenharmony_ci	skb_queue_head_init(&fmdev->rx_q);
154662306a36Sopenharmony_ci	tasklet_setup(&fmdev->rx_task, recv_tasklet);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	fmdev->irq_info.stage = 0;
154962306a36Sopenharmony_ci	atomic_set(&fmdev->tx_cnt, 1);
155062306a36Sopenharmony_ci	fmdev->resp_comp = NULL;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	timer_setup(&fmdev->irq_info.timer, int_timeout_handler, 0);
155362306a36Sopenharmony_ci	/*TODO: add FM_STIC_EVENT later */
155462306a36Sopenharmony_ci	fmdev->irq_info.mask = FM_MAL_EVENT;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	/* Region info */
155762306a36Sopenharmony_ci	fmdev->rx.region = region_configs[default_radio_region];
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	fmdev->rx.mute_mode = FM_MUTE_OFF;
156062306a36Sopenharmony_ci	fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF;
156162306a36Sopenharmony_ci	fmdev->rx.rds.flag = FM_RDS_DISABLE;
156262306a36Sopenharmony_ci	fmdev->rx.freq = FM_UNDEFINED_FREQ;
156362306a36Sopenharmony_ci	fmdev->rx.rds_mode = FM_RDS_SYSTEM_RDS;
156462306a36Sopenharmony_ci	fmdev->rx.af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF;
156562306a36Sopenharmony_ci	fmdev->irq_info.retry = 0;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	fm_rx_reset_rds_cache(fmdev);
156862306a36Sopenharmony_ci	init_waitqueue_head(&fmdev->rx.rds.read_queue);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	fm_rx_reset_station_info(fmdev);
157162306a36Sopenharmony_ci	set_bit(FM_CORE_READY, &fmdev->flag);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	return ret;
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci/*
157762306a36Sopenharmony_ci * This function will be called from FM V4L2 release function.
157862306a36Sopenharmony_ci * Unregister from ST driver.
157962306a36Sopenharmony_ci */
158062306a36Sopenharmony_ciint fmc_release(struct fmdev *fmdev)
158162306a36Sopenharmony_ci{
158262306a36Sopenharmony_ci	static struct st_proto_s fm_st_proto;
158362306a36Sopenharmony_ci	int ret;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
158662306a36Sopenharmony_ci		fmdbg("FM Core is already down\n");
158762306a36Sopenharmony_ci		return 0;
158862306a36Sopenharmony_ci	}
158962306a36Sopenharmony_ci	/* Service pending read */
159062306a36Sopenharmony_ci	wake_up_interruptible(&fmdev->rx.rds.read_queue);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	tasklet_kill(&fmdev->tx_task);
159362306a36Sopenharmony_ci	tasklet_kill(&fmdev->rx_task);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	skb_queue_purge(&fmdev->tx_q);
159662306a36Sopenharmony_ci	skb_queue_purge(&fmdev->rx_q);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	fmdev->resp_comp = NULL;
159962306a36Sopenharmony_ci	fmdev->rx.freq = 0;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	memset(&fm_st_proto, 0, sizeof(fm_st_proto));
160262306a36Sopenharmony_ci	fm_st_proto.chnl_id = 0x08;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	ret = st_unregister(&fm_st_proto);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	if (ret < 0)
160762306a36Sopenharmony_ci		fmerr("Failed to de-register FM from ST %d\n", ret);
160862306a36Sopenharmony_ci	else
160962306a36Sopenharmony_ci		fmdbg("Successfully unregistered from ST\n");
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	clear_bit(FM_CORE_READY, &fmdev->flag);
161262306a36Sopenharmony_ci	return ret;
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci/*
161662306a36Sopenharmony_ci * Module init function. Ask FM V4L module to register video device.
161762306a36Sopenharmony_ci * Allocate memory for FM driver context and RX RDS buffer.
161862306a36Sopenharmony_ci */
161962306a36Sopenharmony_cistatic int __init fm_drv_init(void)
162062306a36Sopenharmony_ci{
162162306a36Sopenharmony_ci	struct fmdev *fmdev = NULL;
162262306a36Sopenharmony_ci	int ret = -ENOMEM;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	fmdbg("FM driver version %s\n", FM_DRV_VERSION);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	fmdev = kzalloc(sizeof(struct fmdev), GFP_KERNEL);
162762306a36Sopenharmony_ci	if (NULL == fmdev) {
162862306a36Sopenharmony_ci		fmerr("Can't allocate operation structure memory\n");
162962306a36Sopenharmony_ci		return ret;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci	fmdev->rx.rds.buf_size = default_rds_buf * FM_RDS_BLK_SIZE;
163262306a36Sopenharmony_ci	fmdev->rx.rds.buff = kzalloc(fmdev->rx.rds.buf_size, GFP_KERNEL);
163362306a36Sopenharmony_ci	if (NULL == fmdev->rx.rds.buff) {
163462306a36Sopenharmony_ci		fmerr("Can't allocate rds ring buffer\n");
163562306a36Sopenharmony_ci		goto rel_dev;
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	ret = fm_v4l2_init_video_device(fmdev, radio_nr);
163962306a36Sopenharmony_ci	if (ret < 0)
164062306a36Sopenharmony_ci		goto rel_rdsbuf;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	fmdev->irq_info.handlers = int_handler_table;
164362306a36Sopenharmony_ci	fmdev->curr_fmmode = FM_MODE_OFF;
164462306a36Sopenharmony_ci	fmdev->tx_data.pwr_lvl = FM_PWR_LVL_DEF;
164562306a36Sopenharmony_ci	fmdev->tx_data.preemph = FM_TX_PREEMPH_50US;
164662306a36Sopenharmony_ci	return ret;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cirel_rdsbuf:
164962306a36Sopenharmony_ci	kfree(fmdev->rx.rds.buff);
165062306a36Sopenharmony_cirel_dev:
165162306a36Sopenharmony_ci	kfree(fmdev);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	return ret;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci/* Module exit function. Ask FM V4L module to unregister video device */
165762306a36Sopenharmony_cistatic void __exit fm_drv_exit(void)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	struct fmdev *fmdev = NULL;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	fmdev = fm_v4l2_deinit_video_device();
166262306a36Sopenharmony_ci	if (fmdev != NULL) {
166362306a36Sopenharmony_ci		kfree(fmdev->rx.rds.buff);
166462306a36Sopenharmony_ci		kfree(fmdev);
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cimodule_init(fm_drv_init);
166962306a36Sopenharmony_cimodule_exit(fm_drv_exit);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci/* ------------- Module Info ------------- */
167262306a36Sopenharmony_ciMODULE_AUTHOR("Manjunatha Halli <manjunatha_halli@ti.com>");
167362306a36Sopenharmony_ciMODULE_DESCRIPTION("FM Driver for TI's Connectivity chip. " FM_DRV_VERSION);
167462306a36Sopenharmony_ciMODULE_VERSION(FM_DRV_VERSION);
167562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1676