radio: iris: Add snapshot of iris FM radio support
Files taken from the below kernel_msm-4.9 commits. commit :8c7bd3dd ("msm: FM: Add snapshot of iris FM radio support"). CRs-Fixed: 2887633 Change-Id: Ie5bec3e6828424057d2a6fa2a2d88e625b186032 Signed-off-by: phaneendra Reddy <phaneend@codeaurora.org>
This commit is contained in:
parent
9d5ec5ad1d
commit
488c6f77c2
@ -520,3 +520,24 @@ config I2C_RTC6226_QCA
|
||||
module will be called radio-i2c-RTC6226_QCA.
|
||||
|
||||
endif # RADIO_ADAPTERS
|
||||
|
||||
config RADIO_IRIS
|
||||
tristate "QTI IRIS FM support"
|
||||
depends on VIDEO_V4L2
|
||||
help
|
||||
Say Y here if you want to use the QTI FM chip (IRIS).
|
||||
This FM chip uses SMD interface
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called radio-iris.
|
||||
|
||||
|
||||
config RADIO_IRIS_TRANSPORT
|
||||
tristate "QTI IRIS Transport"
|
||||
depends on RADIO_IRIS
|
||||
help
|
||||
Say Y here if you want to use the QTI FM chip (IRIS).
|
||||
with SMD as transport.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called radio-iris-transport.
|
||||
|
@ -35,6 +35,8 @@ obj-$(CONFIG_RADIO_WL128X) += wl128x/
|
||||
obj-$(CONFIG_RADIO_TEA575X) += tea575x.o
|
||||
obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o
|
||||
obj-$(CONFIG_I2C_RTC6226_QCA) += rtc6226/
|
||||
obj-$(CONFIG_RADIO_IRIS) += radio-iris.o
|
||||
obj-$(CONFIG_RADIO_IRIS_TRANSPORT) += radio-iris-transport.o
|
||||
|
||||
shark2-objs := radio-shark2.o radio-tea5777.o
|
||||
|
||||
|
262
drivers/media/radio/radio-iris-transport.c
Normal file
262
drivers/media/radio/radio-iris-transport.c
Normal file
@ -0,0 +1,262 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2000-2001, 2011-2012, 2014-2015 The Linux Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <soc/qcom/smd.h>
|
||||
#include <media/radio-iris.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
struct radio_data {
|
||||
struct radio_hci_dev *hdev;
|
||||
struct tasklet_struct rx_task;
|
||||
struct smd_channel *fm_channel;
|
||||
};
|
||||
struct radio_data hs;
|
||||
DEFINE_MUTEX(fm_smd_enable);
|
||||
static int fmsmd_set;
|
||||
static bool chan_opened;
|
||||
static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp);
|
||||
module_param_call(fmsmd_set, hcismd_fm_set_enable, NULL, &fmsmd_set, 0644);
|
||||
static struct work_struct *reset_worker;
|
||||
static void radio_hci_smd_deregister(void);
|
||||
static void radio_hci_smd_exit(void);
|
||||
|
||||
static void radio_hci_smd_destruct(struct radio_hci_dev *hdev)
|
||||
{
|
||||
radio_hci_unregister_dev();
|
||||
}
|
||||
|
||||
|
||||
static void radio_hci_smd_recv_event(unsigned long temp)
|
||||
{
|
||||
int len;
|
||||
int rc;
|
||||
struct sk_buff *skb;
|
||||
unsigned char *buf;
|
||||
struct radio_data *hsmd = &hs;
|
||||
|
||||
len = smd_read_avail(hsmd->fm_channel);
|
||||
|
||||
while (len) {
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
FMDERR("Memory not allocated for the socket\n");
|
||||
return;
|
||||
}
|
||||
|
||||
buf = kmalloc(len, GFP_ATOMIC);
|
||||
if (!buf) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = smd_read(hsmd->fm_channel, (void *)buf, len);
|
||||
|
||||
memcpy(skb_put(skb, len), buf, len);
|
||||
|
||||
skb_orphan(skb);
|
||||
skb->dev = (struct net_device *)hs.hdev;
|
||||
|
||||
rc = radio_hci_recv_frame(skb);
|
||||
|
||||
kfree(buf);
|
||||
len = smd_read_avail(hsmd->fm_channel);
|
||||
}
|
||||
}
|
||||
|
||||
static int radio_hci_smd_send_frame(struct sk_buff *skb)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
FMDBG("skb %pK\n", skb);
|
||||
|
||||
len = smd_write(hs.fm_channel, skb->data, skb->len);
|
||||
if (len < skb->len) {
|
||||
FMDERR("Failed to write Data %d\n", len);
|
||||
kfree_skb(skb);
|
||||
return -ENODEV;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void send_disable_event(struct work_struct *worker)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned char buf[6] = { 0x0f, 0x04, 0x01, 0x02, 0x4c, 0x00 };
|
||||
int len = sizeof(buf);
|
||||
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
FMDERR("Memory not allocated for the socket\n");
|
||||
kfree(worker);
|
||||
return;
|
||||
}
|
||||
|
||||
FMDBG("FM INSERT DISABLE Rx Event\n");
|
||||
|
||||
memcpy(skb_put(skb, len), buf, len);
|
||||
|
||||
skb_orphan(skb);
|
||||
skb->dev = (struct net_device *)hs.hdev;
|
||||
|
||||
radio_hci_recv_frame(skb);
|
||||
kfree(worker);
|
||||
}
|
||||
|
||||
static void radio_hci_smd_notify_cmd(void *data, unsigned int event)
|
||||
{
|
||||
struct radio_hci_dev *hdev = (struct radio_hci_dev *)data;
|
||||
|
||||
FMDBG("data %p event %u\n", data, event);
|
||||
|
||||
if (!hdev) {
|
||||
FMDERR("Frame for unknown HCI device (hdev=NULL)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case SMD_EVENT_DATA:
|
||||
tasklet_schedule(&hs.rx_task);
|
||||
break;
|
||||
case SMD_EVENT_OPEN:
|
||||
break;
|
||||
case SMD_EVENT_CLOSE:
|
||||
reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC);
|
||||
if (reset_worker) {
|
||||
INIT_WORK(reset_worker, send_disable_event);
|
||||
schedule_work(reset_worker);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int radio_hci_smd_register_dev(struct radio_data *hsmd)
|
||||
{
|
||||
struct radio_hci_dev *hdev;
|
||||
int rc;
|
||||
|
||||
FMDBG("hsmd: %pK\n", hsmd);
|
||||
|
||||
if (hsmd == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
tasklet_init(&hsmd->rx_task, radio_hci_smd_recv_event,
|
||||
(unsigned long) hsmd);
|
||||
hdev->send = radio_hci_smd_send_frame;
|
||||
hdev->destruct = radio_hci_smd_destruct;
|
||||
hdev->close_smd = radio_hci_smd_exit;
|
||||
|
||||
/* Open the SMD Channel and device and register the callback function */
|
||||
rc = smd_named_open_on_edge("APPS_FM", SMD_APPS_WCNSS,
|
||||
&hsmd->fm_channel, hdev, radio_hci_smd_notify_cmd);
|
||||
|
||||
if (rc < 0) {
|
||||
FMDERR("Cannot open the command channel\n");
|
||||
hsmd->hdev = NULL;
|
||||
kfree(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
smd_disable_read_intr(hsmd->fm_channel);
|
||||
|
||||
if (radio_hci_register_dev(hdev) < 0) {
|
||||
FMDERR("Can't register HCI device\n");
|
||||
smd_close(hsmd->fm_channel);
|
||||
hsmd->hdev = NULL;
|
||||
kfree(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hsmd->hdev = hdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void radio_hci_smd_deregister(void)
|
||||
{
|
||||
radio_hci_unregister_dev();
|
||||
kfree(hs.hdev);
|
||||
hs.hdev = NULL;
|
||||
|
||||
smd_close(hs.fm_channel);
|
||||
hs.fm_channel = 0;
|
||||
fmsmd_set = 0;
|
||||
}
|
||||
|
||||
static int radio_hci_smd_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (chan_opened) {
|
||||
FMDBG("Channel is already opened\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this should be called with fm_smd_enable lock held */
|
||||
ret = radio_hci_smd_register_dev(&hs);
|
||||
if (ret < 0) {
|
||||
FMDERR("Failed to register smd device\n");
|
||||
chan_opened = false;
|
||||
return ret;
|
||||
}
|
||||
chan_opened = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void radio_hci_smd_exit(void)
|
||||
{
|
||||
if (!chan_opened) {
|
||||
FMDBG("Channel already closed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* this should be called with fm_smd_enable lock held */
|
||||
radio_hci_smd_deregister();
|
||||
chan_opened = false;
|
||||
}
|
||||
|
||||
static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&fm_smd_enable);
|
||||
ret = param_set_int(val, kp);
|
||||
if (ret)
|
||||
goto done;
|
||||
switch (fmsmd_set) {
|
||||
|
||||
case 1:
|
||||
radio_hci_smd_init();
|
||||
break;
|
||||
case 0:
|
||||
radio_hci_smd_exit();
|
||||
break;
|
||||
default:
|
||||
ret = -EFAULT;
|
||||
}
|
||||
done:
|
||||
mutex_unlock(&fm_smd_enable);
|
||||
return ret;
|
||||
}
|
||||
MODULE_DESCRIPTION("FM SMD driver");
|
||||
MODULE_LICENSE("GPL v2");
|
5694
drivers/media/radio/radio-iris.c
Normal file
5694
drivers/media/radio/radio-iris.c
Normal file
File diff suppressed because it is too large
Load Diff
307
include/media/radio-iris.h
Normal file
307
include/media/radio-iris.h
Normal file
@ -0,0 +1,307 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __RADIO_IRIS_H
|
||||
#define __RADIO_IRIS_H
|
||||
|
||||
#include <uapi/media/radio-iris.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#define RDS_PS_SIMPLE_OFFSET 2
|
||||
extern struct mutex fm_smd_enable;
|
||||
|
||||
struct radio_hci_dev {
|
||||
char name[8];
|
||||
unsigned long flags;
|
||||
__u16 id;
|
||||
__u8 bus;
|
||||
__u8 dev_type;
|
||||
__u8 dev_name[248];
|
||||
__u8 dev_class[3];
|
||||
__u8 features[8];
|
||||
__u8 commands[64];
|
||||
|
||||
unsigned int data_block_len;
|
||||
unsigned long cmd_last_tx;
|
||||
|
||||
struct sk_buff *sent_cmd;
|
||||
|
||||
__u32 req_status;
|
||||
__u32 req_result;
|
||||
atomic_t cmd_cnt;
|
||||
|
||||
struct tasklet_struct cmd_task;
|
||||
struct tasklet_struct rx_task;
|
||||
struct tasklet_struct tx_task;
|
||||
|
||||
struct sk_buff_head rx_q;
|
||||
struct sk_buff_head raw_q;
|
||||
struct sk_buff_head cmd_q;
|
||||
|
||||
struct mutex req_lock;
|
||||
wait_queue_head_t req_wait_q;
|
||||
|
||||
int (*open)(struct radio_hci_dev *hdev);
|
||||
int (*close)(struct radio_hci_dev *hdev);
|
||||
int (*flush)(struct radio_hci_dev *hdev);
|
||||
int (*send)(struct sk_buff *skb);
|
||||
void (*destruct)(struct radio_hci_dev *hdev);
|
||||
void (*notify)(struct radio_hci_dev *hdev, unsigned int evt);
|
||||
void (*close_smd)(void);
|
||||
};
|
||||
|
||||
int radio_hci_register_dev(struct radio_hci_dev *hdev);
|
||||
int radio_hci_unregister_dev(void);
|
||||
int radio_hci_recv_frame(struct sk_buff *skb);
|
||||
int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||
void *param);
|
||||
void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb);
|
||||
|
||||
#define hci_req_lock(d) mutex_lock(&d->req_lock)
|
||||
#define hci_req_unlock(d) mutex_unlock(&d->req_lock)
|
||||
|
||||
#undef FMDBG
|
||||
#ifdef FM_DEBUG
|
||||
#define FMDBG(fmt, args...) pr_info("iris_radio: " fmt, ##args)
|
||||
#else
|
||||
#define FMDBG(fmt, args...)
|
||||
#endif
|
||||
|
||||
#undef FMDERR
|
||||
#define FMDERR(fmt, args...) pr_err("iris_radio: " fmt, ##args)
|
||||
|
||||
/* HCI timeouts */
|
||||
#define RADIO_HCI_TIMEOUT (10000) /* 10 seconds */
|
||||
|
||||
int hci_def_data_read(struct hci_fm_def_data_rd_req *arg,
|
||||
struct radio_hci_dev *hdev);
|
||||
int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,
|
||||
struct radio_hci_dev *hdev);
|
||||
int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev);
|
||||
int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev);
|
||||
|
||||
static inline int is_valid_tone(int tone)
|
||||
{
|
||||
if ((tone >= MIN_TX_TONE_VAL) &&
|
||||
(tone <= MAX_TX_TONE_VAL))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_hard_mute(int hard_mute)
|
||||
{
|
||||
if ((hard_mute >= MIN_HARD_MUTE_VAL) &&
|
||||
(hard_mute <= MAX_HARD_MUTE_VAL))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_srch_mode(int srch_mode)
|
||||
{
|
||||
if ((srch_mode >= MIN_SRCH_MODE) &&
|
||||
(srch_mode <= MAX_SRCH_MODE))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_scan_dwell_prd(int scan_dwell_prd)
|
||||
{
|
||||
if ((scan_dwell_prd >= MIN_SCAN_DWELL) &&
|
||||
(scan_dwell_prd <= MAX_SCAN_DWELL))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_sig_th(int sig_th)
|
||||
{
|
||||
if ((sig_th >= MIN_SIG_TH) &&
|
||||
(sig_th <= MAX_SIG_TH))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_pty(int pty)
|
||||
{
|
||||
if ((pty >= MIN_PTY) &&
|
||||
(pty <= MAX_PTY))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_pi(int pi)
|
||||
{
|
||||
if ((pi >= MIN_PI) &&
|
||||
(pi <= MAX_PI))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_srch_station_cnt(int cnt)
|
||||
{
|
||||
if ((cnt >= MIN_SRCH_STATIONS_CNT) &&
|
||||
(cnt <= MAX_SRCH_STATIONS_CNT))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_chan_spacing(int spacing)
|
||||
{
|
||||
if ((spacing >= MIN_CHAN_SPACING) &&
|
||||
(spacing <= MAX_CHAN_SPACING))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_emphasis(int emphasis)
|
||||
{
|
||||
if ((emphasis >= MIN_EMPHASIS) &&
|
||||
(emphasis <= MAX_EMPHASIS))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_rds_std(int rds_std)
|
||||
{
|
||||
if ((rds_std >= MIN_RDS_STD) &&
|
||||
(rds_std <= MAX_RDS_STD))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_antenna(int antenna_type)
|
||||
{
|
||||
if ((antenna_type >= MIN_ANTENNA_VAL) &&
|
||||
(antenna_type <= MAX_ANTENNA_VAL))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_ps_repeat_cnt(int cnt)
|
||||
{
|
||||
if ((cnt >= MIN_TX_PS_REPEAT_CNT) &&
|
||||
(cnt <= MAX_TX_PS_REPEAT_CNT))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_soft_mute(int soft_mute)
|
||||
{
|
||||
if ((soft_mute >= MIN_SOFT_MUTE) &&
|
||||
(soft_mute <= MAX_SOFT_MUTE))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_peek_len(int len)
|
||||
{
|
||||
if ((len >= MIN_PEEK_ACCESS_LEN) &&
|
||||
(len <= MAX_PEEK_ACCESS_LEN))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_reset_cntr(int cntr)
|
||||
{
|
||||
if ((cntr >= MIN_RESET_CNTR) &&
|
||||
(cntr <= MAX_RESET_CNTR))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_hlsi(int hlsi)
|
||||
{
|
||||
if ((hlsi >= MIN_HLSI) &&
|
||||
(hlsi <= MAX_HLSI))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_notch_filter(int filter)
|
||||
{
|
||||
if ((filter >= MIN_NOTCH_FILTER) &&
|
||||
(filter <= MAX_NOTCH_FILTER))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_intf_det_low_th(int th)
|
||||
{
|
||||
if ((th >= MIN_INTF_DET_OUT_LW_TH) &&
|
||||
(th <= MAX_INTF_DET_OUT_LW_TH))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_intf_det_hgh_th(int th)
|
||||
{
|
||||
if ((th >= MIN_INTF_DET_OUT_HG_TH) &&
|
||||
(th <= MAX_INTF_DET_OUT_HG_TH))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_sinr_th(int th)
|
||||
{
|
||||
if ((th >= MIN_SINR_TH) &&
|
||||
(th <= MAX_SINR_TH))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_sinr_samples(int samples_cnt)
|
||||
{
|
||||
if ((samples_cnt >= MIN_SINR_SAMPLES) &&
|
||||
(samples_cnt <= MAX_SINR_SAMPLES))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_fm_state(int state)
|
||||
{
|
||||
if ((state >= 0) && (state < FM_MAX_NO_STATES))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_valid_blend_value(int val)
|
||||
{
|
||||
if ((val >= MIN_BLEND_HI) && (val <= MAX_BLEND_HI))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user