mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 05:39:54 -04:00
slatecom-events-bridge: Add driver for events from Slate
Add driver to send and receive events from Slate co-processor. The driver uses Glink channel to communicate with Slate. Change-Id: I2b7cf9a47e8947f79c7e9cd8b1311ff8c04bf848 Signed-off-by: Ajit Kumar <kajit@codeaurora.org>
This commit is contained in:
parent
a9eb6ca4c9
commit
3f8d1181b9
6 changed files with 801 additions and 0 deletions
|
|
@ -1310,6 +1310,23 @@ config DUMP_XBL_LOG
|
|||
reserved memory region:
|
||||
cat /sys/module/xbl_log/xbl_log
|
||||
|
||||
config MSM_SEB
|
||||
tristate "Provide APIs to send and receive events from Slate chipset"
|
||||
help
|
||||
SEB(Slate event bridge) communicates to Slate over rpmsg driver to
|
||||
send or receive events from Slate. The button events received from
|
||||
Slate are send to input framework. The driver provides functionality
|
||||
for a client to register a callback to receive events from a group.
|
||||
|
||||
config MSM_SEB_RPMSG
|
||||
tristate "Provide support for slate event GLINK channel"
|
||||
depends on MSM_SEB
|
||||
help
|
||||
SEB_RPMSG informs SEB driver if GLINK channel has been
|
||||
opened by remote processor. It doesn't maintain state machine
|
||||
and is probed when Slate opens channel and removed when the
|
||||
channel is closed by remote processor.
|
||||
|
||||
source "drivers/soc/qcom/icnss2/Kconfig"
|
||||
|
||||
source "drivers/soc/qcom/gnsssirf/Kconfig"
|
||||
|
|
|
|||
|
|
@ -122,3 +122,5 @@ obj-$(CONFIG_CPU_V7) += idle-v7.o
|
|||
obj-$(CONFIG_DUMP_XBL_LOG) += dump_boot_log.o
|
||||
# GNSS driver
|
||||
obj-$(CONFIG_GNSS_SIRF) += gnsssirf/
|
||||
obj-$(CONFIG_MSM_SEB) += slate_events_bridge.o
|
||||
obj-$(CONFIG_MSM_SEB_RPMSG) += slate_events_bridge_rpmsg.o
|
||||
|
|
|
|||
618
drivers/soc/qcom/slate_events_bridge.c
Normal file
618
drivers/soc/qcom/slate_events_bridge.c
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <soc/qcom/subsystem_notif.h>
|
||||
#include "slate_events_bridge.h"
|
||||
#include "slate_events_bridge_rpmsg.h"
|
||||
|
||||
struct event {
|
||||
uint8_t sub_id;
|
||||
int16_t evnt_data;
|
||||
uint32_t evnt_tm;
|
||||
};
|
||||
|
||||
#define SEB_GLINK_INTENT_SIZE 0x04
|
||||
#define SEB_MSG_SIZE 0x08
|
||||
#define TIMEOUT_MS 2000
|
||||
#define SEB_SLATEWEAR_SUBSYS "slate-wear"
|
||||
#define HED_EVENT_DATA_TIME_LEN 0x04
|
||||
|
||||
enum seb_state {
|
||||
SEB_STATE_UNKNOWN,
|
||||
SEB_STATE_INIT,
|
||||
SEB_STATE_GLINK_OPEN,
|
||||
SEB_STATE_SLATE_SSR
|
||||
};
|
||||
|
||||
struct seb_msg {
|
||||
uint32_t cmd_id;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
struct seb_notif_info {
|
||||
enum event_group_type event_group;
|
||||
struct srcu_notifier_head seb_notif_rcvr_list;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct gmi_header {
|
||||
uint8_t version;
|
||||
uint8_t reserved;
|
||||
uint16_t opcode;
|
||||
uint32_t payload_size;
|
||||
};
|
||||
|
||||
enum WearManagerGlinkOpcode {
|
||||
GMI_SLATE_EVENT_QBG = 1,
|
||||
GMI_SLATE_EVENT_RSB = 2,
|
||||
GMI_SLATE_EVENT_BUTTON = 3,
|
||||
GMI_SLATE_EVENT_TOUCH = 4,
|
||||
};
|
||||
|
||||
static LIST_HEAD(seb_notify_list);
|
||||
static DEFINE_MUTEX(notif_lock);
|
||||
static DEFINE_MUTEX(notif_add_lock);
|
||||
|
||||
struct seb_priv {
|
||||
void *handle;
|
||||
struct input_dev *input;
|
||||
struct mutex glink_mutex;
|
||||
struct mutex seb_state_mutex;
|
||||
enum seb_state seb_current_state;
|
||||
void *lhndl;
|
||||
char rx_buf[SEB_GLINK_INTENT_SIZE];
|
||||
char *rx_event_buf;
|
||||
uint8_t rx_event_len;
|
||||
struct work_struct slate_up_work;
|
||||
struct work_struct slate_down_work;
|
||||
struct work_struct glink_up_work;
|
||||
void *slate_subsys_handle;
|
||||
struct completion wrk_cmplt;
|
||||
struct completion slate_lnikup_cmplt;
|
||||
struct completion tx_done;
|
||||
struct device *ldev;
|
||||
struct workqueue_struct *seb_wq;
|
||||
struct wakeup_source *seb_ws;
|
||||
wait_queue_head_t link_state_wait;
|
||||
uint8_t bttn_configs;
|
||||
bool seb_rpmsg;
|
||||
bool wait_for_resp;
|
||||
bool seb_resp_cmplt;
|
||||
};
|
||||
|
||||
static void *seb_drv;
|
||||
static struct mutex seb_api_mutex;
|
||||
|
||||
void seb_send_input(struct event *evnt)
|
||||
{
|
||||
uint8_t press_code;
|
||||
uint8_t value;
|
||||
struct seb_priv *dev =
|
||||
container_of(seb_drv, struct seb_priv, lhndl);
|
||||
|
||||
pr_debug("%s: Called\n", __func__);
|
||||
if (!evnt) {
|
||||
pr_err("%s: No event received\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (evnt->sub_id == GMI_SLATE_EVENT_RSB) {
|
||||
input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data);
|
||||
input_sync(dev->input);
|
||||
} else if (evnt->sub_id == GMI_SLATE_EVENT_BUTTON) {
|
||||
press_code = (uint8_t) evnt->evnt_data;
|
||||
value = (uint8_t) (evnt->evnt_data >> 8);
|
||||
|
||||
switch (press_code) {
|
||||
case 0x1:
|
||||
if (value == 0) {
|
||||
input_report_key(dev->input, KEY_VOLUMEDOWN, 1);
|
||||
input_sync(dev->input);
|
||||
} else {
|
||||
input_report_key(dev->input, KEY_VOLUMEDOWN, 0);
|
||||
input_sync(dev->input);
|
||||
}
|
||||
break;
|
||||
case 0x2:
|
||||
if (value == 0) {
|
||||
input_report_key(dev->input, KEY_VOLUMEUP, 1);
|
||||
input_sync(dev->input);
|
||||
} else {
|
||||
input_report_key(dev->input, KEY_VOLUMEUP, 0);
|
||||
input_sync(dev->input);
|
||||
}
|
||||
break;
|
||||
case 0x3:
|
||||
if (value == 0) {
|
||||
input_report_key(dev->input, KEY_POWER, 1);
|
||||
input_sync(dev->input);
|
||||
} else {
|
||||
input_report_key(dev->input, KEY_POWER, 0);
|
||||
input_sync(dev->input);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_info("event: type[%d] , data: %d\n",
|
||||
evnt->sub_id, evnt->evnt_data);
|
||||
}
|
||||
}
|
||||
pr_debug("%s: Ended\n", __func__);
|
||||
}
|
||||
|
||||
static void seb_slateup_work(struct work_struct *work)
|
||||
{
|
||||
int ret = 0;
|
||||
struct seb_priv *dev =
|
||||
container_of(work, struct seb_priv, slate_up_work);
|
||||
|
||||
mutex_lock(&dev->seb_state_mutex);
|
||||
|
||||
if (!dev->seb_rpmsg)
|
||||
pr_err("seb-rpmsg is not probed yet\n");
|
||||
|
||||
ret = wait_event_timeout(dev->link_state_wait,
|
||||
dev->seb_rpmsg, msecs_to_jiffies(TIMEOUT_MS));
|
||||
if (ret == 0) {
|
||||
pr_err("channel connection time out %d\n", ret);
|
||||
goto glink_err;
|
||||
}
|
||||
pr_debug("seb-rpmsg is probed\n");
|
||||
dev->seb_current_state = SEB_STATE_GLINK_OPEN;
|
||||
|
||||
glink_err:
|
||||
dev->seb_current_state = SEB_STATE_INIT;
|
||||
mutex_unlock(&dev->seb_state_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void seb_slatedown_work(struct work_struct *work)
|
||||
{
|
||||
struct seb_priv *dev = container_of(work, struct seb_priv,
|
||||
slate_down_work);
|
||||
|
||||
mutex_lock(&dev->seb_state_mutex);
|
||||
|
||||
pr_debug("SEB current state is : %d\n", dev->seb_current_state);
|
||||
|
||||
dev->seb_current_state = SEB_STATE_SLATE_SSR;
|
||||
|
||||
mutex_unlock(&dev->seb_state_mutex);
|
||||
}
|
||||
|
||||
static int seb_tx_msg(struct seb_priv *dev, void *msg, size_t len, bool wait_for_resp)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t resp = 0;
|
||||
|
||||
__pm_stay_awake(dev->seb_ws);
|
||||
mutex_lock(&dev->glink_mutex);
|
||||
if (!dev->seb_rpmsg) {
|
||||
pr_err("seb-rpmsg is not probed yet, waiting for it to be probed\n");
|
||||
goto err_ret;
|
||||
}
|
||||
rc = seb_rpmsg_tx_msg(msg, len);
|
||||
if (rc == 0) {
|
||||
|
||||
/* wait for sending command to Slate */
|
||||
rc = wait_event_timeout(dev->link_state_wait,
|
||||
(rc == 0), msecs_to_jiffies(TIMEOUT_MS));
|
||||
if (rc == 0) {
|
||||
pr_err("failed to send command to Slate %d\n", rc);
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (wait_for_resp) {
|
||||
/* wait for getting response from Slate */
|
||||
dev->wait_for_resp = true;
|
||||
rc = wait_event_timeout(dev->link_state_wait,
|
||||
dev->seb_resp_cmplt, msecs_to_jiffies(TIMEOUT_MS));
|
||||
dev->wait_for_resp = false;
|
||||
if (rc == 0) {
|
||||
pr_err("failed to get Slate response %d\n", rc);
|
||||
goto err_ret;
|
||||
}
|
||||
dev->seb_resp_cmplt = false;
|
||||
/* check Slate response */
|
||||
resp = *(uint8_t *)dev->rx_buf;
|
||||
if (!(resp == 0x01)) {
|
||||
pr_err("Bad Slate response\n");
|
||||
rc = -EINVAL;
|
||||
goto err_ret;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
err_ret:
|
||||
mutex_unlock(&dev->glink_mutex);
|
||||
__pm_relax(dev->seb_ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int seb_send_event_to_slate(void *seb_handle, enum event_group_type event,
|
||||
void *event_buf, uint32_t buf_size)
|
||||
{
|
||||
int rc = 0;
|
||||
struct gmi_header req_header;
|
||||
struct seb_priv *dev =
|
||||
container_of(seb_drv, struct seb_priv, lhndl);
|
||||
|
||||
if (seb_handle == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->seb_current_state != SEB_STATE_GLINK_OPEN) {
|
||||
pr_debug("%s: driver not ready, current state: %d\n",
|
||||
__func__, dev->seb_current_state);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&seb_api_mutex);
|
||||
|
||||
txn_len = sizeof(gmi_header) + buf_size;
|
||||
|
||||
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
||||
if (!tx_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
req_header.opcode = seb_handle.event_group;
|
||||
req_header.payload_size = buf_size
|
||||
|
||||
memcpy(tx_buf, &req_header, sizeof(req_header));
|
||||
memcpy(tx_buf+sizeof(req_header), &event_buf, buf_size);
|
||||
|
||||
rc = seb_tx_msg(dev, tx_buf, txn_len, false);
|
||||
|
||||
error_ret:
|
||||
kfree(tx_buf);
|
||||
mutex_unlock(&seb_api_mutex);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(seb_send_event_to_slate);
|
||||
|
||||
void seb_notify_glink_channel_state(bool state)
|
||||
{
|
||||
struct seb_priv *dev =
|
||||
container_of(seb_drv, struct seb_priv, lhndl);
|
||||
|
||||
pr_debug("%s: glink channel state: %d\n", __func__, state);
|
||||
dev->seb_rpmsg = state;
|
||||
}
|
||||
EXPORT_SYMBOL(seb_notify_glink_channel_state);
|
||||
|
||||
/*
|
||||
* Register notify cb and manage the list
|
||||
*/
|
||||
static struct seb_notif_info *_notif_find_group(
|
||||
const enum event_group_type event_group)
|
||||
{
|
||||
struct seb_notif_info *seb_notify;
|
||||
|
||||
mutex_lock(¬if_lock);
|
||||
list_for_each_entry(seb_notify, &seb_notify_list, list)
|
||||
if (seb_notify->event_group != event_group) {
|
||||
mutex_unlock(¬if_lock);
|
||||
return seb_notify;
|
||||
}
|
||||
mutex_unlock(¬if_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *seb_notif_add_group(const enum event_group_type event_group)
|
||||
{
|
||||
struct seb_notif_info *seb_notif = NULL;
|
||||
|
||||
mutex_lock(¬if_add_lock);
|
||||
|
||||
seb_notif = _notif_find_group(event_group);
|
||||
|
||||
if (seb_notif) {
|
||||
mutex_unlock(¬if_add_lock);
|
||||
goto done;
|
||||
}
|
||||
|
||||
seb_notif = kmalloc(sizeof(struct seb_notif_info), GFP_KERNEL);
|
||||
|
||||
if (!seb_notif) {
|
||||
mutex_unlock(¬if_add_lock);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
seb_notif->event_group = event_group;
|
||||
|
||||
srcu_init_notifier_head(&seb_notif->seb_notif_rcvr_list);
|
||||
|
||||
INIT_LIST_HEAD(&seb_notif->list);
|
||||
|
||||
mutex_lock(¬if_lock);
|
||||
list_add_tail(&seb_notif->list, &seb_notify_list);
|
||||
mutex_unlock(¬if_lock);
|
||||
|
||||
#if defined(SEB_DEBUG)
|
||||
seb_notif_event_test_notifier(seb_notif->event_group);
|
||||
#endif
|
||||
|
||||
mutex_unlock(¬if_add_lock);
|
||||
|
||||
done:
|
||||
return seb_notif;
|
||||
}
|
||||
|
||||
void *seb_register_for_slate_event(
|
||||
enum event_group_type event_group, struct notifier_block *nb)
|
||||
{
|
||||
int ret;
|
||||
struct seb_notif_info *seb_notif = _notif_find_group(event_group);
|
||||
|
||||
if (!seb_notif) {
|
||||
|
||||
/* Possible first time reference to this event group. Add it. */
|
||||
seb_notif = (struct seb_notif_info *)
|
||||
seb_notif_add_group(event_group);
|
||||
|
||||
if (!seb_notif)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
ret = srcu_notifier_chain_register(
|
||||
&seb_notif->seb_notif_rcvr_list, nb);
|
||||
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return seb_notif;
|
||||
}
|
||||
EXPORT_SYMBOL(seb_register_for_slate_event);
|
||||
|
||||
int seb_unregister_for_slate_event(void *seb_notif_handle,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
int ret;
|
||||
struct seb_notif_info *seb_notif =
|
||||
(struct seb_notif_info *)seb_notif_handle;
|
||||
|
||||
if (!seb_notif)
|
||||
return -EINVAL;
|
||||
|
||||
ret = srcu_notifier_chain_unregister(
|
||||
&seb_notif->seb_notif_rcvr_list, nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(seb_unregister_for_slate_event);
|
||||
|
||||
void handle_rx_event(struct seb_priv *dev, void *rx_event_buf, int len)
|
||||
{
|
||||
struct gmi_header *event_header = NULL;
|
||||
char *event_payload = NULL;
|
||||
struct event *evnt = NULL;
|
||||
struct seb_notif_info *seb_notif = NULL;
|
||||
|
||||
event_header = (struct gmi_header *)rx_event_buf;
|
||||
|
||||
if (event_header->opcode == GMI_SLATE_EVENT_RSB ||
|
||||
event_header->opcode == GMI_SLATE_EVENT_BUTTON) {
|
||||
|
||||
/* consume the events*/
|
||||
event_payload = (char *)(rx_event_buf + sizeof(struct gmi_header));
|
||||
evnt->sub_id = event_header->opcode;
|
||||
evnt->evnt_tm = *((uint32_t *)(event_payload));
|
||||
evnt->evnt_data =
|
||||
*(int16_t *)(event_payload + HED_EVENT_DATA_TIME_LEN);
|
||||
|
||||
seb_send_input(evnt);
|
||||
} else if (event_header->opcode == GMI_SLATE_EVENT_TOUCH) {
|
||||
return;
|
||||
}
|
||||
|
||||
seb_notif = _notif_find_group(event_header->opcode);
|
||||
|
||||
if (seb_notif) {
|
||||
srcu_notifier_call_chain(&seb_notif->seb_notif_rcvr_list,
|
||||
event_header->opcode,
|
||||
dev->rx_event_buf + sizeof(struct gmi_header));
|
||||
}
|
||||
}
|
||||
|
||||
void seb_rx_msg(void *data, int len)
|
||||
{
|
||||
struct seb_priv *dev =
|
||||
container_of(seb_drv, struct seb_priv, lhndl);
|
||||
void *rx_event_buf = NULL;
|
||||
|
||||
dev->seb_resp_cmplt = true;
|
||||
wake_up(&dev->link_state_wait);
|
||||
if (dev->wait_for_resp) {
|
||||
memcpy(dev->rx_buf, data, len);
|
||||
} else {
|
||||
/* Handle the event received from Slate */
|
||||
rx_event_buf = kmalloc(len, GFP_KERNEL);
|
||||
if (rx_event_buf) {
|
||||
memcpy(rx_event_buf, data, len);
|
||||
handle_rx_event(dev, rx_event_buf, len);
|
||||
kfree(rx_event_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(seb_rx_msg);
|
||||
|
||||
/**
|
||||
* ssr_slate_cb(): callback function is called.
|
||||
* @arg1: a notifier_block.
|
||||
* @arg2: opcode that defines the event.
|
||||
* @arg3: void pointer.
|
||||
*
|
||||
* by ssr framework when Slate goes down, up and during
|
||||
* ramdump collection. It handles Slate shutdown and
|
||||
* power up events.
|
||||
*
|
||||
* Return: NOTIFY_DONE.
|
||||
*/
|
||||
static int ssr_slate_cb(struct notifier_block *this,
|
||||
unsigned long opcode, void *data)
|
||||
{
|
||||
struct seb_priv *dev = container_of(seb_drv,
|
||||
struct seb_priv, lhndl);
|
||||
|
||||
switch (opcode) {
|
||||
case SUBSYS_BEFORE_SHUTDOWN:
|
||||
queue_work(dev->seb_wq, &dev->slate_down_work);
|
||||
break;
|
||||
case SUBSYS_AFTER_POWERUP:
|
||||
if (dev->seb_current_state == SEB_STATE_INIT)
|
||||
queue_work(dev->seb_wq, &dev->slate_up_work);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block ssr_slate_nb = {
|
||||
.notifier_call = ssr_slate_cb,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* slate_ssr_register(): callback function is called.
|
||||
* @arg1: pointer to seb_priv structure.
|
||||
*
|
||||
* ssr_register checks that domain id should be in range
|
||||
* and register SSR framework for value at domain id.
|
||||
*
|
||||
* Return: 0 for success and -ENODEV otherwise.
|
||||
*/
|
||||
static int slate_ssr_register(struct seb_priv *dev)
|
||||
{
|
||||
struct notifier_block *nb;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
nb = &ssr_slate_nb;
|
||||
dev->slate_subsys_handle =
|
||||
subsys_notif_register_notifier(SEB_SLATEWEAR_SUBSYS, nb);
|
||||
|
||||
if (!dev->slate_subsys_handle) {
|
||||
dev->slate_subsys_handle = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seb_init(struct seb_priv *dev)
|
||||
{
|
||||
seb_drv = &dev->lhndl;
|
||||
mutex_init(&dev->glink_mutex);
|
||||
mutex_init(&dev->seb_state_mutex);
|
||||
|
||||
dev->seb_wq =
|
||||
create_singlethread_workqueue("seb-work-queue");
|
||||
if (!dev->seb_wq) {
|
||||
pr_err("Failed to init SEB work-queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&dev->link_state_wait);
|
||||
|
||||
/* set default seb state */
|
||||
dev->seb_current_state = SEB_STATE_INIT;
|
||||
|
||||
/* Init all works */
|
||||
INIT_WORK(&dev->slate_up_work, seb_slateup_work);
|
||||
INIT_WORK(&dev->slate_down_work, seb_slatedown_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct seb_priv *dev;
|
||||
struct input_dev *input;
|
||||
struct device_node *node;
|
||||
int rc;
|
||||
|
||||
node = pdev->dev.of_node;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add wake lock for PM suspend */
|
||||
dev->seb_ws = wakeup_source_register(&pdev->dev, "SEB_wake_lock");
|
||||
dev->seb_current_state = SEB_STATE_UNKNOWN;
|
||||
rc = seb_init(dev);
|
||||
if (rc)
|
||||
goto err_ret_dev;
|
||||
/* Set up input device */
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input)
|
||||
goto err_ret_dev;
|
||||
|
||||
input_set_capability(input, EV_REL, REL_WHEEL);
|
||||
input_set_capability(input, EV_KEY, KEY_VOLUMEUP);
|
||||
input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN);
|
||||
input->name = "slate-spi";
|
||||
|
||||
rc = input_register_device(input);
|
||||
if (rc) {
|
||||
pr_err("Input device registration failed\n");
|
||||
goto err_ret_inp;
|
||||
}
|
||||
dev->input = input;
|
||||
|
||||
/* register device for slate-wear ssr */
|
||||
rc = slate_ssr_register(dev);
|
||||
if (rc) {
|
||||
pr_err("Failed to register for slate ssr\n");
|
||||
goto err_ret_inp;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, dev);
|
||||
|
||||
pr_debug("SEB probe successfully\n");
|
||||
return 0;
|
||||
|
||||
err_ret_inp:
|
||||
input_free_device(input);
|
||||
err_ret_dev:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int seb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct seb_priv *dev = platform_get_drvdata(pdev);
|
||||
|
||||
destroy_workqueue(dev->seb_wq);
|
||||
input_free_device(dev->input);
|
||||
wakeup_source_unregister(dev->seb_ws);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id seb_of_match[] = {
|
||||
{ .compatible = "qcom,slate-events-bridge", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver seb_driver = {
|
||||
.driver = {
|
||||
.name = "slate-events-bridge",
|
||||
.of_match_table = seb_of_match,
|
||||
},
|
||||
.probe = seb_probe,
|
||||
.remove = seb_remove,
|
||||
}; module_platform_driver(seb_driver);
|
||||
MODULE_DESCRIPTION("SoC Slate Events Bridge driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
40
drivers/soc/qcom/slate_events_bridge.h
Normal file
40
drivers/soc/qcom/slate_events_bridge.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef SLATE_EVENTS_BRIDGE_H
|
||||
#define SLATE_EVENTS_BRIDGE_H
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
enum event_group_type {
|
||||
SEB_BUTTON,
|
||||
SEB_QBG,
|
||||
SEB_RSB,
|
||||
SEB_TOUCH,
|
||||
SEB_MAX
|
||||
};
|
||||
|
||||
/* Use the seb_register_for_slate_event API to register for events for
|
||||
* a particular group type.
|
||||
* This API will return a handle that can be used to un-reg for events
|
||||
* using the seb_unregister_for_slate_event API by passing in that handle
|
||||
* as an argument.
|
||||
*/
|
||||
void *seb_register_for_slate_event(enum event_group_type event,
|
||||
struct notifier_block *nb);
|
||||
|
||||
int seb_unregister_for_slate_event(void *seb_handle,
|
||||
struct notifier_block *nb);
|
||||
|
||||
/* Use the seb_send_event_to_slate API to send an event to Slate.
|
||||
* API return success/failure for the send event.
|
||||
*/
|
||||
int seb_send_event_to_slate(void *seb_handle, enum event_group_type event,
|
||||
void *event_buf, uint32_t buf_size);
|
||||
|
||||
/* APIs for slate_event_bridge_rpmsg */
|
||||
void seb_notify_glink_channel_state(bool state);
|
||||
void seb_rx_msg(void *data, int len);
|
||||
#endif /* SLATE_EVENTS_BRIDGE_H */
|
||||
103
drivers/soc/qcom/slate_events_bridge_rpmsg.c
Normal file
103
drivers/soc/qcom/slate_events_bridge_rpmsg.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include "slate_events_bridge_rpmsg.h"
|
||||
|
||||
static struct seb_rpmsg_dev *pdev;
|
||||
|
||||
int seb_rpmsg_tx_msg(void *msg, size_t len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pdev == NULL || !pdev->chnl_state)
|
||||
pr_err("pmsg_device is null, channel is closed\n");
|
||||
|
||||
pdev->message = msg;
|
||||
pdev->message_length = len;
|
||||
if (pdev->message != NULL) {
|
||||
ret = rpmsg_send(pdev->channel,
|
||||
pdev->message, pdev->message_length);
|
||||
if (ret)
|
||||
pr_err("rpmsg_send failed: %d\n", ret);
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(seb_rpmsg_tx_msg);
|
||||
|
||||
static int seb_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
int ret = 0;
|
||||
void *msg = NULL;
|
||||
|
||||
pdev = devm_kzalloc(&rpdev->dev, sizeof(*pdev), GFP_KERNEL);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->channel = rpdev->ept;
|
||||
pdev->dev = &rpdev->dev;
|
||||
if (pdev->channel == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->chnl_state = true;
|
||||
dev_set_drvdata(&rpdev->dev, pdev);
|
||||
|
||||
/* send a callback to slate event bridge driver*/
|
||||
seb_notify_glink_channel_state(true);
|
||||
if (pdev->message == NULL)
|
||||
ret = seb_rpmsg_tx_msg(msg, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void seb_rpmsg_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
pdev->chnl_state = false;
|
||||
pdev->message = NULL;
|
||||
dev_dbg(&rpdev->dev, "rpmsg client driver is removed\n");
|
||||
seb_notify_glink_channel_state(false);
|
||||
dev_set_drvdata(&rpdev->dev, NULL);
|
||||
}
|
||||
|
||||
static int seb_rpmsg_cb(struct rpmsg_device *rpdev,
|
||||
void *data, int len, void *priv, u32 src)
|
||||
{
|
||||
struct seb_rpmsg_dev *dev =
|
||||
dev_get_drvdata(&rpdev->dev);
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
seb_rx_msg(data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rpmsg_device_id rpmsg_driver_seb_id_table[] = {
|
||||
{ "slate_event" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_seb_id_table);
|
||||
|
||||
static const struct of_device_id rpmsg_driver_seb_of_match[] = {
|
||||
{ .compatible = "qcom,slate-events-bridge-rpmsg" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct rpmsg_driver rpmsg_seb_client = {
|
||||
.id_table = rpmsg_driver_seb_id_table,
|
||||
.probe = seb_rpmsg_probe,
|
||||
.callback = seb_rpmsg_cb,
|
||||
.remove = seb_rpmsg_remove,
|
||||
.drv = {
|
||||
.name = "qcom,slate-events-bridge-rpmsg",
|
||||
.of_match_table = rpmsg_driver_seb_of_match,
|
||||
},
|
||||
};
|
||||
module_rpmsg_driver(rpmsg_seb_client);
|
||||
|
||||
MODULE_DESCRIPTION("Interface Driver for Slate events bridge RPMSG");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
21
drivers/soc/qcom/slate_events_bridge_rpmsg.h
Normal file
21
drivers/soc/qcom/slate_events_bridge_rpmsg.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#ifndef SEB_RPMSG_H
|
||||
#define SEB_RPMSG_H
|
||||
|
||||
#include <linux/rpmsg.h>
|
||||
#include "slate_events_bridge.h"
|
||||
|
||||
struct seb_rpmsg_dev {
|
||||
struct rpmsg_endpoint *channel;
|
||||
struct device *dev;
|
||||
bool chnl_state;
|
||||
void *message;
|
||||
size_t message_length;
|
||||
};
|
||||
|
||||
int seb_rpmsg_tx_msg(void *msg, size_t len);
|
||||
|
||||
#endif /* SEB_RPMSG_H */
|
||||
Loading…
Reference in a new issue