mirror of
https://git.yoctoproject.org/meta-ti
synced 2026-01-12 01:20:20 +00:00
TI BSP: sync linux-omap 2.6.37 with OE
Signed-off-by: Koen Kooi <koen@dominion.thruhere.net>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
From 92cbd878f5b92d915dadb5bed412eb013141fdfe 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 | 68 +++++++++++++++++++++++++++++++
|
||||
1 files changed, 68 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..f699701 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..f699701 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,21 +82,47 @@ index 64a181e..f699701 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;
|
||||
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);
|
||||
}
|
||||
|
||||
+ if(!strcmp(expansionboard_name, "fixme-beagletoy"))
|
||||
+ {
|
||||
+ if (wl12xx_set_platform_data(&omap_beagle_wlan_data))
|
||||
+ pr_err("error setting wl12xx data\n");
|
||||
+ printk(KERN_INFO "Beagle expansionboard: registering wl12xx platform device\n");
|
||||
+ platform_device_register(&omap_vwlan_device);
|
||||
+ }
|
||||
+
|
||||
usb_musb_init(&musb_board_data);
|
||||
usb_ehci_init(&ehci_pdata);
|
||||
omap3beagle_flash_init();
|
||||
--
|
||||
1.6.6.1
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -434,9 +434,9 @@ CONFIG_CPU_FREQ_TABLE=y
|
||||
CONFIG_CPU_FREQ_DEBUG=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_FREQ_STAT_DETAILS=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=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
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
From a43b9359708691eb3aec983da36461720fb4a556 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Weber <weber@corscience.de>
|
||||
Date: Wed, 19 Jan 2011 09:41:01 +0100
|
||||
Subject: [PATCH] OMAP: Enable Magic SysRq on serial console ttyOx
|
||||
|
||||
Magic SysRq key is not working for OMAP on new serial
|
||||
console ttyOx because SUPPORT_SYSRQ is not defined
|
||||
for omap-serial.
|
||||
|
||||
This patch defines SUPPORT_SYSRQ in omap-serial and
|
||||
enables handling of Magic SysRq character.
|
||||
|
||||
Signed-off-by: Thomas Weber <weber@corscience.de>
|
||||
---
|
||||
drivers/serial/omap-serial.c | 4 ++++
|
||||
1 files changed, 4 insertions(+), 0 deletions(-)
|
||||
|
||||
diff --git a/drivers/serial/omap-serial.c b/drivers/serial/omap-serial.c
|
||||
index 1201eff..907be9b 100644
|
||||
--- a/drivers/serial/omap-serial.c
|
||||
+++ b/drivers/serial/omap-serial.c
|
||||
@@ -20,6 +20,10 @@
|
||||
* this driver as required for the omap-platform.
|
||||
*/
|
||||
|
||||
+#if defined(CONFIG_SERIAL_OMAP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
+#define SUPPORT_SYSRQ
|
||||
+#endif
|
||||
+
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
--
|
||||
1.6.6.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,6 +10,8 @@ PV = "2.6.37"
|
||||
MACHINE_KERNEL_PR_append = "a+gitr${SRCREV}"
|
||||
SRCREV_pn-${PN} = "fa3b4e23ec20cfc944db7cc2b30b0d82c20e4472"
|
||||
|
||||
FILESPATHPKG_prepend = "linux-omap-2.6.37:"
|
||||
|
||||
SRC_URI = "git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6.git;protocol=git \
|
||||
file://defconfig"
|
||||
|
||||
@@ -129,7 +131,25 @@ SRC_URI_append = " \
|
||||
file://dvfs/0018-omap3-4-opp-make-omapx_opp_init-non-static.patch \
|
||||
file://dvfs/0019-OMAP3-beagle-xm-enable-upto-1GHz-OPP.patch \
|
||||
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 \
|
||||
|
||||
Reference in New Issue
Block a user