Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-or-later
menu "AMD SFH HID Support"
depends on X86_64 || COMPILE_TEST
depends on PCI
config AMD_SFH_HID
tristate "AMD Sensor Fusion Hub"
depends on X86
help
If you say yes to this option, support will be included for the
AMD Sensor Fusion Hub.
This driver will enable sensors functionality on AMD platforms
starting from 17h family of RYZEN parts.
This driver can also be built as a module. If so, the module will
be called amd-sfh.
Say Y or M here if you want to support AMD SFH. If unsure, say N.
endmenu
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile - AMD SFH HID drivers
# Copyright (c) 2019-2020, Advanced Micro Devices, Inc.
#
#
obj-$(CONFIG_AMD_SFH_HID) += amd_sfh.o
amd_sfh-objs := amd_sfh_hid.o
amd_sfh-objs += amd_sfh_client.o
amd_sfh-objs += amd_sfh_pcie.o
amd_sfh-objs += hid_descriptor/amd_sfh_hid_desc.o
amd_sfh-objs += sfh1_1/amd_sfh_init.o
amd_sfh-objs += sfh1_1/amd_sfh_interface.o
amd_sfh-objs += sfh1_1/amd_sfh_desc.o
ccflags-y += -I $(src)/
@@ -0,0 +1,385 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD SFH Client Layer
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
* Sandeep Singh <Sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/dma-mapping.h>
#include <linux/hid.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include "hid_descriptor/amd_sfh_hid_desc.h"
#include "amd_sfh_pcie.h"
#include "amd_sfh_hid.h"
void amd_sfh_set_report(struct hid_device *hid, int report_id,
int report_type)
{
struct amdtp_hid_data *hid_data = hid->driver_data;
struct amdtp_cl_data *cli_data = hid_data->cli_data;
int i;
for (i = 0; i < cli_data->num_hid_devices; i++) {
if (cli_data->hid_sensor_hubs[i] == hid) {
cli_data->cur_hid_dev = i;
break;
}
}
amdtp_hid_wakeup(hid);
}
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type)
{
struct amdtp_hid_data *hid_data = hid->driver_data;
struct amdtp_cl_data *cli_data = hid_data->cli_data;
struct request_list *req_list = &cli_data->req_list;
struct amd_input_data *in_data = cli_data->in_data;
struct amd_mp2_dev *mp2;
int i;
mp2 = container_of(in_data, struct amd_mp2_dev, in_data);
guard(mutex)(&mp2->lock);
for (i = 0; i < cli_data->num_hid_devices; i++) {
if (cli_data->hid_sensor_hubs[i] == hid) {
struct request_list *new = kzalloc_obj(*new);
if (!new)
return -ENOMEM;
new->current_index = i;
new->sensor_idx = cli_data->sensor_idx[i];
new->hid = hid;
new->report_type = report_type;
new->report_id = report_id;
cli_data->report_id[i] = report_id;
cli_data->request_done[i] = false;
list_add(&new->list, &req_list->list);
break;
}
}
schedule_delayed_work(&cli_data->work, 0);
return 0;
}
void amd_sfh_work(struct work_struct *work)
{
struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work);
struct request_list *req_list = &cli_data->req_list;
struct amd_input_data *in_data = cli_data->in_data;
struct request_list *req_node;
u8 current_index, sensor_index;
struct amd_mp2_ops *mp2_ops;
struct amd_mp2_dev *mp2;
u8 report_id, node_type;
u8 report_size = 0;
mp2 = container_of(in_data, struct amd_mp2_dev, in_data);
guard(mutex)(&mp2->lock);
req_node = list_last_entry(&req_list->list, struct request_list, list);
list_del(&req_node->list);
current_index = req_node->current_index;
sensor_index = req_node->sensor_idx;
report_id = req_node->report_id;
node_type = req_node->report_type;
kfree(req_node);
mp2_ops = mp2->mp2_ops;
if (node_type == HID_FEATURE_REPORT) {
report_size = mp2_ops->get_feat_rep(sensor_index, report_id,
cli_data->feature_report[current_index]);
if (report_size)
hid_input_report(cli_data->hid_sensor_hubs[current_index],
cli_data->report_type[current_index],
cli_data->feature_report[current_index], report_size, 0);
else
pr_err("AMDSFH: Invalid report size\n");
} else if (node_type == HID_INPUT_REPORT) {
report_size = mp2_ops->get_in_rep(current_index, sensor_index, report_id, in_data);
if (report_size)
hid_input_report(cli_data->hid_sensor_hubs[current_index],
cli_data->report_type[current_index],
in_data->input_report[current_index], report_size, 0);
else
pr_err("AMDSFH: Invalid report size\n");
}
cli_data->cur_hid_dev = current_index;
cli_data->sensor_requested_cnt[current_index] = 0;
amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]);
if (!list_empty(&req_list->list))
schedule_delayed_work(&cli_data->work, 0);
}
void amd_sfh_work_buffer(struct work_struct *work)
{
struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work);
struct amd_input_data *in_data = cli_data->in_data;
struct amd_mp2_dev *mp2;
u8 report_size;
int i;
mp2 = container_of(in_data, struct amd_mp2_dev, in_data);
guard(mutex)(&mp2->lock);
for (i = 0; i < cli_data->num_hid_devices; i++) {
if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {
report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i],
cli_data->report_id[i], in_data);
hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
in_data->input_report[i], report_size, 0);
}
}
schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
}
static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
if (mp2->mp2_ops->response)
sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);
return sensor_sts;
}
static const char *get_sensor_name(int idx)
{
switch (idx) {
case accel_idx:
return "accelerometer";
case gyro_idx:
return "gyroscope";
case mag_idx:
return "magnetometer";
case op_idx:
return "operating-mode";
case als_idx:
case ACS_IDX: /* ambient color sensor */
return "ALS";
case HPD_IDX:
return "HPD";
default:
return "unknown sensor type";
}
}
static void amd_sfh_resume(struct amd_mp2_dev *mp2)
{
struct amdtp_cl_data *cl_data = mp2->cl_data;
struct amd_mp2_sensor_info info;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
info.period = AMD_SFH_IDLE_LOOP;
info.sensor_idx = cl_data->sensor_idx[i];
info.dma_address = cl_data->sensor_dma_addr[i];
mp2->mp2_ops->start(mp2, info);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (status == SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_ENABLED;
dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
amd_sfh_clear_intr(mp2);
}
static void amd_sfh_suspend(struct amd_mp2_dev *mp2)
{
struct amdtp_cl_data *cl_data = mp2->cl_data;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_idx[i] != HPD_IDX &&
cl_data->sensor_sts[i] == SENSOR_ENABLED) {
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
}
cancel_delayed_work_sync(&cl_data->work_buffer);
amd_sfh_clear_intr(mp2);
}
int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
{
struct amd_input_data *in_data = &privdata->in_data;
struct amdtp_cl_data *cl_data = privdata->cl_data;
struct amd_mp2_ops *mp2_ops = privdata->mp2_ops;
struct amd_mp2_sensor_info info;
struct request_list *req_list;
struct device *dev;
u32 feature_report_size;
u32 input_report_size;
int rc, i;
u8 cl_idx;
req_list = &cl_data->req_list;
dev = &privdata->pdev->dev;
amd_sfh_set_desc_ops(mp2_ops);
mp2_ops->suspend = amd_sfh_suspend;
mp2_ops->resume = amd_sfh_resume;
cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]);
if (cl_data->num_hid_devices == 0)
return -ENODEV;
cl_data->is_any_sensor_enabled = false;
INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);
INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);
INIT_LIST_HEAD(&req_list->list);
cl_data->in_data = in_data;
for (i = 0; i < cl_data->num_hid_devices; i++) {
in_data->sensor_virt_addr[i] = dmam_alloc_coherent(dev, sizeof(int) * 8,
&cl_data->sensor_dma_addr[i],
GFP_KERNEL);
if (!in_data->sensor_virt_addr[i]) {
rc = -ENOMEM;
goto cleanup;
}
if (cl_data->sensor_idx[i] == op_idx) {
info.period = AMD_SFH_IDLE_LOOP;
info.sensor_idx = cl_data->sensor_idx[i];
info.dma_address = cl_data->sensor_dma_addr[i];
mp2_ops->start(privdata, info);
cl_data->sensor_sts[i] = amd_sfh_wait_for_response(privdata,
cl_data->sensor_idx[i],
SENSOR_ENABLED);
if (cl_data->sensor_sts[i] == SENSOR_ENABLED)
cl_data->is_any_sensor_enabled = true;
continue;
}
cl_data->sensor_sts[i] = SENSOR_DISABLED;
cl_data->sensor_requested_cnt[i] = 0;
cl_data->cur_hid_dev = i;
cl_idx = cl_data->sensor_idx[i];
cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size);
if (!cl_data->report_descr_sz[i]) {
rc = -EINVAL;
goto cleanup;
}
feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size);
if (!feature_report_size) {
rc = -EINVAL;
goto cleanup;
}
input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size);
if (!input_report_size) {
rc = -EINVAL;
goto cleanup;
}
cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL);
if (!cl_data->feature_report[i]) {
rc = -ENOMEM;
goto cleanup;
}
in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL);
if (!in_data->input_report[i]) {
rc = -ENOMEM;
goto cleanup;
}
info.period = AMD_SFH_IDLE_LOOP;
info.sensor_idx = cl_idx;
info.dma_address = cl_data->sensor_dma_addr[i];
cl_data->report_descr[i] =
devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL);
if (!cl_data->report_descr[i]) {
rc = -ENOMEM;
goto cleanup;
}
rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);
if (rc)
goto cleanup;
mp2_ops->start(privdata, info);
cl_data->sensor_sts[i] = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (cl_data->sensor_sts[i] == SENSOR_ENABLED)
cl_data->is_any_sensor_enabled = true;
}
if (!cl_data->is_any_sensor_enabled ||
(mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) {
dev_warn(dev, "Failed to discover, sensors not enabled is %d\n",
cl_data->is_any_sensor_enabled);
rc = -EOPNOTSUPP;
goto cleanup;
}
for (i = 0; i < cl_data->num_hid_devices; i++) {
cl_data->cur_hid_dev = i;
if (cl_data->sensor_idx[i] == op_idx) {
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
continue;
}
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
rc = amdtp_hid_probe(i, cl_data);
if (rc)
goto cleanup;
} else {
cl_data->sensor_sts[i] = SENSOR_DISABLED;
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0;
cleanup:
amd_sfh_hid_client_deinit(privdata);
for (i = 0; i < cl_data->num_hid_devices; i++) {
devm_kfree(dev, cl_data->feature_report[i]);
devm_kfree(dev, in_data->input_report[i]);
devm_kfree(dev, cl_data->report_descr[i]);
}
return rc;
}
int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
{
struct amdtp_cl_data *cl_data = privdata->cl_data;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
}
cancel_delayed_work_sync(&cl_data->work);
cancel_delayed_work_sync(&cl_data->work_buffer);
amdtp_hid_remove(cl_data);
return 0;
}
@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AMD MP2 common macros and structures
*
* Copyright (c) 2022, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMD_SFH_COMMON_H
#define AMD_SFH_COMMON_H
#include <linux/mutex.h>
#include <linux/pci.h>
#include "amd_sfh_hid.h"
#define PCI_DEVICE_ID_AMD_MP2 0x15E4
#define PCI_DEVICE_ID_AMD_MP2_1_1 0x164A
#define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4))
#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4))
#define AMD_C2P_MSG_V1(regno) (0x10900 + ((regno) * 4))
#define AMD_P2C_MSG_V1(regno) (0x10500 + ((regno) * 4))
#define SENSOR_ENABLED 4
#define SENSOR_DISABLED 5
#define AMD_SFH_IDLE_LOOP 200
enum cmd_id {
NO_OP,
ENABLE_SENSOR,
DISABLE_SENSOR,
STOP_ALL_SENSORS = 8,
};
struct amd_mp2_sensor_info {
u8 sensor_idx;
u32 period;
dma_addr_t dma_address;
};
struct sfh_dev_status {
bool is_hpd_present;
bool is_hpd_enabled;
bool is_als_present;
bool is_sra_present;
};
struct amd_mp2_dev {
struct pci_dev *pdev;
struct amdtp_cl_data *cl_data;
void __iomem *mmio;
void __iomem *vsbase;
const struct amd_sfh1_1_ops *sfh1_1_ops;
struct amd_mp2_ops *mp2_ops;
struct amd_input_data in_data;
/* mp2 active control status */
u32 mp2_acs;
struct sfh_dev_status dev_en;
struct work_struct work;
/* mp2 to protect data */
struct mutex lock;
u8 init_done;
u8 rver;
};
struct amd_mp2_ops {
void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx);
void (*stop_all)(struct amd_mp2_dev *privdata);
int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
void (*clear_intr)(struct amd_mp2_dev *privdata);
int (*init_intr)(struct amd_mp2_dev *privdata);
int (*discovery_status)(struct amd_mp2_dev *privdata);
void (*suspend)(struct amd_mp2_dev *mp2);
void (*resume)(struct amd_mp2_dev *mp2);
void (*remove)(void *privdata);
int (*get_rep_desc)(int sensor_idx, u8 rep_desc[]);
u32 (*get_desc_sz)(int sensor_idx, int descriptor_name);
u8 (*get_feat_rep)(int sensor_idx, int report_id, u8 *feature_report);
u8 (*get_in_rep)(u8 current_index, int sensor_idx, int report_id,
struct amd_input_data *in_data);
};
void amd_sfh_work(struct work_struct *work);
void amd_sfh_work_buffer(struct work_struct *work);
void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata);
int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata);
void amd_sfh_clear_intr(struct amd_mp2_dev *privdata);
int amd_sfh_irq_init(struct amd_mp2_dev *privdata);
static inline u64 amd_get_c2p_val(struct amd_mp2_dev *mp2, u32 idx)
{
return mp2->rver == 1 ? AMD_C2P_MSG_V1(idx) : AMD_C2P_MSG(idx);
}
static inline u64 amd_get_p2c_val(struct amd_mp2_dev *mp2, u32 idx)
{
return mp2->rver == 1 ? AMD_P2C_MSG_V1(idx) : AMD_P2C_MSG(idx);
}
#endif
@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD MP2 Sensors transport driver
*
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
* Sandeep Singh <sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/hid.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include "amd_sfh_hid.h"
#include "amd_sfh_pcie.h"
#define AMD_SFH_RESPONSE_TIMEOUT 1500
/**
* amdtp_hid_parse() - hid-core .parse() callback
* @hid: hid device instance
*
* This function gets called during call to hid_add_device
*
* Return: 0 on success and non zero on error
*/
static int amdtp_hid_parse(struct hid_device *hid)
{
struct amdtp_hid_data *hid_data = hid->driver_data;
struct amdtp_cl_data *cli_data = hid_data->cli_data;
return hid_parse_report(hid, cli_data->report_descr[hid_data->index],
cli_data->report_descr_sz[hid_data->index]);
}
/* Empty callbacks with success return code */
static int amdtp_hid_start(struct hid_device *hid)
{
return 0;
}
static void amdtp_hid_stop(struct hid_device *hid)
{
}
static int amdtp_hid_open(struct hid_device *hid)
{
return 0;
}
static void amdtp_hid_close(struct hid_device *hid)
{
}
static int amdtp_raw_request(struct hid_device *hdev, u8 reportnum,
u8 *buf, size_t len, u8 rtype, int reqtype)
{
return 0;
}
static void amdtp_hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype)
{
int rc;
switch (reqtype) {
case HID_REQ_GET_REPORT:
rc = amd_sfh_get_report(hid, rep->id, rep->type);
if (rc)
dev_err(&hid->dev, "AMDSFH get report error\n");
break;
case HID_REQ_SET_REPORT:
amd_sfh_set_report(hid, rep->id, reqtype);
break;
default:
break;
}
}
static int amdtp_wait_for_response(struct hid_device *hid)
{
struct amdtp_hid_data *hid_data = hid->driver_data;
struct amdtp_cl_data *cli_data = hid_data->cli_data;
int i, ret = 0;
for (i = 0; i < cli_data->num_hid_devices; i++) {
if (cli_data->hid_sensor_hubs[i] == hid)
break;
}
if (!cli_data->request_done[i])
ret = wait_event_interruptible_timeout(hid_data->hid_wait,
cli_data->request_done[i],
msecs_to_jiffies(AMD_SFH_RESPONSE_TIMEOUT));
if (ret == -ERESTARTSYS)
return -ERESTARTSYS;
else if (ret < 0)
return -ETIMEDOUT;
else
return 0;
}
void amdtp_hid_wakeup(struct hid_device *hid)
{
struct amdtp_hid_data *hid_data;
struct amdtp_cl_data *cli_data;
if (hid) {
hid_data = hid->driver_data;
cli_data = hid_data->cli_data;
cli_data->request_done[cli_data->cur_hid_dev] = true;
wake_up_interruptible(&hid_data->hid_wait);
}
}
static const struct hid_ll_driver amdtp_hid_ll_driver = {
.parse = amdtp_hid_parse,
.start = amdtp_hid_start,
.stop = amdtp_hid_stop,
.open = amdtp_hid_open,
.close = amdtp_hid_close,
.request = amdtp_hid_request,
.wait = amdtp_wait_for_response,
.raw_request = amdtp_raw_request,
};
int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
{
struct amd_mp2_dev *mp2 = container_of(cli_data->in_data, struct amd_mp2_dev, in_data);
struct device *dev = &mp2->pdev->dev;
struct hid_device *hid;
struct amdtp_hid_data *hid_data;
int rc;
hid = hid_allocate_device();
if (IS_ERR(hid))
return PTR_ERR(hid);
hid_data = kzalloc_obj(*hid_data);
if (!hid_data) {
rc = -ENOMEM;
goto err_hid_data;
}
hid->ll_driver = &amdtp_hid_ll_driver;
hid_data->index = cur_hid_dev;
hid_data->cli_data = cli_data;
init_waitqueue_head(&hid_data->hid_wait);
hid->driver_data = hid_data;
cli_data->hid_sensor_hubs[cur_hid_dev] = hid;
strscpy(hid->phys, dev->driver ? dev->driver->name : dev_name(dev),
sizeof(hid->phys));
hid->bus = BUS_AMD_SFH;
hid->vendor = AMD_SFH_HID_VENDOR;
hid->product = AMD_SFH_HID_PRODUCT;
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-amdsfh",
hid->vendor, hid->product);
rc = hid_add_device(hid);
if (rc)
goto err_hid_device;
return 0;
err_hid_device:
kfree(hid_data);
err_hid_data:
hid_destroy_device(hid);
return rc;
}
void amdtp_hid_remove(struct amdtp_cl_data *cli_data)
{
int i;
struct amdtp_hid_data *hid_data;
for (i = 0; i < cli_data->num_hid_devices; ++i) {
if (cli_data->hid_sensor_hubs[i]) {
hid_data = cli_data->hid_sensor_hubs[i]->driver_data;
hid_destroy_device(cli_data->hid_sensor_hubs[i]);
kfree(hid_data);
cli_data->hid_sensor_hubs[i] = NULL;
}
}
}
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AMD MP2 Sensors transport driver
*
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
* Sandeep Singh <sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMDSFH_HID_H
#define AMDSFH_HID_H
#define MAX_HID_DEVICES 7
#define AMD_SFH_HID_VENDOR 0x1022
#define AMD_SFH_HID_PRODUCT 0x0001
struct request_list {
struct hid_device *hid;
struct list_head list;
u8 report_id;
u8 sensor_idx;
u8 report_type;
u8 current_index;
};
struct amd_input_data {
u32 *sensor_virt_addr[MAX_HID_DEVICES];
u8 *input_report[MAX_HID_DEVICES];
};
struct amdtp_cl_data {
u8 init_done;
u32 cur_hid_dev;
bool is_any_sensor_enabled;
u32 hid_dev_count;
u32 num_hid_devices;
struct device_info *hid_devices;
u8 *report_descr[MAX_HID_DEVICES];
int report_descr_sz[MAX_HID_DEVICES];
struct hid_device *hid_sensor_hubs[MAX_HID_DEVICES];
u8 *hid_descr[MAX_HID_DEVICES];
int hid_descr_size[MAX_HID_DEVICES];
phys_addr_t phys_addr_base;
dma_addr_t sensor_dma_addr[MAX_HID_DEVICES];
u32 sensor_sts[MAX_HID_DEVICES];
u32 sensor_requested_cnt[MAX_HID_DEVICES];
u8 report_type[MAX_HID_DEVICES];
u8 report_id[MAX_HID_DEVICES];
u8 sensor_idx[MAX_HID_DEVICES];
u8 *feature_report[MAX_HID_DEVICES];
u8 request_done[MAX_HID_DEVICES];
struct amd_input_data *in_data;
struct delayed_work work;
struct delayed_work work_buffer;
struct request_list req_list;
};
/**
* struct amdtp_hid_data - Per instance HID data
* @index: Device index in the order of enumeration
* @request_done: Get Feature/Input report complete flag
* used during get/set request from hid core
* @cli_data: Link to the client instance
* @hid_wait: Completion waitq
*
* Used to tie hid->driver data to driver client instance
*/
struct amdtp_hid_data {
int index;
struct amdtp_cl_data *cli_data;
wait_queue_head_t hid_wait;
};
/* Interface functions between HID LL driver and AMD SFH client */
int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data);
void amdtp_hid_remove(struct amdtp_cl_data *cli_data);
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type);
void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type);
void amdtp_hid_wakeup(struct hid_device *hid);
#endif
@@ -0,0 +1,564 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD MP2 PCIe communication driver
* Copyright 2020-2021 Advanced Micro Devices, Inc.
*
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
* Sandeep Singh <Sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/devm-helpers.h>
#include <linux/dma-mapping.h>
#include <linux/dmi.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include "amd_sfh_pcie.h"
#include "sfh1_1/amd_sfh_init.h"
#define DRIVER_NAME "pcie_mp2_amd"
#define DRIVER_DESC "AMD(R) PCIe MP2 Communication Driver"
#define ACEL_EN BIT(0)
#define GYRO_EN BIT(1)
#define MAGNO_EN BIT(2)
#define OP_EN BIT(15)
#define HPD_EN BIT(16)
#define ALS_EN BIT(19)
#define ACS_EN BIT(22)
static int sensor_mask_override = -1;
module_param_named(sensor_mask, sensor_mask_override, int, 0444);
MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
static bool intr_disable = true;
static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
{
union cmd_response cmd_resp;
/* Get response with status within a max of 10 seconds timeout */
if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
(cmd_resp.response_v2.response == sensor_sts &&
cmd_resp.response_v2.status == 0 && (sid == 0xff ||
cmd_resp.response_v2.sensor_id == sid)), 500, 10000000))
return cmd_resp.response_v2.response;
return SENSOR_DISABLED;
}
static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{
union sfh_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR;
cmd_base.cmd_v2.intr_disable = intr_disable;
cmd_base.cmd_v2.period = info.period;
cmd_base.cmd_v2.sensor_id = info.sensor_idx;
cmd_base.cmd_v2.length = 16;
if (info.sensor_idx == als_idx)
cmd_base.cmd_v2.mem_type = USE_C2P_REG;
writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG1);
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
}
static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx)
{
union sfh_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR;
cmd_base.cmd_v2.intr_disable = intr_disable;
cmd_base.cmd_v2.period = 0;
cmd_base.cmd_v2.sensor_id = sensor_idx;
cmd_base.cmd_v2.length = 16;
writeq(0x0, privdata->mmio + AMD_C2P_MSG1);
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
}
static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata)
{
union sfh_cmd_base cmd_base;
cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS;
cmd_base.cmd_v2.intr_disable = intr_disable;
cmd_base.cmd_v2.period = 0;
cmd_base.cmd_v2.sensor_id = 0;
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
}
void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata)
{
if (readl(privdata->mmio + amd_get_p2c_val(privdata, 4))) {
writel(0, privdata->mmio + amd_get_p2c_val(privdata, 4));
writel(0xf, privdata->mmio + amd_get_p2c_val(privdata, 5));
}
}
void amd_sfh_clear_intr(struct amd_mp2_dev *privdata)
{
if (privdata->mp2_ops->clear_intr)
privdata->mp2_ops->clear_intr(privdata);
}
static irqreturn_t amd_sfh_irq_handler(int irq, void *data)
{
amd_sfh_clear_intr(data);
return IRQ_HANDLED;
}
int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata)
{
int rc;
pcim_intx(privdata->pdev, true);
rc = devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq,
amd_sfh_irq_handler, 0, DRIVER_NAME, privdata);
if (rc) {
dev_err(&privdata->pdev->dev, "failed to request irq %d err=%d\n",
privdata->pdev->irq, rc);
return rc;
}
return 0;
}
static int amd_sfh_dis_sts_v2(struct amd_mp2_dev *privdata)
{
return (readl(privdata->mmio + AMD_P2C_MSG(1)) &
SENSOR_DISCOVERY_STATUS_MASK) >> SENSOR_DISCOVERY_STATUS_SHIFT;
}
static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{
union sfh_cmd_param cmd_param;
union sfh_cmd_base cmd_base;
/* fill up command register */
memset(&cmd_base, 0, sizeof(cmd_base));
cmd_base.s.cmd_id = ENABLE_SENSOR;
cmd_base.s.period = info.period;
cmd_base.s.sensor_id = info.sensor_idx;
/* fill up command param register */
memset(&cmd_param, 0, sizeof(cmd_param));
cmd_param.s.buf_layout = 1;
cmd_param.s.buf_length = 16;
writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2);
writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1);
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
}
static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
{
union sfh_cmd_base cmd_base;
/* fill up command register */
memset(&cmd_base, 0, sizeof(cmd_base));
cmd_base.s.cmd_id = DISABLE_SENSOR;
cmd_base.s.period = 0;
cmd_base.s.sensor_id = sensor_idx;
writeq(0x0, privdata->mmio + AMD_C2P_MSG2);
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
}
static void amd_stop_all_sensors(struct amd_mp2_dev *privdata)
{
union sfh_cmd_base cmd_base;
/* fill up command register */
memset(&cmd_base, 0, sizeof(cmd_base));
cmd_base.s.cmd_id = STOP_ALL_SENSORS;
cmd_base.s.period = 0;
cmd_base.s.sensor_id = 0;
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
}
static const struct dmi_system_id dmi_sensor_mask_overrides[] = {
{
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"),
},
.driver_data = (void *)(ACEL_EN | MAGNO_EN),
},
{
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"),
},
.driver_data = (void *)(ACEL_EN | MAGNO_EN),
},
{ }
};
int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
{
int activestatus, num_of_sensors = 0;
const struct dmi_system_id *dmi_id;
if (sensor_mask_override == -1) {
dmi_id = dmi_first_match(dmi_sensor_mask_overrides);
if (dmi_id)
sensor_mask_override = (long)dmi_id->driver_data;
}
if (sensor_mask_override >= 0) {
activestatus = sensor_mask_override;
} else {
activestatus = privdata->mp2_acs >> 4;
}
if (ACEL_EN & activestatus)
sensor_id[num_of_sensors++] = accel_idx;
if (GYRO_EN & activestatus)
sensor_id[num_of_sensors++] = gyro_idx;
if (MAGNO_EN & activestatus)
sensor_id[num_of_sensors++] = mag_idx;
if (OP_EN & activestatus)
sensor_id[num_of_sensors++] = op_idx;
if (ALS_EN & activestatus)
sensor_id[num_of_sensors++] = als_idx;
if (HPD_EN & activestatus)
sensor_id[num_of_sensors++] = HPD_IDX;
if (ACS_EN & activestatus)
sensor_id[num_of_sensors++] = ACS_IDX;
return num_of_sensors;
}
static void amd_mp2_pci_remove(void *privdata)
{
struct amd_mp2_dev *mp2 = privdata;
amd_sfh_hid_client_deinit(privdata);
mp2->mp2_ops->stop_all(mp2);
pcim_intx(mp2->pdev, false);
amd_sfh_clear_intr(mp2);
}
static struct amd_mp2_ops amd_sfh_ops_v2 = {
.start = amd_start_sensor_v2,
.stop = amd_stop_sensor_v2,
.stop_all = amd_stop_all_sensor_v2,
.response = amd_sfh_wait_response_v2,
.clear_intr = amd_sfh_clear_intr_v2,
.init_intr = amd_sfh_irq_init_v2,
.discovery_status = amd_sfh_dis_sts_v2,
.remove = amd_mp2_pci_remove,
};
static struct amd_mp2_ops amd_sfh_ops = {
.start = amd_start_sensor,
.stop = amd_stop_sensor,
.stop_all = amd_stop_all_sensors,
.remove = amd_mp2_pci_remove,
};
static void mp2_select_ops(struct amd_mp2_dev *privdata)
{
u8 acs;
privdata->mp2_acs = readl(privdata->mmio + AMD_P2C_MSG3);
acs = privdata->mp2_acs & GENMASK(3, 0);
switch (acs) {
case V2_STATUS:
privdata->mp2_ops = &amd_sfh_ops_v2;
break;
default:
privdata->mp2_ops = &amd_sfh_ops;
break;
}
}
int amd_sfh_irq_init(struct amd_mp2_dev *privdata)
{
if (privdata->mp2_ops->init_intr)
return privdata->mp2_ops->init_intr(privdata);
return 0;
}
static int mp2_disable_intr(const struct dmi_system_id *id)
{
intr_disable = false;
return 0;
}
static const struct dmi_system_id dmi_sfh_table[] = {
{
/*
* https://bugzilla.kernel.org/show_bug.cgi?id=218104
*/
.callback = mp2_disable_intr,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"),
},
},
{}
};
static const struct dmi_system_id dmi_nodevs[] = {
{
/*
* Google Chromebooks use Chrome OS Embedded Controller Sensor
* Hub instead of Sensor Hub Fusion and leaves MP2
* uninitialized, which disables all functionalities, even
* including the registers necessary for feature detections.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
},
},
{ }
};
static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", str_enabled_disabled(mp2->dev_en.is_hpd_enabled));
}
static ssize_t hpd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
bool enabled;
int ret;
ret = kstrtobool(buf, &enabled);
if (ret)
return ret;
mp2->sfh1_1_ops->toggle_hpd(mp2, enabled);
return count;
}
static DEVICE_ATTR_RW(hpd);
static umode_t sfh_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
if (!mp2->sfh1_1_ops || !mp2->dev_en.is_hpd_present)
return 0;
return attr->mode;
}
static struct attribute *sfh_attrs[] = {
&dev_attr_hpd.attr,
NULL,
};
static struct attribute_group sfh_attr_group = {
.attrs = sfh_attrs,
.is_visible = sfh_attr_is_visible,
};
static const struct attribute_group *amd_sfh_groups[] = {
&sfh_attr_group,
NULL,
};
static void sfh1_1_init_work(struct work_struct *work)
{
struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work);
int rc;
rc = mp2->sfh1_1_ops->init(mp2);
if (rc)
return;
amd_sfh_clear_intr(mp2);
mp2->init_done = 1;
rc = sysfs_update_group(&mp2->pdev->dev.kobj, &sfh_attr_group);
if (rc)
dev_warn(&mp2->pdev->dev, "failed to update sysfs group\n");
}
static void sfh_init_work(struct work_struct *work)
{
struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work);
struct pci_dev *pdev = mp2->pdev;
int rc;
rc = amd_sfh_hid_client_init(mp2);
if (rc) {
amd_sfh_clear_intr(mp2);
if (rc != -EOPNOTSUPP)
dev_err(&pdev->dev, "amd_sfh_hid_client_init failed err %d\n", rc);
return;
}
amd_sfh_clear_intr(mp2);
mp2->init_done = 1;
}
static void amd_sfh_remove(struct pci_dev *pdev)
{
struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
flush_work(&mp2->work);
if (mp2->init_done)
mp2->mp2_ops->remove(mp2);
}
static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct amd_mp2_dev *privdata;
int rc;
if (dmi_first_match(dmi_nodevs))
return -ENODEV;
dmi_check_system(dmi_sfh_table);
privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL);
if (!privdata)
return -ENOMEM;
privdata->pdev = pdev;
dev_set_drvdata(&pdev->dev, privdata);
rc = pcim_enable_device(pdev);
if (rc)
return rc;
rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME);
if (rc)
return rc;
privdata->mmio = pcim_iomap_table(pdev)[2];
pci_set_master(pdev);
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc) {
dev_err(&pdev->dev, "failed to set DMA mask\n");
return rc;
}
privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL);
if (!privdata->cl_data)
return -ENOMEM;
rc = devm_mutex_init(&pdev->dev, &privdata->lock);
if (rc)
return rc;
privdata->sfh1_1_ops = (const struct amd_sfh1_1_ops *)id->driver_data;
if (privdata->sfh1_1_ops) {
if (boot_cpu_data.x86 >= 0x1A)
privdata->rver = 1;
rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh1_1_init_work);
if (rc)
return rc;
schedule_work(&privdata->work);
return 0;
}
mp2_select_ops(privdata);
rc = amd_sfh_irq_init(privdata);
if (rc) {
dev_err(&pdev->dev, "amd_sfh_irq_init failed\n");
return rc;
}
rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh_init_work);
if (rc) {
amd_sfh_clear_intr(privdata);
return rc;
}
schedule_work(&privdata->work);
return 0;
}
static void amd_sfh_shutdown(struct pci_dev *pdev)
{
struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
if (mp2) {
flush_work(&mp2->work);
if (mp2->init_done)
mp2->mp2_ops->stop_all(mp2);
}
}
static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
{
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
flush_work(&mp2->work);
if (mp2->init_done)
mp2->mp2_ops->resume(mp2);
return 0;
}
static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
{
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
flush_work(&mp2->work);
if (mp2->init_done)
mp2->mp2_ops->suspend(mp2);
return 0;
}
static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend,
amd_mp2_pci_resume);
static const struct pci_device_id amd_mp2_pci_tbl[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2_1_1),
.driver_data = (kernel_ulong_t)&sfh1_1_ops },
{ }
};
MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
static struct pci_driver amd_mp2_pci_driver = {
.name = DRIVER_NAME,
.id_table = amd_mp2_pci_tbl,
.probe = amd_mp2_pci_probe,
.driver.pm = &amd_mp2_pm_ops,
.shutdown = amd_sfh_shutdown,
.remove = amd_sfh_remove,
.dev_groups = amd_sfh_groups,
};
module_pci_driver(amd_mp2_pci_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>");
MODULE_AUTHOR("Basavaraj Natikar <Basavaraj.Natikar@amd.com>");
@@ -0,0 +1,108 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AMD MP2 PCIe communication driver
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
* Sandeep Singh <Sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef PCIE_MP2_AMD_H
#define PCIE_MP2_AMD_H
#include "amd_sfh_common.h"
/* MP2 C2P Message Registers */
#define AMD_C2P_MSG0 0x10500
#define AMD_C2P_MSG1 0x10504
#define AMD_C2P_MSG2 0x10508
/* MP2 P2C Message Registers */
#define AMD_P2C_MSG3 0x1068C /* Supported Sensors info */
#define V2_STATUS 0x2
#define HPD_IDX 16
#define ACS_IDX 22
#define SENSOR_DISCOVERY_STATUS_MASK GENMASK(5, 3)
#define SENSOR_DISCOVERY_STATUS_SHIFT 3
/* SFH Command register */
union sfh_cmd_base {
u32 ul;
struct {
u32 cmd_id : 8;
u32 sensor_id : 8;
u32 period : 16;
} s;
struct {
u32 cmd_id : 4;
u32 intr_disable : 1;
u32 rsvd1 : 3;
u32 length : 7;
u32 mem_type : 1;
u32 sensor_id : 8;
u32 period : 8;
} cmd_v2;
};
union cmd_response {
u32 resp;
struct {
u32 status : 2;
u32 out_in_c2p : 1;
u32 rsvd1 : 1;
u32 response : 4;
u32 sub_cmd : 8;
u32 sensor_id : 6;
u32 rsvd2 : 10;
} response_v2;
};
union sfh_cmd_param {
u32 ul;
struct {
u32 buf_layout : 2;
u32 buf_length : 6;
u32 rsvd : 24;
} s;
};
struct sfh_cmd_reg {
union sfh_cmd_base cmd_base;
union sfh_cmd_param cmd_param;
phys_addr_t phys_addr;
};
enum sensor_idx {
accel_idx = 0,
gyro_idx = 1,
mag_idx = 2,
op_idx = 15,
als_idx = 19
};
enum mem_use_type {
USE_DRAM,
USE_C2P_REG,
};
struct hpd_status {
union {
struct {
u32 object_distance : 16;
u32 probablity : 8;
u32 human_presence_actual : 4;
u32 human_presence_report : 4;
} shpd;
u32 val;
};
};
int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id);
int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata);
int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata);
void amd_sfh_set_desc_ops(struct amd_mp2_ops *mp2_ops);
#endif
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD SFH Report Descriptor generator
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
* Sandeep Singh <sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include "amd_sfh_pcie.h"
#include "amd_sfh_hid_desc.h"
#include "amd_sfh_hid_report_desc.h"
#include "amd_sfh_hid.h"
#define AMD_SFH_FW_MULTIPLIER (1000)
#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x41
#define HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM 0x51
#define HID_DEFAULT_REPORT_INTERVAL 0x50
#define HID_DEFAULT_MIN_VALUE 0X7F
#define HID_DEFAULT_MAX_VALUE 0x80
#define HID_DEFAULT_SENSITIVITY 0x7F
#define HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM 0x01
/* state enums */
#define HID_USAGE_SENSOR_STATE_READY_ENUM 0x02
#define HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM 0x05
#define HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM 0x04
#define ILLUMINANCE_MASK GENMASK(14, 0)
static int get_report_descriptor(int sensor_idx, u8 *rep_desc)
{
switch (sensor_idx) {
case accel_idx: /* accel */
memset(rep_desc, 0, sizeof(accel3_report_descriptor));
memcpy(rep_desc, accel3_report_descriptor,
sizeof(accel3_report_descriptor));
break;
case gyro_idx: /* gyro */
memset(rep_desc, 0, sizeof(gyro3_report_descriptor));
memcpy(rep_desc, gyro3_report_descriptor,
sizeof(gyro3_report_descriptor));
break;
case mag_idx: /* Magnetometer */
memset(rep_desc, 0, sizeof(comp3_report_descriptor));
memcpy(rep_desc, comp3_report_descriptor,
sizeof(comp3_report_descriptor));
break;
case als_idx: /* ambient light sensor */
case ACS_IDX: /* ambient color sensor */
memset(rep_desc, 0, sizeof(als_report_descriptor));
memcpy(rep_desc, als_report_descriptor,
sizeof(als_report_descriptor));
break;
case HPD_IDX: /* HPD sensor */
memset(rep_desc, 0, sizeof(hpd_report_descriptor));
memcpy(rep_desc, hpd_report_descriptor,
sizeof(hpd_report_descriptor));
break;
default:
break;
}
return 0;
}
static u32 get_descr_sz(int sensor_idx, int descriptor_name)
{
switch (sensor_idx) {
case accel_idx:
switch (descriptor_name) {
case descr_size:
return sizeof(accel3_report_descriptor);
case input_size:
return sizeof(struct accel3_input_report);
case feature_size:
return sizeof(struct accel3_feature_report);
}
break;
case gyro_idx:
switch (descriptor_name) {
case descr_size:
return sizeof(gyro3_report_descriptor);
case input_size:
return sizeof(struct gyro_input_report);
case feature_size:
return sizeof(struct gyro_feature_report);
}
break;
case mag_idx:
switch (descriptor_name) {
case descr_size:
return sizeof(comp3_report_descriptor);
case input_size:
return sizeof(struct magno_input_report);
case feature_size:
return sizeof(struct magno_feature_report);
}
break;
case als_idx:
case ACS_IDX: /* ambient color sensor */
switch (descriptor_name) {
case descr_size:
return sizeof(als_report_descriptor);
case input_size:
return sizeof(struct als_input_report);
case feature_size:
return sizeof(struct als_feature_report);
}
break;
case HPD_IDX:
switch (descriptor_name) {
case descr_size:
return sizeof(hpd_report_descriptor);
case input_size:
return sizeof(struct hpd_input_report);
case feature_size:
return sizeof(struct hpd_feature_report);
}
break;
default:
break;
}
return 0;
}
static void get_common_features(struct common_feature_property *common, int report_id)
{
common->report_id = report_id;
common->connection_type = HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM;
common->report_state = HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
common->power_state = HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
common->sensor_state = HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM;
common->report_interval = HID_DEFAULT_REPORT_INTERVAL;
}
static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report)
{
struct accel3_feature_report acc_feature;
struct gyro_feature_report gyro_feature;
struct magno_feature_report magno_feature;
struct hpd_feature_report hpd_feature;
struct als_feature_report als_feature;
u8 report_size = 0;
if (!feature_report)
return report_size;
switch (sensor_idx) {
case accel_idx: /* accel */
get_common_features(&acc_feature.common_property, report_id);
acc_feature.accel_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
acc_feature.accel_sensitivity_min = HID_DEFAULT_MIN_VALUE;
acc_feature.accel_sensitivity_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &acc_feature, sizeof(acc_feature));
report_size = sizeof(acc_feature);
break;
case gyro_idx: /* gyro */
get_common_features(&gyro_feature.common_property, report_id);
gyro_feature.gyro_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
gyro_feature.gyro_sensitivity_min = HID_DEFAULT_MIN_VALUE;
gyro_feature.gyro_sensitivity_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &gyro_feature, sizeof(gyro_feature));
report_size = sizeof(gyro_feature);
break;
case mag_idx: /* Magnetometer */
get_common_features(&magno_feature.common_property, report_id);
magno_feature.magno_headingchange_sensitivity = HID_DEFAULT_SENSITIVITY;
magno_feature.heading_min = HID_DEFAULT_MIN_VALUE;
magno_feature.heading_max = HID_DEFAULT_MAX_VALUE;
magno_feature.flux_change_sensitivity = HID_DEFAULT_MIN_VALUE;
magno_feature.flux_min = HID_DEFAULT_MIN_VALUE;
magno_feature.flux_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &magno_feature, sizeof(magno_feature));
report_size = sizeof(magno_feature);
break;
case als_idx: /* ambient light sensor */
case ACS_IDX: /* ambient color sensor */
get_common_features(&als_feature.common_property, report_id);
als_feature.als_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
als_feature.als_sensitivity_min = HID_DEFAULT_MIN_VALUE;
als_feature.als_sensitivity_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &als_feature, sizeof(als_feature));
report_size = sizeof(als_feature);
break;
case HPD_IDX: /* human presence detection sensor */
get_common_features(&hpd_feature.common_property, report_id);
memcpy(feature_report, &hpd_feature, sizeof(hpd_feature));
report_size = sizeof(hpd_feature);
break;
default:
break;
}
return report_size;
}
static void get_common_inputs(struct common_input_property *common, int report_id)
{
common->report_id = report_id;
common->sensor_state = HID_USAGE_SENSOR_STATE_READY_ENUM;
common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM;
}
static u8 get_input_report(u8 current_index, int sensor_idx, int report_id,
struct amd_input_data *in_data)
{
struct amd_mp2_dev *privdata = container_of(in_data, struct amd_mp2_dev, in_data);
u32 *sensor_virt_addr = in_data->sensor_virt_addr[current_index];
u8 *input_report = in_data->input_report[current_index];
u8 supported_input = privdata->mp2_acs & GENMASK(3, 0);
struct magno_input_report magno_input;
struct accel3_input_report acc_input;
struct gyro_input_report gyro_input;
struct hpd_input_report hpd_input;
struct als_input_report als_input;
struct hpd_status hpdstatus;
u8 report_size = 0;
if (!sensor_virt_addr || !input_report)
return report_size;
switch (sensor_idx) {
case accel_idx: /* accel */
get_common_inputs(&acc_input.common_property, report_id);
acc_input.in_accel_x_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
acc_input.in_accel_y_value = (int)sensor_virt_addr[1] / AMD_SFH_FW_MULTIPLIER;
acc_input.in_accel_z_value = (int)sensor_virt_addr[2] / AMD_SFH_FW_MULTIPLIER;
memcpy(input_report, &acc_input, sizeof(acc_input));
report_size = sizeof(acc_input);
break;
case gyro_idx: /* gyro */
get_common_inputs(&gyro_input.common_property, report_id);
gyro_input.in_angel_x_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
gyro_input.in_angel_y_value = (int)sensor_virt_addr[1] / AMD_SFH_FW_MULTIPLIER;
gyro_input.in_angel_z_value = (int)sensor_virt_addr[2] / AMD_SFH_FW_MULTIPLIER;
memcpy(input_report, &gyro_input, sizeof(gyro_input));
report_size = sizeof(gyro_input);
break;
case mag_idx: /* Magnetometer */
get_common_inputs(&magno_input.common_property, report_id);
magno_input.in_magno_x = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
magno_input.in_magno_y = (int)sensor_virt_addr[1] / AMD_SFH_FW_MULTIPLIER;
magno_input.in_magno_z = (int)sensor_virt_addr[2] / AMD_SFH_FW_MULTIPLIER;
magno_input.in_magno_accuracy = (u16)sensor_virt_addr[3] / AMD_SFH_FW_MULTIPLIER;
memcpy(input_report, &magno_input, sizeof(magno_input));
report_size = sizeof(magno_input);
break;
case als_idx: /* Als */
case ACS_IDX: /* ambient color sensor */
get_common_inputs(&als_input.common_property, report_id);
/* For ALS ,V2 Platforms uses C2P_MSG5 register instead of DRAM access method */
if (supported_input == V2_STATUS)
als_input.illuminance_value =
readl(privdata->mmio + AMD_C2P_MSG(5)) & ILLUMINANCE_MASK;
else
als_input.illuminance_value =
(int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER;
if (sensor_idx == ACS_IDX) {
als_input.light_color_temp = sensor_virt_addr[1];
als_input.chromaticity_x_value = sensor_virt_addr[2];
als_input.chromaticity_y_value = sensor_virt_addr[3];
}
report_size = sizeof(als_input);
memcpy(input_report, &als_input, sizeof(als_input));
break;
case HPD_IDX: /* hpd */
get_common_inputs(&hpd_input.common_property, report_id);
hpdstatus.val = readl(privdata->mmio + AMD_C2P_MSG(4));
hpd_input.human_presence = hpdstatus.shpd.human_presence_actual;
report_size = sizeof(hpd_input);
memcpy(input_report, &hpd_input, sizeof(hpd_input));
break;
default:
break;
}
return report_size;
}
void amd_sfh_set_desc_ops(struct amd_mp2_ops *mp2_ops)
{
mp2_ops->get_rep_desc = get_report_descriptor;
mp2_ops->get_feat_rep = get_feature_report;
mp2_ops->get_in_rep = get_input_report;
mp2_ops->get_desc_sz = get_descr_sz;
}
@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* HID report descriptors, structures and routines
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
* Sandeep Singh <Sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMD_SFH_HID_DESCRIPTOR_H
#define AMD_SFH_HID_DESCRIPTOR_H
enum desc_type {
/* Report descriptor name */
descr_size = 1,
input_size,
feature_size,
};
struct common_feature_property {
/* common properties */
u8 report_id;
u8 connection_type;
u8 report_state;
u8 power_state;
u8 sensor_state;
u32 report_interval;
} __packed;
struct common_input_property {
/* common properties */
u8 report_id;
u8 sensor_state;
u8 event_type;
} __packed;
struct accel3_feature_report {
struct common_feature_property common_property;
/* properties specific to this sensor */
u16 accel_change_sesnitivity;
s16 accel_sensitivity_max;
s16 accel_sensitivity_min;
} __packed;
struct accel3_input_report {
struct common_input_property common_property;
/* values specific to this sensor */
int in_accel_x_value;
int in_accel_y_value;
int in_accel_z_value;
/* include if required to support the "shake" event */
u8 in_accel_shake_detection;
} __packed;
struct gyro_feature_report {
struct common_feature_property common_property;
/* properties specific to this sensor */
u16 gyro_change_sesnitivity;
s16 gyro_sensitivity_max;
s16 gyro_sensitivity_min;
} __packed;
struct gyro_input_report {
struct common_input_property common_property;
/* values specific to this sensor */
int in_angel_x_value;
int in_angel_y_value;
int in_angel_z_value;
} __packed;
struct magno_feature_report {
struct common_feature_property common_property;
/*properties specific to this sensor */
u16 magno_headingchange_sensitivity;
s16 heading_min;
s16 heading_max;
u16 flux_change_sensitivity;
s16 flux_min;
s16 flux_max;
} __packed;
struct magno_input_report {
struct common_input_property common_property;
int in_magno_x;
int in_magno_y;
int in_magno_z;
int in_magno_accuracy;
} __packed;
struct als_feature_report {
struct common_feature_property common_property;
/* properties specific to this sensor */
u16 als_change_sesnitivity;
s16 als_sensitivity_max;
s16 als_sensitivity_min;
} __packed;
struct als_input_report {
struct common_input_property common_property;
/* values specific to this sensor */
int illuminance_value;
int light_color_temp;
int chromaticity_x_value;
int chromaticity_y_value;
} __packed;
struct hpd_feature_report {
struct common_feature_property common_property;
} __packed;
struct hpd_input_report {
struct common_input_property common_property;
/* values specific to human presence sensor */
u8 human_presence;
} __packed;
#endif
@@ -0,0 +1,779 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* HID descriptor stuructures
* Copyright 2020-2021 Advanced Micro Devices, Inc.
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
* Sandeep Singh <Sandeep.singh@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMD_SFH_HID_REPORT_DESCRIPTOR_H
#define AMD_SFH_HID_REPORT_DESCRIPTOR_H
// Accelerometer 3D Sensor
static const u8 accel3_report_descriptor[] = {
0x05, 0x20, /* Usage page */
0x09, 0x73, /* Motion type Accel 3D */
0xA1, 0x00, /* HID Collection (Physical) */
//feature reports(xmit/receive)
0x85, 1, /* HID Report ID */
0x05, 0x20, /* HID usage page sensor */
0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
0x15, 0, /* HID logical MIN_8(0) */
0x25, 2, /* HID logical MAX_8(2) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection (logical) */
0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel*/
0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x40, 0x08, /* Sensor property report state no events sel */
0x0A, 0x41, 0x08, /* Sensor property report state all events sel */
0x0A, 0x42, 0x08, /* Sensor property report state threshold events sel */
0x0A, 0x43, 0x08, /* Sensor property report state no events wake sel */
0x0A, 0x44, 0x08, /* Sensor property report state all events wake sel */
0x0A, 0x45, 0x08, /* Sensor property report state threshold events wake sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x19, 0x03, /* HID usage sensor property power state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x50, 0x08, /* Sensor property power state undefined sel */
0x0A, 0x51, 0x08, /* Sensor property power state D0 full power sel */
0x0A, 0x52, 0x08, /* Sensor property power state D1 low power sel */
0x0A, 0x53, 0x08, /* Sensor property power state D2 standby with wake sel */
0x0A, 0x54, 0x08, /* Sensor property power state D3 sleep with wake sel */
0x0A, 0x55, 0x08, /* Sensor property power state D4 power off sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
0x15, 0, /* HID logical Min_8(0) */
0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count(1) */
0x55, 0, /* HID unit exponent(0) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x52, 0x14, /* Sensor data motion accel and mod change sensitivity ABS) */
0x15, 0, /* HID logical Min_8(0) */
0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x52, 0x24, /* HID usage sensor data (motion accel and mod max) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x52, 0x34, /* HID usage sensor data (motion accel and mod min) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
//input report (transmit)
0x05, 0x20, /* HID usage page sensors */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x02, 0x02, /* HID usage sensor event */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x53, 0x04, /* HID usage sensor data motion Acceleration X axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xff, 0XFF, 0XFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x54, 0x04, /* HID usage sensor data motion Acceleration Y axis */
0x17, 0X00, 0X00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0XFF, 0XFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x55, 0x04, /* HID usage sensor data motion Acceleration Z axis */
0x17, 0X00, 0X00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0XFF, 0XFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x51, 0x04, /* HID usage sensor data motion state */
0x15, 0, /* HID logical Min_8(0) False = Still*/
0x25, 1, /* HID logical Min_8(1) True = In motion */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0xC0 /* HID end collection */
};
static const u8 gyro3_report_descriptor[] = {
0x05, 0x20, /* Usage page */
0x09, 0x76, /* Motion type Gyro3D */
0xA1, 0x00, /* HID Collection (Physical) */
0x85, 2, /* HID Report ID */
0x05, 0x20, /* HID usage page sensor */
0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
0x15, 0, /* HID logical MIN_8(0) */
0x25, 2, /* HID logical MAX_8(2) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection (logical) */
0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel */
0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x40, 0x08, /* Sensor reporting state no events sel */
0x0A, 0x41, 0x08, /* Sensor reporting state all events sel */
0x0A, 0x42, 0x08, /* Sensor reporting state threshold events sel */
0x0A, 0x43, 0x08, /* Sensor reporting state no events wake sel */
0x0A, 0x44, 0x08, /* Sensor reporting state all events wake sel */
0x0A, 0x45, 0x08, /* Sensor reporting state threshold events wake sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x19, 0x03, /* HID usage sensor property power state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x50, 0x08, /* Sensor power state undefined sel */
0x0A, 0x51, 0x08, /* Sensor power state D0 full power sel */
0x0A, 0x52, 0x08, /* Sensor power state D1 low power sel */
0x0A, 0x53, 0x08, /* Sensor power state D2 standby with wake sel */
0x0A, 0x54, 0x08, /* Sensor power state D3 sleep with wake sel */
0x0A, 0x55, 0x08, /* Sensor power state D4 power off sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
0x15, 0, /* HID logical Min_8(0) */
0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count(1) */
0x55, 0, /* HID unit exponent(0) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x56, 0x14, /* Angular velocity and mod change sensitivity ABS)*/
0x15, 0, /* HID logical Min_8(0) */
0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x56, 0x24, /* Sensor data (motion angular velocity and mod max) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x56, 0x34, /* HID usage sensor data (motion accel and mod min) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
//Input reports(transmit)
0x05, 0x20, /* HID usage page sensors */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x02, 0x02, /* HID usage sensor event */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x57, 0x04, /* Sensor data motion Angular velocity X axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x58, 0x04, /* Sensor data motion Angular velocity Y axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x59, 0x04, /* Sensor data motion Angular velocity Z axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
};
static const u8 comp3_report_descriptor[] = {
0x05, 0x20, /* Usage page */
0x09, 0x83, /* Motion type Orientation compass 3D */
0xA1, 0x00, /* HID Collection (Physical) */
0x85, 3, /* HID Report ID */
0x05, 0x20, /* HID usage page sensor */
0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
0x15, 0, /* HID logical MIN_8(0) */
0x25, 2, /* HID logical MAX_8(2) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection (logical) */
0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel */
0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x40, 0x08, /* Sensor reporting state no events sel */
0x0A, 0x41, 0x08, /* Sensor reporting state all events sel */
0x0A, 0x42, 0x08, /* Sensor reporting state threshold events sel */
0x0A, 0x43, 0x08, /* Sensor reporting state no events wake sel */
0x0A, 0x44, 0x08, /* Sensor reporting state all events wake sel */
0x0A, 0x45, 0x08, /* Sensor reporting state threshold events wake sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x19, 0x03, /* HID usage sensor property power state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x50, 0x08, /* Sensor power state undefined sel */
0x0A, 0x51, 0x08, /* Sensor power state D0 full power sel */
0x0A, 0x52, 0x08, /* Sensor power state D1 low power sel */
0x0A, 0x53, 0x08, /* Sensor power state D2 standby with wake sel */
0x0A, 0x54, 0x08, /* Sensor power state D3 sleep with wake sel */
0x0A, 0x55, 0x08, /* Sensor power state D4 power off sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
0x15, 0, /* HID logical Min_8(0) */
0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count(1) */
0x55, 0, /* HID unit exponent(0) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x71, 0x14, /* Orientation and mod change sensitivity ABS)*/
0x15, 0, /* HID logical Min_8(0) */
0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x71, 0x24, /* Sensor data (motion orientation and mod max) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x71, 0x34, /* Sensor data (motion orientation and mod min) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x84, 0x14, /* Maganetic flux and change sensitivity ABS) */
0x15, 0, /* HID logical Min_8(0) */
0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x84, 0x24, /* Maganetic flux and mod change sensitivity Max) */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0x84, 0x34, /* Maganetic flux and mod change sensitivity Min */
0x16, 0x01, 0x80, /* HID logical Min_16(0x01,0x80) */
0x26, 0xFF, 0x7F, /* HID logical Max_16(0xFF,0x7F) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
//Input reports(transmit)
0x05, 0x20, /* HID usage page sensors */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x02, 0x02, /* HID usage sensor event */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x85, 0x04, /* Sensor data orientation magnetic flux X axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0D, /* HID unit exponent(0x0D) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x86, 0x04, /* Sensor data orientation magnetic flux Y axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0D, /* HID unit exponent(0x0D) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x87, 0x04, /* Sensor data orientation magnetic flux Z axis */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0x55, 0x0D, /* HID unit exponent(0x0D) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0x88, 0x04, /* Sensor data orientation magnetometer accuracy */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0xC0 /* HID end collection */
};
static const u8 als_report_descriptor[] = {
0x05, 0x20, /* HID usage page sensor */
0x09, 0x41, /* HID usage sensor type Ambientlight */
0xA1, 0x00, /* HID Collection (Physical) */
//feature reports(xmit/receive)//
0x85, 4, /* HID Report ID */
0x05, 0x20, /* HID usage page sensor */
0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
0x15, 0, /* HID logical MIN_8(0) */
0x25, 2, /* HID logical MAX_8(2) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection (logical) */
0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel */
0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x40, 0x08, /* Sensor reporting state no events sel */
0x0A, 0x41, 0x08, /* Sensor reporting state all events sel */
0x0A, 0x42, 0x08, /* Sensor reporting state threshold events sel */
0x0A, 0x43, 0x08, /* Sensor reporting state no events wake sel */
0x0A, 0x44, 0x08, /* Sensor reporting state all events wake sel */
0x0A, 0x45, 0x08, /* Sensor reporting state threshold events wake sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x19, 0x03, /* HID usage sensor property power state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x50, 0x08, /* Sensor power state undefined sel */
0x0A, 0x51, 0x08, /* Sensor power state D0 full power sel */
0x0A, 0x52, 0x08, /* Sensor power state D1 low power sel */
0x0A, 0x53, 0x08, /* Sensor power state D2 standby with wake sel */
0x0A, 0x54, 0x08, /* Sensor power state D3 sleep with wake sel */
0x0A, 0x55, 0x08, /* Sensor power state D4 power off sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
0x15, 0, /* HID logical Min_8(0) */
0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count(1) */
0x55, 0, /* HID unit exponent(0) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0xD1, 0xE4, /* Light illuminance and sensitivity REL PCT) */
0x15, 0, /* HID logical Min_8(0) */
0x26, 0x10, 0x27, /* HID logical Max_16(0x10,0x27) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0E, /* HID unit exponent(0x0E) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0xD1, 0x24, /* Sensor data (Light illuminance and mod max) */
0x15, 0, /* HID logical Min_8(0) */
0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
0x0A, 0xD1, 0x34, /* Sensor data (Light illuminance and mod min) */
0x15, 0, /* HID logical Min_8(0) */
0x26, 0xFF, 0xFF, /* HID logical Max_16(0xFF,0xFF) */
0x75, 16, /* HID report size(16) */
0x95, 1, /* HID report count(1) */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0xB1, 0x02, /* HID feature (Data_Arr_Abs) */
//Input reports (transmit)
0x05, 0x20, /* HID usage page sensors */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x02, 0x02, /* HID usage sensor event */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0xD1, 0x04, /* HID usage sensor data light illuminance */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x55, 0x0F, /* HID unit exponent(0x0F) */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0xD2, 0x04, /* HID usage sensor data light temperature */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x55, 0x0, /* HID unit exponent(0x0) */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0X81, 0x02, /* HID Input (Data_Arr_Abs) */
0x0A, 0xD4, 0x04, /* HID usage sensor data light chromaticity_x */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x55, 0x0, /* HID unit exponent(0x0) */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count(1) */
0X81, 0x02, /* HID Input (Data_Var_Abs) */
0x0A, 0xD5, 0x04, /* HID usage sensor data light chromaticity_y */
0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */
0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */
0x55, 0x0, /* HID unit exponent(0x0) */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count (1) */
0X81, 0x02, /* HID Input (Data_Var_Abs) */
0xC0 /* HID end collection */
};
/* BIOMETRIC PRESENCE*/
static const u8 hpd_report_descriptor[] = {
0x05, 0x20, /* Usage page */
0x09, 0x11, /* BIOMETRIC PRESENCE */
0xA1, 0x00, /* HID Collection (Physical) */
//feature reports(xmit/receive)
0x85, 5, /* HID Report ID */
0x05, 0x20, /* HID usage page sensor */
0x0A, 0x09, 0x03, /* Sensor property and sensor connection type */
0x15, 0, /* HID logical MIN_8(0) */
0x25, 2, /* HID logical MAX_8(2) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection (logical) */
0x0A, 0x30, 0x08, /* Sensor property connection type intergated sel*/
0x0A, 0x31, 0x08, /* Sensor property connection type attached sel */
0x0A, 0x32, 0x08, /* Sensor property connection type external sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x16, 0x03, /* HID usage sensor property reporting state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x40, 0x08, /* Sensor property report state no events sel */
0x0A, 0x41, 0x08, /* Sensor property report state all events sel */
0x0A, 0x42, 0x08, /* Sensor property report state threshold events sel */
0x0A, 0x43, 0x08, /* Sensor property report state no events wake sel */
0x0A, 0x44, 0x08, /* Sensor property report state all events wake sel */
0x0A, 0x45, 0x08, /* Sensor property report state threshold events wake sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x19, 0x03, /* HID usage sensor property power state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x50, 0x08, /* Sensor property power state undefined sel */
0x0A, 0x51, 0x08, /* Sensor property power state D0 full power sel */
0x0A, 0x52, 0x08, /* Sensor property power state D1 low power sel */
0x0A, 0x53, 0x08, /* Sensor property power state D2 standby with wake sel */
0x0A, 0x54, 0x08, /* Sensor property power state D3 sleep with wake sel */
0x0A, 0x55, 0x08, /* Sensor property power state D4 power off sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count(1) */
0xA1, 0x02, /* HID collection(logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0xB1, 0x00, /* HID feature (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x0E, 0x03, /* HID usage sensor property report interval */
0x15, 0, /* HID logical Min_8(0) */
0x27, 0xFF, 0xFF, 0xFF, 0xFF, /* HID logical Max_32 */
0x75, 32, /* HID report size(32) */
0x95, 1, /* HID report count(1) */
0x55, 0, /* HID unit exponent(0) */
0xB1, 0x02, /* HID feature (Data_Var_Abs) */
//input report (transmit)
0x05, 0x20, /* HID usage page sensors */
0x0A, 0x01, 0x02, /* HID usage sensor state */
0x15, 0, /* HID logical Min_8(0) */
0x25, 6, /* HID logical Max_8(6) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x00, 0x08, /* HID usage sensor state unknown sel */
0x0A, 0x01, 0x08, /* HID usage sensor state ready sel */
0x0A, 0x02, 0x08, /* HID usage sensor state not available sel */
0x0A, 0x03, 0x08, /* HID usage sensor state no data sel */
0x0A, 0x04, 0x08, /* HID usage sensor state initializing sel */
0x0A, 0x05, 0x08, /* HID usage sensor state access denied sel */
0x0A, 0x06, 0x08, /* HID usage sensor state error sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0x02, 0x02, /* HID usage sensor event */
0x15, 0, /* HID logical Min_8(0) */
0x25, 5, /* HID logical Max_8(5) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0xA1, 0x02, /* HID end collection (logical) */
0x0A, 0x10, 0x08, /* HID usage sensor event unknown sel */
0x0A, 0x11, 0x08, /* HID usage sensor event state changed sel */
0x0A, 0x12, 0x08, /* HID usage sensor event property changed sel */
0x0A, 0x13, 0x08, /* HID usage sensor event data updated sel */
0x0A, 0x14, 0x08, /* HID usage sensor event poll response sel */
0x0A, 0x15, 0x08, /* HID usage sensor event change sensitivity sel */
0X81, 0x00, /* HID Input (Data_Arr_Abs) */
0xC0, /* HID end collection */
0x0A, 0xB1, 0x04, /* HID usage sensor data BIOMETRIC HUMAN PRESENCE */
0x15, 0, /* HID logical Min_8(0) */
0x25, 1, /* HID logical Max_8(1) */
0x75, 8, /* HID report size(8) */
0x95, 1, /* HID report count (1) */
0X81, 0x02, /* HID Input (Data_Var_Abs) */
0xC0 /* HID end collection */
};
#endif
@@ -0,0 +1,327 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD MP2 1.1 descriptor interfaces
*
* Copyright (c) 2022, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/hid-sensor-ids.h>
#include "amd_sfh_interface.h"
#include "../hid_descriptor/amd_sfh_hid_desc.h"
#include "../hid_descriptor/amd_sfh_hid_report_desc.h"
#define SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x41
#define SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM 0x51
#define HID_DEFAULT_REPORT_INTERVAL 0x50
#define HID_DEFAULT_MIN_VALUE 0X7F
#define HID_DEFAULT_MAX_VALUE 0x80
#define HID_DEFAULT_SENSITIVITY 0x7F
#define HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM 0x01
/* state enums */
#define HID_USAGE_SENSOR_STATE_READY_ENUM 0x02
#define HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM 0x05
#define HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM 0x04
static int get_report_desc(int sensor_idx, u8 *rep_desc)
{
switch (sensor_idx) {
case ACCEL_IDX: /* accelerometer */
memset(rep_desc, 0, sizeof(accel3_report_descriptor));
memcpy(rep_desc, accel3_report_descriptor,
sizeof(accel3_report_descriptor));
break;
case GYRO_IDX: /* gyroscope */
memset(rep_desc, 0, sizeof(gyro3_report_descriptor));
memcpy(rep_desc, gyro3_report_descriptor,
sizeof(gyro3_report_descriptor));
break;
case MAG_IDX: /* magnetometer */
memset(rep_desc, 0, sizeof(comp3_report_descriptor));
memcpy(rep_desc, comp3_report_descriptor,
sizeof(comp3_report_descriptor));
break;
case ALS_IDX: /* ambient light sensor */
memset(rep_desc, 0, sizeof(als_report_descriptor));
memcpy(rep_desc, als_report_descriptor,
sizeof(als_report_descriptor));
break;
case HPD_IDX: /* HPD sensor */
memset(rep_desc, 0, sizeof(hpd_report_descriptor));
memcpy(rep_desc, hpd_report_descriptor,
sizeof(hpd_report_descriptor));
break;
}
return 0;
}
static void get_common_features(struct common_feature_property *common, int report_id)
{
common->report_id = report_id;
common->connection_type = HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM;
common->report_state = SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
common->power_state = SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
common->sensor_state = HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM;
common->report_interval = HID_DEFAULT_REPORT_INTERVAL;
}
static u8 get_feature_rep(int sensor_idx, int report_id, u8 *feature_report)
{
struct magno_feature_report magno_feature;
struct accel3_feature_report acc_feature;
struct gyro_feature_report gyro_feature;
struct hpd_feature_report hpd_feature;
struct als_feature_report als_feature;
u8 report_size = 0;
if (!feature_report)
return report_size;
switch (sensor_idx) {
case ACCEL_IDX: /* accelerometer */
get_common_features(&acc_feature.common_property, report_id);
acc_feature.accel_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
acc_feature.accel_sensitivity_min = HID_DEFAULT_MIN_VALUE;
acc_feature.accel_sensitivity_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &acc_feature, sizeof(acc_feature));
report_size = sizeof(acc_feature);
break;
case GYRO_IDX: /* gyroscope */
get_common_features(&gyro_feature.common_property, report_id);
gyro_feature.gyro_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
gyro_feature.gyro_sensitivity_min = HID_DEFAULT_MIN_VALUE;
gyro_feature.gyro_sensitivity_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &gyro_feature, sizeof(gyro_feature));
report_size = sizeof(gyro_feature);
break;
case MAG_IDX: /* magnetometer */
get_common_features(&magno_feature.common_property, report_id);
magno_feature.magno_headingchange_sensitivity = HID_DEFAULT_SENSITIVITY;
magno_feature.heading_min = HID_DEFAULT_MIN_VALUE;
magno_feature.heading_max = HID_DEFAULT_MAX_VALUE;
magno_feature.flux_change_sensitivity = HID_DEFAULT_MIN_VALUE;
magno_feature.flux_min = HID_DEFAULT_MIN_VALUE;
magno_feature.flux_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &magno_feature, sizeof(magno_feature));
report_size = sizeof(magno_feature);
break;
case ALS_IDX: /* ambient light sensor */
get_common_features(&als_feature.common_property, report_id);
als_feature.als_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
als_feature.als_sensitivity_min = HID_DEFAULT_MIN_VALUE;
als_feature.als_sensitivity_max = HID_DEFAULT_MAX_VALUE;
memcpy(feature_report, &als_feature, sizeof(als_feature));
report_size = sizeof(als_feature);
break;
case HPD_IDX: /* human presence detection sensor */
get_common_features(&hpd_feature.common_property, report_id);
memcpy(feature_report, &hpd_feature, sizeof(hpd_feature));
report_size = sizeof(hpd_feature);
break;
}
return report_size;
}
static void get_common_inputs(struct common_input_property *common, int report_id)
{
common->report_id = report_id;
common->sensor_state = HID_USAGE_SENSOR_STATE_READY_ENUM;
common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM;
}
int amd_sfh_float_to_int(u32 flt32_val)
{
int fraction, shift, mantissa, sign, exp, zeropre;
mantissa = flt32_val & GENMASK(22, 0);
sign = (flt32_val & BIT(31)) ? -1 : 1;
exp = (flt32_val & ~BIT(31)) >> 23;
if (!exp && !mantissa)
return 0;
/*
* Calculate the exponent and fraction part of floating
* point representation.
*/
exp -= 127;
if (exp < 0) {
exp = -exp;
if (exp >= BITS_PER_TYPE(u32))
return 0;
zeropre = (((BIT(23) + mantissa) * 100) >> 23) >> exp;
return zeropre >= 50 ? sign : 0;
}
shift = 23 - exp;
if (abs(shift) >= BITS_PER_TYPE(u32))
return 0;
if (shift < 0) {
shift = -shift;
flt32_val = BIT(exp) + (mantissa << shift);
shift = 0;
} else {
flt32_val = BIT(exp) + (mantissa >> shift);
}
fraction = (shift == 0) ? 0 : mantissa & GENMASK(shift - 1, 0);
return (((fraction * 100) >> shift) >= 50) ? sign * (flt32_val + 1) : sign * flt32_val;
}
static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
struct amd_input_data *in_data)
{
struct amd_mp2_dev *mp2 = container_of(in_data, struct amd_mp2_dev, in_data);
u8 *input_report = in_data->input_report[current_index];
struct magno_input_report magno_input;
struct accel3_input_report acc_input;
struct gyro_input_report gyro_input;
struct als_input_report als_input;
struct hpd_input_report hpd_input;
struct sfh_accel_data accel_data;
struct sfh_gyro_data gyro_data;
struct sfh_mag_data mag_data;
struct sfh_als_data als_data;
struct hpd_status hpdstatus;
struct sfh_base_info binfo;
void __iomem *sensoraddr;
u8 report_size = 0;
if (!input_report)
return report_size;
switch (sensor_idx) {
case ACCEL_IDX: /* accelerometer */
sensoraddr = mp2->vsbase + (ACCEL_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&accel_data, sensoraddr, sizeof(struct sfh_accel_data));
get_common_inputs(&acc_input.common_property, report_id);
acc_input.in_accel_x_value = amd_sfh_float_to_int(accel_data.acceldata.x) / 100;
acc_input.in_accel_y_value = amd_sfh_float_to_int(accel_data.acceldata.y) / 100;
acc_input.in_accel_z_value = amd_sfh_float_to_int(accel_data.acceldata.z) / 100;
memcpy(input_report, &acc_input, sizeof(acc_input));
report_size = sizeof(acc_input);
break;
case GYRO_IDX: /* gyroscope */
sensoraddr = mp2->vsbase + (GYRO_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&gyro_data, sensoraddr, sizeof(struct sfh_gyro_data));
get_common_inputs(&gyro_input.common_property, report_id);
gyro_input.in_angel_x_value = amd_sfh_float_to_int(gyro_data.gyrodata.x) / 1000;
gyro_input.in_angel_y_value = amd_sfh_float_to_int(gyro_data.gyrodata.y) / 1000;
gyro_input.in_angel_z_value = amd_sfh_float_to_int(gyro_data.gyrodata.z) / 1000;
memcpy(input_report, &gyro_input, sizeof(gyro_input));
report_size = sizeof(gyro_input);
break;
case MAG_IDX: /* magnetometer */
sensoraddr = mp2->vsbase + (MAG_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&mag_data, sensoraddr, sizeof(struct sfh_mag_data));
get_common_inputs(&magno_input.common_property, report_id);
magno_input.in_magno_x = amd_sfh_float_to_int(mag_data.magdata.x) / 100;
magno_input.in_magno_y = amd_sfh_float_to_int(mag_data.magdata.y) / 100;
magno_input.in_magno_z = amd_sfh_float_to_int(mag_data.magdata.z) / 100;
magno_input.in_magno_accuracy = mag_data.accuracy / 100;
memcpy(input_report, &magno_input, sizeof(magno_input));
report_size = sizeof(magno_input);
break;
case ALS_IDX:
sensoraddr = mp2->vsbase + (ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
get_common_inputs(&als_input.common_property, report_id);
als_input.illuminance_value = amd_sfh_float_to_int(als_data.lux);
memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info));
if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) {
als_input.light_color_temp = als_data.light_color_temp;
als_input.chromaticity_x_value =
amd_sfh_float_to_int(als_data.chromaticity_x);
als_input.chromaticity_y_value =
amd_sfh_float_to_int(als_data.chromaticity_y);
}
report_size = sizeof(als_input);
memcpy(input_report, &als_input, sizeof(als_input));
break;
case HPD_IDX:
get_common_inputs(&hpd_input.common_property, report_id);
hpdstatus.val = readl(mp2->mmio + amd_get_c2p_val(mp2, 4));
hpd_input.human_presence = hpdstatus.shpd.presence;
report_size = sizeof(hpd_input);
memcpy(input_report, &hpd_input, sizeof(hpd_input));
break;
}
return report_size;
}
static u32 get_desc_size(int sensor_idx, int descriptor_name)
{
switch (sensor_idx) {
case ACCEL_IDX:
switch (descriptor_name) {
case descr_size:
return sizeof(accel3_report_descriptor);
case input_size:
return sizeof(struct accel3_input_report);
case feature_size:
return sizeof(struct accel3_feature_report);
}
break;
case GYRO_IDX:
switch (descriptor_name) {
case descr_size:
return sizeof(gyro3_report_descriptor);
case input_size:
return sizeof(struct gyro_input_report);
case feature_size:
return sizeof(struct gyro_feature_report);
}
break;
case MAG_IDX:
switch (descriptor_name) {
case descr_size:
return sizeof(comp3_report_descriptor);
case input_size:
return sizeof(struct magno_input_report);
case feature_size:
return sizeof(struct magno_feature_report);
}
break;
case ALS_IDX:
switch (descriptor_name) {
case descr_size:
return sizeof(als_report_descriptor);
case input_size:
return sizeof(struct als_input_report);
case feature_size:
return sizeof(struct als_feature_report);
}
break;
case HPD_IDX:
switch (descriptor_name) {
case descr_size:
return sizeof(hpd_report_descriptor);
case input_size:
return sizeof(struct hpd_input_report);
case feature_size:
return sizeof(struct hpd_feature_report);
}
break;
}
return 0;
}
void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops)
{
mp2_ops->get_rep_desc = get_report_desc;
mp2_ops->get_feat_rep = get_feature_rep;
mp2_ops->get_desc_sz = get_desc_size;
mp2_ops->get_in_rep = get_input_rep;
}
@@ -0,0 +1,430 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD MP2 1.1 communication driver
*
* Copyright (c) 2022, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/delay.h>
#include <linux/hid.h>
#include "amd_sfh_init.h"
#include "amd_sfh_interface.h"
#include "../hid_descriptor/amd_sfh_hid_desc.h"
static int amd_sfh_get_sensor_num(struct amd_mp2_dev *mp2, u8 *sensor_id)
{
struct sfh_sensor_list *slist;
struct sfh_base_info binfo;
int num_of_sensors = 0;
int i;
memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info));
slist = &binfo.sbase.s_list;
for (i = 0; i < MAX_IDX; i++) {
switch (i) {
case ACCEL_IDX:
case GYRO_IDX:
case MAG_IDX:
case SRA_IDX:
case ALS_IDX:
case HPD_IDX:
if (BIT(i) & slist->sl.sensors)
sensor_id[num_of_sensors++] = i;
break;
}
}
return num_of_sensors;
}
static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
{
if (mp2->mp2_ops->response)
return mp2->mp2_ops->response(mp2, sid, cmd_id);
return 0;
}
static const char *get_sensor_name(int idx)
{
switch (idx) {
case ACCEL_IDX:
return "accelerometer";
case GYRO_IDX:
return "gyroscope";
case MAG_IDX:
return "magnetometer";
case SRA_IDX:
return "SRA";
case ALS_IDX:
return "ALS";
case HPD_IDX:
return "HPD";
default:
return "unknown sensor type";
}
}
static int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
{
struct amdtp_cl_data *cl_data = privdata->cl_data;
int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) {
switch (cl_data->sensor_idx[i]) {
case HPD_IDX:
privdata->dev_en.is_hpd_present = false;
break;
case ALS_IDX:
privdata->dev_en.is_als_present = false;
break;
case SRA_IDX:
privdata->dev_en.is_sra_present = false;
break;
}
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], DISABLE_SENSOR);
if (status == 0)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
}
cancel_delayed_work_sync(&cl_data->work);
cancel_delayed_work_sync(&cl_data->work_buffer);
amdtp_hid_remove(cl_data);
return 0;
}
static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
{
struct amd_input_data *in_data = &privdata->in_data;
struct amdtp_cl_data *cl_data = privdata->cl_data;
struct amd_mp2_ops *mp2_ops = privdata->mp2_ops;
struct amd_mp2_sensor_info info;
struct request_list *req_list;
u32 feature_report_size;
u32 input_report_size;
struct device *dev;
int rc, i, status;
u8 cl_idx;
req_list = &cl_data->req_list;
dev = &privdata->pdev->dev;
amd_sfh1_1_set_desc_ops(mp2_ops);
cl_data->num_hid_devices = amd_sfh_get_sensor_num(privdata, &cl_data->sensor_idx[0]);
if (cl_data->num_hid_devices == 0)
return -ENODEV;
cl_data->is_any_sensor_enabled = false;
INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);
INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);
INIT_LIST_HEAD(&req_list->list);
cl_data->in_data = in_data;
for (i = 0; i < cl_data->num_hid_devices; i++) {
cl_data->sensor_sts[i] = SENSOR_DISABLED;
if (cl_data->sensor_idx[i] == SRA_IDX) {
info.sensor_idx = cl_data->sensor_idx[i];
writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0));
mp2_ops->start(privdata, info);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], ENABLE_SENSOR);
cl_data->sensor_sts[i] = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED;
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
cl_data->is_any_sensor_enabled = true;
privdata->dev_en.is_sra_present = true;
}
continue;
}
cl_data->sensor_requested_cnt[i] = 0;
cl_data->cur_hid_dev = i;
cl_idx = cl_data->sensor_idx[i];
cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size);
if (!cl_data->report_descr_sz[i]) {
rc = -EINVAL;
goto cleanup;
}
feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size);
if (!feature_report_size) {
rc = -EINVAL;
goto cleanup;
}
input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size);
if (!input_report_size) {
rc = -EINVAL;
goto cleanup;
}
cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL);
if (!cl_data->feature_report[i]) {
rc = -ENOMEM;
goto cleanup;
}
in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL);
if (!in_data->input_report[i]) {
rc = -ENOMEM;
goto cleanup;
}
info.sensor_idx = cl_idx;
cl_data->report_descr[i] =
devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL);
if (!cl_data->report_descr[i]) {
rc = -ENOMEM;
goto cleanup;
}
rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);
if (rc)
goto cleanup;
mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
amd_sfh_wait_for_response(privdata, cl_data->sensor_idx[i], DISABLE_SENSOR);
writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0));
mp2_ops->start(privdata, info);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], ENABLE_SENSOR);
cl_data->sensor_sts[i] = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED;
}
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_idx[i] == SRA_IDX)
continue;
cl_data->cur_hid_dev = i;
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
cl_data->is_any_sensor_enabled = true;
rc = amdtp_hid_probe(i, cl_data);
if (rc)
goto cleanup;
switch (cl_data->sensor_idx[i]) {
case HPD_IDX:
privdata->dev_en.is_hpd_present = true;
privdata->dev_en.is_hpd_enabled = true;
amd_sfh_toggle_hpd(privdata, false);
break;
case ALS_IDX:
privdata->dev_en.is_als_present = true;
break;
}
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
if (!cl_data->is_any_sensor_enabled) {
dev_warn(dev, "No sensor registered, sensors not enabled is %d\n",
cl_data->is_any_sensor_enabled);
rc = -EOPNOTSUPP;
goto cleanup;
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0;
cleanup:
amd_sfh_hid_client_deinit(privdata);
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_idx[i] == SRA_IDX)
continue;
devm_kfree(dev, cl_data->feature_report[i]);
devm_kfree(dev, in_data->input_report[i]);
devm_kfree(dev, cl_data->report_descr[i]);
}
return rc;
}
static void amd_sfh_resume(struct amd_mp2_dev *mp2)
{
struct amdtp_cl_data *cl_data = mp2->cl_data;
struct amd_mp2_sensor_info info;
int i, status;
if (!cl_data->is_any_sensor_enabled) {
amd_sfh_clear_intr(mp2);
return;
}
for (i = 0; i < cl_data->num_hid_devices; i++) {
/* leave HPD alone; policy is controlled by sysfs */
if (cl_data->sensor_idx[i] == HPD_IDX)
continue;
if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
info.sensor_idx = cl_data->sensor_idx[i];
mp2->mp2_ops->start(mp2, info);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], ENABLE_SENSOR);
if (status == 0)
status = SENSOR_ENABLED;
if (status == SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_ENABLED;
dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
amd_sfh_clear_intr(mp2);
}
static void amd_sfh_suspend(struct amd_mp2_dev *mp2)
{
struct amdtp_cl_data *cl_data = mp2->cl_data;
int i, status;
if (!cl_data->is_any_sensor_enabled) {
amd_sfh_clear_intr(mp2);
return;
}
for (i = 0; i < cl_data->num_hid_devices; i++) {
/* leave HPD alone; policy is controlled by sysfs */
if (cl_data->sensor_idx[i] == HPD_IDX)
continue;
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], DISABLE_SENSOR);
if (status == 0)
status = SENSOR_DISABLED;
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
}
cancel_delayed_work_sync(&cl_data->work_buffer);
amd_sfh_clear_intr(mp2);
}
void amd_sfh_toggle_hpd(struct amd_mp2_dev *mp2, bool enabled)
{
struct amdtp_cl_data *cl_data = mp2->cl_data;
struct amd_mp2_sensor_info info;
int i, status;
if (mp2->dev_en.is_hpd_enabled == enabled)
return;
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (cl_data->sensor_idx[i] != HPD_IDX)
continue;
info.sensor_idx = cl_data->sensor_idx[i];
if (enabled) {
mp2->mp2_ops->start(mp2, info);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], ENABLE_SENSOR);
if (status == 0)
status = SENSOR_ENABLED;
if (status == SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_ENABLED;
} else {
mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(mp2, cl_data->sensor_idx[i], DISABLE_SENSOR);
if (status == 0)
status = SENSOR_DISABLED;
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
}
dev_dbg(&mp2->pdev->dev, "toggle sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
break;
}
mp2->dev_en.is_hpd_enabled = enabled;
}
static void amd_mp2_pci_remove(void *privdata)
{
struct amd_mp2_dev *mp2 = privdata;
sfh_deinit_emp2();
amd_sfh_hid_client_deinit(privdata);
mp2->mp2_ops->stop_all(mp2);
pcim_intx(mp2->pdev, false);
amd_sfh_clear_intr(mp2);
}
static void amd_sfh_set_ops(struct amd_mp2_dev *mp2)
{
struct amd_mp2_ops *mp2_ops;
sfh_interface_init(mp2);
mp2_ops = mp2->mp2_ops;
mp2_ops->clear_intr = amd_sfh_clear_intr_v2;
mp2_ops->init_intr = amd_sfh_irq_init_v2;
mp2_ops->suspend = amd_sfh_suspend;
mp2_ops->resume = amd_sfh_resume;
mp2_ops->remove = amd_mp2_pci_remove;
}
int amd_sfh1_1_init(struct amd_mp2_dev *mp2)
{
u32 phy_base = readl(mp2->mmio + amd_get_c2p_val(mp2, 22));
struct device *dev = &mp2->pdev->dev;
struct sfh_base_info binfo;
int rc;
phy_base <<= 21;
if (!devm_request_mem_region(dev, phy_base, 128 * 1024, "amd_sfh")) {
dev_dbg(dev, "can't reserve mmio registers\n");
return -ENOMEM;
}
mp2->vsbase = devm_ioremap(dev, phy_base, 128 * 1024);
if (!mp2->vsbase) {
dev_dbg(dev, "failed to remap vsbase\n");
return -ENOMEM;
}
/* Before accessing give time for SFH firmware for processing configuration */
msleep(5000);
memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info));
if (binfo.sbase.fw_info.fw_ver == 0 || binfo.sbase.s_list.sl.sensors == 0) {
dev_dbg(dev, "No sensor registered\n");
return -EOPNOTSUPP;
}
dev_dbg(dev, "firmware version 0x%x\n", binfo.sbase.fw_info.fw_ver);
amd_sfh_set_ops(mp2);
rc = amd_sfh_irq_init(mp2);
if (rc) {
sfh_deinit_emp2();
dev_err(dev, "amd_sfh_irq_init failed\n");
return rc;
}
rc = amd_sfh1_1_hid_client_init(mp2);
if (rc) {
sfh_deinit_emp2();
if ((rc != -ENODEV) && (rc != -EOPNOTSUPP))
dev_err(dev, "amd_sfh1_1_hid_client_init failed\n");
return rc;
}
return rc;
}
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AMD MP2 1.1 initialization structures
*
* Copyright (c) 2022, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMD_SFH_INIT_H
#define AMD_SFH_INIT_H
#include "../amd_sfh_common.h"
struct amd_sfh1_1_ops {
int (*init)(struct amd_mp2_dev *mp2);
void (*toggle_hpd)(struct amd_mp2_dev *mp2, bool enable);
};
int amd_sfh1_1_init(struct amd_mp2_dev *mp2);
void amd_sfh_toggle_hpd(struct amd_mp2_dev *mp2, bool enabled);
static const struct amd_sfh1_1_ops __maybe_unused sfh1_1_ops = {
.init = amd_sfh1_1_init,
.toggle_hpd = amd_sfh_toggle_hpd,
};
#endif
@@ -0,0 +1,176 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD MP2 1.1 communication interfaces
*
* Copyright (c) 2022, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/amd-pmf-io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h>
#include "amd_sfh_interface.h"
static struct amd_mp2_dev *emp2;
static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
{
struct sfh_cmd_response cmd_resp;
/* Get response with status within a max of 10000 ms timeout */
if (!readl_poll_timeout(mp2->mmio + amd_get_p2c_val(mp2, 0), cmd_resp.resp,
(cmd_resp.response.response == 0 &&
cmd_resp.response.cmd_id == cmd_id && (sid == 0xff ||
cmd_resp.response.sensor_id == sid)), 500, 10000000))
return cmd_resp.response.response;
return -1;
}
static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{
struct sfh_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.cmd.cmd_id = ENABLE_SENSOR;
cmd_base.cmd.intr_disable = 0;
cmd_base.cmd.sub_cmd_value = 1;
cmd_base.cmd.sensor_id = info.sensor_idx;
writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
}
static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
{
struct sfh_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.cmd.cmd_id = DISABLE_SENSOR;
cmd_base.cmd.intr_disable = 0;
cmd_base.cmd.sub_cmd_value = 1;
cmd_base.cmd.sensor_id = sensor_idx;
writeq(0x0, privdata->mmio + amd_get_c2p_val(privdata, 1));
writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
}
static void amd_stop_all_sensor(struct amd_mp2_dev *privdata)
{
struct sfh_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.cmd.cmd_id = DISABLE_SENSOR;
cmd_base.cmd.intr_disable = 0;
/* 0xf indicates all sensors */
cmd_base.cmd.sensor_id = 0xf;
writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
}
static struct amd_mp2_ops amd_sfh_ops = {
.start = amd_start_sensor,
.stop = amd_stop_sensor,
.stop_all = amd_stop_all_sensor,
.response = amd_sfh_wait_response,
};
void sfh_deinit_emp2(void)
{
emp2 = NULL;
}
void sfh_interface_init(struct amd_mp2_dev *mp2)
{
mp2->mp2_ops = &amd_sfh_ops;
emp2 = mp2;
}
static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement)
{
struct sfh_op_mode mode;
if (!platform_type || !laptop_placement)
return -EINVAL;
if (!emp2 || !emp2->dev_en.is_sra_present)
return -ENODEV;
mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3));
*platform_type = mode.op_mode.devicemode;
if (mode.op_mode.ontablestate == 1) {
*laptop_placement = ON_TABLE;
} else if (mode.op_mode.ontablestate == 2) {
*laptop_placement = ON_LAP_MOTION;
} else if (mode.op_mode.inbagstate == 1) {
*laptop_placement = IN_BAG;
} else if (mode.op_mode.outbagstate == 1) {
*laptop_placement = OUT_OF_BAG;
} else if (mode.op_mode.ontablestate == 0 || mode.op_mode.inbagstate == 0 ||
mode.op_mode.outbagstate == 0) {
*laptop_placement = LP_UNKNOWN;
pr_warn_once("Unknown laptop placement\n");
} else if (mode.op_mode.ontablestate == 3 || mode.op_mode.inbagstate == 3 ||
mode.op_mode.outbagstate == 3) {
*laptop_placement = LP_UNDEFINED;
pr_warn_once("Undefined laptop placement\n");
}
return 0;
}
static int amd_sfh_hpd_info(u8 *user_present)
{
struct hpd_status hpdstatus;
if (!user_present)
return -EINVAL;
if (!emp2 || !emp2->dev_en.is_hpd_present || !emp2->dev_en.is_hpd_enabled)
return -ENODEV;
hpdstatus.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 4));
*user_present = hpdstatus.shpd.presence;
return 0;
}
static int amd_sfh_als_info(u32 *ambient_light)
{
struct sfh_als_data als_data;
void __iomem *sensoraddr;
if (!ambient_light)
return -EINVAL;
if (!emp2 || !emp2->dev_en.is_als_present)
return -ENODEV;
sensoraddr = emp2->vsbase +
(ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
*ambient_light = amd_sfh_float_to_int(als_data.lux);
return 0;
}
int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
{
if (sfh_info) {
switch (op) {
case MT_HPD:
return amd_sfh_hpd_info(&sfh_info->user_present);
case MT_ALS:
return amd_sfh_als_info(&sfh_info->ambient_light);
case MT_SRA:
return amd_sfh_mode_info(&sfh_info->platform_type,
&sfh_info->laptop_placement);
}
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(amd_get_sfh_info);
@@ -0,0 +1,191 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AMD MP2 1.1 communication interfaces
*
* Copyright (c) 2022, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMD_SFH_INTERFACE_H
#define AMD_SFH_INTERFACE_H
#include "../amd_sfh_common.h"
#define SENSOR_DATA_MEM_SIZE_DEFAULT 256
#define TOTAL_STATIC_MEM_DEFAULT 1024
#define OFFSET_SFH_INFO_BASE_DEFAULT 0
#define OFFSET_SENSOR_DATA_DEFAULT (OFFSET_SFH_INFO_BASE_DEFAULT + \
TOTAL_STATIC_MEM_DEFAULT)
enum sensor_index {
ACCEL_IDX,
GYRO_IDX,
MAG_IDX,
SRA_IDX,
ALS_IDX,
HPD_IDX,
MAX_IDX = 15,
};
struct sfh_cmd_base {
union {
u32 ul;
struct {
u32 sensor_id : 4;
u32 cmd_id : 4;
u32 sub_cmd_id : 8;
u32 sub_cmd_value : 12;
u32 rsvd : 3;
u32 intr_disable : 1;
} cmd;
};
};
struct sfh_cmd_response {
union {
u32 resp;
struct {
u32 response : 8;
u32 sensor_id : 4;
u32 cmd_id : 4;
u32 sub_cmd : 6;
u32 rsvd2 : 10;
} response;
};
};
struct sfh_platform_info {
union {
u32 pi;
struct {
u32 cust_id : 16;
u32 plat_id : 6;
u32 interface_id : 4;
u32 rsvd : 6;
} pinfo;
};
};
struct sfh_firmware_info {
union {
u32 fw_ver;
struct {
u32 minor_rev : 8;
u32 major_rev : 8;
u32 minor_ver : 8;
u32 major_ver : 8;
} fver;
};
};
struct sfh_sensor_list {
union {
u32 slist;
struct {
u32 sensors : 16;
u32 rsvd : 16;
} sl;
};
};
struct sfh_sensor_prop {
union {
u32 sprop;
struct {
u32 elist : 16;
u32 feat : 16;
} sf;
};
};
struct sfh_base_info {
union {
u32 sfh_base[24];
struct {
struct sfh_platform_info plat_info;
struct sfh_firmware_info fw_info;
struct sfh_sensor_list s_list;
u32 rsvd;
struct sfh_sensor_prop s_prop[16];
} sbase;
};
};
struct sfh_common_data {
u64 timestamp;
u32 intr_cnt;
u32 featvalid : 16;
u32 rsvd : 13;
u32 sensor_state : 3;
};
struct sfh_float32 {
u32 x;
u32 y;
u32 z;
};
struct sfh_accel_data {
struct sfh_common_data commondata;
struct sfh_float32 acceldata;
u32 accelstatus;
};
struct sfh_gyro_data {
struct sfh_common_data commondata;
struct sfh_float32 gyrodata;
u32 result;
};
struct sfh_mag_data {
struct sfh_common_data commondata;
struct sfh_float32 magdata;
u32 accuracy;
};
struct sfh_als_data {
struct sfh_common_data commondata;
u32 lux;
u32 light_color_temp;
u32 chromaticity_x;
u32 chromaticity_y;
};
struct hpd_status {
union {
struct {
u32 distance : 16;
u32 probablity : 8;
u32 presence : 2;
u32 rsvd : 5;
u32 state : 1;
} shpd;
u32 val;
};
};
struct sfh_op_mode {
union {
u32 val;
struct {
u32 mode : 3;
u32 lidstatus : 1;
u32 angle : 10;
u32 inbagstatedbg : 2;
u32 ontablestate : 2;
u32 inbagstate : 2;
u32 outbagstate : 2;
u32 inbagmlcstate : 1;
u32 powerstate : 2;
u32 data : 3;
u32 devicemode : 4;
} op_mode;
};
};
void sfh_interface_init(struct amd_mp2_dev *mp2);
void sfh_deinit_emp2(void);
void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops);
int amd_sfh_float_to_int(u32 flt32_val);
#endif