summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKoen Kooi <koen@openembedded.org>2011-01-22 18:31:26 +0100
committerKoen Kooi <koen@openembedded.org>2011-01-23 12:52:26 +0100
commit3b6561e0269fcfad39503dfa255c3738e8f3052a (patch)
tree17cb672b54076581b597b5ebc3e7cf4e8956c842
parent1db2deca67399ef2e6978bdc3e4f95a5c14cd8e1 (diff)
downloadopenembedded-3b6561e0269fcfad39503dfa255c3738e8f3052a.tar.gz
linux-omap 2.6.37: fix mmc regulators when using wl1271 expansion board
linux-omap 2.6.37: cherry-pick a few wl1271 patches * tested on a beagleboard xM with wl1271 expansionboard linux-omap 2.6.37: update wl127xx patchset to include BT and FM drivers Signed-off-by: Koen Kooi <koen@openembedded.org>
-rw-r--r--recipes/linux/linux-omap-2.6.37/base/0013-omap3-beagleboard-add-WIP-support-for-beagleboardtoy.patch43
-rw-r--r--recipes/linux/linux-omap-2.6.37/beagleboard/defconfig44
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0001-wl12xx-Read-MAC-address-from-NVS-file-on-HW-startup.patch41
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0002-wl1271-11n-Support-Add-Definitions.patch165
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0003-wl1271-11n-Support-ACX-Commands.patch128
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0004-wl1271-11n-Support-functionality-and-configuration-a.patch249
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0005-wl1271-set-wl-vif-only-if-add_interface-succeeded.patch86
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0006-wl12xx-Unset-bssid-filter-ssid-and-bssid-from-firmwa.patch44
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0007-drivers-media-radio-wl128x-FM-Driver-common-header-f.patch268
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0008-drivers-media-radio-wl128x-FM-Driver-V4L2-sources.patch645
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0009-drivers-media-radio-wl128x-FM-Driver-Common-sources.patch2114
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0010-drivers-media-radio-wl128x-FM-driver-RX-sources.patch938
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0011-drivers-media-radio-wl128x-FM-driver-TX-sources.patch494
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0012-drivers-media-radio-wl128x-Kconfig-Makefile-for-wl12.patch52
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0013-drivers-media-radio-Update-Kconfig-and-Makefile-for-.patch38
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0014-drivers-misc-ti-st-change-protocol-parse-logic.patch900
-rw-r--r--recipes/linux/linux-omap-2.6.37/wl1271/0015-Bluetooth-btwilink-driver.patch463
-rw-r--r--recipes/linux/linux-omap_2.6.37.bb18
18 files changed, 6682 insertions, 48 deletions
diff --git a/recipes/linux/linux-omap-2.6.37/base/0013-omap3-beagleboard-add-WIP-support-for-beagleboardtoy.patch b/recipes/linux/linux-omap-2.6.37/base/0013-omap3-beagleboard-add-WIP-support-for-beagleboardtoy.patch
index 1bb7e63ee5..9b08d5f682 100644
--- a/recipes/linux/linux-omap-2.6.37/base/0013-omap3-beagleboard-add-WIP-support-for-beagleboardtoy.patch
+++ b/recipes/linux/linux-omap-2.6.37/base/0013-omap3-beagleboard-add-WIP-support-for-beagleboardtoy.patch
@@ -1,4 +1,4 @@
-From 554ec2d3959b1cc942337b67560bfb9c7b91f645 Mon Sep 17 00:00:00 2001
+From f7d71be36165002251727019b1a03a19938bfa64 Mon Sep 17 00:00:00 2001
From: Koen Kooi <koen@beagleboard.org>
Date: Mon, 20 Dec 2010 11:57:56 +0100
Subject: [PATCH 13/28] omap3: beagleboard: add WIP support for beagleboardtoys WL12xx board
@@ -7,11 +7,11 @@ Based on a patch by Luciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: Koen Kooi <koen@beagleboard.org>
---
- arch/arm/mach-omap2/board-omap3beagle.c | 76 +++++++++++++++++++++++++++++++
- 1 files changed, 76 insertions(+), 0 deletions(-)
+ arch/arm/mach-omap2/board-omap3beagle.c | 84 ++++++++++++++++++++++++++++++-
+ 1 files changed, 83 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
-index 64a181e..f6c8152 100644
+index 64a181e..59b26da 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -146,6 +146,67 @@ fail0:
@@ -37,12 +37,12 @@ index 64a181e..f6c8152 100644
+ .gpio_wp = 29,
+ },
+ {
-+ .name = "wl1271",
-+ .mmc = 2,
-+ .caps = MMC_CAP_4_BIT_DATA,
-+ .gpio_wp = -EINVAL,
-+ .gpio_cd = -EINVAL,
-+ .nonremovable = true,
++ .name = "wl1271",
++ .mmc = 2,
++ .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD,
++ .gpio_wp = -EINVAL,
++ .gpio_cd = -EINVAL,
++ .nonremovable = true,
+ },
+ {} /* Terminator */
+ };
@@ -82,22 +82,33 @@ index 64a181e..f6c8152 100644
#if defined(CONFIG_ENC28J60) || defined(CONFIG_ENC28J60_MODULE)
#include <plat/mcspi.h>
-@@ -384,7 +445,14 @@ static int beagle_twl_gpio_setup(struct device *dev,
+@@ -384,11 +445,24 @@ static int beagle_twl_gpio_setup(struct device *dev,
}
/* gpio + 0 is "mmc0_cd" (input/IRQ) */
mmc[0].gpio_cd = gpio + 0;
+#if defined(CONFIG_WL1271) || defined(CONFIG_WL1271_MODULE)
-+ if(!strcmp(expansionboard_name, "fixme-beagletoy"))
++ if(!strcmp(expansionboard_name, "fixme-beagletoy")) {
+ omap2_hsmmc_init(mmcbbt);
-+ else
++ /* link regulators to MMC adapters */
++ beagle_vmmc1_supply.dev = mmcbbt[0].dev;
++ beagle_vsim_supply.dev = mmcbbt[0].dev;
++ } else {
+ omap2_hsmmc_init(mmc);
++ /* link regulators to MMC adapters */
++ beagle_vmmc1_supply.dev = mmc[0].dev;
++ beagle_vsim_supply.dev = mmc[0].dev;
++ }
+#else
omap2_hsmmc_init(mmc);
-+#endif
-
+-
/* link regulators to MMC adapters */
beagle_vmmc1_supply.dev = mmc[0].dev;
-@@ -788,6 +856,14 @@ static void __init omap3_beagle_init(void)
+ beagle_vsim_supply.dev = mmc[0].dev;
++#endif
+
+ /* REVISIT: need ehci-omap hooks for external VBUS
+ * power switch and overcurrent detect
+@@ -788,6 +862,14 @@ static void __init omap3_beagle_init(void)
gpio_export(162, 1);
}
diff --git a/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig b/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig
index 7723d85be1..cb21bec1f5 100644
--- a/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig
+++ b/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux/arm 2.6.37-rc7 Kernel Configuration
-# Wed Dec 29 11:57:49 2010
+# Linux/arm 2.6.37 Kernel Configuration
+# Sun Jan 23 11:58:18 2011
#
CONFIG_ARM=y
CONFIG_HAVE_PWM=y
@@ -954,6 +954,7 @@ CONFIG_BT_HCIBFUSB=m
# CONFIG_BT_HCIVHCI is not set
# CONFIG_BT_MRVL is not set
CONFIG_BT_ATH3K=m
+CONFIG_BT_WILINK=m
CONFIG_AF_RXRPC=m
# CONFIG_AF_RXRPC_DEBUG is not set
# CONFIG_RXKAD is not set
@@ -965,7 +966,7 @@ CONFIG_WEXT_PROC=y
CONFIG_WEXT_SPY=y
CONFIG_WEXT_PRIV=y
CONFIG_CFG80211=m
-# CONFIG_NL80211_TESTMODE is not set
+CONFIG_NL80211_TESTMODE=y
# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
# CONFIG_CFG80211_REG_DEBUG is not set
CONFIG_CFG80211_DEFAULT_PS=y
@@ -1341,6 +1342,7 @@ CONFIG_WL1251_SPI=m
CONFIG_WL1251_SDIO=m
CONFIG_WL12XX=m
CONFIG_WL1271=m
+CONFIG_WL1271_HT=y
CONFIG_WL1271_SPI=m
CONFIG_WL1271_SDIO=m
CONFIG_WL12XX_PLATFORM_DATA=y
@@ -1568,8 +1570,6 @@ CONFIG_I2C_MUX=m
# CONFIG_I2C_MUX_PCA954x is not set
CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_ALGOBIT=m
-# CONFIG_I2C_ALGOPCF is not set
-# CONFIG_I2C_ALGOPCA is not set
#
# I2C Hardware Bus support
@@ -1800,6 +1800,7 @@ CONFIG_TPS6507X=m
CONFIG_TWL4030_CORE=y
CONFIG_TWL4030_POWER=y
CONFIG_TWL4030_CODEC=y
+CONFIG_TWL4030_MADC=m
CONFIG_TWL6030_PWM=m
# CONFIG_MFD_STMPE is not set
# CONFIG_MFD_TC35892 is not set
@@ -2072,6 +2073,11 @@ CONFIG_RADIO_ADAPTERS=y
# CONFIG_RADIO_TEA5764 is not set
CONFIG_RADIO_SAA7706H=m
# CONFIG_RADIO_TEF6862 is not set
+
+#
+# Texas Instruments WL128x FM driver (ST based)
+#
+CONFIG_RADIO_WL128X=m
CONFIG_DVB_MAX_ADAPTERS=8
CONFIG_DVB_DYNAMIC_MINORS=y
CONFIG_DVB_CAPTURE_DRIVERS=y
@@ -2131,16 +2137,12 @@ CONFIG_DVB_B2C2_FLEXCOP_USB=m
#
CONFIG_DVB_STB0899=m
CONFIG_DVB_STB6100=m
-# CONFIG_DVB_STV090x is not set
-# CONFIG_DVB_STV6110x is not set
#
# DVB-S (satellite) frontends
#
-# CONFIG_DVB_CX24110 is not set
CONFIG_DVB_CX24123=m
CONFIG_DVB_MT312=m
-# CONFIG_DVB_ZL10036 is not set
CONFIG_DVB_ZL10039=m
CONFIG_DVB_S5H1420=m
CONFIG_DVB_STV0288=m
@@ -2148,29 +2150,18 @@ CONFIG_DVB_STB6000=m
CONFIG_DVB_STV0299=m
CONFIG_DVB_STV6110=m
CONFIG_DVB_STV0900=m
-# CONFIG_DVB_TDA8083 is not set
CONFIG_DVB_TDA10086=m
-# CONFIG_DVB_TDA8261 is not set
-# CONFIG_DVB_VES1X93 is not set
CONFIG_DVB_TUNER_ITD1000=m
CONFIG_DVB_TUNER_CX24113=m
CONFIG_DVB_TDA826X=m
-# CONFIG_DVB_TUA6100 is not set
CONFIG_DVB_CX24116=m
CONFIG_DVB_SI21XX=m
CONFIG_DVB_DS3000=m
-# CONFIG_DVB_MB86A16 is not set
#
# DVB-T (terrestrial) frontends
#
-# CONFIG_DVB_SP8870 is not set
-# CONFIG_DVB_SP887X is not set
-# CONFIG_DVB_CX22700 is not set
CONFIG_DVB_CX22702=m
-# CONFIG_DVB_S5H1432 is not set
-# CONFIG_DVB_DRX397XD is not set
-# CONFIG_DVB_L64781 is not set
CONFIG_DVB_TDA1004X=m
CONFIG_DVB_NXT6000=m
CONFIG_DVB_MT352=m
@@ -2181,13 +2172,10 @@ CONFIG_DVB_DIB7000M=m
CONFIG_DVB_DIB7000P=m
CONFIG_DVB_TDA10048=m
CONFIG_DVB_AF9013=m
-# CONFIG_DVB_EC100 is not set
#
# DVB-C (cable) frontends
#
-# CONFIG_DVB_VES1820 is not set
-# CONFIG_DVB_TDA10021 is not set
CONFIG_DVB_TDA10023=m
CONFIG_DVB_STV0297=m
@@ -2195,19 +2183,15 @@ CONFIG_DVB_STV0297=m
# ATSC (North American/Korean Terrestrial/Cable DTV) frontends
#
CONFIG_DVB_NXT200X=m
-# CONFIG_DVB_OR51211 is not set
-# CONFIG_DVB_OR51132 is not set
CONFIG_DVB_BCM3510=m
CONFIG_DVB_LGDT330X=m
CONFIG_DVB_LGDT3305=m
CONFIG_DVB_S5H1409=m
-# CONFIG_DVB_AU8522 is not set
CONFIG_DVB_S5H1411=m
#
# ISDB-T (terrestrial) frontends
#
-# CONFIG_DVB_S921 is not set
CONFIG_DVB_DIB8000=m
#
@@ -2221,13 +2205,9 @@ CONFIG_DVB_TUNER_DIB0090=m
# SEC control devices for DVB-S
#
CONFIG_DVB_LNBP21=m
-# CONFIG_DVB_ISL6405 is not set
CONFIG_DVB_ISL6421=m
-# CONFIG_DVB_ISL6423 is not set
-# CONFIG_DVB_LGS8GL5 is not set
CONFIG_DVB_LGS8GXX=m
CONFIG_DVB_ATBM8830=m
-# CONFIG_DVB_TDA665x is not set
CONFIG_DVB_IX2505V=m
#
@@ -2294,6 +2274,8 @@ CONFIG_FB_OMAP2_NUM_FBS=2
# OMAP2/3 Display Device Drivers
#
CONFIG_PANEL_GENERIC=y
+# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set
+# CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C is not set
CONFIG_PANEL_SHARP_LS037V7DW01=y
# CONFIG_PANEL_SHARP_LQ043T1DG01 is not set
# CONFIG_PANEL_TAAL is not set
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0001-wl12xx-Read-MAC-address-from-NVS-file-on-HW-startup.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0001-wl12xx-Read-MAC-address-from-NVS-file-on-HW-startup.patch
new file mode 100644
index 0000000000..a4f08736b6
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0001-wl12xx-Read-MAC-address-from-NVS-file-on-HW-startup.patch
@@ -0,0 +1,41 @@
+From 5d302917bbdb377538f6c848243a6265878abcee Mon Sep 17 00:00:00 2001
+From: Arik Nemtsov <arik@wizery.com>
+Date: Sat, 16 Oct 2010 21:49:52 +0200
+Subject: [PATCH 01/15] wl12xx: Read MAC address from NVS file on HW startup
+
+Try to read the MAC address from the on-disk NVS file.
+A non-zero MAC address is required to add an AP interface.
+
+Signed-off-by: Arik Nemtsov <arik@wizery.com>
+Reviewed-by: Luciano Coelho <coelho@ti.com>
+Signed-off-by: Luciano Coelho <coelho@ti.com>
+---
+ drivers/net/wireless/wl12xx/wl1271_main.c | 12 ++++++++++++
+ 1 files changed, 12 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
+index 48a4b99..591de0e 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_main.c
++++ b/drivers/net/wireless/wl12xx/wl1271_main.c
+@@ -2391,6 +2391,18 @@ int wl1271_register_hw(struct wl1271 *wl)
+ if (wl->mac80211_registered)
+ return 0;
+
++ ret = wl1271_fetch_nvs(wl);
++ if (ret == 0) {
++ u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
++
++ wl->mac_addr[0] = nvs_ptr[11];
++ wl->mac_addr[1] = nvs_ptr[10];
++ wl->mac_addr[2] = nvs_ptr[6];
++ wl->mac_addr[3] = nvs_ptr[5];
++ wl->mac_addr[4] = nvs_ptr[4];
++ wl->mac_addr[5] = nvs_ptr[3];
++ }
++
+ SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
+
+ ret = ieee80211_register_hw(wl->hw);
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0002-wl1271-11n-Support-Add-Definitions.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0002-wl1271-11n-Support-Add-Definitions.patch
new file mode 100644
index 0000000000..1da7be6c04
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0002-wl1271-11n-Support-Add-Definitions.patch
@@ -0,0 +1,165 @@
+From 99d1c6c23faa446ec0ebdf056d8aa8f4d983d518 Mon Sep 17 00:00:00 2001
+From: Shahar Levi <shahar_levi@ti.com>
+Date: Wed, 13 Oct 2010 16:09:39 +0200
+Subject: [PATCH 02/15] wl1271: 11n Support, Add Definitions
+
+Two acx commands: ht_capabilities & ht_information, 11n sta capabilities
+macro.
+
+Signed-off-by: Shahar Levi <shahar_levi@ti.com>
+Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com>
+Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
+---
+ drivers/net/wireless/wl12xx/wl1271.h | 11 ++++-
+ drivers/net/wireless/wl12xx/wl1271_acx.h | 81 +++++++++++++++++++++++++++++
+ drivers/net/wireless/wl12xx/wl1271_main.c | 15 +++++
+ 3 files changed, 106 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
+index 8a4cd76..45a2583 100644
+--- a/drivers/net/wireless/wl12xx/wl1271.h
++++ b/drivers/net/wireless/wl12xx/wl1271.h
+@@ -432,7 +432,12 @@ struct wl1271 {
+ /* Our association ID */
+ u16 aid;
+
+- /* currently configured rate set */
++ /*
++ * currently configured rate set:
++ * bits 0-15 - 802.11abg rates
++ * bits 16-23 - 802.11n MCS index mask
++ * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
++ */
+ u32 sta_rate_set;
+ u32 basic_rate_set;
+ u32 basic_rate;
+@@ -509,4 +514,8 @@ int wl1271_plt_stop(struct wl1271 *wl);
+ #define WL1271_PRE_POWER_ON_SLEEP 20 /* in miliseconds */
+ #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */
+
++/* Macros to handle wl1271.sta_rate_set */
++#define HW_BG_RATES_MASK 0xffff
++#define HW_HT_RATES_OFFSET 16
++
+ #endif
+diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
+index ebb341d..f090a04 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
++++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
+@@ -964,6 +964,87 @@ struct wl1271_acx_rssi_snr_avg_weights {
+ u8 snr_data;
+ };
+
++/*
++ * ACX_PEER_HT_CAP
++ * Configure HT capabilities - declare the capabilities of the peer
++ * we are connected to.
++ */
++struct wl1271_acx_ht_capabilities {
++ struct acx_header header;
++
++ /*
++ * bit 0 - Allow HT Operation
++ * bit 1 - Allow Greenfield format in TX
++ * bit 2 - Allow Short GI in TX
++ * bit 3 - Allow L-SIG TXOP Protection in TX
++ * bit 4 - Allow HT Control fields in TX.
++ * Note, driver will still leave space for HT control in packets
++ * regardless of the value of this field. FW will be responsible
++ * to drop the HT field from any frame when this Bit set to 0.
++ * bit 5 - Allow RD initiation in TXOP. FW is allowed to initate RD.
++ * Exact policy setting for this feature is TBD.
++ * Note, this bit can only be set to 1 if bit 3 is set to 1.
++ */
++ __le32 ht_capabilites;
++
++ /*
++ * Indicates to which peer these capabilities apply.
++ * For infrastructure use ff:ff:ff:ff:ff:ff that indicates relevance
++ * for all peers.
++ * Only valid for IBSS/DLS operation.
++ */
++ u8 mac_address[ETH_ALEN];
++
++ /*
++ * This the maximum A-MPDU length supported by the AP. The FW may not
++ * exceed this length when sending A-MPDUs
++ */
++ u8 ampdu_max_length;
++
++ /* This is the minimal spacing required when sending A-MPDUs to the AP*/
++ u8 ampdu_min_spacing;
++} __packed;
++
++/* HT Capabilites Fw Bit Mask Mapping */
++#define WL1271_ACX_FW_CAP_HT_OPERATION BIT(0)
++#define WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT BIT(1)
++#define WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS BIT(2)
++#define WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION BIT(3)
++#define WL1271_ACX_FW_CAP_HT_CONTROL_FIELDS BIT(4)
++#define WL1271_ACX_FW_CAP_RD_INITIATION BIT(5)
++
++
++/*
++ * ACX_HT_BSS_OPERATION
++ * Configure HT capabilities - AP rules for behavior in the BSS.
++ */
++struct wl1271_acx_ht_information {
++ struct acx_header header;
++
++ /* Values: 0 - RIFS not allowed, 1 - RIFS allowed */
++ u8 rifs_mode;
++
++ /* Values: 0 - 3 like in spec */
++ u8 ht_protection;
++
++ /* Values: 0 - GF protection not required, 1 - GF protection required */
++ u8 gf_protection;
++
++ /*Values: 0 - TX Burst limit not required, 1 - TX Burst Limit required*/
++ u8 ht_tx_burst_limit;
++
++ /*
++ * Values: 0 - Dual CTS protection not required,
++ * 1 - Dual CTS Protection required
++ * Note: When this value is set to 1 FW will protect all TXOP with RTS
++ * frame and will not use CTS-to-self regardless of the value of the
++ * ACX_CTS_PROTECTION information element
++ */
++ u8 dual_cts_protection;
++
++ u8 padding[3];
++} __packed;
++
+ struct wl1271_acx_fw_tsf_information {
+ struct acx_header header;
+
+diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
+index 591de0e..785b73c 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_main.c
++++ b/drivers/net/wireless/wl12xx/wl1271_main.c
+@@ -2134,6 +2134,21 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
+ 0 /* CONF_HW_RXTX_RATE_1 */
+ };
+
++/* 11n STA capabilities */
++#define HW_RX_HIGHEST_RATE 72
++
++#define WL1271_HT_CAP { \
++ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \
++ .ht_supported = true, \
++ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
++ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
++ .mcs = { \
++ .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
++ .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
++ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
++ }, \
++}
++
+ /* can't be const, mac80211 writes to this */
+ static struct ieee80211_supported_band wl1271_band_2ghz = {
+ .channels = wl1271_channels,
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0003-wl1271-11n-Support-ACX-Commands.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0003-wl1271-11n-Support-ACX-Commands.patch
new file mode 100644
index 0000000000..8d2412b1a4
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0003-wl1271-11n-Support-ACX-Commands.patch
@@ -0,0 +1,128 @@
+From 160169e1e717020b8456278950c30d2b1f15c314 Mon Sep 17 00:00:00 2001
+From: Shahar Levi <shahar_levi@ti.com>
+Date: Wed, 13 Oct 2010 16:09:40 +0200
+Subject: [PATCH 03/15] wl1271: 11n Support, ACX Commands
+
+Added ACX command to the FW for 11n support.
+
+Signed-off-by: Shahar Levi <shahar_levi@ti.com>
+Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com>
+Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
+---
+ drivers/net/wireless/wl12xx/wl1271_acx.c | 83 ++++++++++++++++++++++++++++++
+ drivers/net/wireless/wl12xx/wl1271_acx.h | 5 ++
+ 2 files changed, 88 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
+index 6189934..bd7f95f 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
++++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
+@@ -1226,6 +1226,89 @@ out:
+ return ret;
+ }
+
++int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
++ struct ieee80211_sta_ht_cap *ht_cap,
++ bool allow_ht_operation)
++{
++ struct wl1271_acx_ht_capabilities *acx;
++ u8 mac_address[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
++ int ret = 0;
++
++ wl1271_debug(DEBUG_ACX, "acx ht capabilities setting");
++
++ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
++ if (!acx) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ /* Allow HT Operation ? */
++ if (allow_ht_operation) {
++ acx->ht_capabilites =
++ WL1271_ACX_FW_CAP_HT_OPERATION;
++ if (ht_cap->cap & IEEE80211_HT_CAP_GRN_FLD)
++ acx->ht_capabilites |=
++ WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT;
++ if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
++ acx->ht_capabilites |=
++ WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS;
++ if (ht_cap->cap & IEEE80211_HT_CAP_LSIG_TXOP_PROT)
++ acx->ht_capabilites |=
++ WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION;
++
++ /* get data from A-MPDU parameters field */
++ acx->ampdu_max_length = ht_cap->ampdu_factor;
++ acx->ampdu_min_spacing = ht_cap->ampdu_density;
++
++ memcpy(acx->mac_address, mac_address, ETH_ALEN);
++ } else { /* HT operations are not allowed */
++ acx->ht_capabilites = 0;
++ }
++
++ ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx));
++ if (ret < 0) {
++ wl1271_warning("acx ht capabilities setting failed: %d", ret);
++ goto out;
++ }
++
++out:
++ kfree(acx);
++ return ret;
++}
++
++int wl1271_acx_set_ht_information(struct wl1271 *wl,
++ u16 ht_operation_mode)
++{
++ struct wl1271_acx_ht_information *acx;
++ int ret = 0;
++
++ wl1271_debug(DEBUG_ACX, "acx ht information setting");
++
++ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
++ if (!acx) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ acx->ht_protection =
++ (u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION);
++ acx->rifs_mode = 0;
++ acx->gf_protection = 0;
++ acx->ht_tx_burst_limit = 0;
++ acx->dual_cts_protection = 0;
++
++ ret = wl1271_cmd_configure(wl, ACX_HT_BSS_OPERATION, acx, sizeof(*acx));
++
++ if (ret < 0) {
++ wl1271_warning("acx ht information setting failed: %d", ret);
++ goto out;
++ }
++
++out:
++ kfree(acx);
++ return ret;
++}
++
+ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
+ {
+ struct wl1271_acx_fw_tsf_information *tsf_info;
+diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
+index f090a04..7589167 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
++++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
+@@ -1174,6 +1174,11 @@ int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
+ int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
+ s16 thold, u8 hyst);
+ int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
++int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
++ struct ieee80211_sta_ht_cap *ht_cap,
++ bool allow_ht_operation);
++int wl1271_acx_set_ht_information(struct wl1271 *wl,
++ u16 ht_operation_mode);
+ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
+
+ #endif /* __WL1271_ACX_H__ */
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0004-wl1271-11n-Support-functionality-and-configuration-a.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0004-wl1271-11n-Support-functionality-and-configuration-a.patch
new file mode 100644
index 0000000000..17ed6e63d7
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0004-wl1271-11n-Support-functionality-and-configuration-a.patch
@@ -0,0 +1,249 @@
+From 9685ab91494ae35d2cb7e0033c5ee1bf3cdf0c64 Mon Sep 17 00:00:00 2001
+From: Shahar Levi <shahar_levi@ti.com>
+Date: Wed, 13 Oct 2010 16:09:41 +0200
+Subject: [PATCH 04/15] wl1271: 11n Support, functionality and configuration ability
+
+Add 11n ability in scan, connection and using MCS rates.
+The configuration is temporary due to the code incomplete and
+still in testing process. That plans to be remove in the future.
+
+Signed-off-by: Shahar Levi <shahar_levi@ti.com>
+Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com>
+Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
+---
+ drivers/net/wireless/wl12xx/Kconfig | 10 +++
+ drivers/net/wireless/wl12xx/wl1271_main.c | 96 +++++++++++++++++++++++------
+ drivers/net/wireless/wl12xx/wl1271_rx.c | 6 ++
+ drivers/net/wireless/wl12xx/wl1271_tx.c | 11 +++
+ 4 files changed, 105 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
+index b447559..1b3b7bd 100644
+--- a/drivers/net/wireless/wl12xx/Kconfig
++++ b/drivers/net/wireless/wl12xx/Kconfig
+@@ -18,6 +18,16 @@ config WL1271
+ If you choose to build a module, it'll be called wl1271. Say N if
+ unsure.
+
++config WL1271_HT
++ bool "TI wl1271 802.11 HT support (EXPERIMENTAL)"
++ depends on WL1271 && EXPERIMENTAL
++ default n
++ ---help---
++ This will enable 802.11 HT support for TI wl1271 chipset.
++
++ That configuration is temporary due to the code incomplete and
++ still in testing process.
++
+ config WL1271_SPI
+ tristate "TI wl1271 SPI support"
+ depends on WL1271 && SPI_MASTER
+diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
+index 785b73c..49ec0ef 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_main.c
++++ b/drivers/net/wireless/wl12xx/wl1271_main.c
+@@ -851,12 +851,32 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ struct ieee80211_sta *sta = txinfo->control.sta;
+ unsigned long flags;
+
+- /* peek into the rates configured in the STA entry */
++ /*
++ * peek into the rates configured in the STA entry.
++ * The rates set after connection stage, The first block only BG sets:
++ * the compare is for bit 0-16 of sta_rate_set. The second block add
++ * HT rates in case of HT supported.
++ */
+ spin_lock_irqsave(&wl->wl_lock, flags);
+- if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) {
++ if (sta &&
++ (sta->supp_rates[conf->channel->band] !=
++ (wl->sta_rate_set & HW_BG_RATES_MASK))) {
+ wl->sta_rate_set = sta->supp_rates[conf->channel->band];
+ set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
+ }
++
++#ifdef CONFIG_WL1271_HT
++ if (sta &&
++ sta->ht_cap.ht_supported &&
++ ((wl->sta_rate_set >> HW_HT_RATES_OFFSET) !=
++ sta->ht_cap.mcs.rx_mask[0])) {
++ /* Clean MCS bits before setting them */
++ wl->sta_rate_set &= HW_BG_RATES_MASK;
++ wl->sta_rate_set |=
++ (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
++ set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
++ }
++#endif
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ /* queue the packet */
+@@ -1709,6 +1729,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
+ {
+ enum wl1271_cmd_ps_mode mode;
+ struct wl1271 *wl = hw->priv;
++ struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid);
+ bool do_join = false;
+ bool set_assoc = false;
+ int ret;
+@@ -1927,6 +1948,37 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
+ }
+ }
+
++ /*
++ * Takes care of: New association with HT enable,
++ * HT information change in beacon.
++ */
++ if (sta &&
++ (changed & BSS_CHANGED_HT) &&
++ (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
++ ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true);
++ if (ret < 0) {
++ wl1271_warning("Set ht cap true failed %d", ret);
++ goto out_sleep;
++ }
++ ret = wl1271_acx_set_ht_information(wl,
++ bss_conf->ht_operation_mode);
++ if (ret < 0) {
++ wl1271_warning("Set ht information failed %d", ret);
++ goto out_sleep;
++ }
++ }
++ /*
++ * Takes care of: New association without HT,
++ * Disassociation.
++ */
++ else if (sta && (changed & BSS_CHANGED_ASSOC)) {
++ ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, false);
++ if (ret < 0) {
++ wl1271_warning("Set ht cap false failed %d", ret);
++ goto out_sleep;
++ }
++ }
++
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ __be32 addr = bss_conf->arp_addr_list[0];
+ WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
+@@ -2107,14 +2159,14 @@ static struct ieee80211_channel wl1271_channels[] = {
+ /* mapping to indexes for wl1271_rates */
+ static const u8 wl1271_rate_to_idx_2ghz[] = {
+ /* MCS rates are used only with 11n */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
++ 7, /* CONF_HW_RXTX_RATE_MCS7 */
++ 6, /* CONF_HW_RXTX_RATE_MCS6 */
++ 5, /* CONF_HW_RXTX_RATE_MCS5 */
++ 4, /* CONF_HW_RXTX_RATE_MCS4 */
++ 3, /* CONF_HW_RXTX_RATE_MCS3 */
++ 2, /* CONF_HW_RXTX_RATE_MCS2 */
++ 1, /* CONF_HW_RXTX_RATE_MCS1 */
++ 0, /* CONF_HW_RXTX_RATE_MCS0 */
+
+ 11, /* CONF_HW_RXTX_RATE_54 */
+ 10, /* CONF_HW_RXTX_RATE_48 */
+@@ -2137,6 +2189,7 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
+ /* 11n STA capabilities */
+ #define HW_RX_HIGHEST_RATE 72
+
++#ifdef CONFIG_WL1271_HT
+ #define WL1271_HT_CAP { \
+ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \
+ .ht_supported = true, \
+@@ -2148,6 +2201,11 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
+ }, \
+ }
++#else
++#define WL1271_HT_CAP { \
++ .ht_supported = false, \
++}
++#endif
+
+ /* can't be const, mac80211 writes to this */
+ static struct ieee80211_supported_band wl1271_band_2ghz = {
+@@ -2155,6 +2213,7 @@ static struct ieee80211_supported_band wl1271_band_2ghz = {
+ .n_channels = ARRAY_SIZE(wl1271_channels),
+ .bitrates = wl1271_rates,
+ .n_bitrates = ARRAY_SIZE(wl1271_rates),
++ .ht_cap = WL1271_HT_CAP,
+ };
+
+ /* 5 GHz data rates for WL1273 */
+@@ -2237,14 +2296,14 @@ static struct ieee80211_channel wl1271_channels_5ghz[] = {
+ /* mapping to indexes for wl1271_rates_5ghz */
+ static const u8 wl1271_rate_to_idx_5ghz[] = {
+ /* MCS rates are used only with 11n */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
+- CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
++ 7, /* CONF_HW_RXTX_RATE_MCS7 */
++ 6, /* CONF_HW_RXTX_RATE_MCS6 */
++ 5, /* CONF_HW_RXTX_RATE_MCS5 */
++ 4, /* CONF_HW_RXTX_RATE_MCS4 */
++ 3, /* CONF_HW_RXTX_RATE_MCS3 */
++ 2, /* CONF_HW_RXTX_RATE_MCS2 */
++ 1, /* CONF_HW_RXTX_RATE_MCS1 */
++ 0, /* CONF_HW_RXTX_RATE_MCS0 */
+
+ 7, /* CONF_HW_RXTX_RATE_54 */
+ 6, /* CONF_HW_RXTX_RATE_48 */
+@@ -2269,6 +2328,7 @@ static struct ieee80211_supported_band wl1271_band_5ghz = {
+ .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
+ .bitrates = wl1271_rates_5ghz,
+ .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
++ .ht_cap = WL1271_HT_CAP,
+ };
+
+ static const u8 *wl1271_band_rate_to_idx[] = {
+diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
+index bea133b..ac13f7d 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
++++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
+@@ -53,6 +53,12 @@ static void wl1271_rx_status(struct wl1271 *wl,
+ status->band = wl->band;
+ status->rate_idx = wl1271_rate_to_idx(wl, desc->rate);
+
++#ifdef CONFIG_WL1271_HT
++ /* 11n support */
++ if (desc->rate <= CONF_HW_RXTX_RATE_MCS0)
++ status->flag |= RX_FLAG_HT;
++#endif
++
+ status->signal = desc->rssi;
+
+ /*
+diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
+index e3dc13c..6a87633 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
++++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
+@@ -201,6 +201,17 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
+ rate_set >>= 1;
+ }
+
++#ifdef CONFIG_WL1271_HT
++ /* MCS rates indication are on bits 16 - 23 */
++ rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
++
++ for (bit = 0; bit < 8; bit++) {
++ if (rate_set & 0x1)
++ enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
++ rate_set >>= 1;
++ }
++#endif
++
+ return enabled_rates;
+ }
+
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0005-wl1271-set-wl-vif-only-if-add_interface-succeeded.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0005-wl1271-set-wl-vif-only-if-add_interface-succeeded.patch
new file mode 100644
index 0000000000..3707b7c36c
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0005-wl1271-set-wl-vif-only-if-add_interface-succeeded.patch
@@ -0,0 +1,86 @@
+From dd812452fb91de492a8fd8d838d16cfc67cbfcf4 Mon Sep 17 00:00:00 2001
+From: Eliad Peller <eliad@wizery.com>
+Date: Thu, 28 Oct 2010 21:46:43 +0200
+Subject: [PATCH 05/15] wl1271: set wl->vif only if add_interface succeeded.
+
+set wl->vif to the newly created interface only after the firmware booted
+successfully. on the way - make the function flow more clear.
+
+Signed-off-by: Eliad Peller <eliad@wizery.com>
+Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com>
+Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
+---
+ drivers/net/wireless/wl12xx/wl1271_main.c | 33 +++++++++++++++++-----------
+ 1 files changed, 20 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
+index 49ec0ef..78273c9 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_main.c
++++ b/drivers/net/wireless/wl12xx/wl1271_main.c
+@@ -939,18 +939,19 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
+ struct wiphy *wiphy = hw->wiphy;
+ int retries = WL1271_BOOT_RETRIES;
+ int ret = 0;
++ bool booted = false;
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
+ vif->type, vif->addr);
+
+ mutex_lock(&wl->mutex);
+ if (wl->vif) {
++ wl1271_debug(DEBUG_MAC80211,
++ "multiple vifs are not supported yet");
+ ret = -EBUSY;
+ goto out;
+ }
+
+- wl->vif = vif;
+-
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ wl->bss_type = BSS_TYPE_STA_BSS;
+@@ -988,15 +989,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
+ if (ret < 0)
+ goto irq_disable;
+
+- wl->state = WL1271_STATE_ON;
+- wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
+-
+- /* update hw/fw version info in wiphy struct */
+- wiphy->hw_version = wl->chip.id;
+- strncpy(wiphy->fw_version, wl->chip.fw_ver,
+- sizeof(wiphy->fw_version));
+-
+- goto out;
++ booted = true;
++ break;
+
+ irq_disable:
+ wl1271_disable_interrupts(wl);
+@@ -1014,8 +1008,21 @@ power_off:
+ wl1271_power_off(wl);
+ }
+
+- wl1271_error("firmware boot failed despite %d retries",
+- WL1271_BOOT_RETRIES);
++ if (!booted) {
++ wl1271_error("firmware boot failed despite %d retries",
++ WL1271_BOOT_RETRIES);
++ goto out;
++ }
++
++ wl->vif = vif;
++ wl->state = WL1271_STATE_ON;
++ wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
++
++ /* update hw/fw version info in wiphy struct */
++ wiphy->hw_version = wl->chip.id;
++ strncpy(wiphy->fw_version, wl->chip.fw_ver,
++ sizeof(wiphy->fw_version));
++
+ out:
+ mutex_unlock(&wl->mutex);
+
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0006-wl12xx-Unset-bssid-filter-ssid-and-bssid-from-firmwa.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0006-wl12xx-Unset-bssid-filter-ssid-and-bssid-from-firmwa.patch
new file mode 100644
index 0000000000..5b83bc96d5
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0006-wl12xx-Unset-bssid-filter-ssid-and-bssid-from-firmwa.patch
@@ -0,0 +1,44 @@
+From 54b32b60bed66ac4ecf00279466496d9d4e80afa Mon Sep 17 00:00:00 2001
+From: Juuso Oikarinen <juuso.oikarinen@nokia.com>
+Date: Mon, 22 Nov 2010 12:59:08 +0200
+Subject: [PATCH 06/15] wl12xx: Unset bssid filter, ssid and bssid from firmware on disassoc
+
+On the disassociation event from the mac80211, the wl12xx driver does not
+clear the chipset configuration related to the AP - i.e. it does not perform
+a DISCONNECT and then a JOIN with zero SSID and dummy BSSID. Also, it does not
+unset the BSSID filter.
+
+Often this is not a problem, as the above is performed upon entering idle
+state. But if a scenario arises where a new association is attempted without
+cycling through idle state, the new association will fail.
+
+Fix this by resetting the firmware state on disassociation.
+
+Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
+Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com>
+Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
+---
+ drivers/net/wireless/wl12xx/wl1271_main.c | 5 ++++-
+ 1 files changed, 4 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
+index 78273c9..db97648 100644
+--- a/drivers/net/wireless/wl12xx/wl1271_main.c
++++ b/drivers/net/wireless/wl12xx/wl1271_main.c
+@@ -1919,9 +1919,12 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
+
+ /* Disable the keep-alive feature */
+ ret = wl1271_acx_keep_alive_mode(wl, false);
+-
+ if (ret < 0)
+ goto out_sleep;
++
++ /* restore the bssid filter and go to dummy bssid */
++ wl1271_unjoin(wl);
++ wl1271_dummy_join(wl);
+ }
+
+ }
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0007-drivers-media-radio-wl128x-FM-Driver-common-header-f.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0007-drivers-media-radio-wl128x-FM-Driver-common-header-f.patch
new file mode 100644
index 0000000000..d104a7270b
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0007-drivers-media-radio-wl128x-FM-Driver-common-header-f.patch
@@ -0,0 +1,268 @@
+From f568ec9bb6ccd1e17278dcab3fbc810cf2e071ac Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:21 +0000
+Subject: [PATCH 07/15] drivers:media:radio: wl128x: FM Driver common header file
+
+These are common headers used in FM submodules (FM V4L2,
+FM common, FM Rx,and FM TX).
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/wl128x/fmdrv.h | 244 ++++++++++++++++++++++++++++++++++++
+ 1 files changed, 244 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/media/radio/wl128x/fmdrv.h
+
+diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
+new file mode 100644
+index 0000000..392b62d
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv.h
+@@ -0,0 +1,244 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ *
++ * Common header for all FM driver sub-modules.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef _FM_DRV_H
++#define _FM_DRV_H
++
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <linux/timer.h>
++#include <linux/version.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-ctrls.h>
++
++#define FM_DRV_VERSION "0.09"
++/* Should match with FM_DRV_VERSION */
++#define FM_DRV_RADIO_VERSION KERNEL_VERSION(0, 0, 1)
++#define FM_DRV_NAME "ti_fmdrv"
++#define FM_DRV_CARD_SHORT_NAME "TI FM Radio"
++#define FM_DRV_CARD_LONG_NAME "Texas Instruments FM Radio"
++
++/* Flag info */
++#define FM_INTTASK_RUNNING 0
++#define FM_INTTASK_SCHEDULE_PENDING 1
++#define FM_FW_DW_INPROGRESS 2
++#define FM_CORE_READY 3
++#define FM_CORE_TRANSPORT_READY 4
++#define FM_AF_SWITCH_INPROGRESS 5
++#define FM_CORE_TX_XMITING 6
++
++#define FM_TUNE_COMPLETE 0x1
++#define FM_BAND_LIMIT 0x2
++
++#define FM_DRV_TX_TIMEOUT (5*HZ) /* 5 seconds */
++#define FM_DRV_RX_SEEK_TIMEOUT (20*HZ) /* 20 seconds */
++
++#define NO_OF_ENTRIES_IN_ARRAY(array) (sizeof(array) / sizeof(array[0]))
++
++#define fmerr(format, ...) \
++ printk(KERN_ERR "fmdrv: " format, ## __VA_ARGS__)
++#define fmwarn(format, ...) \
++ printk(KERN_WARNING "fmdrv: " format, ##__VA_ARGS__)
++#ifdef DEBUG
++#define fmdbg(format, ...) \
++ printk(KERN_DEBUG "fmdrv: " format, ## __VA_ARGS__)
++#else /* DEBUG */
++#define fmdbg(format, ...)
++#endif
++enum {
++ FM_MODE_OFF,
++ FM_MODE_TX,
++ FM_MODE_RX,
++ FM_MODE_ENTRY_MAX
++};
++
++#define FM_RX_RDS_INFO_FIELD_MAX 8 /* 4 Group * 2 Bytes */
++
++/* RX RDS data format */
++struct fm_rdsdata_format {
++ union {
++ struct {
++ u8 buff[FM_RX_RDS_INFO_FIELD_MAX];
++ } groupdatabuff;
++ struct {
++ u16 pidata;
++ u8 blk_b[2];
++ u8 blk_c[2];
++ u8 blk_d[2];
++ } groupgeneral;
++ struct {
++ u16 pidata;
++ u8 blk_b[2];
++ u8 af[2];
++ u8 ps[2];
++ } group0A;
++ struct {
++ u16 pi[2];
++ u8 blk_b[2];
++ u8 ps[2];
++ } group0B;
++ } data;
++};
++
++/* FM region (Europe/US, Japan) info */
++struct region_info {
++ u32 chanl_space;
++ u32 bot_freq;
++ u32 top_freq;
++ u8 fm_band;
++};
++struct fmdev;
++typedef void (*int_handler_prototype) (struct fmdev *);
++
++/* FM Interrupt processing related info */
++struct fm_irq {
++ u8 stage;
++ u16 flag; /* FM interrupt flag */
++ u16 mask; /* FM interrupt mask */
++ /* Interrupt process timeout handler */
++ struct timer_list timer;
++ u8 retry;
++ int_handler_prototype *handlers;
++};
++
++/* RDS info */
++struct fm_rds {
++ u8 flag; /* RX RDS on/off status */
++ u8 last_blk_idx; /* Last received RDS block */
++
++ /* RDS buffer */
++ wait_queue_head_t read_queue;
++ u32 buf_size; /* Size is always multiple of 3 */
++ u32 wr_idx;
++ u32 rd_idx;
++ u8 *buff;
++};
++
++#define FM_RDS_MAX_AF_LIST 25
++
++/*
++ * Current RX channel Alternate Frequency cache.
++ * This info is used to switch to other freq (AF)
++ * when current channel signal strengh is below RSSI threshold.
++ */
++struct tuned_station_info {
++ u16 picode;
++ u32 af_cache[FM_RDS_MAX_AF_LIST];
++ u8 afcache_size;
++ u8 af_list_max;
++};
++
++/* FM RX mode info */
++struct fm_rx {
++ struct region_info region; /* Current selected band */
++ u32 freq; /* Current RX frquency */
++ u8 mute_mode; /* Current mute mode */
++ u8 deemphasis_mode; /* Current deemphasis mode */
++ /* RF dependent soft mute mode */
++ u8 rf_depend_mute;
++ u16 volume; /* Current volume level */
++ u16 rssi_threshold; /* Current RSSI threshold level */
++ /* Holds the index of the current AF jump */
++ u8 afjump_idx;
++ /* Will hold the frequency before the jump */
++ u32 freq_before_jump;
++ u8 rds_mode; /* RDS operation mode (RDS/RDBS) */
++ u8 af_mode; /* Alternate frequency on/off */
++ struct tuned_station_info stat_info;
++ struct fm_rds rds;
++};
++
++#define FMTX_RDS_TXT_STR_SIZE 25
++/*
++ * FM TX RDS data
++ *
++ * @ text_type: is the text following PS or RT
++ * @ text: radio text string which could either be PS or RT
++ * @ af_freq: alternate frequency for Tx
++ * TODO: to be declared in application
++ */
++struct tx_rds {
++ u8 text_type;
++ u8 text[FMTX_RDS_TXT_STR_SIZE];
++ u8 flag;
++ u32 af_freq;
++};
++/*
++ * FM TX global data
++ *
++ * @ pwr_lvl: Power Level of the Transmission from mixer control
++ * @ xmit_state: Transmission state = Updated locally upon Start/Stop
++ * @ audio_io: i2S/Analog
++ * @ tx_frq: Transmission frequency
++ */
++struct fmtx_data {
++ u8 pwr_lvl;
++ u8 xmit_state;
++ u8 audio_io;
++ u8 region;
++ u16 aud_mode;
++ u32 preemph;
++ u32 tx_frq;
++ struct tx_rds rds;
++};
++
++/* FM driver operation structure */
++struct fmdev {
++ struct video_device *radio_dev; /* V4L2 video device pointer */
++ struct snd_card *card; /* Card which holds FM mixer controls */
++ u16 asci_id;
++ spinlock_t rds_buff_lock; /* To protect access to RDS buffer */
++ spinlock_t resp_skb_lock; /* To protect access to received SKB */
++
++ long flag; /* FM driver state machine info */
++ u8 streg_cbdata; /* status of ST registration */
++
++ struct sk_buff_head rx_q; /* RX queue */
++ struct tasklet_struct rx_task; /* RX Tasklet */
++
++ struct sk_buff_head tx_q; /* TX queue */
++ struct tasklet_struct tx_task; /* TX Tasklet */
++ unsigned long last_tx_jiffies; /* Timestamp of last pkt sent */
++ atomic_t tx_cnt; /* Number of packets can send at a time */
++
++ struct sk_buff *resp_skb; /* Response from the chip */
++ /* Main task completion handler */
++ struct completion maintask_comp;
++ /* Opcode of last command sent to the chip */
++ u8 pre_op;
++ /* Handler used for wakeup when response packet is received */
++ struct completion *resp_comp;
++ struct fm_irq irq_info;
++ u8 curr_fmmode; /* Current FM chip mode (TX, RX, OFF) */
++ struct fm_rx rx; /* FM receiver info */
++ struct fmtx_data tx_data;
++
++ /* V4L2 ctrl framwork handler*/
++ struct v4l2_ctrl_handler ctrl_handler;
++
++ /* For core assisted locking */
++ struct mutex mutex;
++};
++#endif
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0008-drivers-media-radio-wl128x-FM-Driver-V4L2-sources.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0008-drivers-media-radio-wl128x-FM-Driver-V4L2-sources.patch
new file mode 100644
index 0000000000..c3eae97f96
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0008-drivers-media-radio-wl128x-FM-Driver-V4L2-sources.patch
@@ -0,0 +1,645 @@
+From d532e33a286ec2275b441c05675de52cd5b069d2 Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:22 +0000
+Subject: [PATCH 08/15] drivers:media:radio: wl128x: FM Driver V4L2 sources
+
+This module interfaces V4L2 subsystem and FM common module.
+It registers itself with V4L2 as Radio module.
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/wl128x/fmdrv_v4l2.c | 580 +++++++++++++++++++++++++++++++
+ drivers/media/radio/wl128x/fmdrv_v4l2.h | 33 ++
+ 2 files changed, 613 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_v4l2.c
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_v4l2.h
+
+diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
+new file mode 100644
+index 0000000..d50e5ac
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
+@@ -0,0 +1,580 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ * This file provides interfaces to V4L2 subsystem.
++ *
++ * This module registers with V4L2 subsystem as Radio
++ * data system interface (/dev/radio). During the registration,
++ * it will expose two set of function pointers.
++ *
++ * 1) File operation related API (open, close, read, write, poll...etc).
++ * 2) Set of V4L2 IOCTL complaint API.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ * Author: Raja Mani <raja_mani@ti.com>
++ * Author: Manjunatha Halli <manjunatha_halli@ti.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include "fmdrv.h"
++#include "fmdrv_v4l2.h"
++#include "fmdrv_common.h"
++#include "fmdrv_rx.h"
++#include "fmdrv_tx.h"
++
++static struct video_device *gradio_dev;
++static u8 radio_disconnected;
++
++/* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */
++
++/* Read RX RDS data */
++static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf,
++ size_t count, loff_t *ppos)
++{
++ u8 rds_mode;
++ int ret;
++ struct fmdev *fmdev;
++
++ fmdev = video_drvdata(file);
++
++ if (!radio_disconnected) {
++ fmerr("FM device is already disconnected\n");
++ return -EIO;
++ }
++
++ /* Turn on RDS mode , if it is disabled */
++ ret = fm_rx_get_rds_mode(fmdev, &rds_mode);
++ if (ret < 0) {
++ fmerr("Unable to read current rds mode\n");
++ return ret;
++ }
++
++ if (rds_mode == FM_RDS_DISABLE) {
++ ret = fmc_set_rds_mode(fmdev, FM_RDS_ENABLE);
++ if (ret < 0) {
++ fmerr("Failed to enable rds mode\n");
++ return ret;
++ }
++ }
++
++ /* Copy RDS data from internal buffer to user buffer */
++ return fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count);
++}
++
++/* Write TX RDS data */
++static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf,
++ size_t count, loff_t *ppos)
++{
++ struct tx_rds rds;
++ int ret;
++ struct fmdev *fmdev;
++
++ ret = copy_from_user(&rds, buf, sizeof(rds));
++ fmdbg("(%d)type: %d, text %s, af %d\n",
++ ret, rds.text_type, rds.text, rds.af_freq);
++
++ fmdev = video_drvdata(file);
++ fm_tx_set_radio_text(fmdev, rds.text, rds.text_type);
++ fm_tx_set_af(fmdev, rds.af_freq);
++
++ return 0;
++}
++
++static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts)
++{
++ int ret;
++ struct fmdev *fmdev;
++
++ fmdev = video_drvdata(file);
++ ret = fmc_is_rds_data_available(fmdev, file, pts);
++ if (ret < 0)
++ return POLLIN | POLLRDNORM;
++
++ return 0;
++}
++
++/*
++ * Handle open request for "/dev/radioX" device.
++ * Start with FM RX mode as default.
++ */
++static int fm_v4l2_fops_open(struct file *file)
++{
++ int ret;
++ struct fmdev *fmdev = NULL;
++
++ /* Don't allow multiple open */
++ if (radio_disconnected) {
++ fmerr("FM device is already opened\n");
++ return -EBUSY;
++ }
++
++ fmdev = video_drvdata(file);
++
++ ret = fmc_prepare(fmdev);
++ if (ret < 0) {
++ fmerr("Unable to prepare FM CORE\n");
++ return ret;
++ }
++
++ fmdbg("Load FM RX firmware..\n");
++
++ ret = fmc_set_mode(fmdev, FM_MODE_RX);
++ if (ret < 0) {
++ fmerr("Unable to load FM RX firmware\n");
++ return ret;
++ }
++ radio_disconnected = 1;
++
++ return ret;
++}
++
++static int fm_v4l2_fops_release(struct file *file)
++{
++ int ret;
++ struct fmdev *fmdev;
++
++ fmdev = video_drvdata(file);
++ if (!radio_disconnected) {
++ fmdbg("FM device is already closed\n");
++ return 0;
++ }
++
++ ret = fmc_set_mode(fmdev, FM_MODE_OFF);
++ if (ret < 0) {
++ fmerr("Unable to turn off the chip\n");
++ return ret;
++ }
++
++ ret = fmc_release(fmdev);
++ if (ret < 0) {
++ fmerr("FM CORE release failed\n");
++ return ret;
++ }
++ radio_disconnected = 0;
++
++ return ret;
++}
++
++/* V4L2 RADIO (/dev/radioX) device IOCTL interfaces */
++static int fm_v4l2_vidioc_querycap(struct file *file, void *priv,
++ struct v4l2_capability *capability)
++{
++ strlcpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver));
++ strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME,
++ sizeof(capability->card));
++ sprintf(capability->bus_info, "UART");
++ capability->version = FM_DRV_RADIO_VERSION;
++ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
++ V4L2_CAP_RADIO | V4L2_CAP_MODULATOR |
++ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
++ V4L2_CAP_RDS_CAPTURE;
++
++ return 0;
++}
++
++static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct fmdev *fmdev = container_of(ctrl->handler,
++ struct fmdev, ctrl_handler);
++
++ switch (ctrl->id) {
++ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
++ ctrl->val = fm_tx_get_tune_cap_val(fmdev);
++ break;
++ default:
++ fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id);
++ break;
++ }
++
++ return 0;
++}
++
++static int fm_v4l2_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct fmdev *fmdev = container_of(ctrl->handler,
++ struct fmdev, ctrl_handler);
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_VOLUME: /* set volume */
++ return fm_rx_set_volume(fmdev, (u16)ctrl->val);
++
++ case V4L2_CID_AUDIO_MUTE: /* set mute */
++ return fmc_set_mute_mode(fmdev, (u8)ctrl->val);
++
++ case V4L2_CID_TUNE_POWER_LEVEL:
++ /* set TX power level - ext control */
++ return fm_tx_set_pwr_lvl(fmdev, (u8)ctrl->val);
++
++ case V4L2_CID_TUNE_PREEMPHASIS:
++ return fm_tx_set_preemph_filter(fmdev, (u8) ctrl->val);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *audio)
++{
++ memset(audio, 0, sizeof(*audio));
++ strcpy(audio->name, "Radio");
++ audio->capability = V4L2_AUDCAP_STEREO;
++
++ return 0;
++}
++
++static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *audio)
++{
++ if (audio->index != 0)
++ return -EINVAL;
++
++ return 0;
++}
++
++/* Get tuner attributes. If current mode is NOT RX, return error */
++static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv,
++ struct v4l2_tuner *tuner)
++{
++ struct fmdev *fmdev = video_drvdata(file);
++ u32 bottom_freq;
++ u32 top_freq;
++ u16 stereo_mono_mode;
++ u16 rssilvl;
++ int ret;
++
++ if (tuner->index != 0)
++ return -EINVAL;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ ret = fm_rx_get_band_freq_range(fmdev, &bottom_freq, &top_freq);
++ if (ret != 0)
++ return ret;
++
++ ret = fm_rx_get_stereo_mono(fmdev, &stereo_mono_mode);
++ if (ret != 0)
++ return ret;
++
++ ret = fm_rx_get_rssi_level(fmdev, &rssilvl);
++ if (ret != 0)
++ return ret;
++
++ strcpy(tuner->name, "FM");
++ tuner->type = V4L2_TUNER_RADIO;
++ /* Store rangelow and rangehigh freq in unit of 62.5 Hz */
++ tuner->rangelow = bottom_freq * 16;
++ tuner->rangehigh = top_freq * 16;
++ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO |
++ ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0);
++ tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
++ V4L2_TUNER_CAP_LOW;
++ tuner->audmode = (stereo_mono_mode ?
++ V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO);
++
++ /*
++ * Actual rssi value lies in between -128 to +127.
++ * Convert this range from 0 to 255 by adding +128
++ */
++ rssilvl += 128;
++
++ /*
++ * Return signal strength value should be within 0 to 65535.
++ * Find out correct signal radio by multiplying (65535/255) = 257
++ */
++ tuner->signal = rssilvl * 257;
++ tuner->afc = 0;
++
++ return ret;
++}
++
++/*
++ * Set tuner attributes. If current mode is NOT RX, set to RX.
++ * Currently, we set only audio mode (mono/stereo) and RDS state (on/off).
++ * Should we set other tuner attributes, too?
++ */
++static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv,
++ struct v4l2_tuner *tuner)
++{
++ struct fmdev *fmdev = video_drvdata(file);
++ u16 aud_mode;
++ u8 rds_mode;
++ int ret;
++
++ if (tuner->index != 0)
++ return -EINVAL;
++
++ aud_mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ?
++ FM_STEREO_MODE : FM_MONO_MODE;
++ rds_mode = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ?
++ FM_RDS_ENABLE : FM_RDS_DISABLE;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX) {
++ ret = fmc_set_mode(fmdev, FM_MODE_RX);
++ if (ret < 0) {
++ fmerr("Failed to set RX mode\n");
++ return ret;
++ }
++ }
++
++ ret = fmc_set_stereo_mono(fmdev, aud_mode);
++ if (ret < 0) {
++ fmerr("Failed to set RX stereo/mono mode\n");
++ return ret;
++ }
++
++ ret = fmc_set_rds_mode(fmdev, rds_mode);
++ if (ret < 0)
++ fmerr("Failed to set RX RDS mode\n");
++
++ return ret;
++}
++
++/* Get tuner or modulator radio frequency */
++static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv,
++ struct v4l2_frequency *freq)
++{
++ struct fmdev *fmdev = video_drvdata(file);
++ int ret;
++
++ ret = fmc_get_freq(fmdev, &freq->frequency);
++ if (ret < 0) {
++ fmerr("Failed to get frequency\n");
++ return ret;
++ }
++
++ /* Frequency unit of 62.5 Hz*/
++ freq->frequency = (u32) freq->frequency * 16;
++
++ return 0;
++}
++
++/* Set tuner or modulator radio frequency */
++static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv,
++ struct v4l2_frequency *freq)
++{
++ struct fmdev *fmdev = video_drvdata(file);
++
++ /*
++ * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency
++ * in units of 62.5 Hz.
++ */
++ freq->frequency = (u32)(freq->frequency / 16);
++
++ return fmc_set_freq(fmdev, freq->frequency);
++}
++
++/* Set hardware frequency seek. If current mode is NOT RX, set it RX. */
++static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv,
++ struct v4l2_hw_freq_seek *seek)
++{
++ struct fmdev *fmdev = video_drvdata(file);
++ int ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX) {
++ ret = fmc_set_mode(fmdev, FM_MODE_RX);
++ if (ret != 0) {
++ fmerr("Failed to set RX mode\n");
++ return ret;
++ }
++ }
++
++ ret = fm_rx_seek(fmdev, seek->seek_upward, seek->wrap_around,
++ seek->spacing);
++ if (ret < 0)
++ fmerr("RX seek failed - %d\n", ret);
++
++ return ret;
++}
++/* Get modulator attributes. If mode is not TX, return no attributes. */
++static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv,
++ struct v4l2_modulator *mod)
++{
++ struct fmdev *fmdev = video_drvdata(file);;
++
++ if (mod->index != 0)
++ return -EINVAL;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX)
++ return -EPERM;
++
++ mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ?
++ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) |
++ ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ?
++ V4L2_TUNER_SUB_RDS : 0);
++
++ mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
++ V4L2_TUNER_CAP_LOW;
++
++ return 0;
++}
++
++/* Set modulator attributes. If mode is not TX, set to TX. */
++static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv,
++ struct v4l2_modulator *mod)
++{
++ struct fmdev *fmdev = video_drvdata(file);
++ u8 rds_mode;
++ u16 aud_mode;
++ int ret;
++
++ if (mod->index != 0)
++ return -EINVAL;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX) {
++ ret = fmc_set_mode(fmdev, FM_MODE_TX);
++ if (ret != 0) {
++ fmerr("Failed to set TX mode\n");
++ return ret;
++ }
++ }
++
++ aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ?
++ FM_STEREO_MODE : FM_MONO_MODE;
++ rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ?
++ FM_RDS_ENABLE : FM_RDS_DISABLE;
++ ret = fm_tx_set_stereo_mono(fmdev, aud_mode);
++ if (ret < 0) {
++ fmerr("Failed to set mono/stereo mode for TX\n");
++ return ret;
++ }
++ ret = fm_tx_set_rds_mode(fmdev, rds_mode);
++ if (ret < 0)
++ fmerr("Failed to set rds mode for TX\n");
++
++ return ret;
++}
++
++static const struct v4l2_file_operations fm_drv_fops = {
++ .owner = THIS_MODULE,
++ .read = fm_v4l2_fops_read,
++ .write = fm_v4l2_fops_write,
++ .poll = fm_v4l2_fops_poll,
++ .unlocked_ioctl = video_ioctl2,
++ .open = fm_v4l2_fops_open,
++ .release = fm_v4l2_fops_release,
++};
++
++static const struct v4l2_ctrl_ops fm_ctrl_ops = {
++ .s_ctrl = fm_v4l2_s_ctrl,
++ .g_volatile_ctrl = fm_g_volatile_ctrl,
++};
++static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = {
++ .vidioc_querycap = fm_v4l2_vidioc_querycap,
++ .vidioc_g_audio = fm_v4l2_vidioc_g_audio,
++ .vidioc_s_audio = fm_v4l2_vidioc_s_audio,
++ .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner,
++ .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner,
++ .vidioc_g_frequency = fm_v4l2_vidioc_g_freq,
++ .vidioc_s_frequency = fm_v4l2_vidioc_s_freq,
++ .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek,
++ .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator,
++ .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator
++};
++
++/* V4L2 RADIO device parent structure */
++static struct video_device fm_viddev_template = {
++ .fops = &fm_drv_fops,
++ .ioctl_ops = &fm_drv_ioctl_ops,
++ .name = FM_DRV_NAME,
++ .release = video_device_release,
++};
++
++int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
++{
++ struct v4l2_ctrl *ctrl;
++ int ret;
++
++ /* Init mutex for core locking */
++ mutex_init(&fmdev->mutex);
++
++ /* Allocate new video device */
++ gradio_dev = video_device_alloc();
++ if (NULL == gradio_dev) {
++ fmerr("Can't allocate video device\n");
++ return -ENOMEM;
++ }
++
++ /* Setup FM driver's V4L2 properties */
++ memcpy(gradio_dev, &fm_viddev_template, sizeof(fm_viddev_template));
++
++ video_set_drvdata(gradio_dev, fmdev);
++
++ gradio_dev->lock = &fmdev->mutex;
++
++ /* Register with V4L2 subsystem as RADIO device */
++ if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
++ video_device_release(gradio_dev);
++ fmerr("Could not register video device\n");
++ return -ENOMEM;
++ }
++
++ fmdev->radio_dev = gradio_dev;
++
++ /* Register to v4l2 ctrl handler framework */
++ fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler;
++
++ ret = v4l2_ctrl_handler_init(&fmdev->ctrl_handler, 5);
++ if (ret < 0) {
++ fmerr("(fmdev): Can't init ctrl handler\n");
++ v4l2_ctrl_handler_free(&fmdev->ctrl_handler);
++ return -EBUSY;
++ }
++
++ /*
++ * Following controls are handled by V4L2 control framework.
++ * Added in ascending ID order.
++ */
++ v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
++ V4L2_CID_AUDIO_VOLUME, FM_RX_VOLUME_MIN,
++ FM_RX_VOLUME_MAX, 1, FM_RX_VOLUME_MAX);
++
++ v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
++
++ v4l2_ctrl_new_std_menu(&fmdev->ctrl_handler, &fm_ctrl_ops,
++ V4L2_CID_TUNE_PREEMPHASIS, V4L2_PREEMPHASIS_75_uS,
++ 0, V4L2_PREEMPHASIS_75_uS);
++
++ v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
++ V4L2_CID_TUNE_POWER_LEVEL, FM_PWR_LVL_LOW,
++ FM_PWR_LVL_HIGH, 1, FM_PWR_LVL_HIGH);
++
++ ctrl = v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops,
++ V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0,
++ 255, 1, 255);
++
++ if (ctrl)
++ ctrl->is_volatile = 1;
++
++ return 0;
++}
++
++void *fm_v4l2_deinit_video_device(void)
++{
++ struct fmdev *fmdev;
++
++
++ fmdev = video_get_drvdata(gradio_dev);
++
++ /* Unregister to v4l2 ctrl handler framework*/
++ v4l2_ctrl_handler_free(&fmdev->ctrl_handler);
++
++ /* Unregister RADIO device from V4L2 subsystem */
++ video_unregister_device(gradio_dev);
++
++ return fmdev;
++}
+diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.h b/drivers/media/radio/wl128x/fmdrv_v4l2.h
+new file mode 100644
+index 0000000..0ba79d7
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_v4l2.h
+@@ -0,0 +1,33 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ *
++ * FM V4L2 module header.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef _FMDRV_V4L2_H
++#define _FMDRV_V4L2_H
++
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-ctrls.h>
++
++int fm_v4l2_init_video_device(struct fmdev *, int);
++void *fm_v4l2_deinit_video_device(void);
++
++#endif
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0009-drivers-media-radio-wl128x-FM-Driver-Common-sources.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0009-drivers-media-radio-wl128x-FM-Driver-Common-sources.patch
new file mode 100644
index 0000000000..61dc164b5d
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0009-drivers-media-radio-wl128x-FM-Driver-Common-sources.patch
@@ -0,0 +1,2114 @@
+From 1c32040233847f9c7998e7c557fa80dfd953e236 Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:23 +0000
+Subject: [PATCH 09/15] drivers:media:radio: wl128x: FM Driver Common sources
+
+These are the sources for the common interfaces required by the
+FM V4L2 driver for TI WL127x and WL128x chips.
+
+These implement the FM channel-8 protocol communication with the
+chip. This makes use of the Shared Transport as its transport.
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/wl128x/fmdrv_common.c | 1677 +++++++++++++++++++++++++++++
+ drivers/media/radio/wl128x/fmdrv_common.h | 402 +++++++
+ 2 files changed, 2079 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_common.c
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_common.h
+
+diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
+new file mode 100644
+index 0000000..12f4c65
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_common.c
+@@ -0,0 +1,1677 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ *
++ * This sub-module of FM driver is common for FM RX and TX
++ * functionality. This module is responsible for:
++ * 1) Forming group of Channel-8 commands to perform particular
++ * functionality (eg., frequency set require more than
++ * one Channel-8 command to be sent to the chip).
++ * 2) Sending each Channel-8 command to the chip and reading
++ * response back over Shared Transport.
++ * 3) Managing TX and RX Queues and Tasklets.
++ * 4) Handling FM Interrupt packet and taking appropriate action.
++ * 5) Loading FM firmware to the chip (common, FM TX, and FM RX
++ * firmware files based on mode selection)
++ *
++ * Copyright (C) 2011 Texas Instruments
++ * Author: Raja Mani <raja_mani@ti.com>
++ * Author: Manjunatha Halli <manjunatha_halli@ti.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include "fmdrv.h"
++#include "fmdrv_v4l2.h"
++#include "fmdrv_common.h"
++#include <linux/ti_wilink_st.h>
++#include "fmdrv_rx.h"
++#include "fmdrv_tx.h"
++
++/* Region info */
++static struct region_info region_configs[] = {
++ /* Europe/US */
++ {
++ .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
++ .bot_freq = 87500, /* 87.5 MHz */
++ .top_freq = 108000, /* 108 MHz */
++ .fm_band = 0,
++ },
++ /* Japan */
++ {
++ .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL,
++ .bot_freq = 76000, /* 76 MHz */
++ .top_freq = 90000, /* 90 MHz */
++ .fm_band = 1,
++ },
++};
++
++/* Band selection */
++static u8 default_radio_region; /* Europe/US */
++module_param(default_radio_region, byte, 0);
++MODULE_PARM_DESC(default_radio_region, "Region: 0=Europe/US, 1=Japan");
++
++/* RDS buffer blocks */
++static u32 default_rds_buf = 300;
++module_param(default_rds_buf, uint, 0444);
++MODULE_PARM_DESC(rds_buf, "RDS buffer entries");
++
++/* Radio Nr */
++static u32 radio_nr = -1;
++module_param(radio_nr, int, 0444);
++MODULE_PARM_DESC(radio_nr, "Radio Nr");
++
++/* FM irq handlers forward declaration */
++static void fm_irq_send_flag_getcmd(struct fmdev *);
++static void fm_irq_handle_flag_getcmd_resp(struct fmdev *);
++static void fm_irq_handle_hw_malfunction(struct fmdev *);
++static void fm_irq_handle_rds_start(struct fmdev *);
++static void fm_irq_send_rdsdata_getcmd(struct fmdev *);
++static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *);
++static void fm_irq_handle_rds_finish(struct fmdev *);
++static void fm_irq_handle_tune_op_ended(struct fmdev *);
++static void fm_irq_handle_power_enb(struct fmdev *);
++static void fm_irq_handle_low_rssi_start(struct fmdev *);
++static void fm_irq_afjump_set_pi(struct fmdev *);
++static void fm_irq_handle_set_pi_resp(struct fmdev *);
++static void fm_irq_afjump_set_pimask(struct fmdev *);
++static void fm_irq_handle_set_pimask_resp(struct fmdev *);
++static void fm_irq_afjump_setfreq(struct fmdev *);
++static void fm_irq_handle_setfreq_resp(struct fmdev *);
++static void fm_irq_afjump_enableint(struct fmdev *);
++static void fm_irq_afjump_enableint_resp(struct fmdev *);
++static void fm_irq_start_afjump(struct fmdev *);
++static void fm_irq_handle_start_afjump_resp(struct fmdev *);
++static void fm_irq_afjump_rd_freq(struct fmdev *);
++static void fm_irq_afjump_rd_freq_resp(struct fmdev *);
++static void fm_irq_handle_low_rssi_finish(struct fmdev *);
++static void fm_irq_send_intmsk_cmd(struct fmdev *);
++static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *);
++
++/*
++ * When FM common module receives interrupt packet, following handlers
++ * will be executed one after another to service the interrupt(s)
++ */
++enum fmc_irq_handler_index {
++ FM_SEND_FLAG_GETCMD_IDX,
++ FM_HANDLE_FLAG_GETCMD_RESP_IDX,
++
++ /* HW malfunction irq handler */
++ FM_HW_MAL_FUNC_IDX,
++
++ /* RDS threshold reached irq handler */
++ FM_RDS_START_IDX,
++ FM_RDS_SEND_RDS_GETCMD_IDX,
++ FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX,
++ FM_RDS_FINISH_IDX,
++
++ /* Tune operation ended irq handler */
++ FM_HW_TUNE_OP_ENDED_IDX,
++
++ /* TX power enable irq handler */
++ FM_HW_POWER_ENB_IDX,
++
++ /* Low RSSI irq handler */
++ FM_LOW_RSSI_START_IDX,
++ FM_AF_JUMP_SETPI_IDX,
++ FM_AF_JUMP_HANDLE_SETPI_RESP_IDX,
++ FM_AF_JUMP_SETPI_MASK_IDX,
++ FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX,
++ FM_AF_JUMP_SET_AF_FREQ_IDX,
++ FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX,
++ FM_AF_JUMP_ENABLE_INT_IDX,
++ FM_AF_JUMP_ENABLE_INT_RESP_IDX,
++ FM_AF_JUMP_START_AFJUMP_IDX,
++ FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX,
++ FM_AF_JUMP_RD_FREQ_IDX,
++ FM_AF_JUMP_RD_FREQ_RESP_IDX,
++ FM_LOW_RSSI_FINISH_IDX,
++
++ /* Interrupt process post action */
++ FM_SEND_INTMSK_CMD_IDX,
++ FM_HANDLE_INTMSK_CMD_RESP_IDX,
++};
++
++/* FM interrupt handler table */
++static int_handler_prototype int_handler_table[] = {
++ fm_irq_send_flag_getcmd,
++ fm_irq_handle_flag_getcmd_resp,
++ fm_irq_handle_hw_malfunction,
++ fm_irq_handle_rds_start, /* RDS threshold reached irq handler */
++ fm_irq_send_rdsdata_getcmd,
++ fm_irq_handle_rdsdata_getcmd_resp,
++ fm_irq_handle_rds_finish,
++ fm_irq_handle_tune_op_ended,
++ fm_irq_handle_power_enb, /* TX power enable irq handler */
++ fm_irq_handle_low_rssi_start,
++ fm_irq_afjump_set_pi,
++ fm_irq_handle_set_pi_resp,
++ fm_irq_afjump_set_pimask,
++ fm_irq_handle_set_pimask_resp,
++ fm_irq_afjump_setfreq,
++ fm_irq_handle_setfreq_resp,
++ fm_irq_afjump_enableint,
++ fm_irq_afjump_enableint_resp,
++ fm_irq_start_afjump,
++ fm_irq_handle_start_afjump_resp,
++ fm_irq_afjump_rd_freq,
++ fm_irq_afjump_rd_freq_resp,
++ fm_irq_handle_low_rssi_finish,
++ fm_irq_send_intmsk_cmd, /* Interrupt process post action */
++ fm_irq_handle_intmsk_cmd_resp
++};
++
++long (*g_st_write) (struct sk_buff *skb);
++static struct completion wait_for_fmdrv_reg_comp;
++
++static inline void fm_irq_call(struct fmdev *fmdev)
++{
++ fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev);
++}
++
++/* Continue next function in interrupt handler table */
++static inline void fm_irq_call_stage(struct fmdev *fmdev, u8 stage)
++{
++ fmdev->irq_info.stage = stage;
++ fm_irq_call(fmdev);
++}
++
++static inline void fm_irq_timeout_stage(struct fmdev *fmdev, u8 stage)
++{
++ fmdev->irq_info.stage = stage;
++ mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT);
++}
++
++#ifdef FM_DUMP_TXRX_PKT
++ /* To dump outgoing FM Channel-8 packets */
++inline void dump_tx_skb_data(struct sk_buff *skb)
++{
++ int len, len_org;
++ u8 index;
++ struct fm_cmd_msg_hdr *cmd_hdr;
++
++ cmd_hdr = (struct fm_cmd_msg_hdr *)skb->data;
++ printk(KERN_INFO "<<%shdr:%02x len:%02x opcode:%02x type:%s dlen:%02x",
++ fm_cb(skb)->completion ? " " : "*", cmd_hdr->hdr,
++ cmd_hdr->len, cmd_hdr->op,
++ cmd_hdr->rd_wr ? "RD" : "WR", cmd_hdr->dlen);
++
++ len_org = skb->len - FM_CMD_MSG_HDR_SIZE;
++ if (len_org > 0) {
++ printk("\n data(%d): ", cmd_hdr->dlen);
++ len = min(len_org, 14);
++ for (index = 0; index < len; index++)
++ printk("%x ",
++ skb->data[FM_CMD_MSG_HDR_SIZE + index]);
++ printk("%s", (len_org > 14) ? ".." : "");
++ }
++ printk("\n");
++}
++
++ /* To dump incoming FM Channel-8 packets */
++inline void dump_rx_skb_data(struct sk_buff *skb)
++{
++ int len, len_org;
++ u8 index;
++ struct fm_event_msg_hdr *evt_hdr;
++
++ evt_hdr = (struct fm_event_msg_hdr *)skb->data;
++ printk(KERN_INFO ">> hdr:%02x len:%02x sts:%02x numhci:%02x "
++ "opcode:%02x type:%s dlen:%02x", evt_hdr->hdr, evt_hdr->len,
++ evt_hdr->status, evt_hdr->num_fm_hci_cmds, evt_hdr->op,
++ (evt_hdr->rd_wr) ? "RD" : "WR", evt_hdr->dlen);
++
++ len_org = skb->len - FM_EVT_MSG_HDR_SIZE;
++ if (len_org > 0) {
++ printk("\n data(%d): ", evt_hdr->dlen);
++ len = min(len_org, 14);
++ for (index = 0; index < len; index++)
++ printk("%x ",
++ skb->data[FM_EVT_MSG_HDR_SIZE + index]);
++ printk("%s", (len_org > 14) ? ".." : "");
++ }
++ printk("\n");
++}
++#endif
++
++void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set)
++{
++ fmdev->rx.region = region_configs[region_to_set];
++}
++
++/*
++ * FM common sub-module will schedule this tasklet whenever it receives
++ * FM packet from ST driver.
++ */
++static void recv_tasklet(unsigned long arg)
++{
++ struct fmdev *fmdev;
++ struct fm_irq *irq_info;
++ struct fm_event_msg_hdr *evt_hdr;
++ struct sk_buff *skb;
++ u8 num_fm_hci_cmds;
++ unsigned long flags;
++
++ fmdev = (struct fmdev *)arg;
++ irq_info = &fmdev->irq_info;
++ /* Process all packets in the RX queue */
++ while ((skb = skb_dequeue(&fmdev->rx_q))) {
++ if (skb->len < sizeof(struct fm_event_msg_hdr)) {
++ fmerr("skb(%p) has only %d bytes"
++ "atleast need %d bytes to decode\n", skb,
++ skb->len, sizeof(struct fm_event_msg_hdr));
++ kfree_skb(skb);
++ continue;
++ }
++
++ evt_hdr = (void *)skb->data;
++ num_fm_hci_cmds = evt_hdr->num_fm_hci_cmds;
++
++ /* FM interrupt packet? */
++ if (evt_hdr->op == FM_INTERRUPT) {
++ /* FM interrupt handler started already? */
++ if (!test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
++ set_bit(FM_INTTASK_RUNNING, &fmdev->flag);
++ if (irq_info->stage != 0) {
++ fmerr("Inval stage resetting to zero\n");
++ irq_info->stage = 0;
++ }
++
++ /*
++ * Execute first function in interrupt handler
++ * table.
++ */
++ irq_info->handlers[irq_info->stage](fmdev);
++ } else {
++ set_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag);
++ }
++ kfree_skb(skb);
++ }
++ /* Anyone waiting for this with completion handler? */
++ else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp != NULL) {
++
++ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
++ fmdev->resp_skb = skb;
++ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
++ complete(fmdev->resp_comp);
++
++ fmdev->resp_comp = NULL;
++ atomic_set(&fmdev->tx_cnt, 1);
++ }
++ /* Is this for interrupt handler? */
++ else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp == NULL) {
++ if (fmdev->resp_skb != NULL)
++ fmerr("Response SKB ptr not NULL\n");
++
++ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
++ fmdev->resp_skb = skb;
++ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
++
++ /* Execute interrupt handler where state index points */
++ irq_info->handlers[irq_info->stage](fmdev);
++
++ kfree_skb(skb);
++ atomic_set(&fmdev->tx_cnt, 1);
++ } else {
++ fmerr("Nobody claimed SKB(%p),purging\n", skb);
++ }
++
++ /*
++ * Check flow control field. If Num_FM_HCI_Commands field is
++ * not zero, schedule FM TX tasklet.
++ */
++ if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt))
++ if (!skb_queue_empty(&fmdev->tx_q))
++ tasklet_schedule(&fmdev->tx_task);
++ }
++}
++
++/* FM send tasklet: is scheduled when FM packet has to be sent to chip */
++static void send_tasklet(unsigned long arg)
++{
++ struct fmdev *fmdev;
++ struct sk_buff *skb;
++ int len;
++
++ fmdev = (struct fmdev *)arg;
++
++ if (!atomic_read(&fmdev->tx_cnt))
++ return;
++
++ /* Check, is there any timeout happenned to last transmitted packet */
++ if ((jiffies - fmdev->last_tx_jiffies) > FM_DRV_TX_TIMEOUT) {
++ fmerr("TX timeout occurred\n");
++ atomic_set(&fmdev->tx_cnt, 1);
++ }
++
++ /* Send queued FM TX packets */
++ skb = skb_dequeue(&fmdev->tx_q);
++ if (!skb)
++ return;
++
++ atomic_dec(&fmdev->tx_cnt);
++ fmdev->pre_op = fm_cb(skb)->fm_op;
++
++ if (fmdev->resp_comp != NULL)
++ fmerr("Response completion handler is not NULL\n");
++
++ fmdev->resp_comp = fm_cb(skb)->completion;
++
++ /* Write FM packet to ST driver */
++ len = g_st_write(skb);
++ if (len < 0) {
++ kfree_skb(skb);
++ fmdev->resp_comp = NULL;
++ fmerr("TX tasklet failed to send skb(%p)\n", skb);
++ atomic_set(&fmdev->tx_cnt, 1);
++ } else {
++ fmdev->last_tx_jiffies = jiffies;
++ }
++}
++
++/*
++ * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for
++ * transmission
++ */
++static u32 fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
++ int payload_len, struct completion *wait_completion)
++{
++ struct sk_buff *skb;
++ struct fm_cmd_msg_hdr *hdr;
++ int size;
++
++ if (fm_op >= FM_INTERRUPT) {
++ fmerr("Invalid fm opcode - %d\n", fm_op);
++ return -EINVAL;
++ }
++ if (test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) && payload == NULL) {
++ fmerr("Payload data is NULL during fw download\n");
++ return -EINVAL;
++ }
++ if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag))
++ size =
++ FM_CMD_MSG_HDR_SIZE + ((payload == NULL) ? 0 : payload_len);
++ else
++ size = payload_len;
++
++ skb = alloc_skb(size, GFP_ATOMIC);
++ if (!skb) {
++ fmerr("No memory to create new SKB\n");
++ return -ENOMEM;
++ }
++ /*
++ * Don't fill FM header info for the commands which come from
++ * FM firmware file.
++ */
++ if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) ||
++ test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
++ /* Fill command header info */
++ hdr = (struct fm_cmd_msg_hdr *)skb_put(skb, FM_CMD_MSG_HDR_SIZE);
++ hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER; /* 0x08 */
++
++ /* 3 (fm_opcode,rd_wr,dlen) + payload len) */
++ hdr->len = ((payload == NULL) ? 0 : payload_len) + 3;
++
++ /* FM opcode */
++ hdr->op = fm_op;
++
++ /* read/write type */
++ hdr->rd_wr = type;
++ hdr->dlen = payload_len;
++ fm_cb(skb)->fm_op = fm_op;
++
++ /*
++ * If firmware download has finished and the command is
++ * not a read command then payload is != NULL - a write
++ * command with u16 payload - convert to be16
++ */
++ if (payload != NULL)
++ *(u16 *)payload = cpu_to_be16(*(u16 *)payload);
++
++ } else if (payload != NULL) {
++ fm_cb(skb)->fm_op = *((u8 *)payload + 2);
++ }
++ if (payload != NULL)
++ memcpy(skb_put(skb, payload_len), payload, payload_len);
++
++ fm_cb(skb)->completion = wait_completion;
++ skb_queue_tail(&fmdev->tx_q, skb);
++ tasklet_schedule(&fmdev->tx_task);
++
++ return 0;
++}
++
++/* Sends FM Channel-8 command to the chip and waits for the response */
++u32 fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
++ unsigned int payload_len, void *response, int *response_len)
++{
++ struct sk_buff *skb;
++ struct fm_event_msg_hdr *evt_hdr;
++ unsigned long flags;
++ u32 ret;
++
++ init_completion(&fmdev->maintask_comp);
++ ret = fm_send_cmd(fmdev, fm_op, type, payload, payload_len,
++ &fmdev->maintask_comp);
++ if (ret)
++ return ret;
++
++ ret = wait_for_completion_timeout(&fmdev->maintask_comp, FM_DRV_TX_TIMEOUT);
++ if (!ret) {
++ fmerr("Timeout(%d sec),didn't get reg"
++ "completion signal from RX tasklet\n",
++ jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
++ return -ETIMEDOUT;
++ }
++ if (!fmdev->resp_skb) {
++ fmerr("Reponse SKB is missing\n");
++ return -EFAULT;
++ }
++ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
++ skb = fmdev->resp_skb;
++ fmdev->resp_skb = NULL;
++ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
++
++ evt_hdr = (void *)skb->data;
++ if (evt_hdr->status != 0) {
++ fmerr("Received event pkt status(%d) is not zero\n",
++ evt_hdr->status);
++ kfree_skb(skb);
++ return -EIO;
++ }
++ /* Send response data to caller */
++ if (response != NULL && response_len != NULL && evt_hdr->dlen) {
++ /* Skip header info and copy only response data */
++ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
++ memcpy(response, skb->data, evt_hdr->dlen);
++ *response_len = evt_hdr->dlen;
++ } else if (response_len != NULL && evt_hdr->dlen == 0) {
++ *response_len = 0;
++ }
++ kfree_skb(skb);
++
++ return 0;
++}
++
++/* --- Helper functions used in FM interrupt handlers ---*/
++static inline u32 check_cmdresp_status(struct fmdev *fmdev,
++ struct sk_buff **skb)
++{
++ struct fm_event_msg_hdr *fm_evt_hdr;
++ unsigned long flags;
++
++ del_timer(&fmdev->irq_info.timer);
++
++ spin_lock_irqsave(&fmdev->resp_skb_lock, flags);
++ *skb = fmdev->resp_skb;
++ fmdev->resp_skb = NULL;
++ spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags);
++
++ fm_evt_hdr = (void *)(*skb)->data;
++ if (fm_evt_hdr->status != 0) {
++ fmerr("irq: opcode %x response status is not zero "
++ "Initiating irq recovery process\n",
++ fm_evt_hdr->op);
++
++ mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT);
++ return -1;
++ }
++
++ return 0;
++}
++
++static inline void fm_irq_common_cmd_resp_helper(struct fmdev *fmdev, u8 stage)
++{
++ struct sk_buff *skb;
++
++ if (!check_cmdresp_status(fmdev, &skb))
++ fm_irq_call_stage(fmdev, stage);
++}
++
++/*
++ * Interrupt process timeout handler.
++ * One of the irq handler did not get proper response from the chip. So take
++ * recovery action here. FM interrupts are disabled in the beginning of
++ * interrupt process. Therefore reset stage index to re-enable default
++ * interrupts. So that next interrupt will be processed as usual.
++ */
++static void int_timeout_handler(unsigned long data)
++{
++ struct fmdev *fmdev;
++ struct fm_irq *fmirq;
++
++ fmdbg("irq: timeout,trying to re-enable fm interrupts\n");
++ fmdev = (struct fmdev *)data;
++ fmirq = &fmdev->irq_info;
++ fmirq->retry++;
++
++ if (fmirq->retry > FM_IRQ_TIMEOUT_RETRY_MAX) {
++ /* Stop recovery action (interrupt reenable process) and
++ * reset stage index & retry count values */
++ fmirq->stage = 0;
++ fmirq->retry = 0;
++ fmerr("Recovery action failed during"
++ "irq processing, max retry reached\n");
++ return;
++ }
++ fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX);
++}
++
++/* --------- FM interrupt handlers ------------*/
++static void fm_irq_send_flag_getcmd(struct fmdev *fmdev)
++{
++ u16 flag;
++
++ /* Send FLAG_GET command , to know the source of interrupt */
++ if (!fm_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, sizeof(flag), NULL))
++ fm_irq_timeout_stage(fmdev, FM_HANDLE_FLAG_GETCMD_RESP_IDX);
++}
++
++static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev)
++{
++ struct sk_buff *skb;
++ struct fm_event_msg_hdr *fm_evt_hdr;
++
++ if (check_cmdresp_status(fmdev, &skb))
++ return;
++
++ fm_evt_hdr = (void *)skb->data;
++
++ /* Skip header info and copy only response data */
++ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
++ memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen);
++
++ fmdev->irq_info.flag = be16_to_cpu(fmdev->irq_info.flag);
++ fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag);
++
++ /* Continue next function in interrupt handler table */
++ fm_irq_call_stage(fmdev, FM_HW_MAL_FUNC_IDX);
++}
++
++static void fm_irq_handle_hw_malfunction(struct fmdev *fmdev)
++{
++ if (fmdev->irq_info.flag & FM_MAL_EVENT & fmdev->irq_info.mask)
++ fmerr("irq: HW MAL int received - do nothing\n");
++
++ /* Continue next function in interrupt handler table */
++ fm_irq_call_stage(fmdev, FM_RDS_START_IDX);
++}
++
++static void fm_irq_handle_rds_start(struct fmdev *fmdev)
++{
++ if (fmdev->irq_info.flag & FM_RDS_EVENT & fmdev->irq_info.mask) {
++ fmdbg("irq: rds threshold reached\n");
++ fmdev->irq_info.stage = FM_RDS_SEND_RDS_GETCMD_IDX;
++ } else {
++ /* Continue next function in interrupt handler table */
++ fmdev->irq_info.stage = FM_HW_TUNE_OP_ENDED_IDX;
++ }
++
++ fm_irq_call(fmdev);
++}
++
++static void fm_irq_send_rdsdata_getcmd(struct fmdev *fmdev)
++{
++ /* Send the command to read RDS data from the chip */
++ if (!fm_send_cmd(fmdev, RDS_DATA_GET, REG_RD, NULL,
++ (FM_RX_RDS_FIFO_THRESHOLD * 3), NULL))
++ fm_irq_timeout_stage(fmdev, FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX);
++}
++
++/* Keeps track of current RX channel AF (Alternate Frequency) */
++static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af)
++{
++ struct tuned_station_info *stat_info = &fmdev->rx.stat_info;
++ u8 reg_idx = fmdev->rx.region.fm_band;
++ u8 index;
++ u32 freq;
++
++ /* First AF indicates the number of AF follows. Reset the list */
++ if ((af >= FM_RDS_1_AF_FOLLOWS) && (af <= FM_RDS_25_AF_FOLLOWS)) {
++ fmdev->rx.stat_info.af_list_max = (af - FM_RDS_1_AF_FOLLOWS + 1);
++ fmdev->rx.stat_info.afcache_size = 0;
++ fmdbg("No of expected AF : %d\n", fmdev->rx.stat_info.af_list_max);
++ return;
++ }
++
++ if (af < FM_RDS_MIN_AF)
++ return;
++ if (reg_idx == FM_BAND_EUROPE_US && af > FM_RDS_MAX_AF)
++ return;
++ if (reg_idx == FM_BAND_JAPAN && af > FM_RDS_MAX_AF_JAPAN)
++ return;
++
++ freq = fmdev->rx.region.bot_freq + (af * 100);
++ if (freq == fmdev->rx.freq) {
++ fmdbg("Current freq(%d) is matching with received AF(%d)\n",
++ fmdev->rx.freq, freq);
++ return;
++ }
++ /* Do check in AF cache */
++ for (index = 0; index < stat_info->afcache_size; index++) {
++ if (stat_info->af_cache[index] == freq)
++ break;
++ }
++ /* Reached the limit of the list - ignore the next AF */
++ if (index == stat_info->af_list_max) {
++ fmdbg("AF cache is full\n");
++ return;
++ }
++ /*
++ * If we reached the end of the list then this AF is not
++ * in the list - add it.
++ */
++ if (index == stat_info->afcache_size) {
++ fmdbg("Storing AF %d to cache index %d\n", freq, index);
++ stat_info->af_cache[index] = freq;
++ stat_info->afcache_size++;
++ }
++}
++
++/*
++ * Converts RDS buffer data from big endian format
++ * to little endian format.
++ */
++static void fm_rdsparse_swapbytes(struct fmdev *fmdev,
++ struct fm_rdsdata_format *rds_format)
++{
++ u8 byte1;
++ u8 index = 0;
++ u8 *rds_buff;
++
++ /*
++ * Since in Orca the 2 RDS Data bytes are in little endian and
++ * in Dolphin they are in big endian, the parsing of the RDS data
++ * is chip dependent
++ */
++ if (fmdev->asci_id != 0x6350) {
++ rds_buff = &rds_format->data.groupdatabuff.buff[0];
++ while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) {
++ byte1 = rds_buff[index];
++ rds_buff[index] = rds_buff[index + 1];
++ rds_buff[index + 1] = byte1;
++ index += 2;
++ }
++ }
++}
++
++static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev)
++{
++ struct sk_buff *skb;
++ struct fm_rdsdata_format rds_fmt;
++ struct fm_rds *rds = &fmdev->rx.rds;
++ unsigned long group_idx, flags;
++ u8 *rds_data, meta_data, tmpbuf[3];
++ u8 type, blk_idx;
++ u16 cur_picode;
++ u32 rds_len;
++
++ if (check_cmdresp_status(fmdev, &skb))
++ return;
++
++ /* Skip header info */
++ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
++ rds_data = skb->data;
++ rds_len = skb->len;
++
++ /* Parse the RDS data */
++ while (rds_len >= FM_RDS_BLK_SIZE) {
++ meta_data = rds_data[2];
++ /* Get the type: 0=A, 1=B, 2=C, 3=C', 4=D, 5=E */
++ type = (meta_data & 0x07);
++
++ /* Transform the blk type into index sequence (0, 1, 2, 3, 4) */
++ blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1));
++ fmdbg("Block index:%d(%s)\n", blk_idx,
++ (meta_data & FM_RDS_STATUS_ERR_MASK) ? "Bad" : "Ok");
++
++ if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0)
++ break;
++
++ if (blk_idx < FM_RDS_BLK_IDX_A || blk_idx > FM_RDS_BLK_IDX_D) {
++ fmdbg("Block sequence mismatch\n");
++ rds->last_blk_idx = -1;
++ break;
++ }
++
++ /* Skip checkword (control) byte and copy only data byte */
++ memcpy(&rds_fmt.data.groupdatabuff.
++ buff[blk_idx * (FM_RDS_BLK_SIZE - 1)],
++ rds_data, (FM_RDS_BLK_SIZE - 1));
++
++ rds->last_blk_idx = blk_idx;
++
++ /* If completed a whole group then handle it */
++ if (blk_idx == FM_RDS_BLK_IDX_D) {
++ fmdbg("Good block received\n");
++ fm_rdsparse_swapbytes(fmdev, &rds_fmt);
++
++ /*
++ * Extract PI code and store in local cache.
++ * We need this during AF switch processing.
++ */
++ cur_picode = be16_to_cpu(rds_fmt.data.groupgeneral.pidata);
++ if (fmdev->rx.stat_info.picode != cur_picode)
++ fmdev->rx.stat_info.picode = cur_picode;
++
++ fmdbg("picode:%d\n", cur_picode);
++
++ group_idx = (rds_fmt.data.groupgeneral.blk_b[0] >> 3);
++ fmdbg("(fmdrv):Group:%ld%s\n", group_idx/2,
++ (group_idx % 2) ? "B" : "A");
++
++ group_idx = 1 << (rds_fmt.data.groupgeneral.blk_b[0] >> 3);
++ if (group_idx == FM_RDS_GROUP_TYPE_MASK_0A) {
++ fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[0]);
++ fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[1]);
++ }
++ }
++ rds_len -= FM_RDS_BLK_SIZE;
++ rds_data += FM_RDS_BLK_SIZE;
++ }
++
++ /* Copy raw rds data to internal rds buffer */
++ rds_data = skb->data;
++ rds_len = skb->len;
++
++ spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
++ while (rds_len > 0) {
++ /*
++ * Fill RDS buffer as per V4L2 specification.
++ * Store control byte
++ */
++ type = (rds_data[2] & 0x07);
++ blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1));
++ tmpbuf[2] = blk_idx; /* Offset name */
++ tmpbuf[2] |= blk_idx << 3; /* Received offset */
++
++ /* Store data byte */
++ tmpbuf[0] = rds_data[0];
++ tmpbuf[1] = rds_data[1];
++
++ memcpy(&rds->buff[rds->wr_idx], &tmpbuf, FM_RDS_BLK_SIZE);
++ rds->wr_idx = (rds->wr_idx + FM_RDS_BLK_SIZE) % rds->buf_size;
++
++ /* Check for overflow & start over */
++ if (rds->wr_idx == rds->rd_idx) {
++ fmdbg("RDS buffer overflow\n");
++ rds->wr_idx = 0;
++ rds->rd_idx = 0;
++ break;
++ }
++ rds_len -= FM_RDS_BLK_SIZE;
++ rds_data += FM_RDS_BLK_SIZE;
++ }
++ spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
++
++ /* Wakeup read queue */
++ if (rds->wr_idx != rds->rd_idx)
++ wake_up_interruptible(&rds->read_queue);
++
++ fm_irq_call_stage(fmdev, FM_RDS_FINISH_IDX);
++}
++
++static void fm_irq_handle_rds_finish(struct fmdev *fmdev)
++{
++ fm_irq_call_stage(fmdev, FM_HW_TUNE_OP_ENDED_IDX);
++}
++
++static void fm_irq_handle_tune_op_ended(struct fmdev *fmdev)
++{
++ if (fmdev->irq_info.flag & (FM_FR_EVENT | FM_BL_EVENT) & fmdev->
++ irq_info.mask) {
++ fmdbg("irq: tune ended/bandlimit reached\n");
++ if (test_and_clear_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag)) {
++ fmdev->irq_info.stage = FM_AF_JUMP_RD_FREQ_IDX;
++ } else {
++ complete(&fmdev->maintask_comp);
++ fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX;
++ }
++ } else
++ fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX;
++
++ fm_irq_call(fmdev);
++}
++
++static void fm_irq_handle_power_enb(struct fmdev *fmdev)
++{
++ if (fmdev->irq_info.flag & FM_POW_ENB_EVENT) {
++ fmdbg("irq: Power Enabled/Disabled\n");
++ complete(&fmdev->maintask_comp);
++ }
++
++ fm_irq_call_stage(fmdev, FM_LOW_RSSI_START_IDX);
++}
++
++static void fm_irq_handle_low_rssi_start(struct fmdev *fmdev)
++{
++ if ((fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) &&
++ (fmdev->irq_info.flag & FM_LEV_EVENT & fmdev->irq_info.mask) &&
++ (fmdev->rx.freq != FM_UNDEFINED_FREQ) &&
++ (fmdev->rx.stat_info.afcache_size != 0)) {
++ fmdbg("irq: rssi level has fallen below threshold level\n");
++
++ /* Disable further low RSSI interrupts */
++ fmdev->irq_info.mask &= ~FM_LEV_EVENT;
++
++ fmdev->rx.afjump_idx = 0;
++ fmdev->rx.freq_before_jump = fmdev->rx.freq;
++ fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX;
++ } else {
++ /* Continue next function in interrupt handler table */
++ fmdev->irq_info.stage = FM_SEND_INTMSK_CMD_IDX;
++ }
++
++ fm_irq_call(fmdev);
++}
++
++static void fm_irq_afjump_set_pi(struct fmdev *fmdev)
++{
++ u16 payload;
++
++ /* Set PI code - must be updated if the AF list is not empty */
++ payload = fmdev->rx.stat_info.picode;
++ if (!fm_send_cmd(fmdev, RDS_PI_SET, REG_WR, &payload, sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_RESP_IDX);
++}
++
++static void fm_irq_handle_set_pi_resp(struct fmdev *fmdev)
++{
++ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SETPI_MASK_IDX);
++}
++
++/*
++ * Set PI mask.
++ * 0xFFFF = Enable PI code matching
++ * 0x0000 = Disable PI code matching
++ */
++static void fm_irq_afjump_set_pimask(struct fmdev *fmdev)
++{
++ u16 payload;
++
++ payload = 0x0000;
++ if (!fm_send_cmd(fmdev, RDS_PI_MASK_SET, REG_WR, &payload, sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX);
++}
++
++static void fm_irq_handle_set_pimask_resp(struct fmdev *fmdev)
++{
++ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SET_AF_FREQ_IDX);
++}
++
++static void fm_irq_afjump_setfreq(struct fmdev *fmdev)
++{
++ u16 frq_index;
++ u16 payload;
++
++ fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]);
++ frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] -
++ fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
++
++ payload = frq_index;
++ if (!fm_send_cmd(fmdev, AF_FREQ_SET, REG_WR, &payload, sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX);
++}
++
++static void fm_irq_handle_setfreq_resp(struct fmdev *fmdev)
++{
++ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_ENABLE_INT_IDX);
++}
++
++static void fm_irq_afjump_enableint(struct fmdev *fmdev)
++{
++ u16 payload;
++
++ /* Enable FR (tuning operation ended) interrupt */
++ payload = FM_FR_EVENT;
++ if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_ENABLE_INT_RESP_IDX);
++}
++
++static void fm_irq_afjump_enableint_resp(struct fmdev *fmdev)
++{
++ fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_START_AFJUMP_IDX);
++}
++
++static void fm_irq_start_afjump(struct fmdev *fmdev)
++{
++ u16 payload;
++
++ payload = FM_TUNER_AF_JUMP_MODE;
++ if (!fm_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
++ sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX);
++}
++
++static void fm_irq_handle_start_afjump_resp(struct fmdev *fmdev)
++{
++ struct sk_buff *skb;
++
++ if (check_cmdresp_status(fmdev, &skb))
++ return;
++
++ fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX;
++ set_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag);
++ clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
++}
++
++static void fm_irq_afjump_rd_freq(struct fmdev *fmdev)
++{
++ u16 payload;
++
++ if (!fm_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_AF_JUMP_RD_FREQ_RESP_IDX);
++}
++
++static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev)
++{
++ struct sk_buff *skb;
++ u16 read_freq;
++ u32 curr_freq, jumped_freq;
++
++ if (check_cmdresp_status(fmdev, &skb))
++ return;
++
++ /* Skip header info and copy only response data */
++ skb_pull(skb, sizeof(struct fm_event_msg_hdr));
++ memcpy(&read_freq, skb->data, sizeof(read_freq));
++ read_freq = be16_to_cpu(read_freq);
++ curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL);
++
++ jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx];
++
++ /* If the frequency was changed the jump succeeded */
++ if ((curr_freq != fmdev->rx.freq_before_jump) && (curr_freq == jumped_freq)) {
++ fmdbg("Successfully switched to alternate freq %d\n", curr_freq);
++ fmdev->rx.freq = curr_freq;
++ fm_rx_reset_rds_cache(fmdev);
++
++ /* AF feature is on, enable low level RSSI interrupt */
++ if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
++ fmdev->irq_info.mask |= FM_LEV_EVENT;
++
++ fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX;
++ } else { /* jump to the next freq in the AF list */
++ fmdev->rx.afjump_idx++;
++
++ /* If we reached the end of the list - stop searching */
++ if (fmdev->rx.afjump_idx >= fmdev->rx.stat_info.afcache_size) {
++ fmdbg("AF switch processing failed\n");
++ fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX;
++ } else { /* AF List is not over - try next one */
++
++ fmdbg("Trying next freq in AF cache\n");
++ fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX;
++ }
++ }
++ fm_irq_call(fmdev);
++}
++
++static void fm_irq_handle_low_rssi_finish(struct fmdev *fmdev)
++{
++ fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX);
++}
++
++static void fm_irq_send_intmsk_cmd(struct fmdev *fmdev)
++{
++ u16 payload;
++
++ /* Re-enable FM interrupts */
++ payload = fmdev->irq_info.mask;
++
++ if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL))
++ fm_irq_timeout_stage(fmdev, FM_HANDLE_INTMSK_CMD_RESP_IDX);
++}
++
++static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev)
++{
++ struct sk_buff *skb;
++
++ if (check_cmdresp_status(fmdev, &skb))
++ return;
++ /*
++ * This is last function in interrupt table to be executed.
++ * So, reset stage index to 0.
++ */
++ fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX;
++
++ /* Start processing any pending interrupt */
++ if (test_and_clear_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag))
++ fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev);
++ else
++ clear_bit(FM_INTTASK_RUNNING, &fmdev->flag);
++}
++
++/* Returns availability of RDS data in internel buffer */
++u32 fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file,
++ struct poll_table_struct *pts)
++{
++ poll_wait(file, &fmdev->rx.rds.read_queue, pts);
++ if (fmdev->rx.rds.rd_idx != fmdev->rx.rds.wr_idx)
++ return 0;
++
++ return -EAGAIN;
++}
++
++/* Copies RDS data from internal buffer to user buffer */
++u32 fmc_transfer_rds_from_internal_buff(struct fmdev *fmdev, struct file *file,
++ u8 __user *buf, size_t count)
++{
++ u32 block_count;
++ unsigned long flags;
++ int ret;
++
++ if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) {
++ if (file->f_flags & O_NONBLOCK)
++ return -EWOULDBLOCK;
++
++ ret = wait_event_interruptible(fmdev->rx.rds.read_queue,
++ (fmdev->rx.rds.wr_idx != fmdev->rx.rds.rd_idx));
++ if (ret)
++ return -EINTR;
++ }
++
++ /* Calculate block count from byte count */
++ count /= 3;
++ block_count = 0;
++ ret = 0;
++
++ spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
++
++ while (block_count < count) {
++ if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx)
++ break;
++
++ if (copy_to_user(buf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx],
++ FM_RDS_BLK_SIZE))
++ break;
++
++ fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE;
++ if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size)
++ fmdev->rx.rds.rd_idx = 0;
++
++ block_count++;
++ buf += FM_RDS_BLK_SIZE;
++ ret += FM_RDS_BLK_SIZE;
++ }
++ spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
++ return ret;
++}
++
++u32 fmc_set_freq(struct fmdev *fmdev, u32 freq_to_set)
++{
++ switch (fmdev->curr_fmmode) {
++ case FM_MODE_RX:
++ return fm_rx_set_freq(fmdev, freq_to_set);
++
++ case FM_MODE_TX:
++ return fm_tx_set_freq(fmdev, freq_to_set);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++u32 fmc_get_freq(struct fmdev *fmdev, u32 *cur_tuned_frq)
++{
++ if (fmdev->rx.freq == FM_UNDEFINED_FREQ) {
++ fmerr("RX frequency is not set\n");
++ return -EPERM;
++ }
++ if (cur_tuned_frq == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ switch (fmdev->curr_fmmode) {
++ case FM_MODE_RX:
++ *cur_tuned_frq = fmdev->rx.freq;
++ return 0;
++
++ case FM_MODE_TX:
++ *cur_tuned_frq = 0; /* TODO : Change this later */
++ return 0;
++
++ default:
++ return -EINVAL;
++ }
++
++}
++
++u32 fmc_set_region(struct fmdev *fmdev, u8 region_to_set)
++{
++ switch (fmdev->curr_fmmode) {
++ case FM_MODE_RX:
++ return fm_rx_set_region(fmdev, region_to_set);
++
++ case FM_MODE_TX:
++ return fm_tx_set_region(fmdev, region_to_set);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++u32 fmc_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
++{
++ switch (fmdev->curr_fmmode) {
++ case FM_MODE_RX:
++ return fm_rx_set_mute_mode(fmdev, mute_mode_toset);
++
++ case FM_MODE_TX:
++ return fm_tx_set_mute_mode(fmdev, mute_mode_toset);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++u32 fmc_set_stereo_mono(struct fmdev *fmdev, u16 mode)
++{
++ switch (fmdev->curr_fmmode) {
++ case FM_MODE_RX:
++ return fm_rx_set_stereo_mono(fmdev, mode);
++
++ case FM_MODE_TX:
++ return fm_tx_set_stereo_mono(fmdev, mode);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++u32 fmc_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
++{
++ switch (fmdev->curr_fmmode) {
++ case FM_MODE_RX:
++ return fm_rx_set_rds_mode(fmdev, rds_en_dis);
++
++ case FM_MODE_TX:
++ return fm_tx_set_rds_mode(fmdev, rds_en_dis);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++/* Sends power off command to the chip */
++static u32 fm_power_down(struct fmdev *fmdev)
++{
++ u16 payload;
++ u32 ret;
++
++ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
++ fmerr("FM core is not ready\n");
++ return -EPERM;
++ }
++ if (fmdev->curr_fmmode == FM_MODE_OFF) {
++ fmdbg("FM chip is already in OFF state\n");
++ return 0;
++ }
++
++ payload = 0x0;
++ ret = fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return fmc_release(fmdev);
++}
++
++/* Reads init command from FM firmware file and loads to the chip */
++static u32 fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name)
++{
++ const struct firmware *fw_entry;
++ struct bts_header *fw_header;
++ struct bts_action *action;
++ struct bts_action_delay *delay;
++ u8 *fw_data;
++ int ret, fw_len, cmd_cnt;
++
++ cmd_cnt = 0;
++ set_bit(FM_FW_DW_INPROGRESS, &fmdev->flag);
++
++ ret = request_firmware(&fw_entry, fw_name,
++ &fmdev->radio_dev->dev);
++ if (ret < 0) {
++ fmerr("Unable to read firmware(%s) content\n", fw_name);
++ return ret;
++ }
++ fmdbg("Firmware(%s) length : %d bytes\n", fw_name, fw_entry->size);
++
++ fw_data = (void *)fw_entry->data;
++ fw_len = fw_entry->size;
++
++ fw_header = (struct bts_header *)fw_data;
++ if (fw_header->magic != FM_FW_FILE_HEADER_MAGIC) {
++ fmerr("%s not a legal TI firmware file\n", fw_name);
++ ret = -EINVAL;
++ goto rel_fw;
++ }
++ fmdbg("FW(%s) magic number : 0x%x\n", fw_name, fw_header->magic);
++
++ /* Skip file header info , we already verified it */
++ fw_data += sizeof(struct bts_header);
++ fw_len -= sizeof(struct bts_header);
++
++ while (fw_data && fw_len > 0) {
++ action = (struct bts_action *)fw_data;
++
++ switch (action->type) {
++ case ACTION_SEND_COMMAND: /* Send */
++ if (fmc_send_cmd(fmdev, 0, 0, action->data,
++ action->size, NULL, NULL))
++ goto rel_fw;
++
++ cmd_cnt++;
++ break;
++
++ case ACTION_DELAY: /* Delay */
++ delay = (struct bts_action_delay *)action->data;
++ mdelay(delay->msec);
++ break;
++ }
++
++ fw_data += (sizeof(struct bts_action) + (action->size));
++ fw_len -= (sizeof(struct bts_action) + (action->size));
++ }
++ fmdbg("Firmware commands(%d) loaded to chip\n", cmd_cnt);
++rel_fw:
++ release_firmware(fw_entry);
++ clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag);
++
++ return ret;
++}
++
++/* Loads default RX configuration to the chip */
++static u32 load_default_rx_configuration(struct fmdev *fmdev)
++{
++ int ret;
++
++ ret = fm_rx_set_volume(fmdev, FM_DEFAULT_RX_VOLUME);
++ if (ret < 0)
++ return ret;
++
++ return fm_rx_set_rssi_threshold(fmdev, FM_DEFAULT_RSSI_THRESHOLD);
++}
++
++/* Does FM power on sequence */
++static u32 fm_power_up(struct fmdev *fmdev, u8 mode)
++{
++ u16 payload, asic_id, asic_ver;
++ int resp_len, ret;
++ u8 fw_name[50];
++
++ if (mode >= FM_MODE_ENTRY_MAX) {
++ fmerr("Invalid firmware download option\n");
++ return -EINVAL;
++ }
++
++ /*
++ * Initialize FM common module. FM GPIO toggling is
++ * taken care in Shared Transport driver.
++ */
++ ret = fmc_prepare(fmdev);
++ if (ret < 0) {
++ fmerr("Unable to prepare FM Common\n");
++ return ret;
++ }
++
++ payload = FM_ENABLE;
++ if (fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload,
++ sizeof(payload), NULL, NULL))
++ goto rel;
++
++ /* Allow the chip to settle down in Channel-8 mode */
++ msleep(20);
++
++ if (fmc_send_cmd(fmdev, ASIC_ID_GET, REG_RD, NULL,
++ sizeof(asic_id), &asic_id, &resp_len))
++ goto rel;
++
++ if (fmc_send_cmd(fmdev, ASIC_VER_GET, REG_RD, NULL,
++ sizeof(asic_ver), &asic_ver, &resp_len))
++ goto rel;
++
++ fmdbg("ASIC ID: 0x%x , ASIC Version: %d\n",
++ be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
++
++ sprintf(fw_name, "%s_%x.%d.bts", FM_FMC_FW_FILE_START,
++ be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
++
++ ret = fm_download_firmware(fmdev, fw_name);
++ if (ret < 0) {
++ fmdbg("Failed to download firmware file %s\n", fw_name);
++ goto rel;
++ }
++ sprintf(fw_name, "%s_%x.%d.bts", (mode == FM_MODE_RX) ?
++ FM_RX_FW_FILE_START : FM_TX_FW_FILE_START,
++ be16_to_cpu(asic_id), be16_to_cpu(asic_ver));
++
++ ret = fm_download_firmware(fmdev, fw_name);
++ if (ret < 0) {
++ fmdbg("Failed to download firmware file %s\n", fw_name);
++ goto rel;
++ } else
++ return ret;
++rel:
++ return fmc_release(fmdev);
++}
++
++/* Set FM Modes(TX, RX, OFF) */
++u32 fmc_set_mode(struct fmdev *fmdev, u8 fm_mode)
++{
++ int ret = 0;
++
++ if (fm_mode >= FM_MODE_ENTRY_MAX) {
++ fmerr("Invalid FM mode\n");
++ return -EINVAL;
++ }
++ if (fmdev->curr_fmmode == fm_mode) {
++ fmdbg("Already fm is in mode(%d)\n", fm_mode);
++ return ret;
++ }
++
++ switch (fm_mode) {
++ case FM_MODE_OFF: /* OFF Mode */
++ ret = fm_power_down(fmdev);
++ if (ret < 0) {
++ fmerr("Failed to set OFF mode\n");
++ return ret;
++ }
++ break;
++
++ case FM_MODE_TX: /* TX Mode */
++ case FM_MODE_RX: /* RX Mode */
++ /* Power down before switching to TX or RX mode */
++ if (fmdev->curr_fmmode != FM_MODE_OFF) {
++ ret = fm_power_down(fmdev);
++ if (ret < 0) {
++ fmerr("Failed to set OFF mode\n");
++ return ret;
++ }
++ msleep(30);
++ }
++ ret = fm_power_up(fmdev, fm_mode);
++ if (ret < 0) {
++ fmerr("Failed to load firmware\n");
++ return ret;
++ }
++ }
++ fmdev->curr_fmmode = fm_mode;
++
++ /* Set default configuration */
++ if (fmdev->curr_fmmode == FM_MODE_RX) {
++ fmdbg("Loading default rx configuration..\n");
++ ret = load_default_rx_configuration(fmdev);
++ if (ret < 0)
++ fmerr("Failed to load default values\n");
++ }
++
++ return ret;
++}
++
++/* Returns current FM mode (TX, RX, OFF) */
++u32 fmc_get_mode(struct fmdev *fmdev, u8 *fmmode)
++{
++ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
++ fmerr("FM core is not ready\n");
++ return -EPERM;
++ }
++ if (fmmode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *fmmode = fmdev->curr_fmmode;
++ return 0;
++}
++
++/* Called by ST layer when FM packet is available */
++static long fm_st_receive(void *arg, struct sk_buff *skb)
++{
++ struct fmdev *fmdev;
++
++ fmdev = (struct fmdev *)arg;
++
++ if (skb == NULL) {
++ fmerr("Invalid SKB received from ST\n");
++ return -EFAULT;
++ }
++
++ if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) {
++ fmerr("Received SKB (%p) is not FM Channel 8 pkt\n", skb);
++ return -EINVAL;
++ }
++
++ memcpy(skb_push(skb, 1), &skb->cb[0], 1);
++ skb_queue_tail(&fmdev->rx_q, skb);
++ tasklet_schedule(&fmdev->rx_task);
++
++ return 0;
++}
++
++/*
++ * Called by ST layer to indicate protocol registration completion
++ * status.
++ */
++static void fm_st_reg_comp_cb(void *arg, char data)
++{
++ struct fmdev *fmdev;
++
++ fmdev = (struct fmdev *)arg;
++ fmdev->streg_cbdata = data;
++ complete(&wait_for_fmdrv_reg_comp);
++}
++
++/*
++ * This function will be called from FM V4L2 open function.
++ * Register with ST driver and initialize driver data.
++ */
++u32 fmc_prepare(struct fmdev *fmdev)
++{
++ static struct st_proto_s fm_st_proto;
++ u32 ret;
++
++ if (test_bit(FM_CORE_READY, &fmdev->flag)) {
++ fmdbg("FM Core is already up\n");
++ return 0;
++ }
++
++ memset(&fm_st_proto, 0, sizeof(fm_st_proto));
++ fm_st_proto.type = ST_FM;
++ fm_st_proto.recv = fm_st_receive;
++ fm_st_proto.match_packet = NULL;
++ fm_st_proto.reg_complete_cb = fm_st_reg_comp_cb;
++ fm_st_proto.write = NULL; /* TI ST driver will fill write pointer */
++ fm_st_proto.priv_data = fmdev;
++
++ ret = st_register(&fm_st_proto);
++ if (ret == -EINPROGRESS) {
++ init_completion(&wait_for_fmdrv_reg_comp);
++ fmdev->streg_cbdata = -EINPROGRESS;
++ fmdbg("%s waiting for ST reg completion signal\n", __func__);
++
++ ret = wait_for_completion_timeout(&wait_for_fmdrv_reg_comp,
++ FM_ST_REG_TIMEOUT);
++
++ if (!ret) {
++ fmerr("Timeout(%d sec), didn't get reg "
++ "completion signal from ST\n",
++ jiffies_to_msecs(FM_ST_REG_TIMEOUT) / 1000);
++ return -ETIMEDOUT;
++ }
++ if (fmdev->streg_cbdata != 0) {
++ fmerr("ST reg comp CB called with error "
++ "status %d\n", fmdev->streg_cbdata);
++ return -EAGAIN;
++ }
++
++ ret = 0;
++ } else if (ret == -1) {
++ fmerr("st_register failed %d\n", ret);
++ return -EAGAIN;
++ }
++
++ if (fm_st_proto.write != NULL) {
++ g_st_write = fm_st_proto.write;
++ } else {
++ fmerr("Failed to get ST write func pointer\n");
++ ret = st_unregister(ST_FM);
++ if (ret < 0)
++ fmerr("st_unregister failed %d\n", ret);
++ return -EAGAIN;
++ }
++
++ spin_lock_init(&fmdev->rds_buff_lock);
++ spin_lock_init(&fmdev->resp_skb_lock);
++
++ /* Initialize TX queue and TX tasklet */
++ skb_queue_head_init(&fmdev->tx_q);
++ tasklet_init(&fmdev->tx_task, send_tasklet, (unsigned long)fmdev);
++
++ /* Initialize RX Queue and RX tasklet */
++ skb_queue_head_init(&fmdev->rx_q);
++ tasklet_init(&fmdev->rx_task, recv_tasklet, (unsigned long)fmdev);
++
++ fmdev->irq_info.stage = 0;
++ atomic_set(&fmdev->tx_cnt, 1);
++ fmdev->resp_comp = NULL;
++
++ init_timer(&fmdev->irq_info.timer);
++ fmdev->irq_info.timer.function = &int_timeout_handler;
++ fmdev->irq_info.timer.data = (unsigned long)fmdev;
++ /*TODO: add FM_STIC_EVENT later */
++ fmdev->irq_info.mask = FM_MAL_EVENT;
++
++ /* Region info */
++ memcpy(&fmdev->rx.region, &region_configs[default_radio_region],
++ sizeof(struct region_info));
++
++ fmdev->rx.mute_mode = FM_MUTE_OFF;
++ fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF;
++ fmdev->rx.rds.flag = FM_RDS_DISABLE;
++ fmdev->rx.freq = FM_UNDEFINED_FREQ;
++ fmdev->rx.rds_mode = FM_RDS_SYSTEM_RDS;
++ fmdev->rx.af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF;
++ fmdev->irq_info.retry = 0;
++
++ fm_rx_reset_rds_cache(fmdev);
++ init_waitqueue_head(&fmdev->rx.rds.read_queue);
++
++ fm_rx_reset_station_info(fmdev);
++ set_bit(FM_CORE_READY, &fmdev->flag);
++
++ return ret;
++}
++
++/*
++ * This function will be called from FM V4L2 release function.
++ * Unregister from ST driver.
++ */
++u32 fmc_release(struct fmdev *fmdev)
++{
++ u32 ret;
++
++ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
++ fmdbg("FM Core is already down\n");
++ return 0;
++ }
++ /* Sevice pending read */
++ wake_up_interruptible(&fmdev->rx.rds.read_queue);
++
++ tasklet_kill(&fmdev->tx_task);
++ tasklet_kill(&fmdev->rx_task);
++
++ skb_queue_purge(&fmdev->tx_q);
++ skb_queue_purge(&fmdev->rx_q);
++
++ fmdev->resp_comp = NULL;
++ fmdev->rx.freq = 0;
++
++ ret = st_unregister(ST_FM);
++ if (ret < 0)
++ fmerr("Failed to de-register FM from ST %d\n", ret);
++ else
++ fmdbg("Successfully unregistered from ST\n");
++
++ clear_bit(FM_CORE_READY, &fmdev->flag);
++ return ret;
++}
++
++/*
++ * Module init function. Ask FM V4L module to register video device.
++ * Allocate memory for FM driver context and RX RDS buffer.
++ */
++static int __init fm_drv_init(void)
++{
++ struct fmdev *fmdev = NULL;
++ u32 ret = -ENOMEM;
++
++ fmdbg("FM driver version %s\n", FM_DRV_VERSION);
++
++ fmdev = kzalloc(sizeof(struct fmdev), GFP_KERNEL);
++ if (NULL == fmdev) {
++ fmerr("Can't allocate operation structure memory\n");
++ return ret;
++ }
++ fmdev->rx.rds.buf_size = default_rds_buf * FM_RDS_BLK_SIZE;
++ fmdev->rx.rds.buff = kzalloc(fmdev->rx.rds.buf_size, GFP_KERNEL);
++ if (NULL == fmdev->rx.rds.buff) {
++ fmerr("Can't allocate rds ring buffer\n");
++ goto rel_dev;
++ }
++
++ ret = fm_v4l2_init_video_device(fmdev, radio_nr);
++ if (ret < 0)
++ goto rel_rdsbuf;
++
++ fmdev->irq_info.handlers = int_handler_table;
++ fmdev->curr_fmmode = FM_MODE_OFF;
++ fmdev->tx_data.pwr_lvl = FM_PWR_LVL_DEF;
++ fmdev->tx_data.preemph = FM_TX_PREEMPH_50US;
++ return ret;
++
++rel_rdsbuf:
++ kfree(fmdev->rx.rds.buff);
++rel_dev:
++ kfree(fmdev);
++
++ return ret;
++}
++
++/* Module exit function. Ask FM V4L module to unregister video device */
++static void __exit fm_drv_exit(void)
++{
++ struct fmdev *fmdev = NULL;
++
++ fmdev = fm_v4l2_deinit_video_device();
++ if (fmdev != NULL) {
++ kfree(fmdev->rx.rds.buff);
++ kfree(fmdev);
++ }
++}
++
++module_init(fm_drv_init);
++module_exit(fm_drv_exit);
++
++/* ------------- Module Info ------------- */
++MODULE_AUTHOR("Manjunatha Halli <manjunatha_halli@ti.com>");
++MODULE_DESCRIPTION("FM Driver for TI's Connectivity chip. " FM_DRV_VERSION);
++MODULE_VERSION(FM_DRV_VERSION);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/radio/wl128x/fmdrv_common.h b/drivers/media/radio/wl128x/fmdrv_common.h
+new file mode 100644
+index 0000000..e5091f4
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_common.h
+@@ -0,0 +1,402 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ * FM Common module header file
++ *
++ * Copyright (C) 2011 Texas Instruments
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef _FMDRV_COMMON_H
++#define _FMDRV_COMMON_H
++
++#define FM_ST_REG_TIMEOUT msecs_to_jiffies(6000) /* 6 sec */
++#define FM_PKT_LOGICAL_CHAN_NUMBER 0x08 /* Logical channel 8 */
++
++#define REG_RD 0x1
++#define REG_WR 0x0
++
++struct fm_reg_table {
++ u8 opcode;
++ u8 type;
++ u8 *name;
++};
++
++#define STEREO_GET 0
++#define RSSI_LVL_GET 1
++#define IF_COUNT_GET 2
++#define FLAG_GET 3
++#define RDS_SYNC_GET 4
++#define RDS_DATA_GET 5
++#define FREQ_SET 10
++#define AF_FREQ_SET 11
++#define MOST_MODE_SET 12
++#define MOST_BLEND_SET 13
++#define DEMPH_MODE_SET 14
++#define SEARCH_LVL_SET 15
++#define BAND_SET 16
++#define MUTE_STATUS_SET 17
++#define RDS_PAUSE_LVL_SET 18
++#define RDS_PAUSE_DUR_SET 19
++#define RDS_MEM_SET 20
++#define RDS_BLK_B_SET 21
++#define RDS_MSK_B_SET 22
++#define RDS_PI_MASK_SET 23
++#define RDS_PI_SET 24
++#define RDS_SYSTEM_SET 25
++#define INT_MASK_SET 26
++#define SEARCH_DIR_SET 27
++#define VOLUME_SET 28
++#define AUDIO_ENABLE_SET 29
++#define PCM_MODE_SET 30
++#define I2S_MODE_CONFIG_SET 31
++#define POWER_SET 32
++#define INTX_CONFIG_SET 33
++#define PULL_EN_SET 34
++#define HILO_SET 35
++#define SWITCH2FREF 36
++#define FREQ_DRIFT_REPORT 37
++
++#define PCE_GET 40
++#define FIRM_VER_GET 41
++#define ASIC_VER_GET 42
++#define ASIC_ID_GET 43
++#define MAN_ID_GET 44
++#define TUNER_MODE_SET 45
++#define STOP_SEARCH 46
++#define RDS_CNTRL_SET 47
++
++#define WRITE_HARDWARE_REG 100
++#define CODE_DOWNLOAD 101
++#define RESET 102
++
++#define FM_POWER_MODE 254
++#define FM_INTERRUPT 255
++
++/* Transmitter API */
++
++#define CHANL_SET 55
++#define CHANL_BW_SET 56
++#define REF_SET 57
++#define POWER_ENB_SET 90
++#define POWER_ATT_SET 58
++#define POWER_LEV_SET 59
++#define AUDIO_DEV_SET 60
++#define PILOT_DEV_SET 61
++#define RDS_DEV_SET 62
++#define TX_BAND_SET 65
++#define PUPD_SET 91
++#define AUDIO_IO_SET 63
++#define PREMPH_SET 64
++#define MONO_SET 66
++#define MUTE 92
++#define MPX_LMT_ENABLE 67
++#define PI_SET 93
++#define ECC_SET 69
++#define PTY 70
++#define AF 71
++#define DISPLAY_MODE 74
++#define RDS_REP_SET 77
++#define RDS_CONFIG_DATA_SET 98
++#define RDS_DATA_SET 99
++#define RDS_DATA_ENB 94
++#define TA_SET 78
++#define TP_SET 79
++#define DI_SET 80
++#define MS_SET 81
++#define PS_SCROLL_SPEED 82
++#define TX_AUDIO_LEVEL_TEST 96
++#define TX_AUDIO_LEVEL_TEST_THRESHOLD 73
++#define TX_AUDIO_INPUT_LEVEL_RANGE_SET 54
++#define RX_ANTENNA_SELECT 87
++#define I2C_DEV_ADDR_SET 86
++#define REF_ERR_CALIB_PARAM_SET 88
++#define REF_ERR_CALIB_PERIODICITY_SET 89
++#define SOC_INT_TRIGGER 52
++#define SOC_AUDIO_PATH_SET 83
++#define SOC_PCMI_OVERRIDE 84
++#define SOC_I2S_OVERRIDE 85
++#define RSSI_BLOCK_SCAN_FREQ_SET 95
++#define RSSI_BLOCK_SCAN_START 97
++#define RSSI_BLOCK_SCAN_DATA_GET 5
++#define READ_FMANT_TUNE_VALUE 104
++
++/* SKB helpers */
++struct fm_skb_cb {
++ __u8 fm_op;
++ struct completion *completion;
++};
++
++#define fm_cb(skb) ((struct fm_skb_cb *)(skb->cb))
++
++/* FM Channel-8 command message format */
++struct fm_cmd_msg_hdr {
++ __u8 hdr; /* Logical Channel-8 */
++ __u8 len; /* Number of bytes follows */
++ __u8 op; /* FM Opcode */
++ __u8 rd_wr; /* Read/Write command */
++ __u8 dlen; /* Length of payload */
++} __attribute__ ((packed));
++
++#define FM_CMD_MSG_HDR_SIZE 5 /* sizeof(struct fm_cmd_msg_hdr) */
++
++/* FM Channel-8 event messgage format */
++struct fm_event_msg_hdr {
++ __u8 header; /* Logical Channel-8 */
++ __u8 len; /* Number of bytes follows */
++ __u8 status; /* Event status */
++ __u8 num_fm_hci_cmds; /* Number of pkts the host allowed to send */
++ __u8 op; /* FM Opcode */
++ __u8 rd_wr; /* Read/Write command */
++ __u8 dlen; /* Length of payload */
++} __attribute__ ((packed));
++
++#define FM_EVT_MSG_HDR_SIZE 7 /* sizeof(struct fm_event_msg_hdr) */
++
++/* TI's magic number in firmware file */
++#define FM_FW_FILE_HEADER_MAGIC 0x42535442
++
++#define FM_ENABLE 1
++#define FM_DISABLE 0
++
++/* FLAG_GET register bits */
++#define FM_FR_EVENT (1 << 0)
++#define FM_BL_EVENT (1 << 1)
++#define FM_RDS_EVENT (1 << 2)
++#define FM_BBLK_EVENT (1 << 3)
++#define FM_LSYNC_EVENT (1 << 4)
++#define FM_LEV_EVENT (1 << 5)
++#define FM_IFFR_EVENT (1 << 6)
++#define FM_PI_EVENT (1 << 7)
++#define FM_PD_EVENT (1 << 8)
++#define FM_STIC_EVENT (1 << 9)
++#define FM_MAL_EVENT (1 << 10)
++#define FM_POW_ENB_EVENT (1 << 11)
++
++/*
++ * Firmware files of FM. ASIC ID and ASIC version will be appened to this,
++ * later.
++ */
++#define FM_FMC_FW_FILE_START ("fmc_ch8")
++#define FM_RX_FW_FILE_START ("fm_rx_ch8")
++#define FM_TX_FW_FILE_START ("fm_tx_ch8")
++
++#define FM_UNDEFINED_FREQ 0xFFFFFFFF
++
++/* Band types */
++#define FM_BAND_EUROPE_US 0
++#define FM_BAND_JAPAN 1
++
++/* Seek directions */
++#define FM_SEARCH_DIRECTION_DOWN 0
++#define FM_SEARCH_DIRECTION_UP 1
++
++/* Tunner modes */
++#define FM_TUNER_STOP_SEARCH_MODE 0
++#define FM_TUNER_PRESET_MODE 1
++#define FM_TUNER_AUTONOMOUS_SEARCH_MODE 2
++#define FM_TUNER_AF_JUMP_MODE 3
++
++/* Min and Max volume */
++#define FM_RX_VOLUME_MIN 0
++#define FM_RX_VOLUME_MAX 70
++
++/* Volume gain step */
++#define FM_RX_VOLUME_GAIN_STEP 0x370
++
++/* Mute modes */
++#define FM_MUTE_OFF 0
++#define FM_MUTE_ON 1
++#define FM_MUTE_ATTENUATE 2
++
++#define FM_RX_UNMUTE_MODE 0x00
++#define FM_RX_RF_DEP_MODE 0x01
++#define FM_RX_AC_MUTE_MODE 0x02
++#define FM_RX_HARD_MUTE_LEFT_MODE 0x04
++#define FM_RX_HARD_MUTE_RIGHT_MODE 0x08
++#define FM_RX_SOFT_MUTE_FORCE_MODE 0x10
++
++/* RF dependent mute mode */
++#define FM_RX_RF_DEPENDENT_MUTE_ON 1
++#define FM_RX_RF_DEPENDENT_MUTE_OFF 0
++
++/* RSSI threshold min and max */
++#define FM_RX_RSSI_THRESHOLD_MIN -128
++#define FM_RX_RSSI_THRESHOLD_MAX 127
++
++/* Stereo/Mono mode */
++#define FM_STEREO_MODE 0
++#define FM_MONO_MODE 1
++#define FM_STEREO_SOFT_BLEND 1
++
++/* FM RX De-emphasis filter modes */
++#define FM_RX_EMPHASIS_FILTER_50_USEC 0
++#define FM_RX_EMPHASIS_FILTER_75_USEC 1
++
++/* FM RDS modes */
++#define FM_RDS_DISABLE 0
++#define FM_RDS_ENABLE 1
++
++#define FM_NO_PI_CODE 0
++
++/* FM and RX RDS block enable/disable */
++#define FM_RX_PWR_SET_FM_ON_RDS_OFF 0x1
++#define FM_RX_PWR_SET_FM_AND_RDS_BLK_ON 0x3
++#define FM_RX_PWR_SET_FM_AND_RDS_BLK_OFF 0x0
++
++/* RX RDS */
++#define FM_RX_RDS_FLUSH_FIFO 0x1
++#define FM_RX_RDS_FIFO_THRESHOLD 64 /* tuples */
++#define FM_RDS_BLK_SIZE 3 /* 3 bytes */
++
++/* RDS block types */
++#define FM_RDS_BLOCK_A 0
++#define FM_RDS_BLOCK_B 1
++#define FM_RDS_BLOCK_C 2
++#define FM_RDS_BLOCK_Ctag 3
++#define FM_RDS_BLOCK_D 4
++#define FM_RDS_BLOCK_E 5
++
++#define FM_RDS_BLK_IDX_A 0
++#define FM_RDS_BLK_IDX_B 1
++#define FM_RDS_BLK_IDX_C 2
++#define FM_RDS_BLK_IDX_D 3
++#define FM_RDS_BLK_IDX_UNKNOWN 0xF0
++
++#define FM_RDS_STATUS_ERR_MASK 0x18
++
++/*
++ * Represents an RDS group type & version.
++ * There are 15 groups, each group has 2 versions: A and B.
++ */
++#define FM_RDS_GROUP_TYPE_MASK_0A ((unsigned long)1<<0)
++#define FM_RDS_GROUP_TYPE_MASK_0B ((unsigned long)1<<1)
++#define FM_RDS_GROUP_TYPE_MASK_1A ((unsigned long)1<<2)
++#define FM_RDS_GROUP_TYPE_MASK_1B ((unsigned long)1<<3)
++#define FM_RDS_GROUP_TYPE_MASK_2A ((unsigned long)1<<4)
++#define FM_RDS_GROUP_TYPE_MASK_2B ((unsigned long)1<<5)
++#define FM_RDS_GROUP_TYPE_MASK_3A ((unsigned long)1<<6)
++#define FM_RDS_GROUP_TYPE_MASK_3B ((unsigned long)1<<7)
++#define FM_RDS_GROUP_TYPE_MASK_4A ((unsigned long)1<<8)
++#define FM_RDS_GROUP_TYPE_MASK_4B ((unsigned long)1<<9)
++#define FM_RDS_GROUP_TYPE_MASK_5A ((unsigned long)1<<10)
++#define FM_RDS_GROUP_TYPE_MASK_5B ((unsigned long)1<<11)
++#define FM_RDS_GROUP_TYPE_MASK_6A ((unsigned long)1<<12)
++#define FM_RDS_GROUP_TYPE_MASK_6B ((unsigned long)1<<13)
++#define FM_RDS_GROUP_TYPE_MASK_7A ((unsigned long)1<<14)
++#define FM_RDS_GROUP_TYPE_MASK_7B ((unsigned long)1<<15)
++#define FM_RDS_GROUP_TYPE_MASK_8A ((unsigned long)1<<16)
++#define FM_RDS_GROUP_TYPE_MASK_8B ((unsigned long)1<<17)
++#define FM_RDS_GROUP_TYPE_MASK_9A ((unsigned long)1<<18)
++#define FM_RDS_GROUP_TYPE_MASK_9B ((unsigned long)1<<19)
++#define FM_RDS_GROUP_TYPE_MASK_10A ((unsigned long)1<<20)
++#define FM_RDS_GROUP_TYPE_MASK_10B ((unsigned long)1<<21)
++#define FM_RDS_GROUP_TYPE_MASK_11A ((unsigned long)1<<22)
++#define FM_RDS_GROUP_TYPE_MASK_11B ((unsigned long)1<<23)
++#define FM_RDS_GROUP_TYPE_MASK_12A ((unsigned long)1<<24)
++#define FM_RDS_GROUP_TYPE_MASK_12B ((unsigned long)1<<25)
++#define FM_RDS_GROUP_TYPE_MASK_13A ((unsigned long)1<<26)
++#define FM_RDS_GROUP_TYPE_MASK_13B ((unsigned long)1<<27)
++#define FM_RDS_GROUP_TYPE_MASK_14A ((unsigned long)1<<28)
++#define FM_RDS_GROUP_TYPE_MASK_14B ((unsigned long)1<<29)
++#define FM_RDS_GROUP_TYPE_MASK_15A ((unsigned long)1<<30)
++#define FM_RDS_GROUP_TYPE_MASK_15B ((unsigned long)1<<31)
++
++/* RX Alternate Frequency info */
++#define FM_RDS_MIN_AF 1
++#define FM_RDS_MAX_AF 204
++#define FM_RDS_MAX_AF_JAPAN 140
++#define FM_RDS_1_AF_FOLLOWS 225
++#define FM_RDS_25_AF_FOLLOWS 249
++
++/* RDS system type (RDS/RBDS) */
++#define FM_RDS_SYSTEM_RDS 0
++#define FM_RDS_SYSTEM_RBDS 1
++
++/* AF on/off */
++#define FM_RX_RDS_AF_SWITCH_MODE_ON 1
++#define FM_RX_RDS_AF_SWITCH_MODE_OFF 0
++
++/* Retry count when interrupt process goes wrong */
++#define FM_IRQ_TIMEOUT_RETRY_MAX 5 /* 5 times */
++
++/* Audio IO set values */
++#define FM_RX_AUDIO_ENABLE_I2S 0x01
++#define FM_RX_AUDIO_ENABLE_ANALOG 0x02
++#define FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG 0x03
++#define FM_RX_AUDIO_ENABLE_DISABLE 0x00
++
++/* HI/LO set values */
++#define FM_RX_IFFREQ_TO_HI_SIDE 0x0
++#define FM_RX_IFFREQ_TO_LO_SIDE 0x1
++#define FM_RX_IFFREQ_HILO_AUTOMATIC 0x2
++
++/*
++ * Default RX mode configuration. Chip will be configured
++ * with this default values after loading RX firmware.
++ */
++#define FM_DEFAULT_RX_VOLUME 10
++#define FM_DEFAULT_RSSI_THRESHOLD 3
++
++/* Range for TX power level in units for dB/uV */
++#define FM_PWR_LVL_LOW 91
++#define FM_PWR_LVL_HIGH 122
++
++/* Chip specific default TX power level value */
++#define FM_PWR_LVL_DEF 4
++
++/* FM TX Pre-emphasis filter values */
++#define FM_TX_PREEMPH_OFF 1
++#define FM_TX_PREEMPH_50US 0
++#define FM_TX_PREEMPH_75US 2
++
++/* FM TX antenna impedence values */
++#define FM_TX_ANT_IMP_50 0
++#define FM_TX_ANT_IMP_200 1
++#define FM_TX_ANT_IMP_500 2
++
++/* Functions exported by FM common sub-module */
++u32 fmc_prepare(struct fmdev *);
++u32 fmc_release(struct fmdev *);
++
++void fmc_update_region_info(struct fmdev *, u8);
++u32 fmc_send_cmd(struct fmdev *, u8, u16,
++ void *, unsigned int, void *, int *);
++u32 fmc_is_rds_data_available(struct fmdev *, struct file *,
++ struct poll_table_struct *);
++u32 fmc_transfer_rds_from_internal_buff(struct fmdev *, struct file *,
++ u8 __user *, size_t);
++
++u32 fmc_set_freq(struct fmdev *, u32);
++u32 fmc_set_mode(struct fmdev *, u8);
++u32 fmc_set_region(struct fmdev *, u8);
++u32 fmc_set_mute_mode(struct fmdev *, u8);
++u32 fmc_set_stereo_mono(struct fmdev *, u16);
++u32 fmc_set_rds_mode(struct fmdev *, u8);
++
++u32 fmc_get_freq(struct fmdev *, u32 *);
++u32 fmc_get_region(struct fmdev *, u8 *);
++u32 fmc_get_mode(struct fmdev *, u8 *);
++
++/*
++ * channel spacing
++ */
++#define FM_CHANNEL_SPACING_50KHZ 1
++#define FM_CHANNEL_SPACING_100KHZ 2
++#define FM_CHANNEL_SPACING_200KHZ 4
++#define FM_FREQ_MUL 50
++
++#endif
++
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0010-drivers-media-radio-wl128x-FM-driver-RX-sources.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0010-drivers-media-radio-wl128x-FM-driver-RX-sources.patch
new file mode 100644
index 0000000000..8899a3187a
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0010-drivers-media-radio-wl128x-FM-driver-RX-sources.patch
@@ -0,0 +1,938 @@
+From ba32e1ae2a43f33dcfd459c1456d4e612da885db Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:24 +0000
+Subject: [PATCH 10/15] drivers:media:radio: wl128x: FM driver RX sources
+
+This has implementation for FM RX functionality.
+It communicates with FM V4l2 module and FM common module
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/wl128x/fmdrv_rx.c | 847 +++++++++++++++++++++++++++++++++
+ drivers/media/radio/wl128x/fmdrv_rx.h | 59 +++
+ 2 files changed, 906 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_rx.c
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_rx.h
+
+diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c
+new file mode 100644
+index 0000000..ec529b5
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_rx.c
+@@ -0,0 +1,847 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ * This sub-module of FM driver implements FM RX functionality.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ * Author: Raja Mani <raja_mani@ti.com>
++ * Author: Manjunatha Halli <manjunatha_halli@ti.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include "fmdrv.h"
++#include "fmdrv_common.h"
++#include "fmdrv_rx.h"
++
++void fm_rx_reset_rds_cache(struct fmdev *fmdev)
++{
++ fmdev->rx.rds.flag = FM_RDS_DISABLE;
++ fmdev->rx.rds.last_blk_idx = 0;
++ fmdev->rx.rds.wr_idx = 0;
++ fmdev->rx.rds.rd_idx = 0;
++
++ if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
++ fmdev->irq_info.mask |= FM_LEV_EVENT;
++}
++
++void fm_rx_reset_station_info(struct fmdev *fmdev)
++{
++ fmdev->rx.stat_info.picode = FM_NO_PI_CODE;
++ fmdev->rx.stat_info.afcache_size = 0;
++ fmdev->rx.stat_info.af_list_max = 0;
++}
++
++u32 fm_rx_set_freq(struct fmdev *fmdev, u32 freq)
++{
++ unsigned long timeleft;
++ u16 payload, curr_frq, intr_flag;
++ u32 curr_frq_in_khz;
++ u32 ret, resp_len;
++
++ if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) {
++ fmerr("Invalid frequency %d\n", freq);
++ return -EINVAL;
++ }
++
++ /* Set audio enable */
++ payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG;
++
++ ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Set hilo to automatic selection */
++ payload = FM_RX_IFFREQ_HILO_AUTOMATIC;
++ ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Calculate frequency index and set*/
++ payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
++
++ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Read flags - just to clear any pending interrupts if we had */
++ ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Enable FR, BL interrupts */
++ intr_flag = fmdev->irq_info.mask;
++ fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
++ payload = fmdev->irq_info.mask;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Start tune */
++ payload = FM_TUNER_PRESET_MODE;
++ ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ goto exit;
++
++ /* Wait for tune ended interrupt */
++ init_completion(&fmdev->maintask_comp);
++ timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
++ FM_DRV_TX_TIMEOUT);
++ if (!timeleft) {
++ fmerr("Timeout(%d sec),didn't get tune ended int\n",
++ jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++
++ /* Read freq back to confirm */
++ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len);
++ if (ret < 0)
++ goto exit;
++
++ curr_frq = be16_to_cpu(curr_frq);
++ curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));
++
++ if (curr_frq_in_khz != freq) {
++ pr_info("Frequency is set to (%d) but "
++ "requested freq is (%d)\n", curr_frq_in_khz, freq);
++ }
++
++ /* Update local cache */
++ fmdev->rx.freq = curr_frq_in_khz;
++exit:
++ /* Re-enable default FM interrupts */
++ fmdev->irq_info.mask = intr_flag;
++ payload = fmdev->irq_info.mask;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Reset RDS cache and current station pointers */
++ fm_rx_reset_rds_cache(fmdev);
++ fm_rx_reset_station_info(fmdev);
++
++ return ret;
++}
++
++static u32 fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing)
++{
++ u16 payload;
++ u32 ret;
++
++ if (spacing > 0 && spacing <= 50000)
++ spacing = FM_CHANNEL_SPACING_50KHZ;
++ else if (spacing > 50000 && spacing <= 100000)
++ spacing = FM_CHANNEL_SPACING_100KHZ;
++ else
++ spacing = FM_CHANNEL_SPACING_200KHZ;
++
++ /* set channel spacing */
++ payload = spacing;
++ ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL;
++
++ return ret;
++}
++
++u32 fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,
++ u32 wrap_around, u32 spacing)
++{
++ u32 resp_len;
++ u16 curr_frq, next_frq, last_frq;
++ u16 payload, int_reason, intr_flag;
++ u16 offset, space_idx;
++ unsigned long timeleft;
++ u32 ret;
++
++ /* Set channel spacing */
++ ret = fm_rx_set_channel_spacing(fmdev, spacing);
++ if (ret < 0) {
++ fmerr("Failed to set channel spacing\n");
++ return ret;
++ }
++
++ /* Read the current frequency from chip */
++ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL,
++ sizeof(curr_frq), &curr_frq, &resp_len);
++ if (ret < 0)
++ return ret;
++
++ curr_frq = be16_to_cpu(curr_frq);
++ last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
++
++ /* Check the offset in order to be aligned to the channel spacing*/
++ space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL;
++ offset = curr_frq % space_idx;
++
++ next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ :
++ curr_frq - space_idx /* Seek Down */ ;
++
++ /*
++ * Add or subtract offset in order to stay aligned to the channel
++ * spacing.
++ */
++ if ((short)next_frq < 0)
++ next_frq = last_frq - offset;
++ else if (next_frq > last_frq)
++ next_frq = 0 + offset;
++
++again:
++ /* Set calculated next frequency to perform seek */
++ payload = next_frq;
++ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Set search direction (0:Seek Down, 1:Seek Up) */
++ payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN);
++ ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Read flags - just to clear any pending interrupts if we had */
++ ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Enable FR, BL interrupts */
++ intr_flag = fmdev->irq_info.mask;
++ fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
++ payload = fmdev->irq_info.mask;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Start seek */
++ payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
++ ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Wait for tune ended/band limit reached interrupt */
++ init_completion(&fmdev->maintask_comp);
++ timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
++ FM_DRV_RX_SEEK_TIMEOUT);
++ if (!timeleft) {
++ fmerr("Timeout(%d sec),didn't get tune ended int\n",
++ jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
++ return -ETIMEDOUT;
++ }
++
++ int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
++
++ /* Re-enable default FM interrupts */
++ fmdev->irq_info.mask = intr_flag;
++ payload = fmdev->irq_info.mask;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ if (int_reason & FM_BL_EVENT) {
++ if (wrap_around == 0) {
++ fmdev->rx.freq = seek_upward ?
++ fmdev->rx.region.top_freq :
++ fmdev->rx.region.bot_freq;
++ } else {
++ fmdev->rx.freq = seek_upward ?
++ fmdev->rx.region.bot_freq :
++ fmdev->rx.region.top_freq;
++ /* Calculate frequency index to write */
++ next_frq = (fmdev->rx.freq -
++ fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
++ goto again;
++ }
++ } else {
++ /* Read freq to know where operation tune operation stopped */
++ ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2,
++ &curr_frq, &resp_len);
++ if (ret < 0)
++ return ret;
++
++ curr_frq = be16_to_cpu(curr_frq);
++ fmdev->rx.freq = (fmdev->rx.region.bot_freq +
++ ((u32)curr_frq * FM_FREQ_MUL));
++
++ }
++ /* Reset RDS cache and current station pointers */
++ fm_rx_reset_rds_cache(fmdev);
++ fm_rx_reset_station_info(fmdev);
++
++ return ret;
++}
++
++u32 fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (vol_to_set < FM_RX_VOLUME_MIN || vol_to_set > FM_RX_VOLUME_MAX) {
++ fmerr("Volume is not within(%d-%d) range\n",
++ FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);
++ return -EINVAL;
++ }
++ vol_to_set *= FM_RX_VOLUME_GAIN_STEP;
++
++ payload = vol_to_set;
++ ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->rx.volume = vol_to_set;
++ return ret;
++}
++
++/* Get volume */
++u32 fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (curr_vol == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP;
++
++ return 0;
++}
++
++/* To get current band's bottom and top frequency */
++u32 fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq)
++{
++ if (bot_freq != NULL)
++ *bot_freq = fmdev->rx.region.bot_freq;
++
++ if (top_freq != NULL)
++ *top_freq = fmdev->rx.region.top_freq;
++
++ return 0;
++}
++
++/* Returns current band index (0-Europe/US; 1-Japan) */
++void fm_rx_get_region(struct fmdev *fmdev, u8 *region)
++{
++ *region = fmdev->rx.region.fm_band;
++}
++
++/* Sets band (0-Europe/US; 1-Japan) */
++u32 fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set)
++{
++ u16 payload;
++ u32 new_frq = 0;
++ u32 ret;
++
++ if (region_to_set != FM_BAND_EUROPE_US &&
++ region_to_set != FM_BAND_JAPAN) {
++ fmerr("Invalid band\n");
++ return -EINVAL;
++ }
++
++ if (fmdev->rx.region.fm_band == region_to_set) {
++ fmerr("Requested band is already configured\n");
++ return 0;
++ }
++
++ /* Send cmd to set the band */
++ payload = (u16)region_to_set;
++ ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmc_update_region_info(fmdev, region_to_set);
++
++ /* Check whether current RX frequency is within band boundary */
++ if (fmdev->rx.freq < fmdev->rx.region.bot_freq)
++ new_frq = fmdev->rx.region.bot_freq;
++ else if (fmdev->rx.freq > fmdev->rx.region.top_freq)
++ new_frq = fmdev->rx.region.top_freq;
++
++ if (new_frq) {
++ fmdbg("Current freq is not within band limit boundary,"
++ "switching to %d KHz\n", new_frq);
++ /* Current RX frequency is not in range. So, update it */
++ ret = fm_rx_set_freq(fmdev, new_frq);
++ }
++
++ return ret;
++}
++
++/* Reads current mute mode (Mute Off/On/Attenuate)*/
++u32 fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (curr_mute_mode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *curr_mute_mode = fmdev->rx.mute_mode;
++
++ return 0;
++}
++
++static u32 fm_config_rx_mute_reg(struct fmdev *fmdev)
++{
++ u16 payload, muteval;
++ u32 ret;
++
++ muteval = 0;
++ switch (fmdev->rx.mute_mode) {
++ case FM_MUTE_ON:
++ muteval = FM_RX_AC_MUTE_MODE;
++ break;
++
++ case FM_MUTE_OFF:
++ muteval = FM_RX_UNMUTE_MODE;
++ break;
++
++ case FM_MUTE_ATTENUATE:
++ muteval = FM_RX_SOFT_MUTE_FORCE_MODE;
++ break;
++ }
++ if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON)
++ muteval |= FM_RX_RF_DEP_MODE;
++ else
++ muteval &= ~FM_RX_RF_DEP_MODE;
++
++ payload = muteval;
++ ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/* Configures mute mode (Mute Off/On/Attenuate) */
++u32 fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
++{
++ u8 org_state;
++ u32 ret;
++
++ if (fmdev->rx.mute_mode == mute_mode_toset)
++ return 0;
++
++ org_state = fmdev->rx.mute_mode;
++ fmdev->rx.mute_mode = mute_mode_toset;
++
++ ret = fm_config_rx_mute_reg(fmdev);
++ if (ret < 0) {
++ fmdev->rx.mute_mode = org_state;
++ return ret;
++ }
++
++ return 0;
++}
++
++/* Gets RF dependent soft mute mode enable/disable status */
++u32 fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (curr_mute_mode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *curr_mute_mode = fmdev->rx.rf_depend_mute;
++
++ return 0;
++}
++
++/* Sets RF dependent soft mute mode */
++u32 fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)
++{
++ u8 org_state;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON &&
++ rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) {
++ fmerr("Invalid RF dependent soft mute\n");
++ return -EINVAL;
++ }
++ if (fmdev->rx.rf_depend_mute == rfdepend_mute)
++ return 0;
++
++ org_state = fmdev->rx.rf_depend_mute;
++ fmdev->rx.rf_depend_mute = rfdepend_mute;
++
++ ret = fm_config_rx_mute_reg(fmdev);
++ if (ret < 0) {
++ fmdev->rx.rf_depend_mute = org_state;
++ return ret;
++ }
++
++ return 0;
++}
++
++/* Returns the signal strength level of current channel */
++u32 fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)
++{
++ u16 curr_rssi_lel;
++ u32 resp_len;
++ u32 ret;
++
++ if (rssilvl == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++ /* Read current RSSI level */
++ ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2,
++ &curr_rssi_lel, &resp_len);
++ if (ret < 0)
++ return ret;
++
++ *rssilvl = be16_to_cpu(curr_rssi_lel);
++
++ return 0;
++}
++
++/*
++ * Sets the signal strength level that once reached
++ * will stop the auto search process
++ */
++u32 fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset)
++{
++ u16 payload;
++ u32 ret;
++
++ if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN ||
++ rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) {
++ fmerr("Invalid RSSI threshold level\n");
++ return -EINVAL;
++ }
++ payload = (u16)rssi_lvl_toset;
++ ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->rx.rssi_threshold = rssi_lvl_toset;
++
++ return 0;
++}
++
++/* Returns current RX RSSI threshold value */
++u32 fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (curr_rssi_lvl == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *curr_rssi_lvl = fmdev->rx.rssi_threshold;
++
++ return 0;
++}
++
++/* Sets RX stereo/mono modes */
++u32 fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
++{
++ u16 payload;
++ u32 ret;
++
++ if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) {
++ fmerr("Invalid mode\n");
++ return -EINVAL;
++ }
++
++ /* Set stereo/mono mode */
++ payload = (u16)mode;
++ ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Set stereo blending mode */
++ payload = FM_STEREO_SOFT_BLEND;
++ ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/* Gets current RX stereo/mono mode */
++u32 fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)
++{
++ u16 curr_mode;
++ u32 ret, resp_len;
++
++ if (mode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2,
++ &curr_mode, &resp_len);
++ if (ret < 0)
++ return ret;
++
++ *mode = be16_to_cpu(curr_mode);
++
++ return 0;
++}
++
++/* Choose RX de-emphasis filter mode (50us/75us) */
++u32 fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (mode != FM_RX_EMPHASIS_FILTER_50_USEC &&
++ mode != FM_RX_EMPHASIS_FILTER_75_USEC) {
++ fmerr("Invalid rx de-emphasis mode (%d)\n", mode);
++ return -EINVAL;
++ }
++
++ payload = mode;
++ ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->rx.deemphasis_mode = mode;
++
++ return 0;
++}
++
++/* Gets current RX de-emphasis filter mode */
++u32 fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (curr_deemphasis_mode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *curr_deemphasis_mode = fmdev->rx.deemphasis_mode;
++
++ return 0;
++}
++
++/* Enable/Disable RX RDS */
++u32 fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
++{
++ u16 payload;
++ u32 ret;
++
++ if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {
++ fmerr("Invalid rds option\n");
++ return -EINVAL;
++ }
++
++ if (rds_en_dis == FM_RDS_ENABLE
++ && fmdev->rx.rds.flag == FM_RDS_DISABLE) {
++ /* Turn on RX RDS and RDS circuit */
++ payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON;
++ ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Clear and reset RDS FIFO */
++ payload = FM_RX_RDS_FLUSH_FIFO;
++ ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Read flags - just to clear any pending interrupts. */
++ ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2,
++ NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Set RDS FIFO threshold value */
++ payload = FM_RX_RDS_FIFO_THRESHOLD;
++ ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Enable RDS interrupt */
++ fmdev->irq_info.mask |= FM_RDS_EVENT;
++ payload = fmdev->irq_info.mask;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0) {
++ fmdev->irq_info.mask &= ~FM_RDS_EVENT;
++ return ret;
++ }
++
++ /* Update our local flag */
++ fmdev->rx.rds.flag = FM_RDS_ENABLE;
++ } else if (rds_en_dis == FM_RDS_DISABLE
++ && fmdev->rx.rds.flag == FM_RDS_ENABLE) {
++ /* Turn off RX RDS */
++ payload = FM_RX_PWR_SET_FM_ON_RDS_OFF;
++ ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Reset RDS pointers */
++ fmdev->rx.rds.last_blk_idx = 0;
++ fmdev->rx.rds.wr_idx = 0;
++ fmdev->rx.rds.rd_idx = 0;
++ fm_rx_reset_station_info(fmdev);
++
++ /* Update RDS local cache */
++ fmdev->irq_info.mask &= ~(FM_RDS_EVENT);
++ fmdev->rx.rds.flag = FM_RDS_DISABLE;
++ }
++
++ return 0;
++}
++
++/* Returns current RX RDS enable/disable status */
++u32 fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (curr_rds_en_dis == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *curr_rds_en_dis = fmdev->rx.rds.flag;
++
++ return 0;
++}
++
++/* Sets RDS operation mode (RDS/RDBS) */
++u32 fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) {
++ fmerr("Invalid rds mode\n");
++ return -EINVAL;
++ }
++ /* Set RDS operation mode */
++ payload = (u16)rds_mode;
++ ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->rx.rds_mode = rds_mode;
++
++ return 0;
++}
++
++/* Returns current RDS operation mode */
++u32 fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (rds_mode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *rds_mode = fmdev->rx.rds_mode;
++
++ return 0;
++}
++
++/* Configures Alternate Frequency switch mode */
++u32 fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON &&
++ af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) {
++ fmerr("Invalid af mode\n");
++ return -EINVAL;
++ }
++ /* Enable/disable low RSSI interrupt based on af_mode */
++ if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
++ fmdev->irq_info.mask |= FM_LEV_EVENT;
++ else
++ fmdev->irq_info.mask &= ~FM_LEV_EVENT;
++
++ payload = fmdev->irq_info.mask;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->rx.af_mode = af_mode;
++
++ return 0;
++}
++
++/* Returns Alternate Frequency switch status */
++u32 fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode)
++{
++ if (fmdev->curr_fmmode != FM_MODE_RX)
++ return -EPERM;
++
++ if (af_mode == NULL) {
++ fmerr("Invalid memory\n");
++ return -ENOMEM;
++ }
++
++ *af_mode = fmdev->rx.af_mode;
++
++ return 0;
++}
+diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h
+new file mode 100644
+index 0000000..329e62f
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_rx.h
+@@ -0,0 +1,59 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ * FM RX module header.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef _FMDRV_RX_H
++#define _FMDRV_RX_H
++
++u32 fm_rx_set_freq(struct fmdev *, u32);
++u32 fm_rx_set_mute_mode(struct fmdev *, u8);
++u32 fm_rx_set_stereo_mono(struct fmdev *, u16);
++u32 fm_rx_set_rds_mode(struct fmdev *, u8);
++u32 fm_rx_set_rds_system(struct fmdev *, u8);
++u32 fm_rx_set_volume(struct fmdev *, u16);
++u32 fm_rx_set_rssi_threshold(struct fmdev *, short);
++u32 fm_rx_set_region(struct fmdev *, u8);
++u32 fm_rx_set_rfdepend_softmute(struct fmdev *, u8);
++u32 fm_rx_set_deemphasis_mode(struct fmdev *, u16);
++u32 fm_rx_set_af_switch(struct fmdev *, u8);
++
++void fm_rx_reset_rds_cache(struct fmdev *);
++void fm_rx_reset_station_info(struct fmdev *);
++
++u32 fm_rx_seek(struct fmdev *, u32, u32, u32);
++
++u32 fm_rx_get_rds_mode(struct fmdev *, u8 *);
++u32 fm_rx_get_rds_system(struct fmdev *, u8 *);
++u32 fm_rx_get_mute_mode(struct fmdev *, u8 *);
++u32 fm_rx_get_volume(struct fmdev *, u16 *);
++u32 fm_rx_get_band_freq_range(struct fmdev *,
++ u32 *, u32 *);
++u32 fm_rx_get_stereo_mono(struct fmdev *, u16 *);
++u32 fm_rx_get_rssi_level(struct fmdev *, u16 *);
++u32 fm_rx_get_rssi_threshold(struct fmdev *, short *);
++u32 fm_rx_get_rfdepend_softmute(struct fmdev *, u8 *);
++u32 fm_rx_get_deemph_mode(struct fmdev *, u16 *);
++u32 fm_rx_get_af_switch(struct fmdev *, u8 *);
++void fm_rx_get_region(struct fmdev *, u8 *);
++
++u32 fm_rx_set_chanl_spacing(struct fmdev *, u8);
++u32 fm_rx_get_chanl_spacing(struct fmdev *, u8 *);
++#endif
++
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0011-drivers-media-radio-wl128x-FM-driver-TX-sources.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0011-drivers-media-radio-wl128x-FM-driver-TX-sources.patch
new file mode 100644
index 0000000000..52b409f867
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0011-drivers-media-radio-wl128x-FM-driver-TX-sources.patch
@@ -0,0 +1,494 @@
+From 87d399bad67bdff67c1601fbb8e54deb5e0cf7e0 Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:25 +0000
+Subject: [PATCH 11/15] drivers:media:radio: wl128x: FM driver TX sources
+
+This has implementation for FM TX functionality.
+It communicates with FM V4l2 module and FM common module.
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/wl128x/fmdrv_tx.c | 425 +++++++++++++++++++++++++++++++++
+ drivers/media/radio/wl128x/fmdrv_tx.h | 37 +++
+ 2 files changed, 462 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_tx.c
+ create mode 100644 drivers/media/radio/wl128x/fmdrv_tx.h
+
+diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c
+new file mode 100644
+index 0000000..be54068
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_tx.c
+@@ -0,0 +1,425 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ * This sub-module of FM driver implements FM TX functionality.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/delay.h>
++#include "fmdrv.h"
++#include "fmdrv_common.h"
++#include "fmdrv_tx.h"
++
++u32 fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->tx_data.aud_mode == mode)
++ return 0;
++
++ fmdbg("stereo mode: %d\n", mode);
++
++ /* Set Stereo/Mono mode */
++ payload = (1 - mode);
++ ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fmdev->tx_data.aud_mode = mode;
++
++ return ret;
++}
++
++static u32 set_rds_text(struct fmdev *fmdev, u8 *rds_text)
++{
++ u16 payload;
++ u32 ret;
++
++ ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text,
++ strlen(rds_text), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Scroll mode */
++ payload = (u16)0x1;
++ ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static u32 set_rds_data_mode(struct fmdev *fmdev, u8 mode)
++{
++ u16 payload;
++ u32 ret;
++
++ /* Setting unique PI TODO: how unique? */
++ payload = (u16)0xcafe;
++ ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Set decoder id */
++ payload = (u16)0xa;
++ ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* TODO: RDS_MODE_GET? */
++ return 0;
++}
++
++static u32 set_rds_len(struct fmdev *fmdev, u8 type, u16 len)
++{
++ u16 payload;
++ u32 ret;
++
++ len |= type << 8;
++ payload = len;
++ ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* TODO: LENGTH_GET? */
++ return 0;
++}
++
++u32 fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
++{
++ u16 payload;
++ u32 ret;
++ u8 rds_text[] = "Zoom2\n";
++
++ fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis,
++ FM_RDS_ENABLE, FM_RDS_DISABLE);
++
++ if (rds_en_dis == FM_RDS_ENABLE) {
++ /* Set RDS length */
++ set_rds_len(fmdev, 0, strlen(rds_text));
++
++ /* Set RDS text */
++ set_rds_text(fmdev, rds_text);
++
++ /* Set RDS mode */
++ set_rds_data_mode(fmdev, 0x0);
++ }
++
++ /* Send command to enable RDS */
++ if (rds_en_dis == FM_RDS_ENABLE)
++ payload = 0x01;
++ else
++ payload = 0x00;
++
++ ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ if (rds_en_dis == FM_RDS_ENABLE) {
++ /* Set RDS length */
++ set_rds_len(fmdev, 0, strlen(rds_text));
++
++ /* Set RDS text */
++ set_rds_text(fmdev, rds_text);
++ }
++ fmdev->tx_data.rds.flag = rds_en_dis;
++
++ return 0;
++}
++
++u32 fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX)
++ return -EPERM;
++
++ fm_tx_set_rds_mode(fmdev, 0);
++
++ /* Set RDS length */
++ set_rds_len(fmdev, rds_type, strlen(rds_text));
++
++ /* Set RDS text */
++ set_rds_text(fmdev, rds_text);
++
++ /* Set RDS mode */
++ set_rds_data_mode(fmdev, 0x0);
++
++ payload = 1;
++ ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++u32 fm_tx_set_af(struct fmdev *fmdev, u32 af)
++{
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX)
++ return -EPERM;
++
++ fmdbg("AF: %d\n", af);
++
++ af = (af - 87500) / 100;
++ payload = (u16)af;
++ ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++u32 fm_tx_set_region(struct fmdev *fmdev, u8 region)
++{
++ u16 payload;
++ u32 ret;
++
++ if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) {
++ fmerr("Invalid band\n");
++ return -EINVAL;
++ }
++
++ /* Send command to set the band */
++ payload = (u16)region;
++ ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++u32 fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
++{
++ u16 payload;
++ u32 ret;
++
++ fmdbg("tx: mute mode %d\n", mute_mode_toset);
++
++ payload = mute_mode_toset;
++ ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/* Set TX Audio I/O */
++static u32 set_audio_io(struct fmdev *fmdev)
++{
++ struct fmtx_data *tx = &fmdev->tx_data;
++ u16 payload;
++ u32 ret;
++
++ /* Set Audio I/O Enable */
++ payload = tx->audio_io;
++ ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* TODO: is audio set? */
++ return 0;
++}
++
++/* Start TX Transmission */
++static u32 enable_xmit(struct fmdev *fmdev, u8 new_xmit_state)
++{
++ struct fmtx_data *tx = &fmdev->tx_data;
++ unsigned long timeleft;
++ u16 payload;
++ u32 ret;
++
++ /* Enable POWER_ENB interrupts */
++ payload = FM_POW_ENB_EVENT;
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Set Power Enable */
++ payload = new_xmit_state;
++ ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* Wait for Power Enabled */
++ init_completion(&fmdev->maintask_comp);
++ timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
++ FM_DRV_TX_TIMEOUT);
++ if (!timeleft) {
++ fmerr("Timeout(%d sec),didn't get tune ended interrupt\n",
++ jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
++ return -ETIMEDOUT;
++ }
++
++ set_bit(FM_CORE_TX_XMITING, &fmdev->flag);
++ tx->xmit_state = new_xmit_state;
++
++ return 0;
++}
++
++/* Set TX power level */
++u32 fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl)
++{
++ u16 payload;
++ struct fmtx_data *tx = &fmdev->tx_data;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX)
++ return -EPERM;
++ fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl);
++
++ /* If the core isn't ready update global variable */
++ if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
++ tx->pwr_lvl = new_pwr_lvl;
++ return 0;
++ }
++
++ /* Set power level: Application will specify power level value in
++ * units of dB/uV, whereas range and step are specific to FM chip.
++ * For TI's WL chips, convert application specified power level value
++ * to chip specific value by subtracting 122 from it. Refer to TI FM
++ * data sheet for details.
++ * */
++
++ payload = (FM_PWR_LVL_HIGH - new_pwr_lvl);
++ ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ /* TODO: is the power level set? */
++ tx->pwr_lvl = new_pwr_lvl;
++
++ return 0;
++}
++
++/*
++ * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us)
++ * Convert V4L2 specified filter values to chip specific filter values.
++ */
++u32 fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis)
++{
++ struct fmtx_data *tx = &fmdev->tx_data;
++ u16 payload;
++ u32 ret;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX)
++ return -EPERM;
++
++ switch (preemphasis) {
++ case V4L2_PREEMPHASIS_DISABLED:
++ payload = FM_TX_PREEMPH_OFF;
++ break;
++ case V4L2_PREEMPHASIS_50_uS:
++ payload = FM_TX_PREEMPH_50US;
++ break;
++ case V4L2_PREEMPHASIS_75_uS:
++ payload = FM_TX_PREEMPH_75US;
++ break;
++ }
++
++ ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ tx->preemph = payload;
++
++ return ret;
++}
++
++/* Get the TX tuning capacitor value.*/
++u32 fm_tx_get_tune_cap_val(struct fmdev *fmdev)
++{
++ u16 curr_val;
++ u32 ret, resp_len;
++
++ if (fmdev->curr_fmmode != FM_MODE_TX)
++ return -EPERM;
++
++ ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD,
++ NULL, sizeof(curr_val), &curr_val, &resp_len);
++ if (ret < 0)
++ return ret;
++
++ curr_val = be16_to_cpu(curr_val);
++
++ return curr_val;
++}
++
++/* Set TX Frequency */
++u32 fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set)
++{
++ struct fmtx_data *tx = &fmdev->tx_data;
++ u16 payload, chanl_index;
++ u32 ret;
++
++ if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) {
++ enable_xmit(fmdev, 0);
++ clear_bit(FM_CORE_TX_XMITING, &fmdev->flag);
++ }
++
++ /* Enable FR, BL interrupts */
++ payload = (FM_FR_EVENT | FM_BL_EVENT);
++ ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ tx->tx_frq = (unsigned long)freq_to_set;
++ fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq);
++
++ chanl_index = freq_to_set / 10;
++
++ /* Set current tuner channel */
++ payload = chanl_index;
++ ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload,
++ sizeof(payload), NULL, NULL);
++ if (ret < 0)
++ return ret;
++
++ fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl);
++ fm_tx_set_preemph_filter(fmdev, tx->preemph);
++
++ tx->audio_io = 0x01; /* I2S */
++ set_audio_io(fmdev);
++
++ enable_xmit(fmdev, 0x01); /* Enable transmission */
++
++ tx->aud_mode = FM_STEREO_MODE;
++ tx->rds.flag = FM_RDS_DISABLE;
++
++ return 0;
++}
++
+diff --git a/drivers/media/radio/wl128x/fmdrv_tx.h b/drivers/media/radio/wl128x/fmdrv_tx.h
+new file mode 100644
+index 0000000..e393a2b
+--- /dev/null
++++ b/drivers/media/radio/wl128x/fmdrv_tx.h
+@@ -0,0 +1,37 @@
++/*
++ * FM Driver for Connectivity chip of Texas Instruments.
++ * FM TX module header.
++ *
++ * Copyright (C) 2011 Texas Instruments
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef _FMDRV_TX_H
++#define _FMDRV_TX_H
++
++u32 fm_tx_set_freq(struct fmdev *, u32);
++u32 fm_tx_set_pwr_lvl(struct fmdev *, u8);
++u32 fm_tx_set_region(struct fmdev *, u8);
++u32 fm_tx_set_mute_mode(struct fmdev *, u8);
++u32 fm_tx_set_stereo_mono(struct fmdev *, u16);
++u32 fm_tx_set_rds_mode(struct fmdev *, u8);
++u32 fm_tx_set_radio_text(struct fmdev *, u8 *, u8);
++u32 fm_tx_set_af(struct fmdev *, u32);
++u32 fm_tx_set_preemph_filter(struct fmdev *, u32);
++u32 fm_tx_get_tune_cap_val(struct fmdev *);
++
++#endif
++
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0012-drivers-media-radio-wl128x-Kconfig-Makefile-for-wl12.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0012-drivers-media-radio-wl128x-Kconfig-Makefile-for-wl12.patch
new file mode 100644
index 0000000000..2e5fda86c1
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0012-drivers-media-radio-wl128x-Kconfig-Makefile-for-wl12.patch
@@ -0,0 +1,52 @@
+From 949d2c98bb76cc321e5ef5a96a632d831e5953bf Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:26 +0000
+Subject: [PATCH 12/15] drivers:media:radio: wl128x: Kconfig & Makefile for wl128x driver
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/wl128x/Kconfig | 17 +++++++++++++++++
+ drivers/media/radio/wl128x/Makefile | 6 ++++++
+ 2 files changed, 23 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/media/radio/wl128x/Kconfig
+ create mode 100644 drivers/media/radio/wl128x/Makefile
+
+diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig
+new file mode 100644
+index 0000000..749f67b
+--- /dev/null
++++ b/drivers/media/radio/wl128x/Kconfig
+@@ -0,0 +1,17 @@
++#
++# TI's wl128x FM driver based on TI's ST driver.
++#
++menu "Texas Instruments WL128x FM driver (ST based)"
++config RADIO_WL128X
++ tristate "Texas Instruments WL128x FM Radio"
++ depends on VIDEO_V4L2 && RFKILL
++ select TI_ST
++ help
++ Choose Y here if you have this FM radio chip.
++
++ In order to control your radio card, you will need to use programs
++ that are compatible with the Video For Linux 2 API. Information on
++ this API and pointers to "v4l2" programs may be found at
++ <file:Documentation/video4linux/API.html>.
++
++endmenu
+diff --git a/drivers/media/radio/wl128x/Makefile b/drivers/media/radio/wl128x/Makefile
+new file mode 100644
+index 0000000..32a0ead
+--- /dev/null
++++ b/drivers/media/radio/wl128x/Makefile
+@@ -0,0 +1,6 @@
++#
++# Makefile for TI's shared transport driver based wl128x
++# FM radio.
++#
++obj-$(CONFIG_RADIO_WL128X) += fm_drv.o
++fm_drv-objs := fmdrv_common.o fmdrv_rx.o fmdrv_tx.o fmdrv_v4l2.o
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0013-drivers-media-radio-Update-Kconfig-and-Makefile-for-.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0013-drivers-media-radio-Update-Kconfig-and-Makefile-for-.patch
new file mode 100644
index 0000000000..ea257a4306
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0013-drivers-media-radio-Update-Kconfig-and-Makefile-for-.patch
@@ -0,0 +1,38 @@
+From 340d2fa4ff21c43309e70cc6b4a88f9ee6c23d95 Mon Sep 17 00:00:00 2001
+From: Manjunatha Halli <manjunatha_halli@ti.com>
+Date: Tue, 11 Jan 2011 11:31:27 +0000
+Subject: [PATCH 13/15] drivers:media:radio: Update Kconfig and Makefile for wl128x FM driver.
+
+Signed-off-by: Manjunatha Halli <manjunatha_halli@ti.com>
+Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
+---
+ drivers/media/radio/Kconfig | 3 +++
+ drivers/media/radio/Makefile | 1 +
+ 2 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
+index 83567b8..4529bc7 100644
+--- a/drivers/media/radio/Kconfig
++++ b/drivers/media/radio/Kconfig
+@@ -452,4 +452,7 @@ config RADIO_TIMBERDALE
+ found behind the Timberdale FPGA on the Russellville board.
+ Enabling this driver will automatically select the DSP and tuner.
+
++# TI's ST based wl128x FM radio
++source "drivers/media/radio/wl128x/Kconfig"
++
+ endif # RADIO_ADAPTERS
+diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
+index f615583..b71f448 100644
+--- a/drivers/media/radio/Makefile
++++ b/drivers/media/radio/Makefile
+@@ -26,5 +26,6 @@ obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
+ obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
+ obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
+ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
++obj-$(CONFIG_RADIO_WL128X) += wl128x/
+
+ EXTRA_CFLAGS += -Isound
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0014-drivers-misc-ti-st-change-protocol-parse-logic.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0014-drivers-misc-ti-st-change-protocol-parse-logic.patch
new file mode 100644
index 0000000000..c16dc453cd
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0014-drivers-misc-ti-st-change-protocol-parse-logic.patch
@@ -0,0 +1,900 @@
+From 46b2c4077bedb96a38cdceff88f2c9b0a9923a8c Mon Sep 17 00:00:00 2001
+From: Pavan Savoy <pavan_savoy@ti.com>
+Date: Tue, 4 Jan 2011 10:59:47 +0000
+Subject: [PATCH 14/15] drivers:misc:ti-st: change protocol parse logic
+
+TI shared transport driver had to specifically know the
+protocol headers for each type of data it can receive to
+properly re-assemble data if its fragmented during UART
+transaction or fragment if the data is an assembly of
+
+different protocol data.
+
+Now the individual protocol drivers provide enough header
+information for shared transport driver to do this in a
+generic way applicable for all protocols.
+
+Signed-off-by: Pavan Savoy <pavan_savoy@ti.com>
+---
+ drivers/misc/ti-st/st_core.c | 355 +++++++++++++-----------------------------
+ drivers/misc/ti-st/st_kim.c | 56 ++++----
+ include/linux/ti_wilink_st.h | 40 ++++--
+ 3 files changed, 167 insertions(+), 284 deletions(-)
+
+diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
+index f9aad06..84d73c5 100644
+--- a/drivers/misc/ti-st/st_core.c
++++ b/drivers/misc/ti-st/st_core.c
+@@ -25,10 +25,9 @@
+ #include <linux/init.h>
+ #include <linux/tty.h>
+
+-/* understand BT, FM and GPS for now */
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci.h>
++#include <linux/seq_file.h>
++#include <linux/skbuff.h>
++
+ #include <linux/ti_wilink_st.h>
+
+ /* function pointer pointing to either,
+@@ -38,21 +37,20 @@
+ void (*st_recv) (void*, const unsigned char*, long);
+
+ /********************************************************************/
+-#if 0
+-/* internal misc functions */
+-bool is_protocol_list_empty(void)
++static void add_channel_to_table(struct st_data_s *st_gdata,
++ struct st_proto_s *new_proto)
+ {
+- unsigned char i = 0;
+- pr_debug(" %s ", __func__);
+- for (i = 0; i < ST_MAX; i++) {
+- if (st_gdata->list[i] != NULL)
+- return ST_NOTEMPTY;
+- /* not empty */
+- }
+- /* list empty */
+- return ST_EMPTY;
++ pr_info("%s: id %d\n", __func__, new_proto->chnl_id);
++ /* list now has the channel id as index itself */
++ st_gdata->list[new_proto->chnl_id] = new_proto;
++}
++
++static void remove_channel_from_table(struct st_data_s *st_gdata,
++ struct st_proto_s *proto)
++{
++ pr_info("%s: id %d\n", __func__, proto->chnl_id);
++ st_gdata->list[proto->chnl_id] = NULL;
+ }
+-#endif
+
+ /* can be called in from
+ * -- KIM (during fw download)
+@@ -82,15 +80,15 @@ int st_int_write(struct st_data_s *st_gdata,
+ * push the skb received to relevant
+ * protocol stacks
+ */
+-void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)
++void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
+ {
+- pr_info(" %s(prot:%d) ", __func__, protoid);
++ pr_info(" %s(prot:%d) ", __func__, chnl_id);
+
+ if (unlikely
+ (st_gdata == NULL || st_gdata->rx_skb == NULL
+- || st_gdata->list[protoid] == NULL)) {
+- pr_err("protocol %d not registered, no data to send?",
+- protoid);
++ || st_gdata->list[chnl_id] == NULL)) {
++ pr_err("chnl_id %d not registered, no data to send?",
++ chnl_id);
+ kfree_skb(st_gdata->rx_skb);
+ return;
+ }
+@@ -99,17 +97,17 @@ void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)
+ * - should be just skb_queue_tail for the
+ * protocol stack driver
+ */
+- if (likely(st_gdata->list[protoid]->recv != NULL)) {
++ if (likely(st_gdata->list[chnl_id]->recv != NULL)) {
+ if (unlikely
+- (st_gdata->list[protoid]->recv
+- (st_gdata->list[protoid]->priv_data, st_gdata->rx_skb)
++ (st_gdata->list[chnl_id]->recv
++ (st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb)
+ != 0)) {
+- pr_err(" proto stack %d's ->recv failed", protoid);
++ pr_err(" proto stack %d's ->recv failed", chnl_id);
+ kfree_skb(st_gdata->rx_skb);
+ return;
+ }
+ } else {
+- pr_err(" proto stack %d's ->recv null", protoid);
++ pr_err(" proto stack %d's ->recv null", chnl_id);
+ kfree_skb(st_gdata->rx_skb);
+ }
+ return;
+@@ -124,7 +122,7 @@ void st_reg_complete(struct st_data_s *st_gdata, char err)
+ {
+ unsigned char i = 0;
+ pr_info(" %s ", __func__);
+- for (i = 0; i < ST_MAX; i++) {
++ for (i = 0; i < ST_MAX_CHANNELS; i++) {
+ if (likely(st_gdata != NULL && st_gdata->list[i] != NULL &&
+ st_gdata->list[i]->reg_complete_cb != NULL))
+ st_gdata->list[i]->reg_complete_cb
+@@ -133,7 +131,7 @@ void st_reg_complete(struct st_data_s *st_gdata, char err)
+ }
+
+ static inline int st_check_data_len(struct st_data_s *st_gdata,
+- int protoid, int len)
++ unsigned char chnl_id, int len)
+ {
+ int room = skb_tailroom(st_gdata->rx_skb);
+
+@@ -144,7 +142,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,
+ * has zero length payload. So, ask ST CORE to
+ * forward the packet to protocol driver (BT/FM/GPS)
+ */
+- st_send_frame(protoid, st_gdata);
++ st_send_frame(chnl_id, st_gdata);
+
+ } else if (len > room) {
+ /* Received packet's payload length is larger.
+@@ -157,7 +155,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,
+ /* Packet header has non-zero payload length and
+ * we have enough space in created skb. Lets read
+ * payload data */
+- st_gdata->rx_state = ST_BT_W4_DATA;
++ st_gdata->rx_state = ST_W4_DATA;
+ st_gdata->rx_count = len;
+ return len;
+ }
+@@ -167,6 +165,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,
+ st_gdata->rx_state = ST_W4_PACKET_TYPE;
+ st_gdata->rx_skb = NULL;
+ st_gdata->rx_count = 0;
++ st_gdata->rx_chnl = 0;
+
+ return 0;
+ }
+@@ -208,13 +207,10 @@ void st_int_recv(void *disc_data,
+ const unsigned char *data, long count)
+ {
+ char *ptr;
+- struct hci_event_hdr *eh;
+- struct hci_acl_hdr *ah;
+- struct hci_sco_hdr *sh;
+- struct fm_event_hdr *fm;
+- struct gps_event_hdr *gps;
+- int len = 0, type = 0, dlen = 0;
+- static enum proto_type protoid = ST_MAX;
++ struct st_proto_s *proto;
++ unsigned short payload_len = 0;
++ int len = 0, type = 0;
++ unsigned char *plen;
+ struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
+
+ ptr = (char *)data;
+@@ -242,64 +238,36 @@ void st_int_recv(void *disc_data,
+
+ /* Check ST RX state machine , where are we? */
+ switch (st_gdata->rx_state) {
+-
+- /* Waiting for complete packet ? */
+- case ST_BT_W4_DATA:
++ /* Waiting for complete packet ? */
++ case ST_W4_DATA:
+ pr_debug("Complete pkt received");
+-
+ /* Ask ST CORE to forward
+ * the packet to protocol driver */
+- st_send_frame(protoid, st_gdata);
++ st_send_frame(st_gdata->rx_chnl, st_gdata);
+
+ st_gdata->rx_state = ST_W4_PACKET_TYPE;
+ st_gdata->rx_skb = NULL;
+- protoid = ST_MAX; /* is this required ? */
+- continue;
+-
+- /* Waiting for Bluetooth event header ? */
+- case ST_BT_W4_EVENT_HDR:
+- eh = (struct hci_event_hdr *)st_gdata->rx_skb->
+- data;
+-
+- pr_debug("Event header: evt 0x%2.2x"
+- "plen %d", eh->evt, eh->plen);
+-
+- st_check_data_len(st_gdata, protoid, eh->plen);
+- continue;
+-
+- /* Waiting for Bluetooth acl header ? */
+- case ST_BT_W4_ACL_HDR:
+- ah = (struct hci_acl_hdr *)st_gdata->rx_skb->
+- data;
+- dlen = __le16_to_cpu(ah->dlen);
+-
+- pr_info("ACL header: dlen %d", dlen);
+-
+- st_check_data_len(st_gdata, protoid, dlen);
+- continue;
+-
+- /* Waiting for Bluetooth sco header ? */
+- case ST_BT_W4_SCO_HDR:
+- sh = (struct hci_sco_hdr *)st_gdata->rx_skb->
+- data;
+-
+- pr_info("SCO header: dlen %d", sh->dlen);
+-
+- st_check_data_len(st_gdata, protoid, sh->dlen);
+- continue;
+- case ST_FM_W4_EVENT_HDR:
+- fm = (struct fm_event_hdr *)st_gdata->rx_skb->
+- data;
+- pr_info("FM Header: ");
+- st_check_data_len(st_gdata, ST_FM, fm->plen);
+ continue;
+- /* TODO : Add GPS packet machine logic here */
+- case ST_GPS_W4_EVENT_HDR:
+- /* [0x09 pkt hdr][R/W byte][2 byte len] */
+- gps = (struct gps_event_hdr *)st_gdata->rx_skb->
+- data;
+- pr_info("GPS Header: ");
+- st_check_data_len(st_gdata, ST_GPS, gps->plen);
++ /* parse the header to know details */
++ case ST_W4_HEADER:
++ proto = st_gdata->list[st_gdata->rx_chnl];
++ plen =
++ &st_gdata->rx_skb->data
++ [proto->offset_len_in_hdr];
++ pr_info("plen pointing to %x\n", *plen);
++ if (proto->len_size == 1)/* 1 byte len field */
++ payload_len = *(unsigned char *)plen;
++ else if (proto->len_size == 2)
++ payload_len =
++ __le16_to_cpu(*(unsigned short *)plen);
++ else
++ pr_info("%s: invalid length "
++ "for id %d\n",
++ __func__, proto->chnl_id);
++ st_check_data_len(st_gdata, proto->chnl_id,
++ payload_len);
++ pr_info("off %d, pay len %d\n",
++ proto->offset_len_in_hdr, payload_len);
+ continue;
+ } /* end of switch rx_state */
+ }
+@@ -308,51 +276,6 @@ void st_int_recv(void *disc_data,
+ /* Check first byte of packet and identify module
+ * owner (BT/FM/GPS) */
+ switch (*ptr) {
+-
+- /* Bluetooth event packet? */
+- case HCI_EVENT_PKT:
+- pr_info("Event packet");
+- st_gdata->rx_state = ST_BT_W4_EVENT_HDR;
+- st_gdata->rx_count = HCI_EVENT_HDR_SIZE;
+- type = HCI_EVENT_PKT;
+- protoid = ST_BT;
+- break;
+-
+- /* Bluetooth acl packet? */
+- case HCI_ACLDATA_PKT:
+- pr_info("ACL packet");
+- st_gdata->rx_state = ST_BT_W4_ACL_HDR;
+- st_gdata->rx_count = HCI_ACL_HDR_SIZE;
+- type = HCI_ACLDATA_PKT;
+- protoid = ST_BT;
+- break;
+-
+- /* Bluetooth sco packet? */
+- case HCI_SCODATA_PKT:
+- pr_info("SCO packet");
+- st_gdata->rx_state = ST_BT_W4_SCO_HDR;
+- st_gdata->rx_count = HCI_SCO_HDR_SIZE;
+- type = HCI_SCODATA_PKT;
+- protoid = ST_BT;
+- break;
+-
+- /* Channel 8(FM) packet? */
+- case ST_FM_CH8_PKT:
+- pr_info("FM CH8 packet");
+- type = ST_FM_CH8_PKT;
+- st_gdata->rx_state = ST_FM_W4_EVENT_HDR;
+- st_gdata->rx_count = FM_EVENT_HDR_SIZE;
+- protoid = ST_FM;
+- break;
+-
+- /* Channel 9(GPS) packet? */
+- case 0x9: /*ST_LL_GPS_CH9_PKT */
+- pr_info("GPS CH9 packet");
+- type = 0x9; /* ST_LL_GPS_CH9_PKT; */
+- protoid = ST_GPS;
+- st_gdata->rx_state = ST_GPS_W4_EVENT_HDR;
+- st_gdata->rx_count = 3; /* GPS_EVENT_HDR_SIZE -1*/
+- break;
+ case LL_SLEEP_IND:
+ case LL_SLEEP_ACK:
+ case LL_WAKE_UP_IND:
+@@ -373,57 +296,22 @@ void st_int_recv(void *disc_data,
+ continue;
+ /* Unknow packet? */
+ default:
+- pr_err("Unknown packet type %2.2x", (__u8) *ptr);
+- ptr++;
+- count--;
+- continue;
++ type = *ptr;
++ st_gdata->rx_skb = alloc_skb(
++ st_gdata->list[type]->max_frame_size,
++ GFP_ATOMIC);
++ skb_reserve(st_gdata->rx_skb,
++ st_gdata->list[type]->reserve);
++ /* next 2 required for BT only */
++ st_gdata->rx_skb->cb[0] = type; /*pkt_type*/
++ st_gdata->rx_skb->cb[1] = 0; /*incoming*/
++ st_gdata->rx_chnl = *ptr;
++ st_gdata->rx_state = ST_W4_HEADER;
++ st_gdata->rx_count = st_gdata->list[type]->hdr_len;
++ pr_info("rx_count %ld\n", st_gdata->rx_count);
+ };
+ ptr++;
+ count--;
+-
+- switch (protoid) {
+- case ST_BT:
+- /* Allocate new packet to hold received data */
+- st_gdata->rx_skb =
+- bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+- if (!st_gdata->rx_skb) {
+- pr_err("Can't allocate mem for new packet");
+- st_gdata->rx_state = ST_W4_PACKET_TYPE;
+- st_gdata->rx_count = 0;
+- return;
+- }
+- bt_cb(st_gdata->rx_skb)->pkt_type = type;
+- break;
+- case ST_FM: /* for FM */
+- st_gdata->rx_skb =
+- alloc_skb(FM_MAX_FRAME_SIZE, GFP_ATOMIC);
+- if (!st_gdata->rx_skb) {
+- pr_err("Can't allocate mem for new packet");
+- st_gdata->rx_state = ST_W4_PACKET_TYPE;
+- st_gdata->rx_count = 0;
+- return;
+- }
+- /* place holder 0x08 */
+- skb_reserve(st_gdata->rx_skb, 1);
+- st_gdata->rx_skb->cb[0] = ST_FM_CH8_PKT;
+- break;
+- case ST_GPS:
+- /* for GPS */
+- st_gdata->rx_skb =
+- alloc_skb(100 /*GPS_MAX_FRAME_SIZE */ , GFP_ATOMIC);
+- if (!st_gdata->rx_skb) {
+- pr_err("Can't allocate mem for new packet");
+- st_gdata->rx_state = ST_W4_PACKET_TYPE;
+- st_gdata->rx_count = 0;
+- return;
+- }
+- /* place holder 0x09 */
+- skb_reserve(st_gdata->rx_skb, 1);
+- st_gdata->rx_skb->cb[0] = 0x09; /*ST_GPS_CH9_PKT; */
+- break;
+- case ST_MAX:
+- break;
+- }
+ }
+ pr_debug("done %s", __func__);
+ return;
+@@ -565,20 +453,28 @@ long st_register(struct st_proto_s *new_proto)
+ unsigned long flags = 0;
+
+ st_kim_ref(&st_gdata, 0);
+- pr_info("%s(%d) ", __func__, new_proto->type);
++ pr_info("%s(%d) ", __func__, new_proto->chnl_id);
+ if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL
+ || new_proto->reg_complete_cb == NULL) {
+ pr_err("gdata/new_proto/recv or reg_complete_cb not ready");
++ if (st_gdata == NULL)
++ pr_err("error 1\n");
++ if (new_proto == NULL)
++ pr_err("error 2\n");
++ if (new_proto->recv == NULL)
++ pr_err("error 3\n");
++ if (new_proto->reg_complete_cb == NULL)
++ pr_err("erro 4\n");
+ return -1;
+ }
+
+- if (new_proto->type < ST_BT || new_proto->type >= ST_MAX) {
+- pr_err("protocol %d not supported", new_proto->type);
++ if (new_proto->chnl_id >= ST_MAX_CHANNELS) {
++ pr_err("chnl_id %d not supported", new_proto->chnl_id);
+ return -EPROTONOSUPPORT;
+ }
+
+- if (st_gdata->list[new_proto->type] != NULL) {
+- pr_err("protocol %d already registered", new_proto->type);
++ if (st_gdata->list[new_proto->chnl_id] != NULL) {
++ pr_err("chnl_id %d already registered", new_proto->chnl_id);
+ return -EALREADY;
+ }
+
+@@ -586,11 +482,11 @@ long st_register(struct st_proto_s *new_proto)
+ spin_lock_irqsave(&st_gdata->lock, flags);
+
+ if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) {
+- pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->type);
++ pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id);
+ /* fw download in progress */
+- st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
++ st_kim_chip_toggle(new_proto->chnl_id, KIM_GPIO_ACTIVE);
+
+- st_gdata->list[new_proto->type] = new_proto;
++ add_channel_to_table(st_gdata, new_proto);
+ st_gdata->protos_registered++;
+ new_proto->write = st_write;
+
+@@ -598,7 +494,7 @@ long st_register(struct st_proto_s *new_proto)
+ spin_unlock_irqrestore(&st_gdata->lock, flags);
+ return -EINPROGRESS;
+ } else if (st_gdata->protos_registered == ST_EMPTY) {
+- pr_info(" protocol list empty :%d ", new_proto->type);
++ pr_info(" chnl_id list empty :%d ", new_proto->chnl_id);
+ set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
+ st_recv = st_kim_recv;
+
+@@ -622,9 +518,9 @@ long st_register(struct st_proto_s *new_proto)
+ return -1;
+ }
+
+- /* the protocol might require other gpios to be toggled
++ /* the chnl_id might require other gpios to be toggled
+ */
+- st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
++ st_kim_chip_toggle(new_proto->chnl_id, KIM_GPIO_ACTIVE);
+
+ clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
+ st_recv = st_int_recv;
+@@ -642,14 +538,14 @@ long st_register(struct st_proto_s *new_proto)
+ /* check for already registered once more,
+ * since the above check is old
+ */
+- if (st_gdata->list[new_proto->type] != NULL) {
++ if (st_gdata->list[new_proto->chnl_id] != NULL) {
+ pr_err(" proto %d already registered ",
+- new_proto->type);
++ new_proto->chnl_id);
+ return -EALREADY;
+ }
+
+ spin_lock_irqsave(&st_gdata->lock, flags);
+- st_gdata->list[new_proto->type] = new_proto;
++ add_channel_to_table(st_gdata, new_proto);
+ st_gdata->protos_registered++;
+ new_proto->write = st_write;
+ spin_unlock_irqrestore(&st_gdata->lock, flags);
+@@ -657,22 +553,7 @@ long st_register(struct st_proto_s *new_proto)
+ }
+ /* if fw is already downloaded & new stack registers protocol */
+ else {
+- switch (new_proto->type) {
+- case ST_BT:
+- /* do nothing */
+- break;
+- case ST_FM:
+- case ST_GPS:
+- st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
+- break;
+- case ST_MAX:
+- default:
+- pr_err("%d protocol not supported",
+- new_proto->type);
+- spin_unlock_irqrestore(&st_gdata->lock, flags);
+- return -EPROTONOSUPPORT;
+- }
+- st_gdata->list[new_proto->type] = new_proto;
++ add_channel_to_table(st_gdata, new_proto);
+ st_gdata->protos_registered++;
+ new_proto->write = st_write;
+
+@@ -680,48 +561,48 @@ long st_register(struct st_proto_s *new_proto)
+ spin_unlock_irqrestore(&st_gdata->lock, flags);
+ return err;
+ }
+- pr_debug("done %s(%d) ", __func__, new_proto->type);
++ pr_debug("done %s(%d) ", __func__, new_proto->chnl_id);
+ }
+ EXPORT_SYMBOL_GPL(st_register);
+
+ /* to unregister a protocol -
+ * to be called from protocol stack driver
+ */
+-long st_unregister(enum proto_type type)
++long st_unregister(struct st_proto_s *proto)
+ {
+ long err = 0;
+ unsigned long flags = 0;
+ struct st_data_s *st_gdata;
+
+- pr_debug("%s: %d ", __func__, type);
++ pr_debug("%s: %d ", __func__, proto->chnl_id);
+
+ st_kim_ref(&st_gdata, 0);
+- if (type < ST_BT || type >= ST_MAX) {
+- pr_err(" protocol %d not supported", type);
++ if (proto->chnl_id >= ST_MAX_CHANNELS) {
++ pr_err(" chnl_id %d not supported", proto->chnl_id);
+ return -EPROTONOSUPPORT;
+ }
+
+ spin_lock_irqsave(&st_gdata->lock, flags);
+
+- if (st_gdata->list[type] == NULL) {
+- pr_err(" protocol %d not registered", type);
++ if (st_gdata->list[proto->chnl_id] == NULL) {
++ pr_err(" chnl_id %d not registered", proto->chnl_id);
+ spin_unlock_irqrestore(&st_gdata->lock, flags);
+ return -EPROTONOSUPPORT;
+ }
+
+ st_gdata->protos_registered--;
+- st_gdata->list[type] = NULL;
++ remove_channel_from_table(st_gdata, proto);
+
+ /* kim ignores BT in the below function
+ * and handles the rest, BT is toggled
+ * only in kim_start and kim_stop
+ */
+- st_kim_chip_toggle(type, KIM_GPIO_INACTIVE);
++ st_kim_chip_toggle(proto->chnl_id, KIM_GPIO_INACTIVE);
+ spin_unlock_irqrestore(&st_gdata->lock, flags);
+
+ if ((st_gdata->protos_registered == ST_EMPTY) &&
+ (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
+- pr_info(" all protocols unregistered ");
++ pr_info(" all chnl_ids unregistered ");
+
+ /* stop traffic on tty */
+ if (st_gdata->tty) {
+@@ -729,7 +610,7 @@ long st_unregister(enum proto_type type)
+ stop_tty(st_gdata->tty);
+ }
+
+- /* all protocols now unregistered */
++ /* all chnl_ids now unregistered */
+ st_kim_stop(st_gdata->kim_data);
+ /* disable ST LL */
+ st_ll_disable(st_gdata);
+@@ -745,7 +626,7 @@ long st_write(struct sk_buff *skb)
+ {
+ struct st_data_s *st_gdata;
+ #ifdef DEBUG
+- enum proto_type protoid = ST_MAX;
++ unsigned char chnl_id = ST_MAX_CHANNELS;
+ #endif
+ long len;
+
+@@ -756,22 +637,10 @@ long st_write(struct sk_buff *skb)
+ return -1;
+ }
+ #ifdef DEBUG /* open-up skb to read the 1st byte */
+- switch (skb->data[0]) {
+- case HCI_COMMAND_PKT:
+- case HCI_ACLDATA_PKT:
+- case HCI_SCODATA_PKT:
+- protoid = ST_BT;
+- break;
+- case ST_FM_CH8_PKT:
+- protoid = ST_FM;
+- break;
+- case 0x09:
+- protoid = ST_GPS;
+- break;
+- }
+- if (unlikely(st_gdata->list[protoid] == NULL)) {
+- pr_err(" protocol %d not registered, and writing? ",
+- protoid);
++ chnl_id = skb->data[0];
++ if (unlikely(st_gdata->list[chnl_id] == NULL)) {
++ pr_err(" chnl_id %d not registered, and writing? ",
++ chnl_id);
+ return -1;
+ }
+ #endif
+@@ -824,7 +693,7 @@ static int st_tty_open(struct tty_struct *tty)
+
+ static void st_tty_close(struct tty_struct *tty)
+ {
+- unsigned char i = ST_MAX;
++ unsigned char i = ST_MAX_CHANNELS;
+ unsigned long flags = 0;
+ struct st_data_s *st_gdata = tty->disc_data;
+
+@@ -835,7 +704,7 @@ static void st_tty_close(struct tty_struct *tty)
+ * un-installed for some reason - what should be done ?
+ */
+ spin_lock_irqsave(&st_gdata->lock, flags);
+- for (i = ST_BT; i < ST_MAX; i++) {
++ for (i = ST_BT; i < ST_MAX_CHANNELS; i++) {
+ if (st_gdata->list[i] != NULL)
+ pr_err("%d not un-registered", i);
+ st_gdata->list[i] = NULL;
+@@ -869,7 +738,7 @@ static void st_tty_close(struct tty_struct *tty)
+ static void st_tty_receive(struct tty_struct *tty, const unsigned char *data,
+ char *tty_flags, int count)
+ {
+-
++#define VERBOSE
+ #ifdef VERBOSE
+ print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE,
+ 16, 1, data, count, 0);
+diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
+index 73b6c8b..707c858 100644
+--- a/drivers/misc/ti-st/st_kim.c
++++ b/drivers/misc/ti-st/st_kim.c
+@@ -32,11 +32,7 @@
+ #include <linux/sched.h>
+ #include <linux/rfkill.h>
+
+-/* understand BT events for fw response */
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci.h>
+-
++#include <linux/skbuff.h>
+ #include <linux/ti_wilink_st.h>
+
+
+@@ -134,7 +130,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len)
+ /* Packet header has non-zero payload length and
+ * we have enough space in created skb. Lets read
+ * payload data */
+- kim_gdata->rx_state = ST_BT_W4_DATA;
++ kim_gdata->rx_state = ST_W4_DATA;
+ kim_gdata->rx_count = len;
+ return len;
+ }
+@@ -158,8 +154,8 @@ void kim_int_recv(struct kim_data_s *kim_gdata,
+ const unsigned char *data, long count)
+ {
+ const unsigned char *ptr;
+- struct hci_event_hdr *eh;
+ int len = 0, type = 0;
++ unsigned char *plen;
+
+ pr_debug("%s", __func__);
+ /* Decode received bytes here */
+@@ -183,29 +179,27 @@ void kim_int_recv(struct kim_data_s *kim_gdata,
+ /* Check ST RX state machine , where are we? */
+ switch (kim_gdata->rx_state) {
+ /* Waiting for complete packet ? */
+- case ST_BT_W4_DATA:
++ case ST_W4_DATA:
+ pr_debug("Complete pkt received");
+ validate_firmware_response(kim_gdata);
+ kim_gdata->rx_state = ST_W4_PACKET_TYPE;
+ kim_gdata->rx_skb = NULL;
+ continue;
+ /* Waiting for Bluetooth event header ? */
+- case ST_BT_W4_EVENT_HDR:
+- eh = (struct hci_event_hdr *)kim_gdata->
+- rx_skb->data;
+- pr_debug("Event header: evt 0x%2.2x"
+- "plen %d", eh->evt, eh->plen);
+- kim_check_data_len(kim_gdata, eh->plen);
++ case ST_W4_HEADER:
++ plen =
++ (unsigned char *)&kim_gdata->rx_skb->data[1];
++ pr_debug("event hdr: plen 0x%02x\n", *plen);
++ kim_check_data_len(kim_gdata, *plen);
+ continue;
+ } /* end of switch */
+ } /* end of if rx_state */
+ switch (*ptr) {
+ /* Bluetooth event packet? */
+- case HCI_EVENT_PKT:
+- pr_info("Event packet");
+- kim_gdata->rx_state = ST_BT_W4_EVENT_HDR;
+- kim_gdata->rx_count = HCI_EVENT_HDR_SIZE;
+- type = HCI_EVENT_PKT;
++ case 0x04:
++ kim_gdata->rx_state = ST_W4_HEADER;
++ kim_gdata->rx_count = 2;
++ type = *ptr;
+ break;
+ default:
+ pr_info("unknown packet");
+@@ -216,16 +210,18 @@ void kim_int_recv(struct kim_data_s *kim_gdata,
+ ptr++;
+ count--;
+ kim_gdata->rx_skb =
+- bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
++ alloc_skb(1024+8, GFP_ATOMIC);
+ if (!kim_gdata->rx_skb) {
+ pr_err("can't allocate mem for new packet");
+ kim_gdata->rx_state = ST_W4_PACKET_TYPE;
+ kim_gdata->rx_count = 0;
+ return;
+ }
+- bt_cb(kim_gdata->rx_skb)->pkt_type = type;
++ skb_reserve(kim_gdata->rx_skb, 8);
++ kim_gdata->rx_skb->cb[0] = 4;
++ kim_gdata->rx_skb->cb[1] = 0;
++
+ }
+- pr_info("done %s", __func__);
+ return;
+ }
+
+@@ -398,7 +394,7 @@ void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state)
+ gpio_set_value(kim_gdata->gpios[ST_GPS], GPIO_LOW);
+ break;
+
+- case ST_MAX:
++ case ST_MAX_CHANNELS:
+ default:
+ break;
+ }
+@@ -416,7 +412,6 @@ void st_kim_recv(void *disc_data, const unsigned char *data, long count)
+ struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
+ struct kim_data_s *kim_gdata = st_gdata->kim_data;
+
+- pr_info(" %s ", __func__);
+ /* copy to local buffer */
+ if (unlikely(data[4] == 0x01 && data[5] == 0x10 && data[0] == 0x04)) {
+ /* must be the read_ver_cmd */
+@@ -578,7 +573,7 @@ static int kim_toggle_radio(void *data, bool blocked)
+ else
+ st_kim_chip_toggle(type, KIM_GPIO_ACTIVE);
+ break;
+- case ST_MAX:
++ case ST_MAX_CHANNELS:
+ pr_err(" wrong proto type ");
+ break;
+ }
+@@ -664,12 +659,13 @@ static int kim_probe(struct platform_device *pdev)
+ /* refer to itself */
+ kim_gdata->core_data->kim_data = kim_gdata;
+
+- for (proto = 0; proto < ST_MAX; proto++) {
++ for (proto = 0; proto < ST_MAX_CHANNELS; proto++) {
+ kim_gdata->gpios[proto] = gpios[proto];
+ pr_info(" %ld gpio to be requested", gpios[proto]);
+ }
+
+- for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) {
++ for (proto = 0; (proto < ST_MAX_CHANNELS)
++ && (gpios[proto] != -1); proto++) {
+ /* Claim the Bluetooth/FM/GPIO
+ * nShutdown gpio from the system
+ */
+@@ -704,7 +700,8 @@ static int kim_probe(struct platform_device *pdev)
+ init_completion(&kim_gdata->kim_rcvd);
+ init_completion(&kim_gdata->ldisc_installed);
+
+- for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) {
++ for (proto = 0; (proto < ST_MAX_CHANNELS)
++ && (gpios[proto] != -1); proto++) {
+ /* TODO: should all types be rfkill_type_bt ? */
+ kim_gdata->rf_protos[proto] = proto;
+ kim_gdata->rfkill[proto] = rfkill_alloc(protocol_names[proto],
+@@ -752,7 +749,8 @@ static int kim_remove(struct platform_device *pdev)
+
+ kim_gdata = dev_get_drvdata(&pdev->dev);
+
+- for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) {
++ for (proto = 0; (proto < ST_MAX_CHANNELS)
++ && (gpios[proto] != -1); proto++) {
+ /* Claim the Bluetooth/FM/GPIO
+ * nShutdown gpio from the system
+ */
+diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
+index 4c7be22..1674ca7 100644
+--- a/include/linux/ti_wilink_st.h
++++ b/include/linux/ti_wilink_st.h
+@@ -42,7 +42,7 @@ enum proto_type {
+ ST_BT,
+ ST_FM,
+ ST_GPS,
+- ST_MAX,
++ ST_MAX_CHANNELS = 16,
+ };
+
+ /**
+@@ -62,6 +62,17 @@ enum proto_type {
+ * @priv_data: privdate data holder for the protocol drivers, sent
+ * from the protocol drivers during registration, and sent back on
+ * reg_complete_cb and recv.
++ * @chnl_id: channel id the protocol driver is interested in, the channel
++ * id is nothing but the 1st byte of the packet in UART frame.
++ * @max_frame_size: size of the largest frame the protocol can receive.
++ * @hdr_len: length of the header structure of the protocol.
++ * @offset_len_in_hdr: this provides the offset of the length field in the
++ * header structure of the protocol header, to assist ST to know
++ * how much to receive, if the data is split across UART frames.
++ * @len_size: whether the length field inside the header is 2 bytes
++ * or 1 byte.
++ * @reserve: the number of bytes ST needs to reserve in the skb being
++ * prepared for the protocol driver.
+ */
+ struct st_proto_s {
+ enum proto_type type;
+@@ -70,10 +81,17 @@ struct st_proto_s {
+ void (*reg_complete_cb) (void *, char data);
+ long (*write) (struct sk_buff *skb);
+ void *priv_data;
++
++ unsigned char chnl_id;
++ unsigned short max_frame_size;
++ unsigned char hdr_len;
++ unsigned char offset_len_in_hdr;
++ unsigned char len_size;
++ unsigned char reserve;
+ };
+
+ extern long st_register(struct st_proto_s *);
+-extern long st_unregister(enum proto_type);
++extern long st_unregister(struct st_proto_s *);
+
+
+ /*
+@@ -114,6 +132,7 @@ extern long st_unregister(enum proto_type);
+ * @rx_skb: the skb where all data for a protocol gets accumulated,
+ * since tty might not call receive when a complete event packet
+ * is received, the states, count and the skb needs to be maintained.
++ * @rx_chnl: the channel ID for which the data is getting accumalated for.
+ * @txq: the list of skbs which needs to be sent onto the TTY.
+ * @tx_waitq: if the chip is not in AWAKE state, the skbs needs to be queued
+ * up in here, PM(WAKEUP_IND) data needs to be sent and then the skbs
+@@ -135,10 +154,11 @@ struct st_data_s {
+ #define ST_TX_SENDING 1
+ #define ST_TX_WAKEUP 2
+ unsigned long tx_state;
+- struct st_proto_s *list[ST_MAX];
++ struct st_proto_s *list[ST_MAX_CHANNELS];
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
++ unsigned char rx_chnl;
+ struct sk_buff_head txq, tx_waitq;
+ spinlock_t lock;
+ unsigned char protos_registered;
+@@ -243,12 +263,12 @@ struct kim_data_s {
+ struct completion kim_rcvd, ldisc_installed;
+ char resp_buffer[30];
+ const struct firmware *fw_entry;
+- long gpios[ST_MAX];
++ long gpios[ST_MAX_CHANNELS];
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+- struct rfkill *rfkill[ST_MAX];
+- enum proto_type rf_protos[ST_MAX];
++ struct rfkill *rfkill[ST_MAX_CHANNELS];
++ enum proto_type rf_protos[ST_MAX_CHANNELS];
+ struct st_data_s *core_data;
+ struct chip_version version;
+ };
+@@ -338,12 +358,8 @@ struct hci_command {
+
+ /* ST LL receiver states */
+ #define ST_W4_PACKET_TYPE 0
+-#define ST_BT_W4_EVENT_HDR 1
+-#define ST_BT_W4_ACL_HDR 2
+-#define ST_BT_W4_SCO_HDR 3
+-#define ST_BT_W4_DATA 4
+-#define ST_FM_W4_EVENT_HDR 5
+-#define ST_GPS_W4_EVENT_HDR 6
++#define ST_W4_HEADER 1
++#define ST_W4_DATA 2
+
+ /* ST LL state machines */
+ #define ST_LL_ASLEEP 0
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap-2.6.37/wl1271/0015-Bluetooth-btwilink-driver.patch b/recipes/linux/linux-omap-2.6.37/wl1271/0015-Bluetooth-btwilink-driver.patch
new file mode 100644
index 0000000000..f45149f330
--- /dev/null
+++ b/recipes/linux/linux-omap-2.6.37/wl1271/0015-Bluetooth-btwilink-driver.patch
@@ -0,0 +1,463 @@
+From e3948bda11a3a0d938500ffbc7ef43603909cc15 Mon Sep 17 00:00:00 2001
+From: Pavan Savoy <pavan_savoy@ti.com>
+Date: Tue, 4 Jan 2011 10:59:48 +0000
+Subject: [PATCH 15/15] Bluetooth: btwilink driver
+
+-- patch description --
+
+This is the bluetooth protocol driver for the TI WiLink7 chipsets.
+Texas Instrument's WiLink chipsets combine wireless technologies
+like BT, FM, GPS and WLAN onto a single chip.
+
+This Bluetooth driver works on top of the TI_ST shared transport
+line discipline driver which also allows other drivers like
+FM V4L2 and GPS character driver to make use of the same UART interface.
+
+Kconfig and Makefile modifications to enable the Bluetooth
+driver for Texas Instrument's WiLink 7 chipset.
+
+Signed-off-by: Pavan Savoy <pavan_savoy@ti.com>
+---
+ drivers/bluetooth/Kconfig | 10 +
+ drivers/bluetooth/Makefile | 1 +
+ drivers/bluetooth/btwilink.c | 397 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 408 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/bluetooth/btwilink.c
+
+diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
+index 02deef4..8e0de9a 100644
+--- a/drivers/bluetooth/Kconfig
++++ b/drivers/bluetooth/Kconfig
+@@ -219,4 +219,14 @@ config BT_ATH3K
+ Say Y here to compile support for "Atheros firmware download driver"
+ into the kernel or say M to compile it as module (ath3k).
+
++config BT_WILINK
++ tristate "Texas Instruments WiLink7 driver"
++ depends on TI_ST
++ help
++ This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS
++ combo devices. This makes use of shared transport line discipline
++ core driver to communicate with the BT core of the combo chip.
++
++ Say Y here to compile support for Texas Instrument's WiLink7 driver
++ into the kernel or say M to compile it as module.
+ endmenu
+diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
+index 71bdf13..f4460f4 100644
+--- a/drivers/bluetooth/Makefile
++++ b/drivers/bluetooth/Makefile
+@@ -18,6 +18,7 @@ obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
+ obj-$(CONFIG_BT_ATH3K) += ath3k.o
+ obj-$(CONFIG_BT_MRVL) += btmrvl.o
+ obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
++obj-$(CONFIG_BT_WILINK) += btwilink.o
+
+ btmrvl-y := btmrvl_main.o
+ btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
+diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
+new file mode 100644
+index 0000000..0201aca
+--- /dev/null
++++ b/drivers/bluetooth/btwilink.c
+@@ -0,0 +1,397 @@
++/*
++ * Texas Instrument's Bluetooth Driver For Shared Transport.
++ *
++ * Bluetooth Driver acts as interface between HCI core and
++ * TI Shared Transport Layer.
++ *
++ * Copyright (C) 2009-2010 Texas Instruments
++ * Author: Raja Mani <raja_mani@ti.com>
++ * Pavan Savoy <pavan_savoy@ti.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#include <linux/ti_wilink_st.h>
++
++/* Bluetooth Driver Version */
++#define VERSION "1.0"
++
++/* Number of seconds to wait for registration completion
++ * when ST returns PENDING status.
++ */
++#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
++
++/**
++ * struct ti_st - driver operation structure
++ * @hdev: hci device pointer which binds to bt driver
++ * @reg_status: ST registration callback status
++ * @st_write: write function provided by the ST driver
++ * to be used by the driver during send_frame.
++ * @wait_reg_completion - completion sync between ti_st_open
++ * and ti_st_registration_completion_cb.
++ */
++struct ti_st {
++ struct hci_dev *hdev;
++ char reg_status;
++ long (*st_write) (struct sk_buff *);
++ struct completion wait_reg_completion;
++};
++
++/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
++static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
++{
++ struct hci_dev *hdev = hst->hdev;
++
++ /* Update HCI stat counters */
++ switch (pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ }
++}
++
++/* ------- Interfaces to Shared Transport ------ */
++
++/* Called by ST layer to indicate protocol registration completion
++ * status.ti_st_open() function will wait for signal from this
++ * API when st_register() function returns ST_PENDING.
++ */
++static void st_registration_completion_cb(void *priv_data, char data)
++{
++ struct ti_st *lhst = priv_data;
++
++ /* Save registration status for use in ti_st_open() */
++ lhst->reg_status = data;
++ /* complete the wait in ti_st_open() */
++ complete(&lhst->wait_reg_completion);
++}
++
++/* Called by Shared Transport layer when receive data is
++ * available */
++static long st_receive(void *priv_data, struct sk_buff *skb)
++{
++ struct ti_st *lhst = priv_data;
++ int err;
++
++ if (!skb)
++ return -EFAULT;
++
++ if (!lhst) {
++ kfree_skb(skb);
++ return -EFAULT;
++ }
++
++ skb->dev = (void *) lhst->hdev;
++
++ /* Forward skb to HCI core layer */
++ err = hci_recv_frame(skb);
++ if (err < 0) {
++ BT_ERR("Unable to push skb to HCI core(%d)", err);
++ return err;
++ }
++
++ lhst->hdev->stat.byte_rx += skb->len;
++
++ return 0;
++}
++
++/* ------- Interfaces to HCI layer ------ */
++/* protocol structure registered with shared transport */
++static struct st_proto_s ti_st_proto[3] = {
++ {
++ .chnl_id = 0x02, /* ACL */
++ .recv = st_receive,
++ .reg_complete_cb = st_registration_completion_cb,
++ .hdr_len = 4,
++ .offset_len_in_hdr = 2,
++ .len_size = 2,
++ .reserve = 8,
++ },
++ {
++ .chnl_id = 0x03, /* SCO */
++ .recv = st_receive,
++ .reg_complete_cb = st_registration_completion_cb,
++ .hdr_len = 3,
++ .offset_len_in_hdr = 2,
++ .len_size = 1,
++ .reserve = 8,
++ },
++ {
++ .chnl_id = 0x04, /* HCI Events */
++ .recv = st_receive,
++ .reg_complete_cb = st_registration_completion_cb,
++ .hdr_len = 2,
++ .offset_len_in_hdr = 1,
++ .len_size = 1,
++ .reserve = 8,
++ },
++};
++
++/* Called from HCI core to initialize the device */
++static int ti_st_open(struct hci_dev *hdev)
++{
++ unsigned long timeleft;
++ struct ti_st *hst;
++ int err, i;
++
++ BT_DBG("%s %p", hdev->name, hdev);
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) {
++ BT_ERR("btwilink already opened");
++ return -EBUSY;
++ }
++
++ /* provide contexts for callbacks from ST */
++ hst = hdev->driver_data;
++
++ for (i = 0; i < 3; i++) {
++ ti_st_proto[i].priv_data = hst;
++ ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
++
++ err = st_register(&ti_st_proto[i]);
++ if (err == -EINPROGRESS) {
++ /* ST is busy with either protocol
++ * registration or firmware download.
++ */
++ /* Prepare wait-for-completion handler data structures.
++ */
++ init_completion(&hst->wait_reg_completion);
++
++ /* Reset ST registration callback status flag,
++ * this value will be updated in
++ * ti_st_registration_completion_cb()
++ * function whenever it called from ST driver.
++ */
++ hst->reg_status = -EINPROGRESS;
++
++ BT_DBG("waiting for registration "
++ "completion signal from ST");
++ timeleft = wait_for_completion_timeout
++ (&hst->wait_reg_completion,
++ msecs_to_jiffies(BT_REGISTER_TIMEOUT));
++ if (!timeleft) {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ BT_ERR("Timeout(%d sec),didn't get reg "
++ "completion signal from ST",
++ BT_REGISTER_TIMEOUT / 1000);
++ return -ETIMEDOUT;
++ }
++
++ /* Is ST registration callback
++ * called with ERROR status? */
++ if (hst->reg_status != 0) {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ BT_ERR("ST registration completed with invalid "
++ "status %d", hst->reg_status);
++ return -EAGAIN;
++ }
++ err = 0;
++ } else if (err != 0) {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ BT_ERR("st_register failed %d", err);
++ return err;
++ }
++ hst->st_write = ti_st_proto[i].write;
++ if (!hst->st_write) {
++ BT_ERR("undefined ST write function");
++ clear_bit(HCI_RUNNING, &hdev->flags);
++
++ /* Undo registration with ST */
++ err = st_unregister(&ti_st_proto[i]);
++ if (err)
++ BT_ERR("st_unregister() failed with "
++ "error %d", err);
++
++ hst->st_write = NULL;
++ /* ti_st_proto.write is filled up by the
++ * underlying shared transport driver
++ * upon registration
++ */
++ return err;
++ }
++ }
++
++ return err;
++}
++
++/* Close device */
++static int ti_st_close(struct hci_dev *hdev)
++{
++ int err, i;
++ struct ti_st *hst = hdev->driver_data;
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ for (i = 0; i < 3; i++) {
++ /* continue to unregister from transport */
++ err = st_unregister(&ti_st_proto[i]);
++ if (err)
++ BT_ERR("st_unregister() failed with error %d", err);
++ }
++
++ hst->st_write = NULL;
++
++ return err;
++}
++
++static int ti_st_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev;
++ struct ti_st *hst;
++ long len;
++
++ hdev = (struct hci_dev *)skb->dev;
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ hst = hdev->driver_data;
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
++
++ BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type,
++ skb->len);
++
++ /* Insert skb to shared transport layer's transmit queue.
++ * Freeing skb memory is taken care in shared transport layer,
++ * so don't free skb memory here.
++ */
++ len = hst->st_write(skb);
++ if (len < 0) {
++ kfree_skb(skb);
++ BT_ERR("ST write failed (%ld)", len);
++ /* Try Again, would only fail if UART has gone bad */
++ return -EAGAIN;
++ }
++
++ /* ST accepted our skb. So, Go ahead and do rest */
++ hdev->stat.byte_tx += len;
++ ti_st_tx_complete(hst, bt_cb(skb)->pkt_type);
++
++ return 0;
++}
++
++static void ti_st_destruct(struct hci_dev *hdev)
++{
++ BT_DBG("%s", hdev->name);
++ kfree(hdev->driver_data);
++}
++
++static int bt_ti_probe(struct platform_device *pdev)
++{
++ static struct ti_st *hst;
++ struct hci_dev *hdev;
++ int err;
++
++ hst = kzalloc(sizeof(struct ti_st), GFP_KERNEL);
++ if (!hst)
++ return -ENOMEM;
++
++ /* Expose "hciX" device to user space */
++ hdev = hci_alloc_dev();
++ if (!hdev) {
++ kfree(hst);
++ return -ENOMEM;
++ }
++
++ BT_DBG("hdev %p", hdev);
++
++ hst->hdev = hdev;
++ hdev->bus = HCI_UART;
++ hdev->driver_data = hst;
++ hdev->open = ti_st_open;
++ hdev->close = ti_st_close;
++ hdev->flush = NULL;
++ hdev->send = ti_st_send_frame;
++ hdev->destruct = ti_st_destruct;
++ hdev->owner = THIS_MODULE;
++
++ err = hci_register_dev(hdev);
++ if (err < 0) {
++ BT_ERR("Can't register HCI device error %d", err);
++ kfree(hst);
++ hci_free_dev(hdev);
++ return err;
++ }
++
++ BT_DBG("HCI device registered (hdev %p)", hdev);
++
++ dev_set_drvdata(&pdev->dev, hst);
++ return err;
++}
++
++static int bt_ti_remove(struct platform_device *pdev)
++{
++ struct hci_dev *hdev;
++ struct ti_st *hst = dev_get_drvdata(&pdev->dev);
++
++ if (!hst)
++ return -EFAULT;
++
++ hdev = hst->hdev;
++ ti_st_close(hdev);
++ hci_unregister_dev(hdev);
++
++ hci_free_dev(hdev);
++ kfree(hst);
++
++ dev_set_drvdata(&pdev->dev, NULL);
++ return 0;
++}
++
++static struct platform_driver btwilink_driver = {
++ .probe = bt_ti_probe,
++ .remove = bt_ti_remove,
++ .driver = {
++ .name = "btwilink",
++ .owner = THIS_MODULE,
++ },
++};
++
++/* ------- Module Init/Exit interfaces ------ */
++static int __init btwilink_init(void)
++{
++ BT_INFO("Bluetooth Driver for TI WiLink - Version %s", VERSION);
++
++ return platform_driver_register(&btwilink_driver);
++}
++
++static void __exit btwilink_exit(void)
++{
++ platform_driver_unregister(&btwilink_driver);
++}
++
++module_init(btwilink_init);
++module_exit(btwilink_exit);
++
++/* ------ Module Info ------ */
++
++MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
++MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
++MODULE_VERSION(VERSION);
++MODULE_LICENSE("GPL");
+--
+1.6.6.1
+
diff --git a/recipes/linux/linux-omap_2.6.37.bb b/recipes/linux/linux-omap_2.6.37.bb
index c6a62b7c18..7855ebe1f0 100644
--- a/recipes/linux/linux-omap_2.6.37.bb
+++ b/recipes/linux/linux-omap_2.6.37.bb
@@ -133,7 +133,23 @@ SRC_URI_append = " \
file://dvfs/0020-omap3-Add-basic-support-for-720MHz-part.patch \
\
file://new/0001-OMAP-Enable-Magic-SysRq-on-serial-console-ttyOx.patch \
- "
+ \
+ file://wl1271/0001-wl12xx-Read-MAC-address-from-NVS-file-on-HW-startup.patch \
+ file://wl1271/0002-wl1271-11n-Support-Add-Definitions.patch \
+ file://wl1271/0003-wl1271-11n-Support-ACX-Commands.patch \
+ file://wl1271/0004-wl1271-11n-Support-functionality-and-configuration-a.patch \
+ file://wl1271/0005-wl1271-set-wl-vif-only-if-add_interface-succeeded.patch \
+ file://wl1271/0006-wl12xx-Unset-bssid-filter-ssid-and-bssid-from-firmwa.patch \
+ file://wl1271/0007-drivers-media-radio-wl128x-FM-Driver-common-header-f.patch \
+ file://wl1271/0008-drivers-media-radio-wl128x-FM-Driver-V4L2-sources.patch \
+ file://wl1271/0009-drivers-media-radio-wl128x-FM-Driver-Common-sources.patch \
+ file://wl1271/0010-drivers-media-radio-wl128x-FM-driver-RX-sources.patch \
+ file://wl1271/0011-drivers-media-radio-wl128x-FM-driver-TX-sources.patch \
+ file://wl1271/0012-drivers-media-radio-wl128x-Kconfig-Makefile-for-wl12.patch \
+ file://wl1271/0013-drivers-media-radio-Update-Kconfig-and-Makefile-for-.patch \
+ file://wl1271/0014-drivers-misc-ti-st-change-protocol-parse-logic.patch \
+ file://wl1271/0015-Bluetooth-btwilink-driver.patch \
+ "
SRC_URI_append_usrp-e1xx = "\
file://usrp/0001-Add-defines-to-set-config-options-in-GPMC-per-CS-con.patch \