diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a6771cef85e2..d156336d8c96 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -18,7 +18,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_encoder.o drm_mode_object.o drm_property.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ - drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o + drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ + drm_notifier_mi.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_DRM_VM) += drm_vm.o diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1638bfe9627c..d8aca426a157 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014 Samsung Electronics Co., Ltd + * Copyright (C) 2021 XiaoMi, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -270,9 +271,15 @@ void drm_bridge_post_disable(struct drm_bridge *bridge) if (!bridge) return; + if (bridge->is_dsi_drm_bridge) + mutex_lock(&bridge->lock); + if (bridge->funcs->post_disable) bridge->funcs->post_disable(bridge); + if (bridge->is_dsi_drm_bridge) + mutex_unlock(&bridge->lock); + drm_bridge_post_disable(bridge->next); } EXPORT_SYMBOL(drm_bridge_post_disable); @@ -321,8 +328,14 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge) drm_bridge_pre_enable(bridge->next); + if (bridge->is_dsi_drm_bridge) + mutex_lock(&bridge->lock); + if (bridge->funcs->pre_enable) bridge->funcs->pre_enable(bridge); + + if (bridge->is_dsi_drm_bridge) + mutex_unlock(&bridge->lock); } EXPORT_SYMBOL(drm_bridge_pre_enable); diff --git a/drivers/gpu/drm/drm_internal_mi.h b/drivers/gpu/drm/drm_internal_mi.h new file mode 100644 index 000000000000..7446b2125d08 --- /dev/null +++ b/drivers/gpu/drm/drm_internal_mi.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2021 XiaoMi, Inc. + * + * 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. + * + */ + +#ifndef _DRM_INTERFACE_MI_H_ +#define _DRM_INTERFACE_MI_H_ + +/* dsi_display_mi.c */ +int dsi_display_set_disp_param(struct drm_connector *connector, + u32 param_type); +int dsi_display_get_disp_param(struct drm_connector *connector, + u32 *param_type); + +ssize_t dsi_display_write_mipi_reg(struct drm_connector *connector, + char *buf); +ssize_t dsi_display_read_mipi_reg(struct drm_connector *connector, + char *buf); + +ssize_t dsi_display_read_oled_pmic_id(struct drm_connector *connector, + char *buf); + +ssize_t dsi_display_read_panel_info(struct drm_connector *connector, + char *buf); + +ssize_t dsi_display_read_wp_info(struct drm_connector *connector, + char *buf); + +ssize_t dsi_display_read_dynamic_fps(struct drm_connector *connector, + char *buf); + +int dsi_display_set_doze_brightness(struct drm_connector *connector, + int doze_brightness); +ssize_t dsi_display_get_doze_brightness(struct drm_connector *connector, + char *buf); + +int dsi_display_read_gamma_param(struct drm_connector *connector); +ssize_t dsi_display_print_gamma_param(struct drm_connector *connector, + char *buf); + +ssize_t dsi_display_fod_get(struct drm_connector *connector, char *buf); + +ssize_t complete_commit_time_get(struct drm_connector *connector, char *buf); + +int dsi_display_set_thermal_hbm_disabled(struct drm_connector *connector, + bool thermal_hbm_disabled); +int dsi_display_get_thermal_hbm_disabled(struct drm_connector *connector, + bool *thermal_hbm_disabled); + +ssize_t dsi_display_get_hw_vsync_info(struct drm_connector *connector, + char *buf); + +#endif /*_DRM_INTERFACE_MI_H_*/ diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 5a672aaa110b..38e59db5cea0 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -2,6 +2,7 @@ * Created: Fri Jan 8 09:01:26 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright (C) 2021 XiaoMi, Inc. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * @@ -514,6 +515,31 @@ int drm_version(struct drm_device *dev, void *data, return err; } +const char *support_list[] = { + "displayfeature", + "DisplayFeature", + "disp_pcc", + "displayeffect", + "factoryreset", + "recovery", + NULL +}; + +static bool drm_master_filter(char *task_name) +{ + unsigned int i = 0; + bool ret = false; + + for (i = 0; support_list[i] != NULL; i++) { + if (!strncmp(task_name, support_list[i], strlen(support_list[i]))) { + ret = true; + break; + } + } + + return ret; +} + /** * drm_ioctl_permit - Check ioctl permissions against caller * @@ -528,6 +554,7 @@ int drm_version(struct drm_device *dev, void *data, */ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) { + struct task_struct *task = get_current(); /* ROOT_ONLY is only for CAP_SYS_ADMIN */ if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN))) return -EACCES; @@ -539,8 +566,11 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) /* MASTER is only for master or control clients */ if (unlikely((flags & DRM_MASTER) && - !drm_is_current_master(file_priv))) - return -EACCES; + !drm_is_current_master(file_priv))) { + if (!drm_master_filter(task->comm)) { + return -EACCES; + } + } /* Render clients must be explicitly allowed */ if (unlikely(!(flags & DRM_RENDER_ALLOW) && diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 14aae7184efd..1505e6c6e102 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -2,6 +2,7 @@ * MIPI DSI Bus * * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. + * Copyright (C) 2021 XiaoMi, Inc. * Andrzej Hajda * * Permission is hereby granted, free of charge, to any person obtaining a @@ -1071,6 +1072,29 @@ int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, } EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness); +/** + * mipi_dsi_dcs_set_display_brightness_bigendian() - sets the brightness value of the + * display with big endian, high byte to 1st parameter, low byte to 2nd parameter + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_display_brightness_big_endian(struct mipi_dsi_device *dsi, + u16 brightness) +{ + u8 payload[2] = { brightness >> 8, brightness & 0xff}; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + payload, sizeof(payload)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness_big_endian); + /** * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value * of the display diff --git a/drivers/gpu/drm/drm_notifier_mi.c b/drivers/gpu/drm/drm_notifier_mi.c new file mode 100644 index 000000000000..792069640ea7 --- /dev/null +++ b/drivers/gpu/drm/drm_notifier_mi.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2021 XiaoMi, Inc. + * + * 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 + +static BLOCKING_NOTIFIER_HEAD(mi_drm_notifier_list); + +/** + * mi_drm_register_client - register a client notifier + * @nb: notifier block to callback on events + * + * This function registers a notifier callback function + * to msm_drm_notifier_list, which would be called when + * received unblank/power down event. + */ +int mi_drm_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&mi_drm_notifier_list, nb); +} +EXPORT_SYMBOL(mi_drm_register_client); + +/** + * mi_drm_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + * + * This function unregisters the callback function from + * msm_drm_notifier_list. + */ +int mi_drm_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&mi_drm_notifier_list, nb); +} +EXPORT_SYMBOL(mi_drm_unregister_client); + +/** + * mi_drm_notifier_call_chain - notify clients of drm_events + * @val: event MSM_DRM_EARLY_EVENT_BLANK or MSM_DRM_EVENT_BLANK + * @v: notifier data, inculde display id and display blank + * event(unblank or power down). + */ +int mi_drm_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&mi_drm_notifier_list, val, v); +} +EXPORT_SYMBOL(mi_drm_notifier_call_chain); \ No newline at end of file diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ecb7b33002bb..30bde6687f7c 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -5,6 +5,7 @@ * does not allow adding attributes. * * Copyright (c) 2004 Jon Smirl + * Copyright (C) 2021 XiaoMi, Inc. * Copyright (c) 2003-2004 Greg Kroah-Hartman * Copyright (c) 2003-2004 IBM Corp. * @@ -21,10 +22,13 @@ #include #include #include "drm_internal.h" +#include "drm_internal_mi.h" + #define to_drm_minor(d) dev_get_drvdata(d) #define to_drm_connector(d) dev_get_drvdata(d) + /** * DOC: overview * @@ -44,6 +48,7 @@ static struct device_type drm_sysfs_device_minor = { }; struct class *drm_class; +struct device *connector_kdev; static char *drm_devnode(struct device *dev, umode_t *mode) { @@ -229,16 +234,251 @@ static ssize_t modes_show(struct device *device, return written; } +static ssize_t disp_param_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_connector *connector = to_drm_connector(device); + char *input_copy, *input_dup = NULL; + u32 param; + int ret; + + input_copy = kstrdup(buf, GFP_KERNEL); + if (!input_copy) { + DRM_ERROR("can not allocate memory\n"); + ret = -ENOMEM; + goto exit; + } + input_dup = input_copy; + /* removes leading and trailing whitespace from input_copy */ + input_copy = strim(input_copy); + ret = kstrtouint(input_copy, 16, ¶m); + if (ret) { + DRM_ERROR("input buffer conversion failed\n"); + ret = -EAGAIN; + goto exit_free; + } + + ret = dsi_display_set_disp_param(connector, param); + +exit_free: + kfree(input_dup); +exit: + return ret ? ret : count; +} + +static ssize_t disp_param_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + u32 param; + + dsi_display_get_disp_param(connector, ¶m); + + return snprintf(buf, PAGE_SIZE, "0x%08X\n", param); +} + +static ssize_t mipi_reg_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_connector *connector = to_drm_connector(device); + int ret; + + ret = dsi_display_write_mipi_reg(connector, (char *)buf); + + return ret ? ret : count; +} + +static ssize_t mipi_reg_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + return dsi_display_read_mipi_reg(connector, buf); +} + +static ssize_t oled_pmic_id_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + return dsi_display_read_oled_pmic_id(connector, buf); +} + +static ssize_t panel_info_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + return dsi_display_read_panel_info(connector, buf); +} + +static ssize_t wp_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + return dsi_display_read_wp_info(connector, buf); +} + +static ssize_t dynamic_fps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + return dsi_display_read_dynamic_fps(connector, buf); +} + +static ssize_t doze_brightness_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_connector *connector = to_drm_connector(device); + int doze_brightness; + int ret; + + ret = kstrtoint(buf, 0, &doze_brightness);; + if (ret) + return ret; + + ret = dsi_display_set_doze_brightness(connector, doze_brightness); + + return ret ? ret : count; +} + +static ssize_t doze_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + return dsi_display_get_doze_brightness(connector, buf); +} + +static ssize_t gamma_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + int ret = 0; + + ret = dsi_display_read_gamma_param(connector); + if (ret) { + pr_err("Failed to update panel id and gamma para!\n"); + } + + ret = dsi_display_print_gamma_param(connector, buf); + return ret; +} + +extern ssize_t smart_fps_value_show(struct device *device, + struct device_attribute *attr, + char *buf); + +static ssize_t fod_ui_ready_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + return dsi_display_fod_get(connector, buf); +} + +static ssize_t complete_commit_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(dev); + return complete_commit_time_get(connector, buf); +} + + +static ssize_t thermal_hbm_disabled_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_connector *connector = to_drm_connector(device); + char *input_copy, *input_dup = NULL; + bool thermal_hbm_disabled; + int ret; + + input_copy = kstrdup(buf, GFP_KERNEL); + if (!input_copy) { + DRM_ERROR("can not allocate memory\n"); + ret = -ENOMEM; + goto exit; + } + input_dup = input_copy; + /* removes leading and trailing whitespace from input_copy */ + input_copy = strim(input_copy); + ret = kstrtobool(input_copy, &thermal_hbm_disabled); + if (ret) { + DRM_ERROR("input buffer conversion failed\n"); + ret = -EAGAIN; + goto exit_free; + } + + DRM_INFO("set thermal_hbm_disabled %d\n", thermal_hbm_disabled); + ret = dsi_display_set_thermal_hbm_disabled(connector, thermal_hbm_disabled); + +exit_free: + kfree(input_dup); +exit: + return ret ? ret : count; +} + +static ssize_t thermal_hbm_disabled_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + bool thermal_hbm_disabled; + + dsi_display_get_thermal_hbm_disabled(connector, &thermal_hbm_disabled); + + return snprintf(buf, PAGE_SIZE, "%d\n", thermal_hbm_disabled); +} + +static ssize_t hw_vsync_info_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + return dsi_display_get_hw_vsync_info(connector, buf); +} + static DEVICE_ATTR_RW(status); static DEVICE_ATTR_RO(enabled); static DEVICE_ATTR_RO(dpms); static DEVICE_ATTR_RO(modes); +static DEVICE_ATTR_RW(disp_param); +static DEVICE_ATTR_RW(mipi_reg); +static DEVICE_ATTR_RO(oled_pmic_id); +static DEVICE_ATTR_RO(panel_info); +static DEVICE_ATTR_RO(wp_info); +static DEVICE_ATTR_RO(dynamic_fps); +static DEVICE_ATTR_RW(doze_brightness); +static DEVICE_ATTR_RO(gamma_test); +static DEVICE_ATTR_RO(fod_ui_ready); +static DEVICE_ATTR_RO(smart_fps_value); +static DEVICE_ATTR_RO(complete_commit_time); +static DEVICE_ATTR_RW(thermal_hbm_disabled); +static DEVICE_ATTR_RO(hw_vsync_info); static struct attribute *connector_dev_attrs[] = { &dev_attr_status.attr, &dev_attr_enabled.attr, &dev_attr_dpms.attr, &dev_attr_modes.attr, + &dev_attr_disp_param.attr, + &dev_attr_mipi_reg.attr, + &dev_attr_oled_pmic_id.attr, + &dev_attr_panel_info.attr, + &dev_attr_wp_info.attr, + &dev_attr_dynamic_fps.attr, + &dev_attr_doze_brightness.attr, + &dev_attr_gamma_test.attr, + &dev_attr_fod_ui_ready.attr, + &dev_attr_smart_fps_value.attr, + &dev_attr_complete_commit_time.attr, + &dev_attr_thermal_hbm_disabled.attr, + &dev_attr_hw_vsync_info.attr, NULL }; @@ -279,6 +519,9 @@ int drm_sysfs_connector_add(struct drm_connector *connector) DRM_DEBUG("adding \"%s\" to sysfs\n", connector->name); + if (!connector_kdev) + connector_kdev = connector->kdev; + if (IS_ERR(connector->kdev)) { DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev)); return PTR_ERR(connector->kdev); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index bd850747ce54..eab2230699d7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Intel Corporation + * Copyright (C) 2021 XiaoMi, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -294,6 +295,8 @@ struct drm_bridge { const struct drm_bridge_funcs *funcs; /** @driver_private: pointer to the bridge driver's internal context */ void *driver_private; + struct mutex lock; + bool is_dsi_drm_bridge; }; void drm_bridge_add(struct drm_bridge *bridge); @@ -314,6 +317,7 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode); void drm_bridge_pre_enable(struct drm_bridge *bridge); void drm_bridge_enable(struct drm_bridge *bridge); +int dsi_bridge_interface_enable(int timeout); #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 328f232f33ee..54e90e53151f 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -2,6 +2,7 @@ * MIPI DSI Bus * * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. + * Copyright (C) 2021 XiaoMi, Inc. * Andrzej Hajda * * This program is free software; you can redistribute it and/or modify @@ -284,6 +285,8 @@ int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format); int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline); int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, u16 brightness); +int mipi_dsi_dcs_set_display_brightness_big_endian(struct mipi_dsi_device *dsi, + u16 brightness); int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, u16 *brightness); diff --git a/include/drm/drm_notifier_mi.h b/include/drm/drm_notifier_mi.h new file mode 100644 index 000000000000..f6fb33fb1e17 --- /dev/null +++ b/include/drm/drm_notifier_mi.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2021 XiaoMi, Inc. + * + * 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. + * + */ + +#ifndef _DRM_NOTIFIER_MI_H_ +#define _DRM_NOTIFIER_MI_H_ + +#include + +/* A hardware display blank change occurred */ +#define MI_DRM_EVENT_BLANK 0x01 +/* A hardware display blank early change occurred */ +#define MI_DRM_EARLY_EVENT_BLANK 0x02 +/* A hardware display blank more early change occured */ +#define MI_DRM_PRE_EVENT_BLANK 0x03 + +enum msm_drm_display_id { + /* primary display */ + MSM_DRM_PRIMARY_DISPLAY, + /* external display */ + MSM_DRM_EXTERNAL_DISPLAY, + MSM_DRM_DISPLAY_MAX +}; + +enum { + /* panel: power on */ + MI_DRM_BLANK_UNBLANK = 0, + MI_DRM_BLANK_LP1 = 1, + MI_DRM_BLANK_LP2 = 2, + MI_DRM_BLANK_STANDBY = 3, + MI_DRM_BLANK_SUSPEND = 4, + /* panel: power off */ + MI_DRM_BLANK_POWERDOWN = 5, +}; + +struct mi_drm_notifier { + enum msm_drm_display_id id; + void *data; +}; + +int mi_drm_register_client(struct notifier_block *nb); +int mi_drm_unregister_client(struct notifier_block *nb); +int mi_drm_notifier_call_chain(unsigned long val, void *v); + +#endif /*_DRM_NOTIFIER_MI_H*/