Merge "disp: msm: sde: parse property for max concurrent TUI displays"
This commit is contained in:

committed by
Gerrit - the friendly Code Review server

commit
0ea5003877
@@ -85,7 +85,12 @@ struct dss_module_power {
|
||||
int msm_dss_ioremap_byname(struct platform_device *pdev,
|
||||
struct dss_io_data *io_data, const char *name);
|
||||
void msm_dss_iounmap(struct dss_io_data *io_data);
|
||||
|
||||
int msm_dss_get_io_mem(struct platform_device *pdev,
|
||||
struct list_head *mem_list);
|
||||
void msm_dss_clean_io_mem(struct list_head *mem_list);
|
||||
int msm_dss_get_io_irq(struct platform_device *pdev,
|
||||
struct list_head *irq_list, u32 label);
|
||||
void msm_dss_clean_io_irq(struct list_head *irq_list);
|
||||
int msm_dss_enable_gpio(struct dss_gpio *in_gpio, int num_gpio, int enable);
|
||||
int msm_dss_gpio_enable(struct dss_gpio *in_gpio, int num_gpio, int enable);
|
||||
|
||||
|
97
include/linux/sde_vm_event.h
Normal file
97
include/linux/sde_vm_event.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SDE_VM_EVENT_H__
|
||||
#define __SDE_VM_EVENT_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drm_device.h>
|
||||
|
||||
/**
|
||||
* struct - msm_io_irq_entry - define irq item
|
||||
* @label: hh_irq_label for the irq
|
||||
* @irq_num: linux mapped irq num
|
||||
* @list: list head pointer
|
||||
*/
|
||||
struct msm_io_irq_entry {
|
||||
u32 label;
|
||||
u32 irq_num;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct - msm_io_mem_entry - define io memory item
|
||||
* @base: reg base
|
||||
* @size: size of the reg range
|
||||
* @list: list head pointer
|
||||
*/
|
||||
struct msm_io_mem_entry {
|
||||
phys_addr_t base;
|
||||
phys_addr_t size;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct - msm_io_res - represents the hw resources for vm sharing
|
||||
* @irq: list of IRQ's of all the dislay sub-devices
|
||||
* @mem: list of IO memory ranges of all the display sub-devices
|
||||
*/
|
||||
struct msm_io_res {
|
||||
struct list_head irq;
|
||||
struct list_head mem;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct msm_vm_ops - hooks for communication with vm clients
|
||||
* @vm_pre_hw_release: invoked before releasing the HW
|
||||
* @vm_post_hw_acquire: invoked before pushing the first commit
|
||||
* @vm_check: invoked to check the readiness of the vm_clients
|
||||
* before releasing the HW
|
||||
* @vm_get_io_resources: invoked to collect HW resources
|
||||
*/
|
||||
struct msm_vm_ops {
|
||||
int (*vm_pre_hw_release)(void *priv_data);
|
||||
int (*vm_post_hw_acquire)(void *priv_data);
|
||||
int (*vm_check)(void *priv_data);
|
||||
int (*vm_get_io_resources)(struct msm_io_res *io_res, void *priv_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* msm_vm_client_entry - defines the vm client info
|
||||
* @ops: client vm_ops
|
||||
* @dev: clients device id. Used in unregister
|
||||
* @data: client custom data
|
||||
* @list: linked list entry
|
||||
*/
|
||||
struct msm_vm_client_entry {
|
||||
struct msm_vm_ops ops;
|
||||
struct device *dev;
|
||||
void *data;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
* msm_register_vm_event - api for display dependent drivers(clients) to
|
||||
* register for vm events
|
||||
* @dev: msm device
|
||||
* @client_dev: client device
|
||||
* @ops: vm event hooks
|
||||
* @priv_data: client custom data
|
||||
*/
|
||||
int msm_register_vm_event(struct device *dev, struct device *client_dev,
|
||||
struct msm_vm_ops *ops, void *priv_data);
|
||||
|
||||
/**
|
||||
* msm_unregister_vm_event - api for display dependent drivers(clients) to
|
||||
* unregister from vm events
|
||||
* @dev: msm device
|
||||
* @client_dev: client device
|
||||
*/
|
||||
void msm_unregister_vm_event(struct device *dev, struct device *client_dev);
|
||||
|
||||
#endif //__SDE_VM_EVENT_H__
|
@@ -48,6 +48,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
|
||||
sde/sde_color_processing.o \
|
||||
sde/sde_vbif.o \
|
||||
sde_io_util.o \
|
||||
sde_vm_event.o \
|
||||
sde/sde_hw_reg_dma_v1_color_proc.o \
|
||||
sde/sde_hw_color_proc_v4.o \
|
||||
sde/sde_hw_ad4.o \
|
||||
|
@@ -394,6 +394,7 @@ static int msm_drm_uninit(struct device *dev)
|
||||
struct drm_device *ddev = platform_get_drvdata(pdev);
|
||||
struct msm_drm_private *priv = ddev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
struct msm_vm_client_entry *client_entry, *tmp;
|
||||
int i;
|
||||
|
||||
/* We must cancel and cleanup any pending vblank enable/disable
|
||||
@@ -455,6 +456,17 @@ static int msm_drm_uninit(struct device *dev)
|
||||
|
||||
sde_power_resource_deinit(pdev, &priv->phandle);
|
||||
|
||||
mutex_lock(&priv->vm_client_lock);
|
||||
|
||||
/* clean up any unregistered clients */
|
||||
list_for_each_entry_safe(client_entry, tmp, &priv->vm_client_list,
|
||||
list) {
|
||||
list_del(&client_entry->list);
|
||||
kfree(client_entry);
|
||||
}
|
||||
|
||||
mutex_unlock(&priv->vm_client_lock);
|
||||
|
||||
msm_mdss_destroy(ddev);
|
||||
|
||||
ddev->dev_private = NULL;
|
||||
@@ -831,6 +843,9 @@ static int msm_drm_component_init(struct device *dev)
|
||||
|
||||
INIT_LIST_HEAD(&priv->client_event_list);
|
||||
INIT_LIST_HEAD(&priv->inactive_list);
|
||||
INIT_LIST_HEAD(&priv->vm_client_list);
|
||||
|
||||
mutex_init(&priv->vm_client_lock);
|
||||
|
||||
/* Bind all our sub-components: */
|
||||
ret = msm_component_bind_all(dev, ddev);
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/sde_io_util.h>
|
||||
#include <linux/sde_vm_event.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
@@ -922,6 +923,9 @@ struct msm_drm_private {
|
||||
|
||||
/* update the flag when msm driver receives shutdown notification */
|
||||
bool shutdown_in_progress;
|
||||
|
||||
struct mutex vm_client_lock;
|
||||
struct list_head vm_client_list;
|
||||
};
|
||||
|
||||
/* get struct msm_kms * from drm_device * */
|
||||
|
@@ -1342,7 +1342,7 @@ static int _sde_encoder_update_rsc_client(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _sde_encoder_irq_control(struct drm_encoder *drm_enc, bool enable)
|
||||
void sde_encoder_irq_control(struct drm_encoder *drm_enc, bool enable)
|
||||
{
|
||||
struct sde_encoder_virt *sde_enc;
|
||||
int i;
|
||||
@@ -1452,7 +1452,7 @@ static int _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
|
||||
}
|
||||
|
||||
/* enable all the irq */
|
||||
_sde_encoder_irq_control(drm_enc, true);
|
||||
sde_encoder_irq_control(drm_enc, true);
|
||||
|
||||
_sde_encoder_pm_qos_add_request(drm_enc);
|
||||
|
||||
@@ -1460,7 +1460,7 @@ static int _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
|
||||
_sde_encoder_pm_qos_remove_request(drm_enc);
|
||||
|
||||
/* disable all the irq */
|
||||
_sde_encoder_irq_control(drm_enc, false);
|
||||
sde_encoder_irq_control(drm_enc, false);
|
||||
|
||||
/* disable DSI clks */
|
||||
sde_connector_clk_ctrl(sde_enc->cur_master->connector, false);
|
||||
@@ -1627,7 +1627,7 @@ static int _sde_encoder_rc_kickoff(struct drm_encoder *drm_enc,
|
||||
}
|
||||
|
||||
if (is_vid_mode && sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) {
|
||||
_sde_encoder_irq_control(drm_enc, true);
|
||||
sde_encoder_irq_control(drm_enc, true);
|
||||
} else {
|
||||
/* enable all the clks and resources */
|
||||
ret = _sde_encoder_resource_control_helper(drm_enc,
|
||||
@@ -1664,7 +1664,7 @@ static int _sde_encoder_rc_pre_stop(struct drm_encoder *drm_enc,
|
||||
|
||||
if (is_vid_mode &&
|
||||
sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) {
|
||||
_sde_encoder_irq_control(drm_enc, true);
|
||||
sde_encoder_irq_control(drm_enc, true);
|
||||
}
|
||||
/* skip if is already OFF or IDLE, resources are off already */
|
||||
else if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF ||
|
||||
@@ -1781,7 +1781,7 @@ static int _sde_encoder_rc_pre_modeset(struct drm_encoder *drm_enc,
|
||||
goto end;
|
||||
}
|
||||
|
||||
_sde_encoder_irq_control(drm_enc, false);
|
||||
sde_encoder_irq_control(drm_enc, false);
|
||||
_sde_encoder_modeset_helper_locked(drm_enc, sw_event);
|
||||
|
||||
SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
|
||||
@@ -1819,7 +1819,7 @@ static int _sde_encoder_rc_post_modeset(struct drm_encoder *drm_enc,
|
||||
}
|
||||
|
||||
_sde_encoder_modeset_helper_locked(drm_enc, sw_event);
|
||||
_sde_encoder_irq_control(drm_enc, true);
|
||||
sde_encoder_irq_control(drm_enc, true);
|
||||
|
||||
_sde_encoder_update_rsc_client(drm_enc, true);
|
||||
|
||||
@@ -1863,7 +1863,7 @@ static int _sde_encoder_rc_idle(struct drm_encoder *drm_enc,
|
||||
}
|
||||
|
||||
if (is_vid_mode) {
|
||||
_sde_encoder_irq_control(drm_enc, false);
|
||||
sde_encoder_irq_control(drm_enc, false);
|
||||
} else {
|
||||
/* disable all the clks and resources */
|
||||
_sde_encoder_update_rsc_client(drm_enc, false);
|
||||
|
@@ -525,6 +525,13 @@ void sde_encoder_needs_hw_reset(struct drm_encoder *enc);
|
||||
*/
|
||||
void sde_encoder_uidle_enable(struct drm_encoder *drm_enc, bool enable);
|
||||
|
||||
/**
|
||||
* sde_encoder_irq_control - control enable/disable of IRQ's
|
||||
* @drm_enc: Pointer to drm encoder structure
|
||||
* @enable: enable/disable flag
|
||||
*/
|
||||
void sde_encoder_irq_control(struct drm_encoder *drm_enc, bool enable);
|
||||
|
||||
/**
|
||||
* sde_encoder_get_kms - retrieve the kms from encoder
|
||||
* @drm_enc: Pointer to drm encoder structure
|
||||
|
@@ -202,6 +202,7 @@ enum sde_prop {
|
||||
SEC_SID_MASK,
|
||||
BASE_LAYER,
|
||||
TRUSTED_VM_ENV,
|
||||
MAX_TRUSTED_VM_DISPLAYS,
|
||||
SDE_PROP_MAX,
|
||||
};
|
||||
|
||||
@@ -574,6 +575,8 @@ static struct sde_prop_type sde_prop[] = {
|
||||
{SEC_SID_MASK, "qcom,sde-secure-sid-mask", false, PROP_TYPE_U32_ARRAY},
|
||||
{BASE_LAYER, "qcom,sde-mixer-stage-base-layer", false, PROP_TYPE_BOOL},
|
||||
{TRUSTED_VM_ENV, "qcom,sde-trusted-vm-env", false, PROP_TYPE_BOOL},
|
||||
{MAX_TRUSTED_VM_DISPLAYS, "qcom,sde-max-trusted-vm-displays", false,
|
||||
PROP_TYPE_U32},
|
||||
};
|
||||
|
||||
static struct sde_prop_type sde_perf_prop[] = {
|
||||
@@ -3752,6 +3755,8 @@ static void _sde_top_parse_dt_helper(struct sde_mdss_cfg *cfg,
|
||||
QSEED_HW_VERSION, 0);
|
||||
cfg->trusted_vm_env = PROP_VALUE_ACCESS(props->values, TRUSTED_VM_ENV,
|
||||
0);
|
||||
cfg->max_trusted_vm_displays = PROP_VALUE_ACCESS(props->values,
|
||||
MAX_TRUSTED_VM_DISPLAYS, 0);
|
||||
}
|
||||
|
||||
static int sde_top_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
|
||||
|
@@ -1364,6 +1364,8 @@ struct sde_perf_cfg {
|
||||
*
|
||||
* @trusted_vm_env set to true, if the driver is executing in
|
||||
* the trusted VM. false, otherwise.
|
||||
* @max_trusted_vm_displays maximum number of concurrent trusted
|
||||
* vm displays supported.
|
||||
* @max_sspp_linewidth max source pipe line width support.
|
||||
* @vig_sspp_linewidth max vig source pipe line width support.
|
||||
* @scaling_linewidth max vig source pipe linewidth for scaling usecases
|
||||
@@ -1438,6 +1440,7 @@ struct sde_perf_cfg {
|
||||
struct sde_mdss_cfg {
|
||||
u32 hwversion;
|
||||
bool trusted_vm_env;
|
||||
u32 max_trusted_vm_displays;
|
||||
|
||||
u32 max_sspp_linewidth;
|
||||
u32 vig_sspp_linewidth;
|
||||
|
@@ -52,6 +52,7 @@
|
||||
#include <linux/qcom_scm.h>
|
||||
#include "soc/qcom/secure_buffer.h"
|
||||
#include <linux/qtee_shmbridge.h>
|
||||
#include <linux/haven/hh_irq_lend.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "sde_trace.h"
|
||||
@@ -349,7 +350,7 @@ static int _sde_kms_scm_call(struct sde_kms *sde_kms, int vmid)
|
||||
set_dma_ops(&dummy, NULL);
|
||||
|
||||
dma_handle = dma_map_single(&dummy, sec_sid,
|
||||
num_sids *sizeof(uint32_t), DMA_TO_DEVICE);
|
||||
num_sids * sizeof(uint32_t), DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&dummy, dma_handle)) {
|
||||
SDE_ERROR("dma_map_single for dummy dev failed vmid 0x%x\n",
|
||||
vmid);
|
||||
@@ -367,7 +368,7 @@ static int _sde_kms_scm_call(struct sde_kms *sde_kms, int vmid)
|
||||
vmid, qtee_en, num_sids, ret);
|
||||
|
||||
dma_unmap_single(&dummy, dma_handle,
|
||||
num_sids *sizeof(uint32_t), DMA_TO_DEVICE);
|
||||
num_sids * sizeof(uint32_t), DMA_TO_DEVICE);
|
||||
|
||||
map_error:
|
||||
if (qtee_en)
|
||||
@@ -3601,6 +3602,26 @@ power_error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sde_kms_get_io_resources(struct sde_kms *sde_kms, struct msm_io_res *io_res)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(sde_kms->dev->dev);
|
||||
int rc = 0;
|
||||
|
||||
rc = msm_dss_get_io_mem(pdev, &io_res->mem);
|
||||
if (rc) {
|
||||
SDE_ERROR("failed to get io mem for KMS, rc = %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = msm_dss_get_io_irq(pdev, &io_res->irq, HH_IRQ_LABEL_SDE);
|
||||
if (rc) {
|
||||
SDE_ERROR("failed to get io irq for KMS");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sde_kms_hw_init(struct msm_kms *kms)
|
||||
{
|
||||
struct sde_kms *sde_kms;
|
||||
|
@@ -691,4 +691,12 @@ int sde_kms_handle_recovery(struct drm_encoder *encoder);
|
||||
*/
|
||||
void sde_kms_irq_enable_notify(struct sde_kms *sde_kms, bool enable);
|
||||
|
||||
/**
|
||||
* sde_kms_get_io_resources() - reads associated register range
|
||||
* @kms: pointer to sde_kms structure
|
||||
* @io_res: pointer to msm_io_res struct to populate the ranges
|
||||
* Return: error code.
|
||||
*/
|
||||
int sde_kms_get_io_resources(struct sde_kms *kms, struct msm_io_res *io_res);
|
||||
|
||||
#endif /* __sde_kms_H__ */
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sde_io_util.h>
|
||||
#include <linux/sde_vm_event.h>
|
||||
|
||||
#define MAX_I2C_CMDS 16
|
||||
void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug)
|
||||
@@ -129,6 +130,127 @@ void msm_dss_iounmap(struct dss_io_data *io_data)
|
||||
} /* msm_dss_iounmap */
|
||||
EXPORT_SYMBOL(msm_dss_iounmap);
|
||||
|
||||
int msm_dss_get_io_mem(struct platform_device *pdev, struct list_head *mem_list)
|
||||
{
|
||||
struct list_head temp_head;
|
||||
struct msm_io_mem_entry *io_mem;
|
||||
struct resource *res = NULL;
|
||||
const char *reg_name, *exclude_reg_name;
|
||||
int i, j, rc = 0;
|
||||
int num_entry, num_exclude_entry;
|
||||
|
||||
INIT_LIST_HEAD(&temp_head);
|
||||
|
||||
num_entry = of_property_count_strings(pdev->dev.of_node,
|
||||
"reg-names");
|
||||
if (num_entry < 0)
|
||||
num_entry = 0;
|
||||
|
||||
/*
|
||||
* check the dt property to know whether the platform device wants
|
||||
* to exclude any reg ranges from the IO list
|
||||
*/
|
||||
num_exclude_entry = of_property_count_strings(pdev->dev.of_node,
|
||||
"qcom,sde-vm-exclude-reg-names");
|
||||
if (num_exclude_entry < 0)
|
||||
num_exclude_entry = 0;
|
||||
|
||||
for (i = 0; i < num_entry; i++) {
|
||||
bool exclude = false;
|
||||
|
||||
of_property_read_string_index(pdev->dev.of_node,
|
||||
"reg-names", i, ®_name);
|
||||
|
||||
for (j = 0; j < num_exclude_entry; j++) {
|
||||
of_property_read_string_index(pdev->dev.of_node,
|
||||
"qcom,sde-vm-exclude-reg-names", j,
|
||||
&exclude_reg_name);
|
||||
|
||||
if (!strcmp(reg_name, exclude_reg_name)) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exclude)
|
||||
continue;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
reg_name);
|
||||
if (!res)
|
||||
break;
|
||||
|
||||
io_mem = kzalloc(sizeof(*io_mem), GFP_KERNEL);
|
||||
if (!io_mem) {
|
||||
msm_dss_clean_io_mem(&temp_head);
|
||||
rc = -ENOMEM;
|
||||
goto parse_fail;
|
||||
}
|
||||
|
||||
io_mem->base = res->start;
|
||||
io_mem->size = resource_size(res);
|
||||
|
||||
list_add(&io_mem->list, &temp_head);
|
||||
}
|
||||
|
||||
list_splice(&temp_head, mem_list);
|
||||
|
||||
return 0;
|
||||
|
||||
parse_fail:
|
||||
msm_dss_clean_io_mem(&temp_head);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dss_get_io_mem);
|
||||
|
||||
void msm_dss_clean_io_mem(struct list_head *mem_list)
|
||||
{
|
||||
struct msm_io_mem_entry *pos, *tmp;
|
||||
|
||||
list_for_each_entry_safe(pos, tmp, mem_list, list) {
|
||||
list_del(&pos->list);
|
||||
kzfree(pos);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dss_clean_io_mem);
|
||||
|
||||
int msm_dss_get_io_irq(struct platform_device *pdev, struct list_head *irq_list,
|
||||
u32 label)
|
||||
{
|
||||
struct msm_io_irq_entry *io_irq;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
pr_err("invalid IRQ\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
io_irq = kzalloc(sizeof(*io_irq), GFP_KERNEL);
|
||||
if (!io_irq)
|
||||
return -ENOMEM;
|
||||
|
||||
io_irq->label = label;
|
||||
io_irq->irq_num = irq;
|
||||
|
||||
list_add(&io_irq->list, irq_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dss_get_io_irq);
|
||||
|
||||
void msm_dss_clean_io_irq(struct list_head *irq_list)
|
||||
{
|
||||
struct msm_io_irq_entry *pos, *tmp;
|
||||
|
||||
list_for_each_entry_safe(pos, tmp, irq_list, list) {
|
||||
list_del(&pos->list);
|
||||
kzfree(pos);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dss_clean_io_irq);
|
||||
|
||||
int msm_dss_get_vreg(struct device *dev, struct dss_vreg *in_vreg,
|
||||
int num_vreg, int enable)
|
||||
{
|
||||
|
58
msm/sde_vm_event.c
Normal file
58
msm/sde_vm_event.c
Normal file
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/sde_vm_event.h>
|
||||
#include "msm_drv.h"
|
||||
|
||||
int msm_register_vm_event(struct device *dev, struct device *client_dev,
|
||||
struct msm_vm_ops *ops, void *priv_data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *ddev = platform_get_drvdata(pdev);
|
||||
struct msm_drm_private *priv = ddev->dev_private;
|
||||
struct msm_vm_client_entry *client_entry;
|
||||
|
||||
if (!client_dev || !ops)
|
||||
return -EINVAL;
|
||||
|
||||
client_entry = kzalloc(sizeof(*client_entry), GFP_KERNEL);
|
||||
if (!client_entry)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&priv->vm_client_lock);
|
||||
|
||||
memcpy(&client_entry->ops, ops, sizeof(*ops));
|
||||
client_entry->dev = client_dev;
|
||||
client_entry->data = priv_data;
|
||||
|
||||
list_add(&client_entry->list, &priv->vm_client_list);
|
||||
|
||||
mutex_unlock(&priv->vm_client_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_register_vm_event);
|
||||
|
||||
void msm_unregister_vm_event(struct device *dev, struct device *client_dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *ddev = platform_get_drvdata(pdev);
|
||||
struct msm_drm_private *priv = ddev->dev_private;
|
||||
struct msm_vm_client_entry *client_entry, *tmp;
|
||||
|
||||
mutex_lock(&priv->vm_client_lock);
|
||||
|
||||
list_for_each_entry_safe(client_entry, tmp, &priv->vm_client_list,
|
||||
list) {
|
||||
if (client_entry->dev == client_dev) {
|
||||
list_del(&client_entry->list);
|
||||
kfree(client_entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&priv->vm_client_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_unregister_vm_event);
|
Viittaa uudesa ongelmassa
Block a user