Loading Documentation/devicetree/bindings/bt-fm/fm.txt 0 → 100644 +28 −0 Original line number Diff line number Diff line Qti radio iris device -FM RX playback with no RDS FM samples is filtered by external RF chips at baseband, then send to Riva-FM core through serial link. FM signal is demodulated then audio L/R samples are stored inside memory. FM Rx received samples data is connected to external audio codec. -Audio playback to FM TX Used to play audio source to FM TX. FM TX module will read the audio samples from memory then modulated samples will be send through serial interface to external RF chip. -RX playback with RDS FM Rx receive audio data along with RDS. -FM TX with RDS Used to send RDS messages to external FM receiver. Required Properties: - compatible: "qcom,iris_fm" Example: qcom,iris-fm { compatible = "qcom,iris_fm"; }; arch/arm64/configs/msmcortex_defconfig +2 −0 Original line number Diff line number Diff line Loading @@ -350,6 +350,8 @@ CONFIG_OV12830=y CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y CONFIG_MSMB_JPEG=y CONFIG_MSM_FD=y CONFIG_RADIO_IRIS=y CONFIG_RADIO_IRIS_TRANSPORT=y CONFIG_RADIO_SILABS=y CONFIG_MSM_KGSL=y CONFIG_FB=y Loading drivers/media/radio/Kconfig +21 −0 Original line number Diff line number Diff line Loading @@ -498,6 +498,27 @@ config RADIO_ZOLTRIX_PORT endif # V4L_RADIO_ISA_DRIVERS 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. config RADIO_SILABS tristate "SILABS FM" depends on I2C && VIDEO_V4L2 Loading drivers/media/radio/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o obj-$(CONFIG_RADIO_WL128X) += wl128x/ obj-$(CONFIG_RADIO_TEA575X) += tea575x.o obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o obj-$(CONFIG_RADIO_IRIS) += radio-iris.o obj-$(CONFIG_RADIO_IRIS_TRANSPORT) += radio-iris-transport.o obj-$(CONFIG_RADIO_SILABS) += silabs/ shark2-objs := radio-shark2.o radio-tea5777.o Loading drivers/media/radio/radio-iris-transport.c 0 → 100644 +244 −0 Original line number Diff line number Diff line /* * QTI's FM Shared Memory Transport Driver * * FM HCI_SMD ( FM HCI Shared Memory Driver) is QTI's Shared memory driver * for the HCI protocol. This file is based on drivers/bluetooth/hci_vhci.c * * Copyright (c) 2000-2001, 2011-2012, 2014-2015 The Linux Foundation. * All rights reserved. * * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org> * * 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. */ #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/wakelock.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; static DEFINE_MUTEX(fm_smd_enable); static int fmsmd_set; static int hcismd_fm_set_enable(const char *val, 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_destruct(struct radio_hci_dev *hdev) { radio_hci_unregister_dev(hs.hdev); } 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"); return; } buf = kmalloc(len, GFP_ATOMIC); if (!buf) { kfree_skb(skb); FMDERR("Error in allocating buffer memory"); 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; len = smd_write(hs.fm_channel, skb->data, skb->len); if (len < skb->len) { FMDERR("Failed to write Data %d", 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"); kfree(worker); return; } FMDERR("FM INSERT DISABLE Rx Event"); 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 = hs.hdev; if (!hdev) { FMDERR("Frame for unknown HCI device (hdev=NULL)"); 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) { FMDERR("Out of memory"); break; } 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; if (hsmd == NULL) return -ENODEV; hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL); if (hdev == NULL) return -ENODEV; hsmd->hdev = hdev; 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_deregister; /* 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"); 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"); smd_close(hsmd->fm_channel); hsmd->hdev = NULL; kfree(hdev); return -ENODEV; } return 0; } static void radio_hci_smd_deregister(void) { smd_close(hs.fm_channel); hs.fm_channel = 0; fmsmd_set = 0; } static int radio_hci_smd_init(void) { return radio_hci_smd_register_dev(&hs); } static void radio_hci_smd_exit(void) { radio_hci_smd_deregister(); } static int hcismd_fm_set_enable(const char *val, 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"); Loading
Documentation/devicetree/bindings/bt-fm/fm.txt 0 → 100644 +28 −0 Original line number Diff line number Diff line Qti radio iris device -FM RX playback with no RDS FM samples is filtered by external RF chips at baseband, then send to Riva-FM core through serial link. FM signal is demodulated then audio L/R samples are stored inside memory. FM Rx received samples data is connected to external audio codec. -Audio playback to FM TX Used to play audio source to FM TX. FM TX module will read the audio samples from memory then modulated samples will be send through serial interface to external RF chip. -RX playback with RDS FM Rx receive audio data along with RDS. -FM TX with RDS Used to send RDS messages to external FM receiver. Required Properties: - compatible: "qcom,iris_fm" Example: qcom,iris-fm { compatible = "qcom,iris_fm"; };
arch/arm64/configs/msmcortex_defconfig +2 −0 Original line number Diff line number Diff line Loading @@ -350,6 +350,8 @@ CONFIG_OV12830=y CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y CONFIG_MSMB_JPEG=y CONFIG_MSM_FD=y CONFIG_RADIO_IRIS=y CONFIG_RADIO_IRIS_TRANSPORT=y CONFIG_RADIO_SILABS=y CONFIG_MSM_KGSL=y CONFIG_FB=y Loading
drivers/media/radio/Kconfig +21 −0 Original line number Diff line number Diff line Loading @@ -498,6 +498,27 @@ config RADIO_ZOLTRIX_PORT endif # V4L_RADIO_ISA_DRIVERS 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. config RADIO_SILABS tristate "SILABS FM" depends on I2C && VIDEO_V4L2 Loading
drivers/media/radio/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o obj-$(CONFIG_RADIO_WL128X) += wl128x/ obj-$(CONFIG_RADIO_TEA575X) += tea575x.o obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o obj-$(CONFIG_RADIO_IRIS) += radio-iris.o obj-$(CONFIG_RADIO_IRIS_TRANSPORT) += radio-iris-transport.o obj-$(CONFIG_RADIO_SILABS) += silabs/ shark2-objs := radio-shark2.o radio-tea5777.o Loading
drivers/media/radio/radio-iris-transport.c 0 → 100644 +244 −0 Original line number Diff line number Diff line /* * QTI's FM Shared Memory Transport Driver * * FM HCI_SMD ( FM HCI Shared Memory Driver) is QTI's Shared memory driver * for the HCI protocol. This file is based on drivers/bluetooth/hci_vhci.c * * Copyright (c) 2000-2001, 2011-2012, 2014-2015 The Linux Foundation. * All rights reserved. * * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org> * * 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. */ #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/wakelock.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; static DEFINE_MUTEX(fm_smd_enable); static int fmsmd_set; static int hcismd_fm_set_enable(const char *val, 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_destruct(struct radio_hci_dev *hdev) { radio_hci_unregister_dev(hs.hdev); } 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"); return; } buf = kmalloc(len, GFP_ATOMIC); if (!buf) { kfree_skb(skb); FMDERR("Error in allocating buffer memory"); 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; len = smd_write(hs.fm_channel, skb->data, skb->len); if (len < skb->len) { FMDERR("Failed to write Data %d", 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"); kfree(worker); return; } FMDERR("FM INSERT DISABLE Rx Event"); 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 = hs.hdev; if (!hdev) { FMDERR("Frame for unknown HCI device (hdev=NULL)"); 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) { FMDERR("Out of memory"); break; } 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; if (hsmd == NULL) return -ENODEV; hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL); if (hdev == NULL) return -ENODEV; hsmd->hdev = hdev; 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_deregister; /* 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"); 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"); smd_close(hsmd->fm_channel); hsmd->hdev = NULL; kfree(hdev); return -ENODEV; } return 0; } static void radio_hci_smd_deregister(void) { smd_close(hs.fm_channel); hs.fm_channel = 0; fmsmd_set = 0; } static int radio_hci_smd_init(void) { return radio_hci_smd_register_dev(&hs); } static void radio_hci_smd_exit(void) { radio_hci_smd_deregister(); } static int hcismd_fm_set_enable(const char *val, 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");