Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c7bd3dd authored by himta ram's avatar himta ram
Browse files

msm: FM: Add snapshot of iris FM radio support



Files taken from the below kernel_msm-3.18 commits.
commit :fb9b44e7 ("radio: iris: Add snapshot of iris FM radio support")

Change-Id: Ib33d33ab8ccd587473c0104e6b48d93bbcfa6f01
Signed-off-by: default avatarhimta ram <hram@codeaurora.org>
parent 9d4600a1
Loading
Loading
Loading
Loading
+29 −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";
        };
+22 −0
Original line number Diff line number Diff line
@@ -499,3 +499,25 @@ config RADIO_ZOLTRIX_PORT
endif # V4L_RADIO_ISA_DRIVERS

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.
+2 −0
Original line number Diff line number Diff line
@@ -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

shark2-objs := radio-shark2.o radio-tea5777.o

+274 −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/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, 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, 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");
+5646 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading