Files
android_kernel_eebbk_sm6150/drivers/soc/qcom/hab/hab_ghs.c
T

202 lines
4.6 KiB
C
Raw Normal View History

/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "hab.h"
#include "hab_ghs.h"
#define GIPC_VM_SET_CNT 22
/* same vmid assignment for all the vms. it should matches dt_gipc_path_name */
static int mmid_order[GIPC_VM_SET_CNT] = {
MM_AUD_1,
MM_AUD_2,
MM_AUD_3,
MM_AUD_4,
MM_CAM_1,
MM_CAM_2,
MM_DISP_1,
MM_DISP_2,
MM_DISP_3,
MM_DISP_4,
MM_DISP_5,
MM_GFX,
MM_VID,
MM_MISC,
MM_QCPE_VM1,
MM_VID_2, /* newly recycled */
0,
0,
MM_CLK_VM1,
MM_CLK_VM2,
MM_FDE_1,
MM_BUFFERQ_1,
};
struct ghs_vmm_plugin_info_s ghs_vmm_plugin_info = {
dt_gipc_path_name,
mmid_order,
0,
0,
};
int get_dt_name_idx(int vmid_base, int mmid,
struct ghs_vmm_plugin_info_s *plugin_info)
{
int idx = -1;
int i;
if (vmid_base < 0 || vmid_base > plugin_info->probe_cnt /
GIPC_VM_SET_CNT) {
pr_err("vmid %d overflow expected max %d\n", vmid_base,
plugin_info->probe_cnt / GIPC_VM_SET_CNT);
return idx;
}
for (i = 0; i < GIPC_VM_SET_CNT; i++) {
if (mmid == plugin_info->mmid_dt_mapping[i]) {
idx = vmid_base * GIPC_VM_SET_CNT + i;
if (idx > plugin_info->probe_cnt) {
pr_err("dt name idx %d overflow max %d\n",
idx, plugin_info->probe_cnt);
idx = -1;
}
break;
}
}
return idx;
}
/* static struct physical_channel *habhyp_commdev_alloc(int id) */
int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote,
struct hab_device *mmid_device)
{
struct ghs_vdev *dev = NULL;
struct ghs_vdev_os *dev_os = NULL;
struct physical_channel *pchan = NULL;
struct physical_channel **ppchan = (struct physical_channel **)commdev;
int ret = 0;
if (ghs_vmm_plugin_info.curr > ghs_vmm_plugin_info.probe_cnt) {
pr_err("too many commdev alloc %d, supported is %d\n",
ghs_vmm_plugin_info.curr,
ghs_vmm_plugin_info.probe_cnt);
ret = -ENOENT;
goto err;
}
/* common part for hyp_data */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
pr_err("allocate struct ghs_vdev failed %zu bytes on pchan %s\n",
sizeof(*dev), name);
goto err;
}
memset(dev, 0, sizeof(*dev));
/* os specific part for hyp_data */
dev_os = kzalloc(sizeof(*dev_os), GFP_KERNEL);
if (!dev_os) {
ret = -ENOMEM;
pr_err("allocate ghs_vdev_os failed %zu bytes on pchan %s\n",
sizeof(*dev_os), name);
goto err;
}
dev->os_data = dev_os;
spin_lock_init(&dev->io_lock);
/*
* TODO: ExtractEndpoint is in ghs_comm.c because it blocks.
* Extrace and Request should be in roughly the same spot
*/
ret = hab_gipc_ep_attach(is_be, name, vmid_remote, mmid_device, dev);
if (ret)
goto err;
/* add pchan into the mmid_device list */
pchan = hab_pchan_alloc(mmid_device, vmid_remote);
if (!pchan) {
pr_err("hab_pchan_alloc failed for %s, cnt %d\n",
mmid_device->name, mmid_device->pchan_cnt);
ret = -ENOMEM;
goto err;
}
pchan->closed = 0;
pchan->hyp_data = (void *)dev;
pchan->is_be = is_be;
strlcpy(dev->name, name, sizeof(dev->name));
strlcpy(pchan->name, name, sizeof(pchan->name));
*ppchan = pchan;
dev->read_data = kmalloc(GIPC_RECV_BUFF_SIZE_BYTES, GFP_KERNEL);
if (!dev->read_data) {
ret = -ENOMEM;
goto err;
}
ret = habhyp_commdev_create_dispatcher(pchan);
if (ret)
goto err;
/* this value could be more than devp total */
ghs_vmm_plugin_info.curr++;
return 0;
err:
hab_pchan_put(pchan);
kfree(dev);
kfree(dev_os);
return ret;
}
int habhyp_commdev_dealloc(void *commdev)
{
struct physical_channel *pchan = (struct physical_channel *)commdev;
struct ghs_vdev *dev = pchan->hyp_data;
/* os specific deallocation for this commdev */
habhyp_commdev_dealloc_os(commdev);
kfree(dev->read_data);
kfree(dev->os_data);
kfree(dev);
if (get_refcnt(pchan->refcount) > 1) {
pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
pchan->name, pchan->vcnt, get_refcnt(pchan->refcount));
}
hab_pchan_put(pchan);
return 0;
}
void hab_hypervisor_unregister(void)
{
pr_debug("total %d\n", hab_driver.ndevices);
hab_hypervisor_unregister_common();
ghs_vmm_plugin_info.curr = 0;
}
int hab_hypervisor_register(void)
{
int ret = 0;
/* os-specific registration work */
ret = hab_hypervisor_register_os();
return ret;
}