diff --git a/include/linux/sde_io_util.h b/include/linux/sde_io_util.h index c759d45865a4..312488fce55f 100644 --- a/include/linux/sde_io_util.h +++ b/include/linux/sde_io_util.h @@ -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); diff --git a/include/linux/sde_vm_event.h b/include/linux/sde_vm_event.h new file mode 100644 index 000000000000..0fa15eac1b6c --- /dev/null +++ b/include/linux/sde_vm_event.h @@ -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 +#include +#include +#include +#include + +/** + * 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__ diff --git a/msm/Makefile b/msm/Makefile index 952746f5f5da..a4fd8a710f37 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -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 \ diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 9071fa2d8823..9eb1590540fc 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -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); diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 55ea0f1c42fb..356fb6404ce1 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -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 * */ diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 2c96618310ce..3aaba7b1bc1c 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -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); diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index c94f754b7dd9..e7b3924d4338 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -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 diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 0c1d0c73c4d1..62e7ad53595c 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -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) diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index fd4f7d06bd15..8c0b2bd69d67 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -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; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 165d1404c5d1..4e817870d5ba 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -52,6 +52,7 @@ #include #include "soc/qcom/secure_buffer.h" #include +#include #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; diff --git a/msm/sde/sde_kms.h b/msm/sde/sde_kms.h index bb3ac724944a..e23db6468e71 100644 --- a/msm/sde/sde_kms.h +++ b/msm/sde/sde_kms.h @@ -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__ */ diff --git a/msm/sde_io_util.c b/msm/sde_io_util.c index 088a6850da51..d86bcacfdb22 100644 --- a/msm/sde_io_util.c +++ b/msm/sde_io_util.c @@ -9,6 +9,7 @@ #include #include #include +#include #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) { diff --git a/msm/sde_vm_event.c b/msm/sde_vm_event.c new file mode 100644 index 000000000000..f0a571159e0f --- /dev/null +++ b/msm/sde_vm_event.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include +#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);