diff options
Diffstat (limited to 'include/drm')
176 files changed, 23452 insertions, 8258 deletions
diff --git a/include/drm/Makefile b/include/drm/Makefile new file mode 100644 index 000000000000..48fae3f167c7 --- /dev/null +++ b/include/drm/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ensure drm headers are self-contained and pass kernel-doc +hdrtest-files := \ + $(shell cd $(src) && find * -name '*.h' 2>/dev/null) + +always-$(CONFIG_DRM_HEADER_TEST) += \ + $(patsubst %.h,%.hdrtest, $(hdrtest-files)) + +# Include the header twice to detect missing include guard. +quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) + cmd_hdrtest = \ + $(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \ + PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ + touch $@ + +$(obj)/%.hdrtest: $(src)/%.h FORCE + $(call if_changed_dep,hdrtest) diff --git a/include/drm/ttm/ttm_module.h b/include/drm/amd/isp.h index 45fa318c1585..ec868288abf2 100644 --- a/include/drm/ttm/ttm_module.h +++ b/include/drm/amd/isp.h @@ -1,6 +1,6 @@ -/************************************************************************** - * - * Copyright 2008-2009 VMware, Inc., Palo Alto, CA., USA +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -11,10 +11,6 @@ * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL @@ -23,18 +19,33 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * - **************************************************************************/ -/* - * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * */ -#ifndef _TTM_MODULE_H_ -#define _TTM_MODULE_H_ +#ifndef __ISP_H__ +#define __ISP_H__ + +#include <linux/types.h> + +struct device; + +struct isp_platform_data { + void *adev; + u32 asic_type; + resource_size_t base_rmmio_size; +}; + +int isp_user_buffer_alloc(struct device *dev, void *dmabuf, + void **buf_obj, u64 *buf_addr); + +void isp_user_buffer_free(void *buf_obj); -#include <linux/kernel.h> -struct kobject; +int isp_kernel_buffer_alloc(struct device *dev, u64 size, + void **buf_obj, u64 *gpu_addr, void **cpu_addr); -#define TTM_PFX "[TTM] " -extern struct kobject *ttm_get_kobj(void); +void isp_kernel_buffer_free(void **buf_obj, u64 *gpu_addr, void **cpu_addr); -#endif /* _TTM_MODULE_H_ */ +#endif diff --git a/include/drm/amd_asic_type.h b/include/drm/amd_asic_type.h index dd63d08cc54e..9be85b821aa6 100644 --- a/include/drm/amd_asic_type.h +++ b/include/drm/amd_asic_type.h @@ -22,34 +22,58 @@ #ifndef __AMD_ASIC_TYPE_H__ #define __AMD_ASIC_TYPE_H__ + +#include <linux/types.h> + /* * Supported ASIC types */ enum amd_asic_type { CHIP_TAHITI = 0, - CHIP_PITCAIRN, - CHIP_VERDE, - CHIP_OLAND, - CHIP_HAINAN, - CHIP_BONAIRE, - CHIP_KAVERI, - CHIP_KABINI, - CHIP_HAWAII, - CHIP_MULLINS, - CHIP_TOPAZ, - CHIP_TONGA, - CHIP_FIJI, - CHIP_CARRIZO, - CHIP_STONEY, - CHIP_POLARIS10, - CHIP_POLARIS11, - CHIP_POLARIS12, - CHIP_VEGAM, - CHIP_VEGA10, - CHIP_VEGA12, - CHIP_VEGA20, - CHIP_RAVEN, + CHIP_PITCAIRN, /* 1 */ + CHIP_VERDE, /* 2 */ + CHIP_OLAND, /* 3 */ + CHIP_HAINAN, /* 4 */ + CHIP_BONAIRE, /* 5 */ + CHIP_KAVERI, /* 6 */ + CHIP_KABINI, /* 7 */ + CHIP_HAWAII, /* 8 */ + CHIP_MULLINS, /* 9 */ + CHIP_TOPAZ, /* 10 */ + CHIP_TONGA, /* 11 */ + CHIP_FIJI, /* 12 */ + CHIP_CARRIZO, /* 13 */ + CHIP_STONEY, /* 14 */ + CHIP_POLARIS10, /* 15 */ + CHIP_POLARIS11, /* 16 */ + CHIP_POLARIS12, /* 17 */ + CHIP_VEGAM, /* 18 */ + CHIP_VEGA10, /* 19 */ + CHIP_VEGA12, /* 20 */ + CHIP_VEGA20, /* 21 */ + CHIP_RAVEN, /* 22 */ + CHIP_ARCTURUS, /* 23 */ + CHIP_RENOIR, /* 24 */ + CHIP_ALDEBARAN, /* 25 */ + CHIP_NAVI10, /* 26 */ + CHIP_CYAN_SKILLFISH, /* 27 */ + CHIP_NAVI14, /* 28 */ + CHIP_NAVI12, /* 29 */ + CHIP_SIENNA_CICHLID, /* 30 */ + CHIP_NAVY_FLOUNDER, /* 31 */ + CHIP_VANGOGH, /* 32 */ + CHIP_DIMGREY_CAVEFISH, /* 33 */ + CHIP_BEIGE_GOBY, /* 34 */ + CHIP_YELLOW_CARP, /* 35 */ + CHIP_IP_DISCOVERY, /* 36 */ CHIP_LAST, }; +extern const char *amdgpu_asic_name[]; + +struct amdgpu_asic_type_quirk { + unsigned short device; /* PCI device ID */ + u8 revision; /* revision ID */ + unsigned short type; /* real ASIC type */ +}; #endif /*__AMD_ASIC_TYPE_H__ */ diff --git a/include/drm/ati_pcigart.h b/include/drm/ati_pcigart.h deleted file mode 100644 index a728a1364e66..000000000000 --- a/include/drm/ati_pcigart.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef DRM_ATI_PCIGART_H -#define DRM_ATI_PCIGART_H - -#include <drm/drm_legacy.h> - -/* location of GART table */ -#define DRM_ATI_GART_MAIN 1 -#define DRM_ATI_GART_FB 2 - -#define DRM_ATI_GART_PCI 1 -#define DRM_ATI_GART_PCIE 2 -#define DRM_ATI_GART_IGP 3 - -struct drm_ati_pcigart_info { - int gart_table_location; - int gart_reg_if; - void *addr; - dma_addr_t bus_addr; - dma_addr_t table_mask; - struct drm_dma_handle *table_handle; - struct drm_local_map mapping; - int table_size; -}; - -extern int drm_ati_pcigart_init(struct drm_device *dev, - struct drm_ati_pcigart_info * gart_info); -extern int drm_ati_pcigart_cleanup(struct drm_device *dev, - struct drm_ati_pcigart_info * gart_info); - -#endif diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 475b706b49de..cf17646c1310 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -1,12 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Analogix DP (Display Port) Core interface driver. * * Copyright (C) 2015 Rockchip Electronics Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #ifndef _ANALOGIX_DP_H_ #define _ANALOGIX_DP_H_ @@ -14,16 +10,18 @@ #include <drm/drm_crtc.h> struct analogix_dp_device; +struct drm_dp_aux; enum analogix_dp_devtype { EXYNOS_DP, RK3288_DP, RK3399_EDP, + RK3588_EDP, }; static inline bool is_rockchip(enum analogix_dp_devtype type) { - return type == RK3288_DP || type == RK3399_EDP; + return type == RK3288_DP || type == RK3399_EDP || type == RK3588_EDP; } struct analogix_dp_plat_data { @@ -33,8 +31,7 @@ struct analogix_dp_plat_data { struct drm_connector *connector; bool skip_connector; - int (*power_on_start)(struct analogix_dp_plat_data *); - int (*power_on_end)(struct analogix_dp_plat_data *); + int (*power_on)(struct analogix_dp_plat_data *); int (*power_off)(struct analogix_dp_plat_data *); int (*attach)(struct analogix_dp_plat_data *, struct drm_bridge *, struct drm_connector *); @@ -42,19 +39,18 @@ struct analogix_dp_plat_data { struct drm_connector *); }; -int analogix_dp_psr_enabled(struct analogix_dp_device *dp); -int analogix_dp_enable_psr(struct analogix_dp_device *dp); -int analogix_dp_disable_psr(struct analogix_dp_device *dp); - int analogix_dp_resume(struct analogix_dp_device *dp); int analogix_dp_suspend(struct analogix_dp_device *dp); struct analogix_dp_device * -analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, - struct analogix_dp_plat_data *plat_data); +analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data); +int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev); void analogix_dp_unbind(struct analogix_dp_device *dp); int analogix_dp_start_crc(struct drm_connector *connector); int analogix_dp_stop_crc(struct drm_connector *connector); +struct analogix_dp_plat_data *analogix_dp_aux_to_plat_data(struct drm_dp_aux *aux); +struct drm_dp_aux *analogix_dp_get_aux(struct analogix_dp_device *dp); + #endif /* _ANALOGIX_DP_H_ */ diff --git a/include/drm/bridge/aux-bridge.h b/include/drm/bridge/aux-bridge.h new file mode 100644 index 000000000000..c2f5a855512f --- /dev/null +++ b/include/drm/bridge/aux-bridge.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Linaro Ltd. + * + * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> + */ +#ifndef DRM_AUX_BRIDGE_H +#define DRM_AUX_BRIDGE_H + +#include <drm/drm_connector.h> + +struct auxiliary_device; + +#if IS_ENABLED(CONFIG_DRM_AUX_BRIDGE) +int drm_aux_bridge_register(struct device *parent); +#else +static inline int drm_aux_bridge_register(struct device *parent) +{ + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_DRM_AUX_HPD_BRIDGE) +struct auxiliary_device *devm_drm_dp_hpd_bridge_alloc(struct device *parent, struct device_node *np); +int devm_drm_dp_hpd_bridge_add(struct device *dev, struct auxiliary_device *adev); +struct device *drm_dp_hpd_bridge_register(struct device *parent, + struct device_node *np); +void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status); +#else +static inline struct auxiliary_device *devm_drm_dp_hpd_bridge_alloc(struct device *parent, + struct device_node *np) +{ + return NULL; +} + +static inline int devm_drm_dp_hpd_bridge_add(struct device *dev, struct auxiliary_device *adev) +{ + return 0; +} + +static inline struct device *drm_dp_hpd_bridge_register(struct device *parent, + struct device_node *np) +{ + return NULL; +} + +static inline void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status) +{ +} +#endif + +#endif diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h new file mode 100644 index 000000000000..d05df49fd884 --- /dev/null +++ b/include/drm/bridge/dw_dp.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + */ + +#ifndef __DW_DP__ +#define __DW_DP__ + +#include <linux/device.h> + +struct drm_encoder; +struct dw_dp; + +struct dw_dp_plat_data { + u32 max_link_rate; +}; + +struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder, + const struct dw_dp_plat_data *plat_data); +#endif /* __DW_DP__ */ diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 9c56412bb2cf..336f062e1f9d 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -1,18 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2011 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __DW_HDMI__ #define __DW_HDMI__ -#include <drm/drmP.h> +#include <sound/hdmi-codec.h> +struct drm_display_info; +struct drm_display_mode; +struct drm_encoder; struct dw_hdmi; +struct platform_device; /** * DOC: Supported input formats and encodings @@ -114,7 +114,8 @@ struct dw_hdmi_phy_config { struct dw_hdmi_phy_ops { int (*init)(struct dw_hdmi *hdmi, void *data, - struct drm_display_mode *mode); + const struct drm_display_info *display, + const struct drm_display_mode *mode); void (*disable)(struct dw_hdmi *hdmi, void *data); enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data); void (*update_hpd)(struct dw_hdmi *hdmi, void *data, @@ -124,10 +125,34 @@ struct dw_hdmi_phy_ops { struct dw_hdmi_plat_data { struct regmap *regm; - enum drm_mode_status (*mode_valid)(struct drm_connector *connector, - const struct drm_display_mode *mode); - unsigned long input_bus_format; + + unsigned int output_port; + unsigned long input_bus_encoding; + bool use_drm_infoframe; + bool ycbcr_420_allowed; + + /* + * Private data passed to all the .mode_valid() and .configure_phy() + * callback functions. + */ + void *priv_data; + + /* Platform-specific mode validation (optional). */ + enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *info, + const struct drm_display_mode *mode); + + /* + * priv_audio is specially used for additional audio device to get + * driver data through this dw_hdmi_plat_data. + */ + void *priv_audio; + + /* Platform-specific audio enable/disable (optional) */ + void (*enable_audio)(struct dw_hdmi *hdmi, int channel, + int width, int rate, int non_pcm, int iec958); + void (*disable_audio)(struct dw_hdmi *hdmi); /* Vendor PHY support */ const struct dw_hdmi_phy_ops *phy_ops; @@ -139,9 +164,10 @@ struct dw_hdmi_plat_data { const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_curr_ctrl *cur_ctr; const struct dw_hdmi_phy_config *phy_config; - int (*configure_phy)(struct dw_hdmi *hdmi, - const struct dw_hdmi_plat_data *pdata, + int (*configure_phy)(struct dw_hdmi *hdmi, void *data, unsigned long mpixelclock); + + unsigned int disable_cec : 1; }; struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, @@ -152,20 +178,34 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data); +void dw_hdmi_resume(struct dw_hdmi *hdmi); + void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); +int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn, + struct device *codec_dev); +void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm); +void dw_hdmi_set_sample_iec958(struct dw_hdmi *hdmi, unsigned int iec958); +void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width); void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); +void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt); +void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status); +void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); +void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi, + const struct drm_display_info *display); /* PHY configuration */ void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address); void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, unsigned char addr); +void dw_hdmi_phy_gen1_reset(struct dw_hdmi *hdmi); + void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable); void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable); -void dw_hdmi_phy_reset(struct dw_hdmi *hdmi); +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi); enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data); @@ -173,4 +213,8 @@ void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, bool force, bool disabled, bool rxsense); void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); +bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi); + +const struct dw_hdmi_plat_data *dw_hdmi_to_plat_data(struct dw_hdmi *hdmi); + #endif /* __IMX_HDMI_H__ */ diff --git a/include/drm/bridge/dw_hdmi_qp.h b/include/drm/bridge/dw_hdmi_qp.h new file mode 100644 index 000000000000..3f461f6b9bbf --- /dev/null +++ b/include/drm/bridge/dw_hdmi_qp.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. + * Copyright (c) 2024 Collabora Ltd. + */ + +#ifndef __DW_HDMI_QP__ +#define __DW_HDMI_QP__ + +struct device; +struct drm_encoder; +struct dw_hdmi_qp; +struct platform_device; + +struct dw_hdmi_qp_phy_ops { + int (*init)(struct dw_hdmi_qp *hdmi, void *data); + void (*disable)(struct dw_hdmi_qp *hdmi, void *data); + enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, void *data); + void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); +}; + +struct dw_hdmi_qp_plat_data { + const struct dw_hdmi_qp_phy_ops *phy_ops; + void *phy_data; + int main_irq; + int cec_irq; + unsigned long ref_clk_rate; + /* Supported output formats: bitmask of @hdmi_colorspace */ + unsigned int supported_formats; + /* Maximum bits per color channel: 8, 10 or 12 */ + unsigned int max_bpc; +}; + +struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, + struct drm_encoder *encoder, + const struct dw_hdmi_qp_plat_data *plat_data); +void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); +#endif /* __DW_HDMI_QP__ */ diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 48a671e782ca..65d5e68065e3 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -1,22 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) STMicroelectronics SA 2017 * * Authors: Philippe Cornu <philippe.cornu@st.com> * Yannick Fertre <yannick.fertre@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ #ifndef __DW_MIPI_DSI__ #define __DW_MIPI_DSI__ +#include <linux/types.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_modes.h> + +struct drm_display_mode; +struct drm_encoder; struct dw_mipi_dsi; +struct mipi_dsi_device; +struct platform_device; + +struct dw_mipi_dsi_dphy_timing { + u16 data_hs2lp; + u16 data_lp2hs; + u16 clk_hs2lp; + u16 clk_lp2hs; +}; struct dw_mipi_dsi_phy_ops { int (*init)(void *priv_data); - int (*get_lane_mbps)(void *priv_data, struct drm_display_mode *mode, + void (*power_on)(void *priv_data); + void (*power_off)(void *priv_data); + int (*get_lane_mbps)(void *priv_data, + const struct drm_display_mode *mode, unsigned long mode_flags, u32 lanes, u32 format, unsigned int *lane_mbps); + int (*get_timing)(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing); + int (*get_esc_clk_rate)(void *priv_data, unsigned int *esc_clk_rate); }; struct dw_mipi_dsi_host_ops { @@ -31,7 +55,20 @@ struct dw_mipi_dsi_plat_data { unsigned int max_data_lanes; enum drm_mode_status (*mode_valid)(void *priv_data, - const struct drm_display_mode *mode); + const struct drm_display_mode *mode, + unsigned long mode_flags, + u32 lanes, u32 format); + + bool (*mode_fixup)(void *priv_data, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + u32 *(*get_input_bus_fmts)(void *priv_data, + struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); const struct dw_mipi_dsi_phy_ops *phy_ops; const struct dw_mipi_dsi_host_ops *host_ops; @@ -46,5 +83,6 @@ void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi); int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder); void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi); void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave); +struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi); #endif /* __DW_MIPI_DSI__ */ diff --git a/include/drm/bridge/dw_mipi_dsi2.h b/include/drm/bridge/dw_mipi_dsi2.h new file mode 100644 index 000000000000..c18c49379247 --- /dev/null +++ b/include/drm/bridge/dw_mipi_dsi2.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024, Fuzhou Rockchip Electronics Co., Ltd + * + * Authors: Guochun Huang <hero.huang@rock-chips.com> + * Heiko Stuebner <heiko.stuebner@cherry.de> + */ + +#ifndef __DW_MIPI_DSI2__ +#define __DW_MIPI_DSI2__ + +#include <linux/regmap.h> +#include <linux/types.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_modes.h> + +struct drm_display_mode; +struct drm_encoder; +struct dw_mipi_dsi2; +struct mipi_dsi_device; +struct platform_device; + +enum dw_mipi_dsi2_phy_type { + DW_MIPI_DSI2_DPHY, + DW_MIPI_DSI2_CPHY, +}; + +struct dw_mipi_dsi2_phy_iface { + int ppi_width; + enum dw_mipi_dsi2_phy_type phy_type; +}; + +struct dw_mipi_dsi2_phy_timing { + u32 data_hs2lp; + u32 data_lp2hs; +}; + +struct dw_mipi_dsi2_phy_ops { + int (*init)(void *priv_data); + void (*power_on)(void *priv_data); + void (*power_off)(void *priv_data); + void (*get_interface)(void *priv_data, struct dw_mipi_dsi2_phy_iface *iface); + int (*get_lane_mbps)(void *priv_data, + const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps); + int (*get_timing)(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi2_phy_timing *timing); + int (*get_esc_clk_rate)(void *priv_data, unsigned int *esc_clk_rate); +}; + +struct dw_mipi_dsi2_host_ops { + int (*attach)(void *priv_data, + struct mipi_dsi_device *dsi); + int (*detach)(void *priv_data, + struct mipi_dsi_device *dsi); +}; + +struct dw_mipi_dsi2_plat_data { + struct regmap *regmap; + unsigned int max_data_lanes; + + enum drm_mode_status (*mode_valid)(void *priv_data, + const struct drm_display_mode *mode, + unsigned long mode_flags, + u32 lanes, u32 format); + + bool (*mode_fixup)(void *priv_data, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + u32 *(*get_input_bus_fmts)(void *priv_data, + struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + + const struct dw_mipi_dsi2_phy_ops *phy_ops; + const struct dw_mipi_dsi2_host_ops *host_ops; + + void *priv_data; +}; + +struct dw_mipi_dsi2 *dw_mipi_dsi2_probe(struct platform_device *pdev, + const struct dw_mipi_dsi2_plat_data *plat_data); +void dw_mipi_dsi2_remove(struct dw_mipi_dsi2 *dsi2); +int dw_mipi_dsi2_bind(struct dw_mipi_dsi2 *dsi2, struct drm_encoder *encoder); +void dw_mipi_dsi2_unbind(struct dw_mipi_dsi2 *dsi2); + +#endif /* __DW_MIPI_DSI2__ */ diff --git a/include/drm/bridge/imx.h b/include/drm/bridge/imx.h new file mode 100644 index 000000000000..b93f719fe0e7 --- /dev/null +++ b/include/drm/bridge/imx.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Sascha Hauer, Pengutronix + */ + +#ifndef DRM_IMX_BRIDGE_H +#define DRM_IMX_BRIDGE_H + +struct device; +struct device_node; +struct drm_bridge; + +struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev, + struct device_node *np, + int type); + +#endif diff --git a/include/drm/bridge/mhl.h b/include/drm/bridge/mhl.h index 96a5e0f6ff12..d96626a0e3fa 100644 --- a/include/drm/bridge/mhl.h +++ b/include/drm/bridge/mhl.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Defines for Mobile High-Definition Link (MHL) interface * @@ -6,10 +7,6 @@ * * Based on MHL driver for Android devices. * Copyright (C) 2013-2014 Silicon Image, 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 as - * published by the Free Software Foundation. */ #ifndef __MHL_H__ @@ -330,13 +327,13 @@ struct mhl_burst_bits_per_pixel_fmt { struct { u8 stream_id; u8 pixel_format; - } __packed desc[0]; + } __packed desc[]; } __packed; struct mhl_burst_emsc_support { struct mhl3_burst_header hdr; u8 num_entries; - __be16 burst_id[0]; + __be16 burst_id[]; } __packed; struct mhl_burst_audio_descr { diff --git a/include/drm/bridge/samsung-dsim.h b/include/drm/bridge/samsung-dsim.h new file mode 100644 index 000000000000..31d7ed589233 --- /dev/null +++ b/include/drm/bridge/samsung-dsim.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2022 Amarula Solutions(India) + * Author: Jagan Teki <jagan@amarulasolutions.com> + */ + +#ifndef __SAMSUNG_DSIM__ +#define __SAMSUNG_DSIM__ + +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> + +struct platform_device; +struct samsung_dsim; + +#define DSIM_STATE_ENABLED BIT(0) +#define DSIM_STATE_INITIALIZED BIT(1) +#define DSIM_STATE_CMD_LPM BIT(2) +#define DSIM_STATE_VIDOUT_AVAILABLE BIT(3) + +enum samsung_dsim_type { + DSIM_TYPE_EXYNOS3250, + DSIM_TYPE_EXYNOS4210, + DSIM_TYPE_EXYNOS5410, + DSIM_TYPE_EXYNOS5422, + DSIM_TYPE_EXYNOS5433, + DSIM_TYPE_EXYNOS7870, + DSIM_TYPE_IMX8MM, + DSIM_TYPE_IMX8MP, + DSIM_TYPE_COUNT, +}; + +#define samsung_dsim_hw_is_exynos(hw) \ + ((hw) >= DSIM_TYPE_EXYNOS3250 && (hw) <= DSIM_TYPE_EXYNOS5433) + +struct samsung_dsim_transfer { + struct list_head list; + struct completion completed; + int result; + struct mipi_dsi_packet packet; + u16 flags; + u16 tx_done; + + u8 *rx_payload; + u16 rx_len; + u16 rx_done; +}; + +struct samsung_dsim_driver_data { + const unsigned int *reg_ofs; + unsigned int plltmr_reg; + unsigned int has_legacy_status_reg:1; + unsigned int has_freqband:1; + unsigned int has_clklane_stop:1; + unsigned int has_broken_fifoctrl_emptyhdr:1; + unsigned int has_sfrctrl:1; + struct clk_bulk_data *clk_data; + unsigned int num_clks; + unsigned int min_freq; + unsigned int max_freq; + unsigned int wait_for_hdr_fifo; + unsigned int wait_for_reset; + unsigned int num_bits_resol; + unsigned int video_mode_bit; + unsigned int pll_stable_bit; + unsigned int esc_clken_bit; + unsigned int byte_clken_bit; + unsigned int tx_req_hsclk_bit; + unsigned int lane_esc_clk_bit; + unsigned int lane_esc_data_offset; + unsigned int pll_p_offset; + unsigned int pll_m_offset; + unsigned int pll_s_offset; + unsigned int main_vsa_offset; + const unsigned int *reg_values; + unsigned int pll_fin_min; + unsigned int pll_fin_max; + u16 m_min; + u16 m_max; +}; + +struct samsung_dsim_host_ops { + int (*register_host)(struct samsung_dsim *dsim); + void (*unregister_host)(struct samsung_dsim *dsim); + int (*attach)(struct samsung_dsim *dsim, struct mipi_dsi_device *device); + void (*detach)(struct samsung_dsim *dsim, struct mipi_dsi_device *device); + irqreturn_t (*te_irq_handler)(struct samsung_dsim *dsim); +}; + +struct samsung_dsim_plat_data { + enum samsung_dsim_type hw_type; + const struct samsung_dsim_host_ops *host_ops; +}; + +struct samsung_dsim { + struct mipi_dsi_host dsi_host; + struct drm_bridge bridge; + struct drm_bridge *out_bridge; + struct device *dev; + struct drm_display_mode mode; + + void __iomem *reg_base; + struct phy *phy; + struct clk *pll_clk; + struct regulator_bulk_data supplies[2]; + int irq; + struct gpio_desc *te_gpio; + + u32 pll_clk_rate; + u32 burst_clk_rate; + u32 hs_clock; + u32 esc_clk_rate; + u32 lanes; + u32 mode_flags; + u32 format; + + bool swap_dn_dp_clk; + bool swap_dn_dp_data; + int state; + struct drm_property *brightness; + struct completion completed; + + spinlock_t transfer_lock; /* protects transfer_list */ + struct list_head transfer_list; + + const struct samsung_dsim_driver_data *driver_data; + const struct samsung_dsim_plat_data *plat_data; + + void *priv; +}; + +extern int samsung_dsim_probe(struct platform_device *pdev); +extern void samsung_dsim_remove(struct platform_device *pdev); +extern const struct dev_pm_ops samsung_dsim_pm_ops; + +#endif /* __SAMSUNG_DSIM__ */ diff --git a/include/drm/clients/drm_client_setup.h b/include/drm/clients/drm_client_setup.h new file mode 100644 index 000000000000..46aab3fb46be --- /dev/null +++ b/include/drm/clients/drm_client_setup.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_CLIENT_SETUP_H +#define DRM_CLIENT_SETUP_H + +#include <linux/types.h> + +struct drm_device; +struct drm_format_info; + +#if defined(CONFIG_DRM_CLIENT_SETUP) +void drm_client_setup(struct drm_device *dev, const struct drm_format_info *format); +void drm_client_setup_with_fourcc(struct drm_device *dev, u32 fourcc); +void drm_client_setup_with_color_mode(struct drm_device *dev, unsigned int color_mode); +#else +static inline void drm_client_setup(struct drm_device *dev, + const struct drm_format_info *format) +{ } +static inline void drm_client_setup_with_fourcc(struct drm_device *dev, u32 fourcc) +{ } +static inline void drm_client_setup_with_color_mode(struct drm_device *dev, + unsigned int color_mode) +{ } +#endif + +#endif diff --git a/include/drm/drm_dp_helper.h b/include/drm/display/drm_dp.h index 2d4fc2d33810..e4eebabab975 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/display/drm_dp.h @@ -20,12 +20,10 @@ * OF THIS SOFTWARE. */ -#ifndef _DRM_DP_HELPER_H_ -#define _DRM_DP_HELPER_H_ +#ifndef _DRM_DP_H_ +#define _DRM_DP_H_ #include <linux/types.h> -#include <linux/i2c.h> -#include <linux/delay.h> /* * Unless otherwise noted, all values are from the DP 1.1a spec. Note that @@ -42,6 +40,48 @@ * 1.2 formally includes both eDP and DPI definitions. */ +/* MSA (Main Stream Attribute) MISC bits (as MISC1<<8|MISC0) */ +#define DP_MSA_MISC_SYNC_CLOCK (1 << 0) +#define DP_MSA_MISC_INTERLACE_VTOTAL_EVEN (1 << 8) +#define DP_MSA_MISC_STEREO_NO_3D (0 << 9) +#define DP_MSA_MISC_STEREO_PROG_RIGHT_EYE (1 << 9) +#define DP_MSA_MISC_STEREO_PROG_LEFT_EYE (3 << 9) +/* bits per component for non-RAW */ +#define DP_MSA_MISC_6_BPC (0 << 5) +#define DP_MSA_MISC_8_BPC (1 << 5) +#define DP_MSA_MISC_10_BPC (2 << 5) +#define DP_MSA_MISC_12_BPC (3 << 5) +#define DP_MSA_MISC_16_BPC (4 << 5) +/* bits per component for RAW */ +#define DP_MSA_MISC_RAW_6_BPC (1 << 5) +#define DP_MSA_MISC_RAW_7_BPC (2 << 5) +#define DP_MSA_MISC_RAW_8_BPC (3 << 5) +#define DP_MSA_MISC_RAW_10_BPC (4 << 5) +#define DP_MSA_MISC_RAW_12_BPC (5 << 5) +#define DP_MSA_MISC_RAW_14_BPC (6 << 5) +#define DP_MSA_MISC_RAW_16_BPC (7 << 5) +/* pixel encoding/colorimetry format */ +#define _DP_MSA_MISC_COLOR(misc1_7, misc0_21, misc0_3, misc0_4) \ + ((misc1_7) << 15 | (misc0_4) << 4 | (misc0_3) << 3 | ((misc0_21) << 1)) +#define DP_MSA_MISC_COLOR_RGB _DP_MSA_MISC_COLOR(0, 0, 0, 0) +#define DP_MSA_MISC_COLOR_CEA_RGB _DP_MSA_MISC_COLOR(0, 0, 1, 0) +#define DP_MSA_MISC_COLOR_RGB_WIDE_FIXED _DP_MSA_MISC_COLOR(0, 3, 0, 0) +#define DP_MSA_MISC_COLOR_RGB_WIDE_FLOAT _DP_MSA_MISC_COLOR(0, 3, 0, 1) +#define DP_MSA_MISC_COLOR_Y_ONLY _DP_MSA_MISC_COLOR(1, 0, 0, 0) +#define DP_MSA_MISC_COLOR_RAW _DP_MSA_MISC_COLOR(1, 1, 0, 0) +#define DP_MSA_MISC_COLOR_YCBCR_422_BT601 _DP_MSA_MISC_COLOR(0, 1, 1, 0) +#define DP_MSA_MISC_COLOR_YCBCR_422_BT709 _DP_MSA_MISC_COLOR(0, 1, 1, 1) +#define DP_MSA_MISC_COLOR_YCBCR_444_BT601 _DP_MSA_MISC_COLOR(0, 2, 1, 0) +#define DP_MSA_MISC_COLOR_YCBCR_444_BT709 _DP_MSA_MISC_COLOR(0, 2, 1, 1) +#define DP_MSA_MISC_COLOR_XVYCC_422_BT601 _DP_MSA_MISC_COLOR(0, 1, 0, 0) +#define DP_MSA_MISC_COLOR_XVYCC_422_BT709 _DP_MSA_MISC_COLOR(0, 1, 0, 1) +#define DP_MSA_MISC_COLOR_XVYCC_444_BT601 _DP_MSA_MISC_COLOR(0, 2, 0, 0) +#define DP_MSA_MISC_COLOR_XVYCC_444_BT709 _DP_MSA_MISC_COLOR(0, 2, 0, 1) +#define DP_MSA_MISC_COLOR_OPRGB _DP_MSA_MISC_COLOR(0, 0, 1, 1) +#define DP_MSA_MISC_COLOR_DCI_P3 _DP_MSA_MISC_COLOR(0, 3, 1, 0) +#define DP_MSA_MISC_COLOR_COLOR_PROFILE _DP_MSA_MISC_COLOR(0, 3, 1, 1) +#define DP_MSA_MISC_COLOR_VSC_SDP (1 << 14) + #define DP_AUX_MAX_PAYLOAD_BYTES 16 #define DP_AUX_I2C_WRITE 0x0 @@ -61,8 +101,9 @@ #define DP_AUX_I2C_REPLY_DEFER (0x2 << 2) #define DP_AUX_I2C_REPLY_MASK (0x3 << 2) -/* AUX CH addresses */ -/* DPCD */ +/* DPCD Field Address Mapping */ + +/* Receiver Capability */ #define DP_DPCD_REV 0x000 # define DP_DPCD_REV_10 0x10 # define DP_DPCD_REV_11 0x11 @@ -74,11 +115,13 @@ #define DP_MAX_LANE_COUNT 0x002 # define DP_MAX_LANE_COUNT_MASK 0x1f +# define DP_POST_LT_ADJ_REQ_SUPPORTED (1 << 5) /* 1.3 */ # define DP_TPS3_SUPPORTED (1 << 6) /* 1.2 */ # define DP_ENHANCED_FRAME_CAP (1 << 7) #define DP_MAX_DOWNSPREAD 0x003 # define DP_MAX_DOWNSPREAD_0_5 (1 << 0) +# define DP_STREAM_REGENERATION_STATUS_CAP (1 << 1) /* 2.0 */ # define DP_NO_AUX_HANDSHAKE_LINK_TRAINING (1 << 6) # define DP_TPS4_SUPPORTED (1 << 7) @@ -95,6 +138,8 @@ # define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) /* DPI */ #define DP_MAIN_LINK_CHANNEL_CODING 0x006 +# define DP_CAP_ANSI_8B10B (1 << 0) +# define DP_CAP_ANSI_128B132B (1 << 1) /* 2.0 */ #define DP_DOWN_STREAM_PORT_COUNT 0x007 # define DP_PORT_COUNT_MASK 0x0f @@ -104,6 +149,7 @@ #define DP_RECEIVE_PORT_0_CAP_0 0x008 # define DP_LOCAL_EDID_PRESENT (1 << 1) # define DP_ASSOCIATED_TO_PRECEDING_PORT (1 << 2) +# define DP_HBLANK_EXPANSION_CAPABLE (1 << 3) #define DP_RECEIVE_PORT_0_BUFFER_SIZE 0x009 @@ -138,8 +184,14 @@ #define DP_FAUX_CAP 0x020 /* 1.2 */ # define DP_FAUX_CAP_1 (1 << 0) +#define DP_SINK_VIDEO_FALLBACK_FORMATS 0x020 /* 2.0 */ +# define DP_FALLBACK_1024x768_60HZ_24BPP (1 << 0) +# define DP_FALLBACK_1280x720_60HZ_24BPP (1 << 1) +# define DP_FALLBACK_1920x1080_60HZ_24BPP (1 << 2) + #define DP_MSTM_CAP 0x021 /* 1.2 */ # define DP_MST_CAP (1 << 0) +# define DP_SINGLE_STREAM_SIDEBAND_MSG (1 << 1) /* 2.0 */ #define DP_NUMBER_OF_AUDIO_ENDPOINTS 0x022 /* 1.2 */ @@ -181,6 +233,8 @@ #define DP_RECEIVER_ALPM_CAP 0x02e /* eDP 1.4 */ # define DP_ALPM_CAP (1 << 0) +# define DP_ALPM_PM_STATE_2A_SUPPORT (1 << 1) /* eDP 1.5 */ +# define DP_ALPM_AUX_LESS_CAP (1 << 2) /* eDP 1.5 */ #define DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP 0x02f /* eDP 1.4 */ # define DP_AUX_FRAME_SYNC_CAP (1 << 0) @@ -189,6 +243,9 @@ #define DP_DSC_SUPPORT 0x060 /* DP 1.4 */ # define DP_DSC_DECOMPRESSION_IS_SUPPORTED (1 << 0) +# define DP_DSC_PASSTHROUGH_IS_SUPPORTED (1 << 1) +# define DP_DSC_DYNAMIC_PPS_UPDATE_SUPPORT_COMP_TO_COMP (1 << 2) +# define DP_DSC_DYNAMIC_PPS_UPDATE_SUPPORT_UNCOMP_TO_COMP (1 << 3) #define DP_DSC_REV 0x061 # define DP_DSC_MAJOR_MASK (0xf << 0) @@ -201,6 +258,8 @@ # define DP_DSC_RC_BUF_BLK_SIZE_4 0x1 # define DP_DSC_RC_BUF_BLK_SIZE_16 0x2 # define DP_DSC_RC_BUF_BLK_SIZE_64 0x3 +# define DP_DSC_THROUGHPUT_MODE_0_DELTA_SHIFT 3 /* DP 2.1a, in units of 2 MPixels/sec */ +# define DP_DSC_THROUGHPUT_MODE_0_DELTA_MASK (0x1f << DP_DSC_THROUGHPUT_MODE_0_DELTA_SHIFT) #define DP_DSC_RC_BUF_SIZE 0x063 @@ -227,12 +286,14 @@ #define DP_DSC_BLK_PREDICTION_SUPPORT 0x066 # define DP_DSC_BLK_PREDICTION_IS_SUPPORTED (1 << 0) +# define DP_DSC_RGB_COLOR_CONV_BYPASS_SUPPORT (1 << 1) #define DP_DSC_MAX_BITS_PER_PIXEL_LOW 0x067 /* eDP 1.4 */ #define DP_DSC_MAX_BITS_PER_PIXEL_HI 0x068 /* eDP 1.4 */ # define DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK (0x3 << 0) -# define DP_DSC_MAX_BITS_PER_PIXEL_HI_SHIFT 8 +# define DP_DSC_MAX_BPP_DELTA_VERSION_MASK (0x3 << 5) /* eDP 1.5 & DP 2.0 */ +# define DP_DSC_MAX_BPP_DELTA_AVAILABILITY (1 << 7) /* eDP 1.5 & DP 2.0 */ #define DP_DSC_DEC_COLOR_FORMAT_CAP 0x069 # define DP_DSC_RGB (1 << 0) @@ -249,6 +310,7 @@ #define DP_DSC_PEAK_THROUGHPUT 0x06B # define DP_DSC_THROUGHPUT_MODE_0_MASK (0xf << 0) # define DP_DSC_THROUGHPUT_MODE_0_SHIFT 0 +# define DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED 0 # define DP_DSC_THROUGHPUT_MODE_0_340 (1 << 0) # define DP_DSC_THROUGHPUT_MODE_0_400 (2 << 0) # define DP_DSC_THROUGHPUT_MODE_0_450 (3 << 0) @@ -263,8 +325,10 @@ # define DP_DSC_THROUGHPUT_MODE_0_900 (12 << 0) # define DP_DSC_THROUGHPUT_MODE_0_950 (13 << 0) # define DP_DSC_THROUGHPUT_MODE_0_1000 (14 << 0) +# define DP_DSC_THROUGHPUT_MODE_0_170 (15 << 0) /* 1.4a */ # define DP_DSC_THROUGHPUT_MODE_1_MASK (0xf << 4) # define DP_DSC_THROUGHPUT_MODE_1_SHIFT 4 +# define DP_DSC_THROUGHPUT_MODE_1_UNSUPPORTED 0 # define DP_DSC_THROUGHPUT_MODE_1_340 (1 << 4) # define DP_DSC_THROUGHPUT_MODE_1_400 (2 << 4) # define DP_DSC_THROUGHPUT_MODE_1_450 (3 << 4) @@ -279,6 +343,7 @@ # define DP_DSC_THROUGHPUT_MODE_1_900 (12 << 4) # define DP_DSC_THROUGHPUT_MODE_1_950 (13 << 4) # define DP_DSC_THROUGHPUT_MODE_1_1000 (14 << 4) +# define DP_DSC_THROUGHPUT_MODE_1_170 (15 << 4) #define DP_DSC_MAX_SLICE_WIDTH 0x06C #define DP_DSC_MIN_SLICE_WIDTH_VALUE 2560 @@ -290,16 +355,20 @@ # define DP_DSC_24_PER_DP_DSC_SINK (1 << 2) #define DP_DSC_BITS_PER_PIXEL_INC 0x06F +# define DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK 0x1f +# define DP_DSC_RGB_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 # define DP_DSC_BITS_PER_PIXEL_1_16 0x0 # define DP_DSC_BITS_PER_PIXEL_1_8 0x1 # define DP_DSC_BITS_PER_PIXEL_1_4 0x2 # define DP_DSC_BITS_PER_PIXEL_1_2 0x3 -# define DP_DSC_BITS_PER_PIXEL_1 0x4 +# define DP_DSC_BITS_PER_PIXEL_1_1 0x4 +# define DP_DSC_BITS_PER_PIXEL_MASK 0x7 #define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 # define DP_PSR2_IS_SUPPORTED 2 /* eDP 1.4 */ # define DP_PSR2_WITH_Y_COORD_IS_SUPPORTED 3 /* eDP 1.4a */ +# define DP_PSR2_WITH_Y_COORD_ET_SUPPORTED 4 /* eDP 1.5, adopted eDP 1.4b SCR */ #define DP_PSR_CAPS 0x071 /* XXX 1.2? */ # define DP_PSR_NO_TRAIN_ON_EXIT 1 @@ -314,6 +383,11 @@ # define DP_PSR_SETUP_TIME_SHIFT 1 # define DP_PSR2_SU_Y_COORDINATE_REQUIRED (1 << 4) /* eDP 1.4a */ # define DP_PSR2_SU_GRANULARITY_REQUIRED (1 << 5) /* eDP 1.4b */ +# define DP_PSR2_SU_AUX_FRAME_SYNC_NOT_NEEDED (1 << 6)/* eDP 1.5, adopted eDP 1.4b SCR */ + +#define DP_PSR2_SU_X_GRANULARITY 0x072 /* eDP 1.4b */ +#define DP_PSR2_SU_Y_GRANULARITY 0x074 /* eDP 1.4b */ + /* * 0x80-0x8f describe downstream port capabilities, but there are two layouts * based on whether DP_DETAILED_CAP_INFO_AVAILABLE was set. If it was not, @@ -333,13 +407,54 @@ # define DP_DS_PORT_TYPE_DP_DUALMODE 5 # define DP_DS_PORT_TYPE_WIRELESS 6 # define DP_DS_PORT_HPD (1 << 3) +# define DP_DS_NON_EDID_MASK (0xf << 4) +# define DP_DS_NON_EDID_720x480i_60 (1 << 4) +# define DP_DS_NON_EDID_720x480i_50 (2 << 4) +# define DP_DS_NON_EDID_1920x1080i_60 (3 << 4) +# define DP_DS_NON_EDID_1920x1080i_50 (4 << 4) +# define DP_DS_NON_EDID_1280x720_60 (5 << 4) +# define DP_DS_NON_EDID_1280x720_50 (7 << 4) /* offset 1 for VGA is maximum megapixels per second / 8 */ -/* offset 2 */ +/* offset 1 for DVI/HDMI is maximum TMDS clock in Mbps / 2.5 */ +/* offset 2 for VGA/DVI/HDMI */ # define DP_DS_MAX_BPC_MASK (3 << 0) # define DP_DS_8BPC 0 # define DP_DS_10BPC 1 # define DP_DS_12BPC 2 # define DP_DS_16BPC 3 +/* HDMI2.1 PCON FRL CONFIGURATION */ +# define DP_PCON_MAX_FRL_BW (7 << 2) +# define DP_PCON_MAX_0GBPS (0 << 2) +# define DP_PCON_MAX_9GBPS (1 << 2) +# define DP_PCON_MAX_18GBPS (2 << 2) +# define DP_PCON_MAX_24GBPS (3 << 2) +# define DP_PCON_MAX_32GBPS (4 << 2) +# define DP_PCON_MAX_40GBPS (5 << 2) +# define DP_PCON_MAX_48GBPS (6 << 2) +# define DP_PCON_SOURCE_CTL_MODE (1 << 5) + +/* offset 3 for DVI */ +# define DP_DS_DVI_DUAL_LINK (1 << 1) +# define DP_DS_DVI_HIGH_COLOR_DEPTH (1 << 2) +/* offset 3 for HDMI */ +# define DP_DS_HDMI_FRAME_SEQ_TO_FRAME_PACK (1 << 0) +# define DP_DS_HDMI_YCBCR422_PASS_THROUGH (1 << 1) +# define DP_DS_HDMI_YCBCR420_PASS_THROUGH (1 << 2) +# define DP_DS_HDMI_YCBCR444_TO_422_CONV (1 << 3) +# define DP_DS_HDMI_YCBCR444_TO_420_CONV (1 << 4) + +/* + * VESA DP-to-HDMI PCON Specification adds caps for colorspace + * conversion in DFP cap DPCD 83h. Sec6.1 Table-3. + * Based on the available support the source can enable + * color conversion by writing into PROTOCOL_COVERTER_CONTROL_2 + * DPCD 3052h. + */ +# define DP_DS_HDMI_BT601_RGB_YCBCR_CONV (1 << 5) +# define DP_DS_HDMI_BT709_RGB_YCBCR_CONV (1 << 6) +# define DP_DS_HDMI_BT2020_RGB_YCBCR_CONV (1 << 7) + +#define DP_MAX_DOWNSTREAM_PORTS 0x10 /* DP Forward error Correction Registers */ #define DP_FEC_CAPABILITY 0x090 /* 1.4 */ @@ -347,23 +462,138 @@ # define DP_FEC_UNCORR_BLK_ERROR_COUNT_CAP (1 << 1) # define DP_FEC_CORR_BLK_ERROR_COUNT_CAP (1 << 2) # define DP_FEC_BIT_ERROR_COUNT_CAP (1 << 3) - -/* link configuration */ +#define DP_FEC_CAPABILITY_1 0x091 /* 2.0 */ + +/* DP-HDMI2.1 PCON DSC ENCODER SUPPORT */ +#define DP_PCON_DSC_ENCODER_CAP_SIZE 0xD /* 0x92 through 0x9E */ +#define DP_PCON_DSC_ENCODER 0x092 +# define DP_PCON_DSC_ENCODER_SUPPORTED (1 << 0) +# define DP_PCON_DSC_PPS_ENC_OVERRIDE (1 << 1) + +/* DP-HDMI2.1 PCON DSC Version */ +#define DP_PCON_DSC_VERSION 0x093 +# define DP_PCON_DSC_MAJOR_MASK (0xF << 0) +# define DP_PCON_DSC_MINOR_MASK (0xF << 4) +# define DP_PCON_DSC_MAJOR_SHIFT 0 +# define DP_PCON_DSC_MINOR_SHIFT 4 + +/* DP-HDMI2.1 PCON DSC RC Buffer block size */ +#define DP_PCON_DSC_RC_BUF_BLK_INFO 0x094 +# define DP_PCON_DSC_RC_BUF_BLK_SIZE (0x3 << 0) +# define DP_PCON_DSC_RC_BUF_BLK_1KB 0 +# define DP_PCON_DSC_RC_BUF_BLK_4KB 1 +# define DP_PCON_DSC_RC_BUF_BLK_16KB 2 +# define DP_PCON_DSC_RC_BUF_BLK_64KB 3 + +/* DP-HDMI2.1 PCON DSC RC Buffer size */ +#define DP_PCON_DSC_RC_BUF_SIZE 0x095 + +/* DP-HDMI2.1 PCON DSC Slice capabilities-1 */ +#define DP_PCON_DSC_SLICE_CAP_1 0x096 +# define DP_PCON_DSC_1_PER_DSC_ENC (0x1 << 0) +# define DP_PCON_DSC_2_PER_DSC_ENC (0x1 << 1) +# define DP_PCON_DSC_4_PER_DSC_ENC (0x1 << 3) +# define DP_PCON_DSC_6_PER_DSC_ENC (0x1 << 4) +# define DP_PCON_DSC_8_PER_DSC_ENC (0x1 << 5) +# define DP_PCON_DSC_10_PER_DSC_ENC (0x1 << 6) +# define DP_PCON_DSC_12_PER_DSC_ENC (0x1 << 7) + +#define DP_PCON_DSC_BUF_BIT_DEPTH 0x097 +# define DP_PCON_DSC_BIT_DEPTH_MASK (0xF << 0) +# define DP_PCON_DSC_DEPTH_9_BITS 0 +# define DP_PCON_DSC_DEPTH_10_BITS 1 +# define DP_PCON_DSC_DEPTH_11_BITS 2 +# define DP_PCON_DSC_DEPTH_12_BITS 3 +# define DP_PCON_DSC_DEPTH_13_BITS 4 +# define DP_PCON_DSC_DEPTH_14_BITS 5 +# define DP_PCON_DSC_DEPTH_15_BITS 6 +# define DP_PCON_DSC_DEPTH_16_BITS 7 +# define DP_PCON_DSC_DEPTH_8_BITS 8 + +#define DP_PCON_DSC_BLOCK_PREDICTION 0x098 +# define DP_PCON_DSC_BLOCK_PRED_SUPPORT (0x1 << 0) + +#define DP_PCON_DSC_ENC_COLOR_FMT_CAP 0x099 +# define DP_PCON_DSC_ENC_RGB (0x1 << 0) +# define DP_PCON_DSC_ENC_YUV444 (0x1 << 1) +# define DP_PCON_DSC_ENC_YUV422_S (0x1 << 2) +# define DP_PCON_DSC_ENC_YUV422_N (0x1 << 3) +# define DP_PCON_DSC_ENC_YUV420_N (0x1 << 4) + +#define DP_PCON_DSC_ENC_COLOR_DEPTH_CAP 0x09A +# define DP_PCON_DSC_ENC_8BPC (0x1 << 1) +# define DP_PCON_DSC_ENC_10BPC (0x1 << 2) +# define DP_PCON_DSC_ENC_12BPC (0x1 << 3) + +#define DP_PCON_DSC_MAX_SLICE_WIDTH 0x09B + +/* DP-HDMI2.1 PCON DSC Slice capabilities-2 */ +#define DP_PCON_DSC_SLICE_CAP_2 0x09C +# define DP_PCON_DSC_16_PER_DSC_ENC (0x1 << 0) +# define DP_PCON_DSC_20_PER_DSC_ENC (0x1 << 1) +# define DP_PCON_DSC_24_PER_DSC_ENC (0x1 << 2) + +/* DP-HDMI2.1 PCON HDMI TX Encoder Bits/pixel increment */ +#define DP_PCON_DSC_BPP_INCR 0x09E +# define DP_PCON_DSC_BPP_INCR_MASK (0x7 << 0) +# define DP_PCON_DSC_ONE_16TH_BPP 0 +# define DP_PCON_DSC_ONE_8TH_BPP 1 +# define DP_PCON_DSC_ONE_4TH_BPP 2 +# define DP_PCON_DSC_ONE_HALF_BPP 3 +# define DP_PCON_DSC_ONE_BPP 4 + +/* DP Extended DSC Capabilities */ +#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_0 0x0a0 /* DP 1.4a SCR */ +#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_1 0x0a1 +#define DP_DSC_BRANCH_MAX_LINE_WIDTH 0x0a2 + +/* DFP Capability Extension */ +#define DP_DFP_CAPABILITY_EXTENSION_SUPPORT 0x0a3 /* 2.0 */ + +#define DP_PANEL_REPLAY_CAP_SUPPORT 0x0b0 /* DP 2.0 */ +# define DP_PANEL_REPLAY_SUPPORT (1 << 0) +# define DP_PANEL_REPLAY_SU_SUPPORT (1 << 1) +# define DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT (1 << 2) /* eDP 1.5 */ + +#define DP_PANEL_REPLAY_CAP_SIZE 7 + +#define DP_PANEL_REPLAY_CAP_CAPABILITY 0xb1 +# define DP_PANEL_REPLAY_DSC_DECODE_CAPABILITY_IN_PR_SHIFT 1 /* DP 2.1a */ +# define DP_PANEL_REPLAY_DSC_DECODE_CAPABILITY_IN_PR_MASK (3 << DP_PANEL_REPLAY_DSC_DECODE_CAPABILITY_IN_PR_SHIFT) +# define DP_DSC_DECODE_CAPABILITY_IN_PR_SUPPORTED 0x00 +# define DP_DSC_DECODE_CAPABILITY_IN_PR_FULL_FRAME_ONLY 0x01 +# define DP_DSC_DECODE_CAPABILITY_IN_PR_NOT_SUPPORTED 0x02 +# define DP_DSC_DECODE_CAPABILITY_IN_PR_RESERVED 0x03 +# define DP_PANEL_REPLAY_ASYNC_VIDEO_TIMING_NOT_SUPPORTED_IN_PR (1 << 3) +# define DP_PANEL_REPLAY_DSC_CRC_OF_MULTIPLE_SUS_SUPPORTED (1 << 4) +# define DP_PANEL_REPLAY_SU_GRANULARITY_REQUIRED (1 << 5) +# define DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_CAPABILITY_SUPPORTED (1 << 6) +# define DP_PANEL_REPLAY_LINK_OFF_SUPPORTED_IN_PR_AFTER_ADAPTIVE_SYNC_SDP (1 << 7) + +#define DP_PANEL_REPLAY_CAP_X_GRANULARITY 0xb2 +#define DP_PANEL_REPLAY_CAP_Y_GRANULARITY 0xb4 + +/* Link Configuration */ #define DP_LINK_BW_SET 0x100 # define DP_LINK_RATE_TABLE 0x00 /* eDP 1.4 */ # define DP_LINK_BW_1_62 0x06 # define DP_LINK_BW_2_7 0x0a # define DP_LINK_BW_5_4 0x14 /* 1.2 */ # define DP_LINK_BW_8_1 0x1e /* 1.4 */ +# define DP_LINK_BW_10 0x01 /* 2.0 128b/132b Link Layer */ +# define DP_LINK_BW_13_5 0x04 /* 2.0 128b/132b Link Layer */ +# define DP_LINK_BW_20 0x02 /* 2.0 128b/132b Link Layer */ #define DP_LANE_COUNT_SET 0x101 # define DP_LANE_COUNT_MASK 0x0f +# define DP_POST_LT_ADJ_REQ_GRANTED (1 << 5) /* 1.3 */ # define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7) #define DP_TRAINING_PATTERN_SET 0x102 # define DP_TRAINING_PATTERN_DISABLE 0 # define DP_TRAINING_PATTERN_1 1 # define DP_TRAINING_PATTERN_2 2 +# define DP_TRAINING_PATTERN_2_CDS 3 /* 2.0 E11 */ # define DP_TRAINING_PATTERN_3 3 /* 1.2 */ # define DP_TRAINING_PATTERN_4 7 /* 1.4 */ # define DP_TRAINING_PATTERN_MASK 0x3 @@ -406,12 +636,16 @@ # define DP_TRAIN_PRE_EMPHASIS_SHIFT 3 # define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5) +# define DP_TX_FFE_PRESET_VALUE_MASK (0xf << 0) /* 2.0 128b/132b Link Layer */ + #define DP_DOWNSPREAD_CTRL 0x107 # define DP_SPREAD_AMP_0_5 (1 << 4) +# define DP_FIXED_VTOTAL_AS_SDP_EN_IN_PR_ACTIVE (1 << 6) # define DP_MSA_TIMING_PAR_IGNORE_EN (1 << 7) /* eDP */ #define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 # define DP_SET_ANSI_8B10B (1 << 0) +# define DP_SET_ANSI_128B132B (1 << 1) #define DP_I2C_SPEED_CONTROL_STATUS 0x109 /* DPI */ /* bitmask as for DP_I2C_SPEED_CAP */ @@ -430,8 +664,22 @@ # define DP_LINK_QUAL_PATTERN_ERROR_RATE 2 # define DP_LINK_QUAL_PATTERN_PRBS7 3 # define DP_LINK_QUAL_PATTERN_80BIT_CUSTOM 4 -# define DP_LINK_QUAL_PATTERN_HBR2_EYE 5 -# define DP_LINK_QUAL_PATTERN_MASK 7 +# define DP_LINK_QUAL_PATTERN_CP2520_PAT_1 5 +# define DP_LINK_QUAL_PATTERN_CP2520_PAT_2 6 +# define DP_LINK_QUAL_PATTERN_CP2520_PAT_3 7 +/* DP 2.0 UHBR10, UHBR13.5, UHBR20 */ +# define DP_LINK_QUAL_PATTERN_128B132B_TPS1 0x08 +# define DP_LINK_QUAL_PATTERN_128B132B_TPS2 0x10 +# define DP_LINK_QUAL_PATTERN_PRSBS9 0x18 +# define DP_LINK_QUAL_PATTERN_PRSBS11 0x20 +# define DP_LINK_QUAL_PATTERN_PRSBS15 0x28 +# define DP_LINK_QUAL_PATTERN_PRSBS23 0x30 +# define DP_LINK_QUAL_PATTERN_PRSBS31 0x38 +# define DP_LINK_QUAL_PATTERN_CUSTOM 0x40 +# define DP_LINK_QUAL_PATTERN_SQUARE 0x48 +# define DP_LINK_QUAL_PATTERN_SQUARE_PRESHOOT_DISABLED 0x49 +# define DP_LINK_QUAL_PATTERN_SQUARE_DEEMPHASIS_DISABLED 0x4a +# define DP_LINK_QUAL_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED 0x4b #define DP_TRAINING_LANE0_1_SET2 0x10f #define DP_TRAINING_LANE2_3_SET2 0x110 @@ -455,7 +703,8 @@ #define DP_RECEIVER_ALPM_CONFIG 0x116 /* eDP 1.4 */ # define DP_ALPM_ENABLE (1 << 0) -# define DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE (1 << 1) +# define DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE (1 << 1) /* eDP 1.5 */ +# define DP_ALPM_MODE_AUX_LESS (1 << 2) /* eDP 1.5 */ #define DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF 0x117 /* eDP 1.4 */ # define DP_AUX_FRAME_SYNC_ENABLE (1 << 0) @@ -464,6 +713,9 @@ #define DP_UPSTREAM_DEVICE_DP_PWR_NEED 0x118 /* 1.2 */ # define DP_PWR_NOT_NEEDED (1 << 0) +#define DP_EXTENDED_DPRX_SLEEP_WAKE_TIMEOUT_GRANT 0x119 /* 1.4a */ +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_GRANTED (1 << 0) + #define DP_FEC_CONFIGURATION 0x120 /* 1.4 */ # define DP_FEC_READY (1 << 0) # define DP_FEC_ERR_COUNT_SEL_MASK (7 << 1) @@ -477,20 +729,26 @@ # define DP_FEC_LANE_2_SELECT (2 << 4) # define DP_FEC_LANE_3_SELECT (3 << 4) +#define DP_SDP_ERROR_DETECTION_CONFIGURATION 0x121 /* DP 2.0 E11 */ +#define DP_SDP_CRC16_128B132B_EN BIT(0) + #define DP_AUX_FRAME_SYNC_VALUE 0x15c /* eDP 1.4 */ # define DP_AUX_FRAME_SYNC_VALID (1 << 0) #define DP_DSC_ENABLE 0x160 /* DP 1.4 */ # define DP_DECOMPRESSION_EN (1 << 0) - -#define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ -# define DP_PSR_ENABLE (1 << 0) -# define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) -# define DP_PSR_CRC_VERIFICATION (1 << 2) -# define DP_PSR_FRAME_CAPTURE (1 << 3) -# define DP_PSR_SELECTIVE_UPDATE (1 << 4) -# define DP_PSR_IRQ_HPD_WITH_CRC_ERRORS (1 << 5) -# define DP_PSR_ENABLE_PSR2 (1 << 6) /* eDP 1.4a */ +# define DP_DSC_PASSTHROUGH_EN (1 << 1) +#define DP_DSC_CONFIGURATION 0x161 /* DP 2.0 */ + +#define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ +# define DP_PSR_ENABLE BIT(0) +# define DP_PSR_MAIN_LINK_ACTIVE BIT(1) +# define DP_PSR_CRC_VERIFICATION BIT(2) +# define DP_PSR_FRAME_CAPTURE BIT(3) +# define DP_PSR_SU_REGION_SCANLINE_CAPTURE BIT(4) /* eDP 1.4a */ +# define DP_PSR_IRQ_HPD_WITH_CRC_ERRORS BIT(5) /* eDP 1.4a */ +# define DP_PSR_ENABLE_PSR2 BIT(6) /* eDP 1.4a */ +# define DP_PSR_ENABLE_SU_REGION_ET BIT(7) /* eDP 1.5 */ #define DP_ADAPTER_CTRL 0x1a0 # define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE (1 << 0) @@ -498,10 +756,28 @@ #define DP_BRANCH_DEVICE_CTRL 0x1a1 # define DP_BRANCH_DEVICE_IRQ_HPD (1 << 0) +#define PANEL_REPLAY_CONFIG 0x1b0 /* DP 2.0 */ +# define DP_PANEL_REPLAY_ENABLE (1 << 0) +# define DP_PANEL_REPLAY_VSC_SDP_CRC_EN (1 << 1) /* eDP 1.5 */ +# define DP_PANEL_REPLAY_UNRECOVERABLE_ERROR_EN (1 << 3) +# define DP_PANEL_REPLAY_RFB_STORAGE_ERROR_EN (1 << 4) +# define DP_PANEL_REPLAY_ACTIVE_FRAME_CRC_ERROR_EN (1 << 5) +# define DP_PANEL_REPLAY_SU_ENABLE (1 << 6) +# define DP_PANEL_REPLAY_ENABLE_SU_REGION_ET (1 << 7) /* DP 2.1 */ + +#define PANEL_REPLAY_CONFIG2 0x1b1 /* eDP 1.5 */ +# define DP_PANEL_REPLAY_SINK_REFRESH_RATE_UNLOCK_GRANTED (1 << 0) +# define DP_PANEL_REPLAY_CRC_VERIFICATION (1 << 1) +# define DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_EN (1 << 2) +# define DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_VAL_SEL_SHIFT 3 +# define DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_VAL_SEL_MASK (0xf << 3) +# define DP_PANEL_REPLAY_SU_REGION_SCANLINE_CAPTURE (1 << 7) + #define DP_PAYLOAD_ALLOCATE_SET 0x1c0 #define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1 #define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2 +/* Link/Sink Device Status */ #define DP_SINK_COUNT 0x200 /* prior to 1.2 bit 7 was reserved mbz */ # define DP_GET_SINK_COUNT(x) ((((x) & 0x80) >> 1) | ((x) & 0x3f)) @@ -526,16 +802,20 @@ DP_LANE_CHANNEL_EQ_DONE | \ DP_LANE_SYMBOL_LOCKED) -#define DP_LANE_ALIGN_STATUS_UPDATED 0x204 - -#define DP_INTERLANE_ALIGN_DONE (1 << 0) -#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) -#define DP_LINK_STATUS_UPDATED (1 << 7) +#define DP_LANE_ALIGN_STATUS_UPDATED 0x204 +#define DP_INTERLANE_ALIGN_DONE (1 << 0) +#define DP_POST_LT_ADJ_REQ_IN_PROGRESS (1 << 1) /* 1.3 */ +#define DP_128B132B_DPRX_EQ_INTERLANE_ALIGN_DONE (1 << 2) /* 2.0 E11 */ +#define DP_128B132B_DPRX_CDS_INTERLANE_ALIGN_DONE (1 << 3) /* 2.0 E11 */ +#define DP_128B132B_LT_FAILED (1 << 4) /* 2.0 E11 */ +#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DP_LINK_STATUS_UPDATED (1 << 7) #define DP_SINK_STATUS 0x205 - -#define DP_RECEIVE_PORT_0_STATUS (1 << 0) -#define DP_RECEIVE_PORT_1_STATUS (1 << 1) +# define DP_RECEIVE_PORT_0_STATUS (1 << 0) +# define DP_RECEIVE_PORT_1_STATUS (1 << 1) +# define DP_STREAM_REGENERATION_STATUS (1 << 2) /* 2.0 */ +# define DP_INTRA_HOP_AUX_REPLY_INDICATION (1 << 3) /* 2.0 */ #define DP_ADJUST_REQUEST_LANE0_1 0x206 #define DP_ADJUST_REQUEST_LANE2_3 0x207 @@ -548,7 +828,21 @@ # define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 # define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 +/* DP 2.0 128b/132b Link Layer */ +# define DP_ADJUST_TX_FFE_PRESET_LANE0_MASK (0xf << 0) +# define DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT 0 +# define DP_ADJUST_TX_FFE_PRESET_LANE1_MASK (0xf << 4) +# define DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT 4 + #define DP_ADJUST_REQUEST_POST_CURSOR2 0x20c +# define DP_ADJUST_POST_CURSOR2_LANE0_MASK 0x03 +# define DP_ADJUST_POST_CURSOR2_LANE0_SHIFT 0 +# define DP_ADJUST_POST_CURSOR2_LANE1_MASK 0x0c +# define DP_ADJUST_POST_CURSOR2_LANE1_SHIFT 2 +# define DP_ADJUST_POST_CURSOR2_LANE2_MASK 0x30 +# define DP_ADJUST_POST_CURSOR2_LANE2_SHIFT 4 +# define DP_ADJUST_POST_CURSOR2_LANE3_MASK 0xc0 +# define DP_ADJUST_POST_CURSOR2_LANE3_SHIFT 6 #define DP_TEST_REQUEST 0x218 # define DP_TEST_LINK_TRAINING (1 << 0) @@ -556,6 +850,8 @@ # define DP_TEST_LINK_EDID_READ (1 << 2) # define DP_TEST_LINK_PHY_TEST_PATTERN (1 << 3) /* DPCD >= 1.1 */ # define DP_TEST_LINK_FAUX_PATTERN (1 << 4) /* DPCD >= 1.2 */ +# define DP_TEST_LINK_AUDIO_PATTERN (1 << 5) /* DPCD >= 1.2 */ +# define DP_TEST_LINK_AUDIO_DISABLED_VIDEO (1 << 6) /* DPCD >= 1.2 */ #define DP_TEST_LINK_RATE 0x219 # define DP_LINK_RATE_162 (0x6) @@ -604,6 +900,7 @@ # define DP_COLOR_FORMAT_RGB (0 << 1) # define DP_COLOR_FORMAT_YCbCr422 (1 << 1) # define DP_COLOR_FORMAT_YCbCr444 (2 << 1) +# define DP_TEST_DYNAMIC_RANGE_VESA (0 << 3) # define DP_TEST_DYNAMIC_RANGE_CEA (1 << 3) # define DP_TEST_YCBCR_COEFFICIENTS (1 << 4) # define DP_YCBCR_COEFFICIENTS_ITU601 (0 << 4) @@ -632,7 +929,18 @@ # define DP_TEST_CRC_SUPPORTED (1 << 5) # define DP_TEST_COUNT_MASK 0xf -#define DP_TEST_PHY_PATTERN 0x248 +#define DP_PHY_TEST_PATTERN 0x248 +# define DP_PHY_TEST_PATTERN_SEL_MASK 0x7 +# define DP_PHY_TEST_PATTERN_NONE 0x0 +# define DP_PHY_TEST_PATTERN_D10_2 0x1 +# define DP_PHY_TEST_PATTERN_ERROR_COUNT 0x2 +# define DP_PHY_TEST_PATTERN_PRBS7 0x3 +# define DP_PHY_TEST_PATTERN_80BIT_CUSTOM 0x4 +# define DP_PHY_TEST_PATTERN_CP2520 0x5 + +#define DP_PHY_SQUARE_PATTERN 0x249 + +#define DP_TEST_HBR2_SCRAMBLER_RESET 0x24A #define DP_TEST_80BIT_CUSTOM_PATTERN_7_0 0x250 #define DP_TEST_80BIT_CUSTOM_PATTERN_15_8 0x251 #define DP_TEST_80BIT_CUSTOM_PATTERN_23_16 0x252 @@ -653,6 +961,16 @@ #define DP_TEST_SINK 0x270 # define DP_TEST_SINK_START (1 << 0) +#define DP_TEST_AUDIO_MODE 0x271 +#define DP_TEST_AUDIO_PATTERN_TYPE 0x272 +#define DP_TEST_AUDIO_PERIOD_CH1 0x273 +#define DP_TEST_AUDIO_PERIOD_CH2 0x274 +#define DP_TEST_AUDIO_PERIOD_CH3 0x275 +#define DP_TEST_AUDIO_PERIOD_CH4 0x276 +#define DP_TEST_AUDIO_PERIOD_CH5 0x277 +#define DP_TEST_AUDIO_PERIOD_CH6 0x278 +#define DP_TEST_AUDIO_PERIOD_CH7 0x279 +#define DP_TEST_AUDIO_PERIOD_CH8 0x27A #define DP_FEC_STATUS 0x280 /* 1.4 */ # define DP_FEC_DECODE_EN_DETECTED (1 << 0) @@ -671,20 +989,27 @@ #define DP_VC_PAYLOAD_ID_SLOT_1 0x2c1 /* 1.2 MST */ /* up to ID_SLOT_63 at 0x2ff */ +/* Source Device-specific */ #define DP_SOURCE_OUI 0x300 + +/* Sink Device-specific */ #define DP_SINK_OUI 0x400 + +/* Branch Device-specific */ #define DP_BRANCH_OUI 0x500 #define DP_BRANCH_ID 0x503 #define DP_BRANCH_REVISION_START 0x509 #define DP_BRANCH_HW_REV 0x509 #define DP_BRANCH_SW_REV 0x50A +/* Link/Sink Device Power Control */ #define DP_SET_POWER 0x600 # define DP_SET_POWER_D0 0x1 # define DP_SET_POWER_D3 0x2 # define DP_SET_POWER_MASK 0x3 # define DP_SET_POWER_D3_AUX_ON 0x5 +/* eDP-specific */ #define DP_EDP_DPCD_REV 0x700 /* eDP 1.2 */ # define DP_EDP_11 0x00 # define DP_EDP_12 0x01 @@ -692,6 +1017,7 @@ # define DP_EDP_14 0x03 # define DP_EDP_14a 0x04 /* eDP 1.4a */ # define DP_EDP_14b 0x05 /* eDP 1.4b */ +# define DP_EDP_15 0x06 /* eDP 1.5 */ #define DP_EDP_GENERAL_CAP_1 0x701 # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) @@ -715,6 +1041,8 @@ #define DP_EDP_GENERAL_CAP_2 0x703 # define DP_EDP_OVERDRIVE_ENGINE_ENABLED (1 << 0) +# define DP_EDP_PANEL_LUMINANCE_CONTROL_CAPABLE (1 << 4) +# define DP_EDP_SMOOTH_BRIGHTNESS_CAPABLE (1 << 6) /* eDP 2.0 */ #define DP_EDP_GENERAL_CAP_3 0x704 /* eDP 1.4 */ # define DP_EDP_X_REGION_CAP_MASK (0xf << 0) @@ -740,6 +1068,7 @@ # define DP_EDP_DYNAMIC_BACKLIGHT_ENABLE (1 << 4) # define DP_EDP_REGIONAL_BACKLIGHT_ENABLE (1 << 5) # define DP_EDP_UPDATE_REGION_BRIGHTNESS (1 << 6) /* eDP 1.4 */ +# define DP_EDP_PANEL_LUMINANCE_CONTROL_ENABLE (1 << 7) #define DP_EDP_BACKLIGHT_BRIGHTNESS_MSB 0x722 #define DP_EDP_BACKLIGHT_BRIGHTNESS_LSB 0x723 @@ -764,20 +1093,25 @@ #define DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET 0x732 #define DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET 0x733 +#define DP_EDP_PANEL_TARGET_LUMINANCE_VALUE 0x734 #define DP_EDP_REGIONAL_BACKLIGHT_BASE 0x740 /* eDP 1.4 */ #define DP_EDP_REGIONAL_BACKLIGHT_0 0x741 /* eDP 1.4 */ +#define DP_EDP_MSO_LINK_CAPABILITIES 0x7a4 /* eDP 1.4 */ +# define DP_EDP_MSO_NUMBER_OF_LINKS_MASK (7 << 0) +# define DP_EDP_MSO_NUMBER_OF_LINKS_SHIFT 0 +# define DP_EDP_MSO_INDEPENDENT_LINK_BIT (1 << 3) + +/* Sideband MSG Buffers */ #define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ #define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ #define DP_SIDEBAND_MSG_DOWN_REP_BASE 0x1400 /* 1.2 MST */ #define DP_SIDEBAND_MSG_UP_REQ_BASE 0x1600 /* 1.2 MST */ -#define DP_SINK_COUNT_ESI 0x2002 /* 1.2 */ -/* 0-5 sink count */ -# define DP_SINK_COUNT_CP_READY (1 << 6) - -#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x2003 /* 1.2 */ +/* DPRX Event Status Indicator */ +#define DP_SINK_COUNT_ESI 0x2002 /* same as 0x200 */ +#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x2003 /* same as 0x201 */ #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1 0x2004 /* 1.2 */ # define DP_RX_GTC_MSTR_REQ_STATUS_CHANGE (1 << 0) @@ -785,6 +1119,12 @@ # define DP_CEC_IRQ (1 << 2) #define DP_LINK_SERVICE_IRQ_VECTOR_ESI0 0x2005 /* 1.2 */ +# define RX_CAP_CHANGED (1 << 0) +# define LINK_STATUS_CHANGED (1 << 1) +# define STREAM_STATUS_CHANGED (1 << 2) +# define HDMI_LINK_STATUS_CHANGED (1 << 3) +# define CONNECTED_OFF_ENTRY_REQUESTED (1 << 4) +# define DP_TUNNELING_IRQ (1 << 5) #define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) @@ -826,8 +1166,20 @@ #define DP_LANE_ALIGN_STATUS_UPDATED_ESI 0x200e /* status same as 0x204 */ #define DP_SINK_STATUS_ESI 0x200f /* status same as 0x205 */ +#define DP_PANEL_REPLAY_ERROR_STATUS 0x2020 /* DP 2.1*/ +# define DP_PANEL_REPLAY_LINK_CRC_ERROR (1 << 0) +# define DP_PANEL_REPLAY_RFB_STORAGE_ERROR (1 << 1) +# define DP_PANEL_REPLAY_VSC_SDP_UNCORRECTABLE_ERROR (1 << 2) + +#define DP_SINK_DEVICE_PR_AND_FRAME_LOCK_STATUS 0x2022 /* DP 2.1 */ +# define DP_SINK_DEVICE_PANEL_REPLAY_STATUS_MASK (7 << 0) +# define DP_SINK_FRAME_LOCKED_SHIFT 3 +# define DP_SINK_FRAME_LOCKED_MASK (3 << 3) +# define DP_SINK_FRAME_LOCKED_STATUS_VALID_SHIFT 5 +# define DP_SINK_FRAME_LOCKED_STATUS_VALID_MASK (1 << 5) + +/* Extended Receiver Capability: See DP_DPCD_REV for definitions */ #define DP_DP13_DPCD_REV 0x2200 -#define DP_DP13_MAX_LINK_RATE 0x2201 #define DP_DPRX_FEATURE_ENUMERATION_LIST 0x2210 /* DP 1.3 */ # define DP_GTC_CAP (1 << 0) /* DP 1.3 */ @@ -839,6 +1191,51 @@ # define DP_VSC_EXT_CEA_SDP_SUPPORTED (1 << 6) /* DP 1.4 */ # define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED (1 << 7) /* DP 1.4 */ +#define DP_EXTENDED_DPRX_SLEEP_WAKE_TIMEOUT_REQUEST 0x2211 /* 1.4a */ +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_MASK 0xff +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_1_MS 0x00 +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_20_MS 0x01 +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_40_MS 0x02 +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_60_MS 0x03 +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_80_MS 0x04 +# define DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_100_MS 0x05 + +#define DP_DPRX_FEATURE_ENUMERATION_LIST_CONT_1 0x2214 /* 2.0 E11 */ +# define DP_ADAPTIVE_SYNC_SDP_SUPPORTED (1 << 0) +# define DP_ADAPTIVE_SYNC_SDP_OPERATION_MODE GENMASK(1, 0) +# define DP_ADAPTIVE_SYNC_SDP_LENGTH GENMASK(5, 0) +# define DP_AS_SDP_FIRST_HALF_LINE_OR_3840_PIXEL_CYCLE_WINDOW_NOT_SUPPORTED (1 << 1) +# define DP_VSC_EXT_SDP_FRAMEWORK_VERSION_1_SUPPORTED (1 << 4) + +#define DP_128B132B_SUPPORTED_LINK_RATES 0x2215 /* 2.0 */ +# define DP_UHBR10 (1 << 0) +# define DP_UHBR20 (1 << 1) +# define DP_UHBR13_5 (1 << 2) + +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */ +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_1MS_UNIT (1 << 7) +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US 0x00 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS 0x01 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS 0x02 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS 0x03 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS 0x04 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS 0x05 +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS 0x06 + +#define DP_TEST_264BIT_CUSTOM_PATTERN_7_0 0x2230 +#define DP_TEST_264BIT_CUSTOM_PATTERN_263_256 0x2250 + +/* DSC Extended Capability Branch Total DSC Resources */ +#define DP_DSC_SUPPORT_AND_DSC_DECODER_COUNT 0x2260 /* 2.0 */ +# define DP_DSC_DECODER_COUNT_MASK (0b111 << 5) +# define DP_DSC_DECODER_COUNT_SHIFT 5 +#define DP_DSC_MAX_SLICE_COUNT_AND_AGGREGATION_0 0x2270 /* 2.0 */ +# define DP_DSC_DECODER_0_MAXIMUM_SLICE_COUNT_MASK (1 << 0) +# define DP_DSC_DECODER_0_AGGREGATION_SUPPORT_MASK (0b111 << 1) +# define DP_DSC_DECODER_0_AGGREGATION_SUPPORT_SHIFT 1 + +/* Protocol Converter Extension */ /* HDMI CEC tunneling over AUX DP 1.3 section 5.3.3.3.1 DPCD 1.4+ */ #define DP_CEC_TUNNELING_CAPABILITY 0x3000 # define DP_CEC_TUNNELING_CAPABLE (1 << 0) @@ -895,6 +1292,106 @@ #define DP_CEC_TX_MESSAGE_BUFFER 0x3020 #define DP_CEC_MESSAGE_BUFFER_LENGTH 0x10 +/* PCON CONFIGURE-1 FRL FOR HDMI SINK */ +#define DP_PCON_HDMI_LINK_CONFIG_1 0x305A +# define DP_PCON_ENABLE_MAX_FRL_BW (7 << 0) +# define DP_PCON_ENABLE_MAX_BW_0GBPS 0 +# define DP_PCON_ENABLE_MAX_BW_9GBPS 1 +# define DP_PCON_ENABLE_MAX_BW_18GBPS 2 +# define DP_PCON_ENABLE_MAX_BW_24GBPS 3 +# define DP_PCON_ENABLE_MAX_BW_32GBPS 4 +# define DP_PCON_ENABLE_MAX_BW_40GBPS 5 +# define DP_PCON_ENABLE_MAX_BW_48GBPS 6 +# define DP_PCON_ENABLE_SOURCE_CTL_MODE (1 << 3) +# define DP_PCON_ENABLE_CONCURRENT_LINK (1 << 4) +# define DP_PCON_ENABLE_SEQUENTIAL_LINK (0 << 4) +# define DP_PCON_ENABLE_LINK_FRL_MODE (1 << 5) +# define DP_PCON_ENABLE_HPD_READY (1 << 6) +# define DP_PCON_ENABLE_HDMI_LINK (1 << 7) + +/* PCON CONFIGURE-2 FRL FOR HDMI SINK */ +#define DP_PCON_HDMI_LINK_CONFIG_2 0x305B +# define DP_PCON_MAX_LINK_BW_MASK (0x3F << 0) +# define DP_PCON_FRL_BW_MASK_9GBPS (1 << 0) +# define DP_PCON_FRL_BW_MASK_18GBPS (1 << 1) +# define DP_PCON_FRL_BW_MASK_24GBPS (1 << 2) +# define DP_PCON_FRL_BW_MASK_32GBPS (1 << 3) +# define DP_PCON_FRL_BW_MASK_40GBPS (1 << 4) +# define DP_PCON_FRL_BW_MASK_48GBPS (1 << 5) +# define DP_PCON_FRL_LINK_TRAIN_EXTENDED (1 << 6) +# define DP_PCON_FRL_LINK_TRAIN_NORMAL (0 << 6) + +/* PCON HDMI LINK STATUS */ +#define DP_PCON_HDMI_TX_LINK_STATUS 0x303B +# define DP_PCON_HDMI_TX_LINK_ACTIVE (1 << 0) +# define DP_PCON_FRL_READY (1 << 1) + +/* PCON HDMI POST FRL STATUS */ +#define DP_PCON_HDMI_POST_FRL_STATUS 0x3036 +# define DP_PCON_HDMI_LINK_MODE (1 << 0) +# define DP_PCON_HDMI_MODE_TMDS 0 +# define DP_PCON_HDMI_MODE_FRL 1 +# define DP_PCON_HDMI_FRL_TRAINED_BW (0x3F << 1) +# define DP_PCON_FRL_TRAINED_BW_9GBPS (1 << 1) +# define DP_PCON_FRL_TRAINED_BW_18GBPS (1 << 2) +# define DP_PCON_FRL_TRAINED_BW_24GBPS (1 << 3) +# define DP_PCON_FRL_TRAINED_BW_32GBPS (1 << 4) +# define DP_PCON_FRL_TRAINED_BW_40GBPS (1 << 5) +# define DP_PCON_FRL_TRAINED_BW_48GBPS (1 << 6) + +#define DP_PROTOCOL_CONVERTER_CONTROL_0 0x3050 /* DP 1.3 */ +# define DP_HDMI_DVI_OUTPUT_CONFIG (1 << 0) /* DP 1.3 */ +#define DP_PROTOCOL_CONVERTER_CONTROL_1 0x3051 /* DP 1.3 */ +# define DP_CONVERSION_TO_YCBCR420_ENABLE (1 << 0) /* DP 1.3 */ +# define DP_HDMI_EDID_PROCESSING_DISABLE (1 << 1) /* DP 1.4 */ +# define DP_HDMI_AUTONOMOUS_SCRAMBLING_DISABLE (1 << 2) /* DP 1.4 */ +# define DP_HDMI_FORCE_SCRAMBLING (1 << 3) /* DP 1.4 */ +#define DP_PROTOCOL_CONVERTER_CONTROL_2 0x3052 /* DP 1.3 */ +# define DP_CONVERSION_TO_YCBCR422_ENABLE (1 << 0) /* DP 1.3 */ +# define DP_PCON_ENABLE_DSC_ENCODER (1 << 1) +# define DP_PCON_ENCODER_PPS_OVERRIDE_MASK (0x3 << 2) +# define DP_PCON_ENC_PPS_OVERRIDE_DISABLED 0 +# define DP_PCON_ENC_PPS_OVERRIDE_EN_PARAMS 1 +# define DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER 2 +# define DP_CONVERSION_RGB_YCBCR_MASK (7 << 4) +# define DP_CONVERSION_BT601_RGB_YCBCR_ENABLE (1 << 4) +# define DP_CONVERSION_BT709_RGB_YCBCR_ENABLE (1 << 5) +# define DP_CONVERSION_BT2020_RGB_YCBCR_ENABLE (1 << 6) + +/* PCON Downstream HDMI ERROR Status per Lane */ +#define DP_PCON_HDMI_ERROR_STATUS_LN0 0x3037 +#define DP_PCON_HDMI_ERROR_STATUS_LN1 0x3038 +#define DP_PCON_HDMI_ERROR_STATUS_LN2 0x3039 +#define DP_PCON_HDMI_ERROR_STATUS_LN3 0x303A +# define DP_PCON_HDMI_ERROR_COUNT_MASK (0x7 << 0) +# define DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS (1 << 0) +# define DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS (1 << 1) +# define DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS (1 << 2) + +/* PCON HDMI CONFIG PPS Override Buffer + * Valid Offsets to be added to Base : 0-127 + */ +#define DP_PCON_HDMI_PPS_OVERRIDE_BASE 0x3100 + +/* PCON HDMI CONFIG PPS Override Parameter: Slice height + * Offset-0 8LSBs of the Slice height. + * Offset-1 8MSBs of the Slice height. + */ +#define DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT 0x3180 + +/* PCON HDMI CONFIG PPS Override Parameter: Slice width + * Offset-0 8LSBs of the Slice width. + * Offset-1 8MSBs of the Slice width. + */ +#define DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH 0x3182 + +/* PCON HDMI CONFIG PPS Override Parameter: bits_per_pixel + * Offset-0 8LSBs of the bits_per_pixel. + * Offset-1 2MSBs of the bits_per_pixel. + */ +#define DP_PCON_HDMI_PPS_OVRD_BPP 0x3184 + +/* HDCP 1.3 and HDCP 2.2 */ #define DP_AUX_HDCP_BKSV 0x68000 #define DP_AUX_HDCP_RI_PRIME 0x68005 #define DP_AUX_HDCP_AKSV 0x68007 @@ -940,6 +1437,174 @@ #define DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET 0x69494 #define DP_HDCP_2_2_REG_DBG_OFFSET 0x69518 +/* DP-tunneling */ +#define DP_TUNNELING_OUI 0xe0000 +#define DP_TUNNELING_OUI_BYTES 3 + +#define DP_TUNNELING_DEV_ID 0xe0003 +#define DP_TUNNELING_DEV_ID_BYTES 6 + +#define DP_TUNNELING_HW_REV 0xe0009 +#define DP_TUNNELING_HW_REV_MAJOR_SHIFT 4 +#define DP_TUNNELING_HW_REV_MAJOR_MASK (0xf << DP_TUNNELING_HW_REV_MAJOR_SHIFT) +#define DP_TUNNELING_HW_REV_MINOR_SHIFT 0 +#define DP_TUNNELING_HW_REV_MINOR_MASK (0xf << DP_TUNNELING_HW_REV_MINOR_SHIFT) + +#define DP_TUNNELING_SW_REV_MAJOR 0xe000a +#define DP_TUNNELING_SW_REV_MINOR 0xe000b + +#define DP_TUNNELING_CAPABILITIES 0xe000d +#define DP_IN_BW_ALLOCATION_MODE_SUPPORT (1 << 7) +#define DP_PANEL_REPLAY_OPTIMIZATION_SUPPORT (1 << 6) +#define DP_TUNNELING_SUPPORT (1 << 0) + +#define DP_IN_ADAPTER_INFO 0xe000e +#define DP_IN_ADAPTER_NUMBER_BITS 7 +#define DP_IN_ADAPTER_NUMBER_MASK ((1 << DP_IN_ADAPTER_NUMBER_BITS) - 1) + +#define DP_USB4_DRIVER_ID 0xe000f +#define DP_USB4_DRIVER_ID_BITS 4 +#define DP_USB4_DRIVER_ID_MASK ((1 << DP_USB4_DRIVER_ID_BITS) - 1) + +#define DP_USB4_DRIVER_BW_CAPABILITY 0xe0020 +#define DP_USB4_DRIVER_BW_ALLOCATION_MODE_SUPPORT (1 << 7) + +#define DP_IN_ADAPTER_TUNNEL_INFORMATION 0xe0021 +#define DP_GROUP_ID_BITS 3 +#define DP_GROUP_ID_MASK ((1 << DP_GROUP_ID_BITS) - 1) + +#define DP_BW_GRANULARITY 0xe0022 +#define DP_BW_GRANULARITY_MASK 0x3 + +#define DP_ESTIMATED_BW 0xe0023 +#define DP_ALLOCATED_BW 0xe0024 + +#define DP_TUNNELING_STATUS 0xe0025 +#define DP_BW_ALLOCATION_CAPABILITY_CHANGED (1 << 3) +#define DP_ESTIMATED_BW_CHANGED (1 << 2) +#define DP_BW_REQUEST_SUCCEEDED (1 << 1) +#define DP_BW_REQUEST_FAILED (1 << 0) + +#define DP_TUNNELING_MAX_LINK_RATE 0xe0028 + +#define DP_TUNNELING_MAX_LANE_COUNT 0xe0029 +#define DP_TUNNELING_MAX_LANE_COUNT_MASK 0x1f + +#define DP_DPTX_BW_ALLOCATION_MODE_CONTROL 0xe0030 +#define DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE (1 << 7) +#define DP_UNMASK_BW_ALLOCATION_IRQ (1 << 6) + +#define DP_REQUEST_BW 0xe0031 +#define MAX_DP_REQUEST_BW 255 + +/* LTTPR: Link Training (LT)-tunable PHY Repeaters */ +#define DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV 0xf0000 /* 1.3 */ +#define DP_MAX_LINK_RATE_PHY_REPEATER 0xf0001 /* 1.4a */ +#define DP_PHY_REPEATER_CNT 0xf0002 /* 1.3 */ +#define DP_PHY_REPEATER_MODE 0xf0003 /* 1.3 */ +#define DP_MAX_LANE_COUNT_PHY_REPEATER 0xf0004 /* 1.4a */ +#define DP_Repeater_FEC_CAPABILITY 0xf0004 /* 1.4 */ +#define DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT 0xf0005 /* 1.4a */ +# define DP_EXTENDED_WAKE_TIMEOUT_REQUEST_MASK 0x7f +# define DP_EXTENDED_WAKE_TIMEOUT_GRANT (1 << 7) +#define DP_MAIN_LINK_CHANNEL_CODING_PHY_REPEATER 0xf0006 /* 2.0 */ +# define DP_PHY_REPEATER_128B132B_SUPPORTED (1 << 0) +/* See DP_128B132B_SUPPORTED_LINK_RATES for values */ +#define DP_PHY_REPEATER_128B132B_RATES 0xf0007 /* 2.0 */ +#define DP_PHY_REPEATER_EQ_DONE 0xf0008 /* 2.0 E11 */ + +enum drm_dp_phy { + DP_PHY_DPRX, + + DP_PHY_LTTPR1, + DP_PHY_LTTPR2, + DP_PHY_LTTPR3, + DP_PHY_LTTPR4, + DP_PHY_LTTPR5, + DP_PHY_LTTPR6, + DP_PHY_LTTPR7, + DP_PHY_LTTPR8, + + DP_MAX_LTTPR_COUNT = DP_PHY_LTTPR8, +}; + +#define DP_PHY_LTTPR(i) (DP_PHY_LTTPR1 + (i)) + +#define __DP_LTTPR1_BASE 0xf0010 /* 1.3 */ +#define __DP_LTTPR2_BASE 0xf0060 /* 1.3 */ +#define DP_LTTPR_BASE(dp_phy) \ + (__DP_LTTPR1_BASE + (__DP_LTTPR2_BASE - __DP_LTTPR1_BASE) * \ + ((dp_phy) - DP_PHY_LTTPR1)) + +#define DP_LTTPR_REG(dp_phy, lttpr1_reg) \ + (DP_LTTPR_BASE(dp_phy) - DP_LTTPR_BASE(DP_PHY_LTTPR1) + (lttpr1_reg)) + +#define DP_TRAINING_PATTERN_SET_PHY_REPEATER1 0xf0010 /* 1.3 */ +#define DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_TRAINING_PATTERN_SET_PHY_REPEATER1) + +#define DP_TRAINING_LANE0_SET_PHY_REPEATER1 0xf0011 /* 1.3 */ +#define DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_TRAINING_LANE0_SET_PHY_REPEATER1) + +#define DP_TRAINING_LANE1_SET_PHY_REPEATER1 0xf0012 /* 1.3 */ +#define DP_TRAINING_LANE2_SET_PHY_REPEATER1 0xf0013 /* 1.3 */ +#define DP_TRAINING_LANE3_SET_PHY_REPEATER1 0xf0014 /* 1.3 */ +#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0020 /* 1.4a */ +#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) + +#define DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1 0xf0021 /* 1.4a */ +# define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED BIT(0) +# define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED BIT(1) + +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0022 /* 2.0 */ +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) +/* see DP_128B132B_TRAINING_AUX_RD_INTERVAL for values */ + +#define DP_LANE0_1_STATUS_PHY_REPEATER1 0xf0030 /* 1.3 */ +#define DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_LANE0_1_STATUS_PHY_REPEATER1) + +#define DP_LANE2_3_STATUS_PHY_REPEATER1 0xf0031 /* 1.3 */ + +#define DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1 0xf0032 /* 1.3 */ +#define DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1 0xf0033 /* 1.3 */ +#define DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1 0xf0034 /* 1.3 */ +#define DP_SYMBOL_ERROR_COUNT_LANE0_PHY_REPEATER1 0xf0035 /* 1.3 */ +#define DP_SYMBOL_ERROR_COUNT_LANE1_PHY_REPEATER1 0xf0037 /* 1.3 */ +#define DP_SYMBOL_ERROR_COUNT_LANE2_PHY_REPEATER1 0xf0039 /* 1.3 */ +#define DP_SYMBOL_ERROR_COUNT_LANE3_PHY_REPEATER1 0xf003b /* 1.3 */ + +#define DP_OUI_PHY_REPEATER1 0xf003d /* 1.3 */ +#define DP_OUI_PHY_REPEATER(dp_phy) \ + DP_LTTPR_REG(dp_phy, DP_OUI_PHY_REPEATER1) + +#define __DP_FEC1_BASE 0xf0290 /* 1.4 */ +#define __DP_FEC2_BASE 0xf0298 /* 1.4 */ +#define DP_FEC_BASE(dp_phy) \ + (__DP_FEC1_BASE + ((__DP_FEC2_BASE - __DP_FEC1_BASE) * \ + ((dp_phy) - DP_PHY_LTTPR1))) + +#define DP_FEC_REG(dp_phy, fec1_reg) \ + (DP_FEC_BASE(dp_phy) - DP_FEC_BASE(DP_PHY_LTTPR1) + fec1_reg) + +#define DP_FEC_STATUS_PHY_REPEATER1 0xf0290 /* 1.4 */ +#define DP_FEC_STATUS_PHY_REPEATER(dp_phy) \ + DP_FEC_REG(dp_phy, DP_FEC_STATUS_PHY_REPEATER1) + +#define DP_FEC_ERROR_COUNT_PHY_REPEATER1 0xf0291 /* 1.4 */ +#define DP_FEC_CAPABILITY_PHY_REPEATER1 0xf0294 /* 1.4a */ + +#define DP_LTTPR_MAX_ADD 0xf02ff /* 1.4 */ + +#define DP_DPCD_MAX_ADD 0xfffff /* 1.4 */ + +/* Repeater modes */ +#define DP_PHY_REPEATER_MODE_TRANSPARENT 0x55 /* 1.3 */ +#define DP_PHY_REPEATER_MODE_NON_TRANSPARENT 0xaa /* 1.3 */ + /* DP HDCP message start offsets in DPCD address space */ #define DP_HDCP_2_2_AKE_INIT_OFFSET DP_HDCP_2_2_REG_RTX_OFFSET #define DP_HDCP_2_2_AKE_SEND_CERT_OFFSET DP_HDCP_2_2_REG_CERT_RX_OFFSET @@ -972,6 +1637,7 @@ #define DP_PEER_DEVICE_DP_LEGACY_CONV 0x4 /* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */ +#define DP_GET_MSG_TRANSACTION_VERSION 0x00 /* DP 1.3 */ #define DP_LINK_ADDRESS 0x01 #define DP_CONNECTION_STATUS_NOTIFY 0x02 #define DP_ENUM_PATH_RESOURCES 0x10 @@ -987,6 +1653,13 @@ #define DP_POWER_DOWN_PHY 0x25 #define DP_SINK_EVENT_NOTIFY 0x30 #define DP_QUERY_STREAM_ENC_STATUS 0x38 +#define DP_QUERY_STREAM_ENC_STATUS_STATE_NO_EXIST 0 +#define DP_QUERY_STREAM_ENC_STATUS_STATE_INACTIVE 1 +#define DP_QUERY_STREAM_ENC_STATUS_STATE_ACTIVE 2 + +/* DP 1.2 MST sideband reply types */ +#define DP_SIDEBAND_REPLY_ACK 0x00 +#define DP_SIDEBAND_REPLY_NAK 0x01 /* DP 1.2 MST sideband nak reasons - table 2.84 */ #define DP_NAK_WRITE_FAILURE 0x01 @@ -1009,27 +1682,17 @@ #define DP_MST_PHYSICAL_PORT_0 0 #define DP_MST_LOGICAL_PORT_0 8 +#define DP_LINK_CONSTANT_N_VALUE 0x8000 #define DP_LINK_STATUS_SIZE 6 -bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane_count); -bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane_count); -u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane); -u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], - int lane); #define DP_BRANCH_OUI_HEADER_SIZE 0xc #define DP_RECEIVER_CAP_SIZE 0xf -#define DP_DSC_RECEIVER_CAP_SIZE 0xf +#define DP_DSC_RECEIVER_CAP_SIZE 0x10 /* DSC Capabilities 0x60 through 0x6F */ +#define DP_DSC_BRANCH_CAP_SIZE 3 #define EDP_PSR_RECEIVER_CAP_SIZE 2 -#define EDP_DISPLAY_CTL_CAP_SIZE 3 - -void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]); -void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]); - -u8 drm_dp_link_rate_to_bw_code(int link_rate); -int drm_dp_bw_code_to_link_rate(u8 link_bw); +#define EDP_DISPLAY_CTL_CAP_SIZE 5 +#define DP_LTTPR_COMMON_CAP_SIZE 8 +#define DP_LTTPR_PHY_CAP_SIZE 3 #define DP_SDP_AUDIO_TIMESTAMP 0x01 #define DP_SDP_AUDIO_STREAM 0x02 @@ -1037,387 +1700,172 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw); #define DP_SDP_AUDIO_COPYMANAGEMENT 0x05 /* DP 1.2 */ #define DP_SDP_ISRC 0x06 /* DP 1.2 */ #define DP_SDP_VSC 0x07 /* DP 1.2 */ +#define DP_SDP_ADAPTIVE_SYNC 0x22 /* DP 1.4 */ #define DP_SDP_CAMERA_GENERIC(i) (0x08 + (i)) /* 0-7, DP 1.3 */ #define DP_SDP_PPS 0x10 /* DP 1.4 */ #define DP_SDP_VSC_EXT_VESA 0x20 /* DP 1.4 */ #define DP_SDP_VSC_EXT_CEA 0x21 /* DP 1.4 */ + /* 0x80+ CEA-861 infoframe types */ +#define DP_SDP_AUDIO_INFOFRAME_HB2 0x1b + +/** + * struct dp_sdp_header - DP secondary data packet header + * @HB0: Secondary Data Packet ID + * @HB1: Secondary Data Packet Type + * @HB2: Secondary Data Packet Specific header, Byte 0 + * @HB3: Secondary Data packet Specific header, Byte 1 + */ struct dp_sdp_header { - u8 HB0; /* Secondary Data Packet ID */ - u8 HB1; /* Secondary Data Packet Type */ - u8 HB2; /* Secondary Data Packet Specific header, Byte 0 */ - u8 HB3; /* Secondary Data packet Specific header, Byte 1 */ + u8 HB0; + u8 HB1; + u8 HB2; + u8 HB3; } __packed; #define EDP_SDP_HEADER_REVISION_MASK 0x1F #define EDP_SDP_HEADER_VALID_PAYLOAD_BYTES 0x1F #define DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1 0x7F -struct edp_vsc_psr { +/** + * struct dp_sdp - DP secondary data packet + * @sdp_header: DP secondary data packet header + * @db: DP secondaray data packet data blocks + * VSC SDP Payload for PSR + * db[0]: Stereo Interface + * db[1]: 0 - PSR State; 1 - Update RFB; 2 - CRC Valid + * db[2]: CRC value bits 7:0 of the R or Cr component + * db[3]: CRC value bits 15:8 of the R or Cr component + * db[4]: CRC value bits 7:0 of the G or Y component + * db[5]: CRC value bits 15:8 of the G or Y component + * db[6]: CRC value bits 7:0 of the B or Cb component + * db[7]: CRC value bits 15:8 of the B or Cb component + * db[8] - db[31]: Reserved + * VSC SDP Payload for Pixel Encoding/Colorimetry Format + * db[0] - db[15]: Reserved + * db[16]: Pixel Encoding and Colorimetry Formats + * db[17]: Dynamic Range and Component Bit Depth + * db[18]: Content Type + * db[19] - db[31]: Reserved + */ +struct dp_sdp { struct dp_sdp_header sdp_header; - u8 DB0; /* Stereo Interface */ - u8 DB1; /* 0 - PSR State; 1 - Update RFB; 2 - CRC Valid */ - u8 DB2; /* CRC value bits 7:0 of the R or Cr component */ - u8 DB3; /* CRC value bits 15:8 of the R or Cr component */ - u8 DB4; /* CRC value bits 7:0 of the G or Y component */ - u8 DB5; /* CRC value bits 15:8 of the G or Y component */ - u8 DB6; /* CRC value bits 7:0 of the B or Cb component */ - u8 DB7; /* CRC value bits 15:8 of the B or Cb component */ - u8 DB8_31[24]; /* Reserved */ + u8 db[32]; } __packed; #define EDP_VSC_PSR_STATE_ACTIVE (1<<0) #define EDP_VSC_PSR_UPDATE_RFB (1<<1) #define EDP_VSC_PSR_CRC_VALUES_VALID (1<<2) -int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]); - -static inline int -drm_dp_max_link_rate(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]); -} - -static inline u8 -drm_dp_max_lane_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; -} - -static inline bool -drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return dpcd[DP_DPCD_REV] >= 0x11 && - (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP); -} - -static inline bool -drm_dp_tps3_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return dpcd[DP_DPCD_REV] >= 0x12 && - dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED; -} - -static inline bool -drm_dp_tps4_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return dpcd[DP_DPCD_REV] >= 0x14 && - dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED; -} - -static inline u8 -drm_dp_training_pattern_mask(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return (dpcd[DP_DPCD_REV] >= 0x14) ? DP_TRAINING_PATTERN_MASK_1_4 : - DP_TRAINING_PATTERN_MASK; -} - -static inline bool -drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) -{ - return dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT; -} - -/* DP/eDP DSC support */ -u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], - bool is_edp); -u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]); -int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpc[DP_DSC_RECEIVER_CAP_SIZE], - u8 dsc_bpc[3]); - -static inline bool -drm_dp_sink_supports_dsc(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) -{ - return dsc_dpcd[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & - DP_DSC_DECOMPRESSION_IS_SUPPORTED; -} - -static inline u16 -drm_edp_dsc_sink_output_bpp(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) -{ - return dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] | - (dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] & - DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK << - DP_DSC_MAX_BITS_PER_PIXEL_HI_SHIFT); -} - -static inline u32 -drm_dp_dsc_sink_max_slice_width(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) -{ - /* Max Slicewidth = Number of Pixels * 320 */ - return dsc_dpcd[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * - DP_DSC_SLICE_WIDTH_MULTIPLIER; -} - -/* Forward Error Correction Support on DP 1.4 */ -static inline bool -drm_dp_sink_supports_fec(const u8 fec_capable) -{ - return fec_capable & DP_FEC_CAPABLE; -} - -/* - * DisplayPort AUX channel - */ - /** - * struct drm_dp_aux_msg - DisplayPort AUX channel transaction - * @address: address of the (first) register to access - * @request: contains the type of transaction (see DP_AUX_* macros) - * @reply: upon completion, contains the reply type of the transaction - * @buffer: pointer to a transmission or reception buffer - * @size: size of @buffer - */ -struct drm_dp_aux_msg { - unsigned int address; - u8 request; - u8 reply; - void *buffer; - size_t size; -}; - -struct cec_adapter; -struct edid; - -/** - * struct drm_dp_aux_cec - DisplayPort CEC-Tunneling-over-AUX - * @lock: mutex protecting this struct - * @adap: the CEC adapter for CEC-Tunneling-over-AUX support. - * @name: name of the CEC adapter - * @parent: parent device of the CEC adapter - * @unregister_work: unregister the CEC adapter + * enum dp_pixelformat - drm DP Pixel encoding formats + * + * This enum is used to indicate DP VSC SDP Pixel encoding formats. + * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through + * DB18] + * + * @DP_PIXELFORMAT_RGB: RGB pixel encoding format + * @DP_PIXELFORMAT_YUV444: YCbCr 4:4:4 pixel encoding format + * @DP_PIXELFORMAT_YUV422: YCbCr 4:2:2 pixel encoding format + * @DP_PIXELFORMAT_YUV420: YCbCr 4:2:0 pixel encoding format + * @DP_PIXELFORMAT_Y_ONLY: Y Only pixel encoding format + * @DP_PIXELFORMAT_RAW: RAW pixel encoding format + * @DP_PIXELFORMAT_RESERVED: Reserved pixel encoding format */ -struct drm_dp_aux_cec { - struct mutex lock; - struct cec_adapter *adap; - const char *name; - struct device *parent; - struct delayed_work unregister_work; +enum dp_pixelformat { + DP_PIXELFORMAT_RGB = 0, + DP_PIXELFORMAT_YUV444 = 0x1, + DP_PIXELFORMAT_YUV422 = 0x2, + DP_PIXELFORMAT_YUV420 = 0x3, + DP_PIXELFORMAT_Y_ONLY = 0x4, + DP_PIXELFORMAT_RAW = 0x5, + DP_PIXELFORMAT_RESERVED = 0x6, }; /** - * struct drm_dp_aux - DisplayPort AUX channel - * @name: user-visible name of this AUX channel and the I2C-over-AUX adapter - * @ddc: I2C adapter that can be used for I2C-over-AUX communication - * @dev: pointer to struct device that is the parent for this AUX channel - * @crtc: backpointer to the crtc that is currently using this AUX channel - * @hw_mutex: internal mutex used for locking transfers - * @crc_work: worker that captures CRCs for each frame - * @crc_count: counter of captured frame CRCs - * @transfer: transfers a message representing a single AUX transaction - * - * The .dev field should be set to a pointer to the device that implements - * the AUX channel. - * - * The .name field may be used to specify the name of the I2C adapter. If set to - * NULL, dev_name() of .dev will be used. + * enum dp_colorimetry - drm DP Colorimetry formats * - * Drivers provide a hardware-specific implementation of how transactions - * are executed via the .transfer() function. A pointer to a drm_dp_aux_msg - * structure describing the transaction is passed into this function. Upon - * success, the implementation should return the number of payload bytes - * that were transferred, or a negative error-code on failure. Helpers - * propagate errors from the .transfer() function, with the exception of - * the -EBUSY error, which causes a transaction to be retried. On a short, - * helpers will return -EPROTO to make it simpler to check for failure. + * This enum is used to indicate DP VSC SDP Colorimetry formats. + * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through + * DB18] and a name of enum member follows enum drm_colorimetry definition. * - * An AUX channel can also be used to transport I2C messages to a sink. A - * typical application of that is to access an EDID that's present in the - * sink device. The .transfer() function can also be used to execute such - * transactions. The drm_dp_aux_register() function registers an I2C - * adapter that can be passed to drm_probe_ddc(). Upon removal, drivers - * should call drm_dp_aux_unregister() to remove the I2C adapter. - * The I2C adapter uses long transfers by default; if a partial response is - * received, the adapter will drop down to the size given by the partial - * response for this transaction only. - * - * Note that the aux helper code assumes that the .transfer() function - * only modifies the reply field of the drm_dp_aux_msg structure. The - * retry logic and i2c helpers assume this is the case. + * @DP_COLORIMETRY_DEFAULT: sRGB (IEC 61966-2-1) or + * ITU-R BT.601 colorimetry format + * @DP_COLORIMETRY_RGB_WIDE_FIXED: RGB wide gamut fixed point colorimetry format + * @DP_COLORIMETRY_BT709_YCC: ITU-R BT.709 colorimetry format + * @DP_COLORIMETRY_RGB_WIDE_FLOAT: RGB wide gamut floating point + * (scRGB (IEC 61966-2-2)) colorimetry format + * @DP_COLORIMETRY_XVYCC_601: xvYCC601 colorimetry format + * @DP_COLORIMETRY_OPRGB: OpRGB colorimetry format + * @DP_COLORIMETRY_XVYCC_709: xvYCC709 colorimetry format + * @DP_COLORIMETRY_DCI_P3_RGB: DCI-P3 (SMPTE RP 431-2) colorimetry format + * @DP_COLORIMETRY_SYCC_601: sYCC601 colorimetry format + * @DP_COLORIMETRY_RGB_CUSTOM: RGB Custom Color Profile colorimetry format + * @DP_COLORIMETRY_OPYCC_601: opYCC601 colorimetry format + * @DP_COLORIMETRY_BT2020_RGB: ITU-R BT.2020 R' G' B' colorimetry format + * @DP_COLORIMETRY_BT2020_CYCC: ITU-R BT.2020 Y'c C'bc C'rc colorimetry format + * @DP_COLORIMETRY_BT2020_YCC: ITU-R BT.2020 Y' C'b C'r colorimetry format */ -struct drm_dp_aux { - const char *name; - struct i2c_adapter ddc; - struct device *dev; - struct drm_crtc *crtc; - struct mutex hw_mutex; - struct work_struct crc_work; - u8 crc_count; - ssize_t (*transfer)(struct drm_dp_aux *aux, - struct drm_dp_aux_msg *msg); - /** - * @i2c_nack_count: Counts I2C NACKs, used for DP validation. - */ - unsigned i2c_nack_count; - /** - * @i2c_defer_count: Counts I2C DEFERs, used for DP validation. - */ - unsigned i2c_defer_count; - /** - * @cec: struct containing fields used for CEC-Tunneling-over-AUX. - */ - struct drm_dp_aux_cec cec; +enum dp_colorimetry { + DP_COLORIMETRY_DEFAULT = 0, + DP_COLORIMETRY_RGB_WIDE_FIXED = 0x1, + DP_COLORIMETRY_BT709_YCC = 0x1, + DP_COLORIMETRY_RGB_WIDE_FLOAT = 0x2, + DP_COLORIMETRY_XVYCC_601 = 0x2, + DP_COLORIMETRY_OPRGB = 0x3, + DP_COLORIMETRY_XVYCC_709 = 0x3, + DP_COLORIMETRY_DCI_P3_RGB = 0x4, + DP_COLORIMETRY_SYCC_601 = 0x4, + DP_COLORIMETRY_RGB_CUSTOM = 0x5, + DP_COLORIMETRY_OPYCC_601 = 0x5, + DP_COLORIMETRY_BT2020_RGB = 0x6, + DP_COLORIMETRY_BT2020_CYCC = 0x6, + DP_COLORIMETRY_BT2020_YCC = 0x7, }; -ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, - void *buffer, size_t size); -ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, - void *buffer, size_t size); - /** - * drm_dp_dpcd_readb() - read a single byte from the DPCD - * @aux: DisplayPort AUX channel - * @offset: address of the register to read - * @valuep: location where the value of the register will be stored + * enum dp_dynamic_range - drm DP Dynamic Range * - * Returns the number of bytes transferred (1) on success, or a negative - * error code on failure. - */ -static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux, - unsigned int offset, u8 *valuep) -{ - return drm_dp_dpcd_read(aux, offset, valuep, 1); -} - -/** - * drm_dp_dpcd_writeb() - write a single byte to the DPCD - * @aux: DisplayPort AUX channel - * @offset: address of the register to write - * @value: value to write to the register + * This enum is used to indicate DP VSC SDP Dynamic Range. + * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through + * DB18] * - * Returns the number of bytes transferred (1) on success, or a negative - * error code on failure. - */ -static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, - unsigned int offset, u8 value) -{ - return drm_dp_dpcd_write(aux, offset, &value, 1); -} - -int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, - u8 status[DP_LINK_STATUS_SIZE]); - -/* - * DisplayPort link + * @DP_DYNAMIC_RANGE_VESA: VESA range + * @DP_DYNAMIC_RANGE_CTA: CTA range */ -#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) - -struct drm_dp_link { - unsigned char revision; - unsigned int rate; - unsigned int num_lanes; - unsigned long capabilities; +enum dp_dynamic_range { + DP_DYNAMIC_RANGE_VESA = 0, + DP_DYNAMIC_RANGE_CTA = 1, }; -int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link); -int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link); -int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link); -int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link); -int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], - const u8 port_cap[4]); -int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], - const u8 port_cap[4]); -int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); -void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE], - const u8 port_cap[4], struct drm_dp_aux *aux); - -void drm_dp_aux_init(struct drm_dp_aux *aux); -int drm_dp_aux_register(struct drm_dp_aux *aux); -void drm_dp_aux_unregister(struct drm_dp_aux *aux); - -int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc); -int drm_dp_stop_crc(struct drm_dp_aux *aux); - -struct drm_dp_dpcd_ident { - u8 oui[3]; - u8 device_id[6]; - u8 hw_rev; - u8 sw_major_rev; - u8 sw_minor_rev; -} __packed; - /** - * struct drm_dp_desc - DP branch/sink device descriptor - * @ident: DP device identification from DPCD 0x400 (sink) or 0x500 (branch). - * @quirks: Quirks; use drm_dp_has_quirk() to query for the quirks. + * enum dp_content_type - drm DP Content Type + * + * This enum is used to indicate DP VSC SDP Content Types. + * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through + * DB18] + * CTA-861-G defines content types and expected processing by a sink device + * + * @DP_CONTENT_TYPE_NOT_DEFINED: Not defined type + * @DP_CONTENT_TYPE_GRAPHICS: Graphics type + * @DP_CONTENT_TYPE_PHOTO: Photo type + * @DP_CONTENT_TYPE_VIDEO: Video type + * @DP_CONTENT_TYPE_GAME: Game type */ -struct drm_dp_desc { - struct drm_dp_dpcd_ident ident; - u32 quirks; +enum dp_content_type { + DP_CONTENT_TYPE_NOT_DEFINED = 0x00, + DP_CONTENT_TYPE_GRAPHICS = 0x01, + DP_CONTENT_TYPE_PHOTO = 0x02, + DP_CONTENT_TYPE_VIDEO = 0x03, + DP_CONTENT_TYPE_GAME = 0x04, }; -int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc, - bool is_branch); - -/** - * enum drm_dp_quirk - Display Port sink/branch device specific quirks - * - * Display Port sink and branch devices in the wild have a variety of bugs, try - * to collect them here. The quirks are shared, but it's up to the drivers to - * implement workarounds for them. - */ -enum drm_dp_quirk { - /** - * @DP_DPCD_QUIRK_CONSTANT_N: - * - * The device requires main link attributes Mvid and Nvid to be limited - * to 16 bits. So will give a constant value (0x8000) for compatability. - */ - DP_DPCD_QUIRK_CONSTANT_N, - /** - * @DP_DPCD_QUIRK_NO_PSR: - * - * The device does not support PSR even if reports that it supports or - * driver still need to implement proper handling for such device. - */ - DP_DPCD_QUIRK_NO_PSR, +enum operation_mode { + DP_AS_SDP_AVT_DYNAMIC_VTOTAL = 0x00, + DP_AS_SDP_AVT_FIXED_VTOTAL = 0x01, + DP_AS_SDP_FAVT_TRR_NOT_REACHED = 0x02, + DP_AS_SDP_FAVT_TRR_REACHED = 0x03 }; -/** - * drm_dp_has_quirk() - does the DP device have a specific quirk - * @desc: Device decriptor filled by drm_dp_read_desc() - * @quirk: Quirk to query for - * - * Return true if DP device identified by @desc has @quirk. - */ -static inline bool -drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk) -{ - return desc->quirks & BIT(quirk); -} - -#ifdef CONFIG_DRM_DP_CEC -void drm_dp_cec_irq(struct drm_dp_aux *aux); -void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name, - struct device *parent); -void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux); -void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid); -void drm_dp_cec_unset_edid(struct drm_dp_aux *aux); -#else -static inline void drm_dp_cec_irq(struct drm_dp_aux *aux) -{ -} - -static inline void drm_dp_cec_register_connector(struct drm_dp_aux *aux, - const char *name, - struct device *parent) -{ -} - -static inline void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux) -{ -} - -static inline void drm_dp_cec_set_edid(struct drm_dp_aux *aux, - const struct edid *edid) -{ -} - -static inline void drm_dp_cec_unset_edid(struct drm_dp_aux *aux) -{ -} - -#endif - -#endif /* _DRM_DP_HELPER_H_ */ +#endif /* _DRM_DP_H_ */ diff --git a/include/drm/display/drm_dp_aux_bus.h b/include/drm/display/drm_dp_aux_bus.h new file mode 100644 index 000000000000..8a0a486383c5 --- /dev/null +++ b/include/drm/display/drm_dp_aux_bus.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2021 Google Inc. + * + * The DP AUX bus is used for devices that are connected over a DisplayPort + * AUX bus. The devices on the far side of the bus are referred to as + * endpoints in this code. + */ + +#ifndef _DP_AUX_BUS_H_ +#define _DP_AUX_BUS_H_ + +#include <linux/device.h> +#include <linux/mod_devicetable.h> + +/** + * struct dp_aux_ep_device - Main dev structure for DP AUX endpoints + * + * This is used to instantiate devices that are connected via a DP AUX + * bus. Usually the device is a panel, but conceivable other devices could + * be hooked up there. + */ +struct dp_aux_ep_device { + /** @dev: The normal dev pointer */ + struct device dev; + /** @aux: Pointer to the aux bus */ + struct drm_dp_aux *aux; +}; + +struct dp_aux_ep_driver { + int (*probe)(struct dp_aux_ep_device *aux_ep); + void (*remove)(struct dp_aux_ep_device *aux_ep); + void (*shutdown)(struct dp_aux_ep_device *aux_ep); + struct device_driver driver; +}; + +static inline struct dp_aux_ep_device *to_dp_aux_ep_dev(struct device *dev) +{ + return container_of(dev, struct dp_aux_ep_device, dev); +} + +static inline struct dp_aux_ep_driver *to_dp_aux_ep_drv(struct device_driver *drv) +{ + return container_of(drv, struct dp_aux_ep_driver, driver); +} + +int of_dp_aux_populate_bus(struct drm_dp_aux *aux, + int (*done_probing)(struct drm_dp_aux *aux)); +void of_dp_aux_depopulate_bus(struct drm_dp_aux *aux); +int devm_of_dp_aux_populate_bus(struct drm_dp_aux *aux, + int (*done_probing)(struct drm_dp_aux *aux)); + +/* Deprecated versions of the above functions. To be removed when no callers. */ +static inline int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux) +{ + int ret; + + ret = of_dp_aux_populate_bus(aux, NULL); + + /* New API returns -ENODEV for no child case; adapt to old assumption */ + return (ret != -ENODEV) ? ret : 0; +} + +static inline int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux) +{ + int ret; + + ret = devm_of_dp_aux_populate_bus(aux, NULL); + + /* New API returns -ENODEV for no child case; adapt to old assumption */ + return (ret != -ENODEV) ? ret : 0; +} + +static inline void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux) +{ + of_dp_aux_depopulate_bus(aux); +} + +#define dp_aux_dp_driver_register(aux_ep_drv) \ + __dp_aux_dp_driver_register(aux_ep_drv, THIS_MODULE) +int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *aux_ep_drv, + struct module *owner); +void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *aux_ep_drv); + +#endif /* _DP_AUX_BUS_H_ */ diff --git a/include/drm/drm_dp_dual_mode_helper.h b/include/drm/display/drm_dp_dual_mode_helper.h index 4c42db81fcb4..7ac6969db935 100644 --- a/include/drm/drm_dp_dual_mode_helper.h +++ b/include/drm/display/drm_dp_dual_mode_helper.h @@ -62,6 +62,7 @@ #define DP_DUAL_MODE_LSPCON_CURRENT_MODE 0x41 #define DP_DUAL_MODE_LSPCON_MODE_PCON 0x1 +struct drm_device; struct i2c_adapter; ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter, @@ -103,17 +104,18 @@ enum drm_dp_dual_mode_type { DRM_DP_DUAL_MODE_LSPCON, }; -enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(struct i2c_adapter *adapter); -int drm_dp_dual_mode_max_tmds_clock(enum drm_dp_dual_mode_type type, +enum drm_dp_dual_mode_type +drm_dp_dual_mode_detect(const struct drm_device *dev, struct i2c_adapter *adapter); +int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter); -int drm_dp_dual_mode_get_tmds_output(enum drm_dp_dual_mode_type type, +int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter, bool *enabled); -int drm_dp_dual_mode_set_tmds_output(enum drm_dp_dual_mode_type type, +int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter, bool enable); const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type); -int drm_lspcon_get_mode(struct i2c_adapter *adapter, +int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter, enum drm_lspcon_mode *current_mode); -int drm_lspcon_set_mode(struct i2c_adapter *adapter, - enum drm_lspcon_mode reqd_mode); +int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter, + enum drm_lspcon_mode reqd_mode, int time_out); #endif diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h new file mode 100644 index 000000000000..df2f24b950e4 --- /dev/null +++ b/include/drm/display/drm_dp_helper.h @@ -0,0 +1,1013 @@ +/* + * Copyright © 2008 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _DRM_DP_HELPER_H_ +#define _DRM_DP_HELPER_H_ + +#include <linux/delay.h> +#include <linux/i2c.h> + +#include <drm/display/drm_dp.h> +#include <drm/drm_connector.h> + +struct drm_device; +struct drm_dp_aux; +struct drm_panel; + +bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); +bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); +bool drm_dp_post_lt_adj_req_in_progress(const u8 link_status[DP_LINK_STATUS_SIZE]); +u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane); +u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane); +u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane); + +int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr); +int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, bool uhbr); + +void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE]); +void drm_dp_lttpr_link_train_clock_recovery_delay(void); +void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE]); +void drm_dp_lttpr_link_train_channel_eq_delay(const struct drm_dp_aux *aux, + const u8 caps[DP_LTTPR_PHY_CAP_SIZE]); + +int drm_dp_128b132b_read_aux_rd_interval(struct drm_dp_aux *aux); +bool drm_dp_128b132b_lane_channel_eq_done(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); +bool drm_dp_128b132b_lane_symbol_locked(const u8 link_status[DP_LINK_STATUS_SIZE], + int lane_count); +bool drm_dp_128b132b_eq_interlane_align_done(const u8 link_status[DP_LINK_STATUS_SIZE]); +bool drm_dp_128b132b_cds_interlane_align_done(const u8 link_status[DP_LINK_STATUS_SIZE]); +bool drm_dp_128b132b_link_training_failed(const u8 link_status[DP_LINK_STATUS_SIZE]); + +u8 drm_dp_link_rate_to_bw_code(int link_rate); +int drm_dp_bw_code_to_link_rate(u8 link_bw); + +const char *drm_dp_phy_name(enum drm_dp_phy dp_phy); + +/** + * struct drm_dp_vsc_sdp - drm DP VSC SDP + * + * This structure represents a DP VSC SDP of drm + * It is based on DP 1.4 spec [Table 2-116: VSC SDP Header Bytes] and + * [Table 2-117: VSC SDP Payload for DB16 through DB18] + * + * @sdp_type: secondary-data packet type + * @revision: revision number + * @length: number of valid data bytes + * @pixelformat: pixel encoding format + * @colorimetry: colorimetry format + * @bpc: bit per color + * @dynamic_range: dynamic range information + * @content_type: CTA-861-G defines content types and expected processing by a sink device + */ +struct drm_dp_vsc_sdp { + unsigned char sdp_type; + unsigned char revision; + unsigned char length; + enum dp_pixelformat pixelformat; + enum dp_colorimetry colorimetry; + int bpc; + enum dp_dynamic_range dynamic_range; + enum dp_content_type content_type; +}; + +/** + * struct drm_dp_as_sdp - drm DP Adaptive Sync SDP + * + * This structure represents a DP AS SDP of drm + * It is based on DP 2.1 spec [Table 2-126: Adaptive-Sync SDP Header Bytes] and + * [Table 2-127: Adaptive-Sync SDP Payload for DB0 through DB8] + * + * @sdp_type: Secondary-data packet type + * @revision: Revision Number + * @length: Number of valid data bytes + * @vtotal: Minimum Vertical Vtotal + * @target_rr: Target Refresh + * @duration_incr_ms: Successive frame duration increase + * @duration_decr_ms: Successive frame duration decrease + * @target_rr_divider: Target refresh rate divider + * @mode: Adaptive Sync Operation Mode + */ +struct drm_dp_as_sdp { + unsigned char sdp_type; + unsigned char revision; + unsigned char length; + int vtotal; + int target_rr; + int duration_incr_ms; + int duration_decr_ms; + bool target_rr_divider; + enum operation_mode mode; +}; + +void drm_dp_as_sdp_log(struct drm_printer *p, + const struct drm_dp_as_sdp *as_sdp); +void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc); + +bool drm_dp_vsc_sdp_supported(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]); +bool drm_dp_as_sdp_supported(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]); + +int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]); + +static inline int +drm_dp_max_link_rate(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]); +} + +static inline u8 +drm_dp_max_lane_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; +} + +static inline bool +drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x11 && + (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP); +} + +static inline bool +drm_dp_post_lt_adj_req_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x13 && + (dpcd[DP_MAX_LANE_COUNT] & DP_POST_LT_ADJ_REQ_SUPPORTED); +} + +static inline bool +drm_dp_fast_training_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x11 && + (dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING); +} + +static inline bool +drm_dp_tps3_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x12 && + dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED; +} + +static inline bool +drm_dp_max_downspread(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x11 || + dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5; +} + +static inline bool +drm_dp_tps4_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x14 && + dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED; +} + +static inline u8 +drm_dp_training_pattern_mask(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return (dpcd[DP_DPCD_REV] >= 0x14) ? DP_TRAINING_PATTERN_MASK_1_4 : + DP_TRAINING_PATTERN_MASK; +} + +static inline bool +drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT; +} + +/* DP/eDP DSC support */ +u8 drm_dp_dsc_sink_bpp_incr(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]); +u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], + bool is_edp); +u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]); +int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpc[DP_DSC_RECEIVER_CAP_SIZE], + u8 dsc_bpc[3]); +int drm_dp_dsc_sink_max_slice_throughput(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], + int peak_pixel_rate, bool is_rgb_yuv444); +int drm_dp_dsc_branch_max_overall_throughput(const u8 dsc_branch_dpcd[DP_DSC_BRANCH_CAP_SIZE], + bool is_rgb_yuv444); +int drm_dp_dsc_branch_max_line_width(const u8 dsc_branch_dpcd[DP_DSC_BRANCH_CAP_SIZE]); + +static inline bool +drm_dp_sink_supports_dsc(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) +{ + return dsc_dpcd[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & + DP_DSC_DECOMPRESSION_IS_SUPPORTED; +} + +static inline u16 +drm_edp_dsc_sink_output_bpp(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) +{ + return dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] | + ((dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] & + DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK) << 8); +} + +static inline u32 +drm_dp_dsc_sink_max_slice_width(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) +{ + /* Max Slicewidth = Number of Pixels * 320 */ + return dsc_dpcd[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * + DP_DSC_SLICE_WIDTH_MULTIPLIER; +} + +/** + * drm_dp_dsc_sink_supports_format() - check if sink supports DSC with given output format + * @dsc_dpcd : DSC-capability DPCDs of the sink + * @output_format: output_format which is to be checked + * + * Returns true if the sink supports DSC with the given output_format, false otherwise. + */ +static inline bool +drm_dp_dsc_sink_supports_format(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], u8 output_format) +{ + return dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT] & output_format; +} + +/* Forward Error Correction Support on DP 1.4 */ +static inline bool +drm_dp_sink_supports_fec(const u8 fec_capable) +{ + return fec_capable & DP_FEC_CAPABLE; +} + +static inline bool +drm_dp_channel_coding_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_MAIN_LINK_CHANNEL_CODING] & DP_CAP_ANSI_8B10B; +} + +static inline bool +drm_dp_128b132b_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_MAIN_LINK_CHANNEL_CODING] & DP_CAP_ANSI_128B132B; +} + +static inline bool +drm_dp_alternate_scrambler_reset_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_EDP_CONFIGURATION_CAP] & + DP_ALTERNATE_SCRAMBLER_RESET_CAP; +} + +/* Ignore MSA timing for Adaptive Sync support on DP 1.4 */ +static inline bool +drm_dp_sink_can_do_video_without_timing_msa(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DOWN_STREAM_PORT_COUNT] & + DP_MSA_TIMING_PAR_IGNORED; +} + +/** + * drm_edp_backlight_supported() - Check an eDP DPCD for VESA backlight support + * @edp_dpcd: The DPCD to check + * + * Note that currently this function will return %false for panels which support various DPCD + * backlight features but which require the brightness be set through PWM, and don't support setting + * the brightness level via the DPCD. + * + * Returns: %True if @edp_dpcd indicates that VESA backlight controls are supported, %false + * otherwise + */ +static inline bool +drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]) +{ + return !!(edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP); +} + +/** + * drm_dp_is_uhbr_rate - Determine if a link rate is UHBR + * @link_rate: link rate in 10kbits/s units + * + * Determine if the provided link rate is an UHBR rate. + * + * Returns: %True if @link_rate is an UHBR rate. + */ +static inline bool drm_dp_is_uhbr_rate(int link_rate) +{ + return link_rate >= 1000000; +} + +/* + * DisplayPort AUX channel + */ + +/** + * struct drm_dp_aux_msg - DisplayPort AUX channel transaction + * @address: address of the (first) register to access + * @request: contains the type of transaction (see DP_AUX_* macros) + * @reply: upon completion, contains the reply type of the transaction + * @buffer: pointer to a transmission or reception buffer + * @size: size of @buffer + */ +struct drm_dp_aux_msg { + unsigned int address; + u8 request; + u8 reply; + void *buffer; + size_t size; +}; + +struct cec_adapter; +struct drm_connector; +struct drm_edid; + +/** + * struct drm_dp_aux_cec - DisplayPort CEC-Tunneling-over-AUX + * @lock: mutex protecting this struct + * @adap: the CEC adapter for CEC-Tunneling-over-AUX support. + * @connector: the connector this CEC adapter is associated with + * @unregister_work: unregister the CEC adapter + */ +struct drm_dp_aux_cec { + struct mutex lock; + struct cec_adapter *adap; + struct drm_connector *connector; + struct delayed_work unregister_work; +}; + +/** + * struct drm_dp_aux - DisplayPort AUX channel + * + * An AUX channel can also be used to transport I2C messages to a sink. A + * typical application of that is to access an EDID that's present in the sink + * device. The @transfer() function can also be used to execute such + * transactions. The drm_dp_aux_register() function registers an I2C adapter + * that can be passed to drm_probe_ddc(). Upon removal, drivers should call + * drm_dp_aux_unregister() to remove the I2C adapter. The I2C adapter uses long + * transfers by default; if a partial response is received, the adapter will + * drop down to the size given by the partial response for this transaction + * only. + */ +struct drm_dp_aux { + /** + * @name: user-visible name of this AUX channel and the + * I2C-over-AUX adapter. + * + * It's also used to specify the name of the I2C adapter. If set + * to %NULL, dev_name() of @dev will be used. + */ + const char *name; + + /** + * @ddc: I2C adapter that can be used for I2C-over-AUX + * communication + */ + struct i2c_adapter ddc; + + /** + * @dev: pointer to struct device that is the parent for this + * AUX channel. + */ + struct device *dev; + + /** + * @drm_dev: pointer to the &drm_device that owns this AUX channel. + * Beware, this may be %NULL before drm_dp_aux_register() has been + * called. + * + * It should be set to the &drm_device that will be using this AUX + * channel as early as possible. For many graphics drivers this should + * happen before drm_dp_aux_init(), however it's perfectly fine to set + * this field later so long as it's assigned before calling + * drm_dp_aux_register(). + */ + struct drm_device *drm_dev; + + /** + * @crtc: backpointer to the crtc that is currently using this + * AUX channel + */ + struct drm_crtc *crtc; + + /** + * @hw_mutex: internal mutex used for locking transfers. + * + * Note that if the underlying hardware is shared among multiple + * channels, the driver needs to do additional locking to + * prevent concurrent access. + */ + struct mutex hw_mutex; + + /** + * @crc_work: worker that captures CRCs for each frame + */ + struct work_struct crc_work; + + /** + * @crc_count: counter of captured frame CRCs + */ + u8 crc_count; + + /** + * @transfer: transfers a message representing a single AUX + * transaction. + * + * This is a hardware-specific implementation of how + * transactions are executed that the drivers must provide. + * + * A pointer to a &drm_dp_aux_msg structure describing the + * transaction is passed into this function. Upon success, the + * implementation should return the number of payload bytes that + * were transferred, or a negative error-code on failure. + * + * Helpers will propagate these errors, with the exception of + * the %-EBUSY error, which causes a transaction to be retried. + * On a short, helpers will return %-EPROTO to make it simpler + * to check for failure. + * + * The @transfer() function must only modify the reply field of + * the &drm_dp_aux_msg structure. The retry logic and i2c + * helpers assume this is the case. + * + * Also note that this callback can be called no matter the + * state @dev is in and also no matter what state the panel is + * in. It's expected: + * + * - If the @dev providing the AUX bus is currently unpowered then + * it will power itself up for the transfer. + * + * - If we're on eDP (using a drm_panel) and the panel is not in a + * state where it can respond (it's not powered or it's in a + * low power state) then this function may return an error, but + * not crash. It's up to the caller of this code to make sure that + * the panel is powered on if getting an error back is not OK. If a + * drm_panel driver is initiating a DP AUX transfer it may power + * itself up however it wants. All other code should ensure that + * the pre_enable() bridge chain (which eventually calls the + * drm_panel prepare function) has powered the panel. + */ + ssize_t (*transfer)(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg); + + /** + * @wait_hpd_asserted: wait for HPD to be asserted + * + * This is mainly useful for eDP panels drivers to wait for an eDP + * panel to finish powering on. It is optional for DP AUX controllers + * to implement this function. It is required for DP AUX endpoints + * (panel drivers) to call this function after powering up but before + * doing AUX transfers unless the DP AUX endpoint driver knows that + * we're not using the AUX controller's HPD. One example of the panel + * driver not needing to call this is if HPD is hooked up to a GPIO + * that the panel driver can read directly. + * + * If a DP AUX controller does not implement this function then it + * may still support eDP panels that use the AUX controller's built-in + * HPD signal by implementing a long wait for HPD in the transfer() + * callback, though this is deprecated. + * + * This function will efficiently wait for the HPD signal to be + * asserted. The `wait_us` parameter that is passed in says that we + * know that the HPD signal is expected to be asserted within `wait_us` + * microseconds. This function could wait for longer than `wait_us` if + * the logic in the DP controller has a long debouncing time. The + * important thing is that if this function returns success that the + * DP controller is ready to send AUX transactions. + * + * This function returns 0 if HPD was asserted or -ETIMEDOUT if time + * expired and HPD wasn't asserted. This function should not print + * timeout errors to the log. + * + * The semantics of this function are designed to match the + * readx_poll_timeout() function. That means a `wait_us` of 0 means + * to wait forever. Like readx_poll_timeout(), this function may sleep. + * + * NOTE: this function specifically reports the state of the HPD pin + * that's associated with the DP AUX channel. This is different from + * the HPD concept in much of the rest of DRM which is more about + * physical presence of a display. For eDP, for instance, a display is + * assumed always present even if the HPD pin is deasserted. + */ + int (*wait_hpd_asserted)(struct drm_dp_aux *aux, unsigned long wait_us); + + /** + * @i2c_nack_count: Counts I2C NACKs, used for DP validation. + */ + unsigned i2c_nack_count; + /** + * @i2c_defer_count: Counts I2C DEFERs, used for DP validation. + */ + unsigned i2c_defer_count; + /** + * @cec: struct containing fields used for CEC-Tunneling-over-AUX. + */ + struct drm_dp_aux_cec cec; + /** + * @is_remote: Is this AUX CH actually using sideband messaging. + */ + bool is_remote; + + /** + * @powered_down: If true then the remote endpoint is powered down. + */ + bool powered_down; + + /** + * @no_zero_sized: If the hw can't use zero sized transfers (NVIDIA) + */ + bool no_zero_sized; + + /** + * @dpcd_probe_disabled: If probing before a DPCD access is disabled. + */ + bool dpcd_probe_disabled; +}; + +int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset); +void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered); +void drm_dp_dpcd_set_probe(struct drm_dp_aux *aux, bool enable); +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size); +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size); + +/** + * drm_dp_dpcd_read_data() - read a series of bytes from the DPCD + * @aux: DisplayPort AUX channel (SST or MST) + * @offset: address of the (first) register to read + * @buffer: buffer to store the register values + * @size: number of bytes in @buffer + * + * Returns zero (0) on success, or a negative error + * code on failure. -EIO is returned if the request was NAKed by the sink or + * if the retry count was exceeded. If not all bytes were transferred, this + * function returns -EPROTO. Errors from the underlying AUX channel transfer + * function, with the exception of -EBUSY (which causes the transaction to + * be retried), are propagated to the caller. + */ +static inline int drm_dp_dpcd_read_data(struct drm_dp_aux *aux, + unsigned int offset, + void *buffer, size_t size) +{ + int ret; + + ret = drm_dp_dpcd_read(aux, offset, buffer, size); + if (ret < 0) + return ret; + if (ret < size) + return -EPROTO; + + return 0; +} + +/** + * drm_dp_dpcd_write_data() - write a series of bytes to the DPCD + * @aux: DisplayPort AUX channel (SST or MST) + * @offset: address of the (first) register to write + * @buffer: buffer containing the values to write + * @size: number of bytes in @buffer + * + * Returns zero (0) on success, or a negative error + * code on failure. -EIO is returned if the request was NAKed by the sink or + * if the retry count was exceeded. If not all bytes were transferred, this + * function returns -EPROTO. Errors from the underlying AUX channel transfer + * function, with the exception of -EBUSY (which causes the transaction to + * be retried), are propagated to the caller. + */ +static inline int drm_dp_dpcd_write_data(struct drm_dp_aux *aux, + unsigned int offset, + void *buffer, size_t size) +{ + int ret; + + ret = drm_dp_dpcd_write(aux, offset, buffer, size); + if (ret < 0) + return ret; + if (ret < size) + return -EPROTO; + + return 0; +} + +/** + * drm_dp_dpcd_readb() - read a single byte from the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the register to read + * @valuep: location where the value of the register will be stored + * + * Returns the number of bytes transferred (1) on success, or a negative + * error code on failure. In most of the cases you should be using + * drm_dp_dpcd_read_byte() instead. + */ +static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux, + unsigned int offset, u8 *valuep) +{ + return drm_dp_dpcd_read(aux, offset, valuep, 1); +} + +/** + * drm_dp_dpcd_writeb() - write a single byte to the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the register to write + * @value: value to write to the register + * + * Returns the number of bytes transferred (1) on success, or a negative + * error code on failure. In most of the cases you should be using + * drm_dp_dpcd_write_byte() instead. + */ +static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, + unsigned int offset, u8 value) +{ + return drm_dp_dpcd_write(aux, offset, &value, 1); +} + +/** + * drm_dp_dpcd_read_byte() - read a single byte from the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the register to read + * @valuep: location where the value of the register will be stored + * + * Returns zero (0) on success, or a negative error code on failure. + */ +static inline int drm_dp_dpcd_read_byte(struct drm_dp_aux *aux, + unsigned int offset, u8 *valuep) +{ + return drm_dp_dpcd_read_data(aux, offset, valuep, 1); +} + +/** + * drm_dp_dpcd_write_byte() - write a single byte to the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the register to write + * @value: value to write to the register + * + * Returns zero (0) on success, or a negative error code on failure. + */ +static inline int drm_dp_dpcd_write_byte(struct drm_dp_aux *aux, + unsigned int offset, u8 value) +{ + return drm_dp_dpcd_write_data(aux, offset, &value, 1); +} + +int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux, + u8 dpcd[DP_RECEIVER_CAP_SIZE]); + +int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, + u8 status[DP_LINK_STATUS_SIZE]); + +int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux, + enum drm_dp_phy dp_phy, + u8 link_status[DP_LINK_STATUS_SIZE]); +int drm_dp_link_power_up(struct drm_dp_aux *aux, unsigned char revision); +int drm_dp_link_power_down(struct drm_dp_aux *aux, unsigned char revision); + +int drm_dp_dpcd_write_payload(struct drm_dp_aux *aux, + int vcpid, u8 start_time_slot, u8 time_slot_count); +int drm_dp_dpcd_clear_payload(struct drm_dp_aux *aux); +int drm_dp_dpcd_poll_act_handled(struct drm_dp_aux *aux, int timeout_ms); + +bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux, + u8 real_edid_checksum); + +int drm_dp_read_downstream_info(struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]); +bool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], u8 type); +bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], + const struct drm_edid *drm_edid); +int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); +int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], + const struct drm_edid *drm_edid); +int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], + const struct drm_edid *drm_edid); +int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], + const struct drm_edid *drm_edid); +bool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); +bool drm_dp_downstream_444_to_420_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); +struct drm_display_mode *drm_dp_downstream_mode(struct drm_device *dev, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); +int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); +void drm_dp_downstream_debug(struct seq_file *m, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], + const struct drm_edid *drm_edid, + struct drm_dp_aux *aux); +enum drm_mode_subconnector +drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); +void drm_dp_set_subconnector_property(struct drm_connector *connector, + enum drm_connector_status status, + const u8 *dpcd, + const u8 port_cap[4]); + +struct drm_dp_desc; +bool drm_dp_read_sink_count_cap(struct drm_connector *connector, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const struct drm_dp_desc *desc); +int drm_dp_read_sink_count(struct drm_dp_aux *aux); + +int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + u8 caps[DP_LTTPR_COMMON_CAP_SIZE]); +int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + enum drm_dp_phy dp_phy, + u8 caps[DP_LTTPR_PHY_CAP_SIZE]); +int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]); +int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]); +int drm_dp_lttpr_set_transparent_mode(struct drm_dp_aux *aux, bool enable); +int drm_dp_lttpr_init(struct drm_dp_aux *aux, int lttpr_count); +int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]); +bool drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]); +bool drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]); +void drm_dp_lttpr_wake_timeout_setup(struct drm_dp_aux *aux, bool transparent_mode); + +void drm_dp_remote_aux_init(struct drm_dp_aux *aux); +void drm_dp_aux_init(struct drm_dp_aux *aux); +int drm_dp_aux_register(struct drm_dp_aux *aux); +void drm_dp_aux_unregister(struct drm_dp_aux *aux); + +int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc); +int drm_dp_stop_crc(struct drm_dp_aux *aux); + +struct drm_dp_dpcd_ident { + u8 oui[3]; + u8 device_id[6]; + u8 hw_rev; + u8 sw_major_rev; + u8 sw_minor_rev; +} __packed; + +/** + * struct drm_dp_desc - DP branch/sink device descriptor + * @ident: DP device identification from DPCD 0x400 (sink) or 0x500 (branch). + * @quirks: Quirks; use drm_dp_has_quirk() to query for the quirks. + */ +struct drm_dp_desc { + struct drm_dp_dpcd_ident ident; + u32 quirks; +}; + +int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc, + bool is_branch); + +int drm_dp_dump_lttpr_desc(struct drm_dp_aux *aux, enum drm_dp_phy dp_phy); + +/** + * enum drm_dp_quirk - Display Port sink/branch device specific quirks + * + * Display Port sink and branch devices in the wild have a variety of bugs, try + * to collect them here. The quirks are shared, but it's up to the drivers to + * implement workarounds for them. + */ +enum drm_dp_quirk { + /** + * @DP_DPCD_QUIRK_CONSTANT_N: + * + * The device requires main link attributes Mvid and Nvid to be limited + * to 16 bits. So will give a constant value (0x8000) for compatability. + */ + DP_DPCD_QUIRK_CONSTANT_N, + /** + * @DP_DPCD_QUIRK_NO_PSR: + * + * The device does not support PSR even if reports that it supports or + * driver still need to implement proper handling for such device. + */ + DP_DPCD_QUIRK_NO_PSR, + /** + * @DP_DPCD_QUIRK_NO_SINK_COUNT: + * + * The device does not set SINK_COUNT to a non-zero value. + * The driver should ignore SINK_COUNT during detection. Note that + * drm_dp_read_sink_count_cap() automatically checks for this quirk. + */ + DP_DPCD_QUIRK_NO_SINK_COUNT, + /** + * @DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD: + * + * The device supports MST DSC despite not supporting Virtual DPCD. + * The DSC caps can be read from the physical aux instead. + */ + DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD, + /** + * @DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS: + * + * The device supports a link rate of 3.24 Gbps (multiplier 0xc) despite + * the DP_MAX_LINK_RATE register reporting a lower max multiplier. + */ + DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS, + /** + * @DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC: + * + * The device applies HBLANK expansion for some modes, but this + * requires enabling DSC. + */ + DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC, + /** + * @DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT: + * + * The device doesn't support DSC decompression at the maximum DSC + * pixel throughput and compressed bpp it indicates via its DPCD DSC + * capabilities. The compressed bpp must be limited above a device + * specific DSC pixel throughput. + */ + DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT, +}; + +/** + * drm_dp_has_quirk() - does the DP device have a specific quirk + * @desc: Device descriptor filled by drm_dp_read_desc() + * @quirk: Quirk to query for + * + * Return true if DP device identified by @desc has @quirk. + */ +static inline bool +drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk) +{ + return desc->quirks & BIT(quirk); +} + +/** + * struct drm_edp_backlight_info - Probed eDP backlight info struct + * @pwmgen_bit_count: The pwmgen bit count + * @pwm_freq_pre_divider: The PWM frequency pre-divider value being used for this backlight, if any + * @max: The maximum backlight level that may be set + * @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register? + * @aux_enable: Does the panel support the AUX enable cap? + * @aux_set: Does the panel support setting the brightness through AUX? + * @luminance_set: Does the panel support setting the brightness through AUX using luminance values? + * + * This structure contains various data about an eDP backlight, which can be populated by using + * drm_edp_backlight_init(). + */ +struct drm_edp_backlight_info { + u8 pwmgen_bit_count; + u8 pwm_freq_pre_divider; + u32 max; + + bool lsb_reg_used : 1; + bool aux_enable : 1; + bool aux_set : 1; + bool luminance_set : 1; +}; + +int +drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, + u32 max_luminance, + u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE], + u32 *current_level, u8 *current_mode, bool need_luminance); +int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + u32 level); +int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, + u32 level); +int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl); + +#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ + (IS_MODULE(CONFIG_DRM_KMS_HELPER) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE))) + +int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux); + +#else + +static inline int drm_panel_dp_aux_backlight(struct drm_panel *panel, + struct drm_dp_aux *aux) +{ + return 0; +} + +#endif + +#ifdef CONFIG_DRM_DISPLAY_DP_AUX_CEC +void drm_dp_cec_irq(struct drm_dp_aux *aux); +void drm_dp_cec_register_connector(struct drm_dp_aux *aux, + struct drm_connector *connector); +void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux); +void drm_dp_cec_attach(struct drm_dp_aux *aux, u16 source_physical_address); +void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid); +void drm_dp_cec_unset_edid(struct drm_dp_aux *aux); +#else +static inline void drm_dp_cec_irq(struct drm_dp_aux *aux) +{ +} + +static inline void +drm_dp_cec_register_connector(struct drm_dp_aux *aux, + struct drm_connector *connector) +{ +} + +static inline void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux) +{ +} + +static inline void drm_dp_cec_attach(struct drm_dp_aux *aux, + u16 source_physical_address) +{ +} + +static inline void drm_dp_cec_set_edid(struct drm_dp_aux *aux, + const struct edid *edid) +{ +} + +static inline void drm_dp_cec_unset_edid(struct drm_dp_aux *aux) +{ +} + +#endif + +/** + * struct drm_dp_phy_test_params - DP Phy Compliance parameters + * @link_rate: Requested Link rate from DPCD 0x219 + * @num_lanes: Number of lanes requested by sing through DPCD 0x220 + * @phy_pattern: DP Phy test pattern from DPCD 0x248 + * @hbr2_reset: DP HBR2_COMPLIANCE_SCRAMBLER_RESET from DCPD 0x24A and 0x24B + * @custom80: DP Test_80BIT_CUSTOM_PATTERN from DPCDs 0x250 through 0x259 + * @enhanced_frame_cap: flag for enhanced frame capability. + */ +struct drm_dp_phy_test_params { + int link_rate; + u8 num_lanes; + u8 phy_pattern; + u8 hbr2_reset[2]; + u8 custom80[10]; + bool enhanced_frame_cap; +}; + +int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux, + struct drm_dp_phy_test_params *data); +int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux, + struct drm_dp_phy_test_params *data, u8 dp_rev); +int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); +int drm_dp_pcon_frl_prepare(struct drm_dp_aux *aux, bool enable_frl_ready_hpd); +bool drm_dp_pcon_is_frl_ready(struct drm_dp_aux *aux); +int drm_dp_pcon_frl_configure_1(struct drm_dp_aux *aux, int max_frl_gbps, + u8 frl_mode); +int drm_dp_pcon_frl_configure_2(struct drm_dp_aux *aux, int max_frl_mask, + u8 frl_type); +int drm_dp_pcon_reset_frl_config(struct drm_dp_aux *aux); +int drm_dp_pcon_frl_enable(struct drm_dp_aux *aux); + +bool drm_dp_pcon_hdmi_link_active(struct drm_dp_aux *aux); +int drm_dp_pcon_hdmi_link_mode(struct drm_dp_aux *aux, u8 *frl_trained_mask); +void drm_dp_pcon_hdmi_frl_link_error_count(struct drm_dp_aux *aux, + struct drm_connector *connector); +bool drm_dp_pcon_enc_is_dsc_1_2(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]); +int drm_dp_pcon_dsc_max_slices(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]); +int drm_dp_pcon_dsc_max_slice_width(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]); +int drm_dp_pcon_dsc_bpp_incr(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]); +int drm_dp_pcon_pps_default(struct drm_dp_aux *aux); +int drm_dp_pcon_pps_override_buf(struct drm_dp_aux *aux, u8 pps_buf[128]); +int drm_dp_pcon_pps_override_param(struct drm_dp_aux *aux, u8 pps_param[6]); +bool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], u8 color_spc); +int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc); + +#define DRM_DP_BW_OVERHEAD_MST BIT(0) +#define DRM_DP_BW_OVERHEAD_UHBR BIT(1) +#define DRM_DP_BW_OVERHEAD_SSC_REF_CLK BIT(2) +#define DRM_DP_BW_OVERHEAD_FEC BIT(3) +#define DRM_DP_BW_OVERHEAD_DSC BIT(4) + +int drm_dp_bw_overhead(int lane_count, int hactive, + int dsc_slice_count, + int bpp_x16, unsigned long flags); +int drm_dp_bw_channel_coding_efficiency(bool is_uhbr); +int drm_dp_max_dprx_data_rate(int max_link_rate, int max_lanes); + +ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, struct dp_sdp *sdp); +int drm_dp_link_symbol_cycles(int lane_count, int pixels, int dsc_slice_count, + int bpp_x16, int symbol_size, bool is_mst); + +#endif /* _DRM_DP_HELPER_H_ */ diff --git a/include/drm/display/drm_dp_mst_helper.h b/include/drm/display/drm_dp_mst_helper.h new file mode 100644 index 000000000000..2cfe1d4bfc96 --- /dev/null +++ b/include/drm/display/drm_dp_mst_helper.h @@ -0,0 +1,1076 @@ +/* + * Copyright © 2014 Red Hat. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#ifndef _DRM_DP_MST_HELPER_H_ +#define _DRM_DP_MST_HELPER_H_ + +#include <linux/types.h> +#include <drm/display/drm_dp_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_fixed.h> + +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) +#include <linux/stackdepot.h> +#include <linux/timekeeping.h> + +enum drm_dp_mst_topology_ref_type { + DRM_DP_MST_TOPOLOGY_REF_GET, + DRM_DP_MST_TOPOLOGY_REF_PUT, +}; + +struct drm_dp_mst_topology_ref_history { + struct drm_dp_mst_topology_ref_entry { + enum drm_dp_mst_topology_ref_type type; + int count; + ktime_t ts_nsec; + depot_stack_handle_t backtrace; + } *entries; + int len; +}; +#endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */ + +enum drm_dp_mst_payload_allocation { + DRM_DP_MST_PAYLOAD_ALLOCATION_NONE, + DRM_DP_MST_PAYLOAD_ALLOCATION_LOCAL, + DRM_DP_MST_PAYLOAD_ALLOCATION_DFP, + DRM_DP_MST_PAYLOAD_ALLOCATION_REMOTE, +}; + +struct drm_dp_mst_branch; + +/** + * struct drm_dp_mst_port - MST port + * @port_num: port number + * @input: if this port is an input port. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @mcs: message capability status - DP 1.2 spec. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @pdt: Peer Device Type. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @ldps: Legacy Device Plug Status. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @dpcd_rev: DPCD revision of device on this port. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @num_sdp_streams: Number of simultaneous streams. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @num_sdp_stream_sinks: Number of stream sinks. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @full_pbn: Max possible bandwidth for this port. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @next: link to next port on this branch device + * @aux: i2c aux transport to talk to device connected to this port, protected + * by &drm_dp_mst_topology_mgr.base.lock. + * @passthrough_aux: parent aux to which DSC pass-through requests should be + * sent, only set if DSC pass-through is possible. + * @parent: branch device parent of this port + * @connector: DRM connector this port is connected to. Protected by + * &drm_dp_mst_topology_mgr.base.lock. + * @mgr: topology manager this port lives under. + * + * This structure represents an MST port endpoint on a device somewhere + * in the MST topology. + */ +struct drm_dp_mst_port { + /** + * @topology_kref: refcount for this port's lifetime in the topology, + * only the DP MST helpers should need to touch this + */ + struct kref topology_kref; + + /** + * @malloc_kref: refcount for the memory allocation containing this + * structure. See drm_dp_mst_get_port_malloc() and + * drm_dp_mst_put_port_malloc(). + */ + struct kref malloc_kref; + +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) + /** + * @topology_ref_history: A history of each topology + * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS. + */ + struct drm_dp_mst_topology_ref_history topology_ref_history; +#endif + + u8 port_num; + bool input; + bool mcs; + bool ddps; + u8 pdt; + bool ldps; + u8 dpcd_rev; + u8 num_sdp_streams; + u8 num_sdp_stream_sinks; + uint16_t full_pbn; + struct list_head next; + /** + * @mstb: the branch device connected to this port, if there is one. + * This should be considered protected for reading by + * &drm_dp_mst_topology_mgr.lock. There are two exceptions to this: + * &drm_dp_mst_topology_mgr.up_req_work and + * &drm_dp_mst_topology_mgr.work, which do not grab + * &drm_dp_mst_topology_mgr.lock during reads but are the only + * updaters of this list and are protected from writing concurrently + * by &drm_dp_mst_topology_mgr.probe_lock. + */ + struct drm_dp_mst_branch *mstb; + struct drm_dp_aux aux; /* i2c bus for this port? */ + struct drm_dp_aux *passthrough_aux; + struct drm_dp_mst_branch *parent; + + struct drm_connector *connector; + struct drm_dp_mst_topology_mgr *mgr; + + /** + * @cached_edid: for DP logical ports - make tiling work by ensuring + * that the EDID for all connectors is read immediately. + */ + const struct drm_edid *cached_edid; + + /** + * @fec_capable: bool indicating if FEC can be supported up to that + * point in the MST topology. + */ + bool fec_capable; +}; + +/* sideband msg header - not bit struct */ +struct drm_dp_sideband_msg_hdr { + u8 lct; + u8 lcr; + u8 rad[8]; + bool broadcast; + bool path_msg; + u8 msg_len; + bool somt; + bool eomt; + bool seqno; +}; + +struct drm_dp_sideband_msg_rx { + u8 chunk[48]; + u8 msg[256]; + u8 curchunk_len; + u8 curchunk_idx; /* chunk we are parsing now */ + u8 curchunk_hdrlen; + u8 curlen; /* total length of the msg */ + bool have_somt; + bool have_eomt; + struct drm_dp_sideband_msg_hdr initial_hdr; +}; + +/** + * struct drm_dp_mst_branch - MST branch device. + * @rad: Relative Address to talk to this branch device. + * @lct: Link count total to talk to this branch device. + * @num_ports: number of ports on the branch. + * @port_parent: pointer to the port parent, NULL if toplevel. + * @mgr: topology manager for this branch device. + * @link_address_sent: if a link address message has been sent to this device yet. + * @guid: guid for DP 1.2 branch device. port under this branch can be + * identified by port #. + * + * This structure represents an MST branch device, there is one + * primary branch device at the root, along with any other branches connected + * to downstream port of parent branches. + */ +struct drm_dp_mst_branch { + /** + * @topology_kref: refcount for this branch device's lifetime in the + * topology, only the DP MST helpers should need to touch this + */ + struct kref topology_kref; + + /** + * @malloc_kref: refcount for the memory allocation containing this + * structure. See drm_dp_mst_get_mstb_malloc() and + * drm_dp_mst_put_mstb_malloc(). + */ + struct kref malloc_kref; + +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) + /** + * @topology_ref_history: A history of each topology + * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS. + */ + struct drm_dp_mst_topology_ref_history topology_ref_history; +#endif + + /** + * @destroy_next: linked-list entry used by + * drm_dp_delayed_destroy_work() + */ + struct list_head destroy_next; + + /** + * @rad: Relative Address of the MST branch. + * For &drm_dp_mst_topology_mgr.mst_primary, it's rad[8] are all 0, + * unset and unused. For MST branches connected after mst_primary, + * in each element of rad[] the nibbles are ordered by the most + * signifcant 4 bits first and the least significant 4 bits second. + */ + u8 rad[8]; + u8 lct; + int num_ports; + + /** + * @ports: the list of ports on this branch device. This should be + * considered protected for reading by &drm_dp_mst_topology_mgr.lock. + * There are two exceptions to this: + * &drm_dp_mst_topology_mgr.up_req_work and + * &drm_dp_mst_topology_mgr.work, which do not grab + * &drm_dp_mst_topology_mgr.lock during reads but are the only + * updaters of this list and are protected from updating the list + * concurrently by @drm_dp_mst_topology_mgr.probe_lock + */ + struct list_head ports; + + struct drm_dp_mst_port *port_parent; + struct drm_dp_mst_topology_mgr *mgr; + + bool link_address_sent; + + /* global unique identifier to identify branch devices */ + guid_t guid; +}; + + +struct drm_dp_nak_reply { + guid_t guid; + u8 reason; + u8 nak_data; +}; + +struct drm_dp_link_address_ack_reply { + guid_t guid; + u8 nports; + struct drm_dp_link_addr_reply_port { + bool input_port; + u8 peer_device_type; + u8 port_number; + bool mcs; + bool ddps; + bool legacy_device_plug_status; + u8 dpcd_revision; + guid_t peer_guid; + u8 num_sdp_streams; + u8 num_sdp_stream_sinks; + } ports[16]; +}; + +struct drm_dp_remote_dpcd_read_ack_reply { + u8 port_number; + u8 num_bytes; + u8 bytes[255]; +}; + +struct drm_dp_remote_dpcd_write_ack_reply { + u8 port_number; +}; + +struct drm_dp_remote_dpcd_write_nak_reply { + u8 port_number; + u8 reason; + u8 bytes_written_before_failure; +}; + +struct drm_dp_remote_i2c_read_ack_reply { + u8 port_number; + u8 num_bytes; + u8 bytes[255]; +}; + +struct drm_dp_remote_i2c_read_nak_reply { + u8 port_number; + u8 nak_reason; + u8 i2c_nak_transaction; +}; + +struct drm_dp_remote_i2c_write_ack_reply { + u8 port_number; +}; + +struct drm_dp_query_stream_enc_status_ack_reply { + /* Bit[23:16]- Stream Id */ + u8 stream_id; + + /* Bit[15]- Signed */ + bool reply_signed; + + /* Bit[10:8]- Stream Output Sink Type */ + bool unauthorizable_device_present; + bool legacy_device_present; + bool query_capable_device_present; + + /* Bit[12:11]- Stream Output CP Type */ + bool hdcp_1x_device_present; + bool hdcp_2x_device_present; + + /* Bit[4]- Stream Authentication */ + bool auth_completed; + + /* Bit[3]- Stream Encryption */ + bool encryption_enabled; + + /* Bit[2]- Stream Repeater Function Present */ + bool repeater_present; + + /* Bit[1:0]- Stream State */ + u8 state; +}; + +#define DRM_DP_MAX_SDP_STREAMS 16 +struct drm_dp_allocate_payload { + u8 port_number; + u8 number_sdp_streams; + u8 vcpi; + u16 pbn; + u8 sdp_stream_sink[DRM_DP_MAX_SDP_STREAMS]; +}; + +struct drm_dp_allocate_payload_ack_reply { + u8 port_number; + u8 vcpi; + u16 allocated_pbn; +}; + +struct drm_dp_connection_status_notify { + guid_t guid; + u8 port_number; + bool legacy_device_plug_status; + bool displayport_device_plug_status; + bool message_capability_status; + bool input_port; + u8 peer_device_type; +}; + +struct drm_dp_remote_dpcd_read { + u8 port_number; + u32 dpcd_address; + u8 num_bytes; +}; + +struct drm_dp_remote_dpcd_write { + u8 port_number; + u32 dpcd_address; + u8 num_bytes; + u8 *bytes; +}; + +#define DP_REMOTE_I2C_READ_MAX_TRANSACTIONS 4 +struct drm_dp_remote_i2c_read { + u8 num_transactions; + u8 port_number; + struct drm_dp_remote_i2c_read_tx { + u8 i2c_dev_id; + u8 num_bytes; + u8 *bytes; + u8 no_stop_bit; + u8 i2c_transaction_delay; + } transactions[DP_REMOTE_I2C_READ_MAX_TRANSACTIONS]; + u8 read_i2c_device_id; + u8 num_bytes_read; +}; + +struct drm_dp_remote_i2c_write { + u8 port_number; + u8 write_i2c_device_id; + u8 num_bytes; + u8 *bytes; +}; + +struct drm_dp_query_stream_enc_status { + u8 stream_id; + u8 client_id[7]; /* 56-bit nonce */ + u8 stream_event; + bool valid_stream_event; + u8 stream_behavior; + u8 valid_stream_behavior; +}; + +/* this covers ENUM_RESOURCES, POWER_DOWN_PHY, POWER_UP_PHY */ +struct drm_dp_port_number_req { + u8 port_number; +}; + +struct drm_dp_enum_path_resources_ack_reply { + u8 port_number; + bool fec_capable; + u16 full_payload_bw_number; + u16 avail_payload_bw_number; +}; + +/* covers POWER_DOWN_PHY, POWER_UP_PHY */ +struct drm_dp_port_number_rep { + u8 port_number; +}; + +struct drm_dp_query_payload { + u8 port_number; + u8 vcpi; +}; + +struct drm_dp_resource_status_notify { + u8 port_number; + guid_t guid; + u16 available_pbn; +}; + +struct drm_dp_query_payload_ack_reply { + u8 port_number; + u16 allocated_pbn; +}; + +struct drm_dp_sideband_msg_req_body { + u8 req_type; + union ack_req { + struct drm_dp_connection_status_notify conn_stat; + struct drm_dp_port_number_req port_num; + struct drm_dp_resource_status_notify resource_stat; + + struct drm_dp_query_payload query_payload; + struct drm_dp_allocate_payload allocate_payload; + + struct drm_dp_remote_dpcd_read dpcd_read; + struct drm_dp_remote_dpcd_write dpcd_write; + + struct drm_dp_remote_i2c_read i2c_read; + struct drm_dp_remote_i2c_write i2c_write; + + struct drm_dp_query_stream_enc_status enc_status; + } u; +}; + +struct drm_dp_sideband_msg_reply_body { + u8 reply_type; + u8 req_type; + union ack_replies { + struct drm_dp_nak_reply nak; + struct drm_dp_link_address_ack_reply link_addr; + struct drm_dp_port_number_rep port_number; + + struct drm_dp_enum_path_resources_ack_reply path_resources; + struct drm_dp_allocate_payload_ack_reply allocate_payload; + struct drm_dp_query_payload_ack_reply query_payload; + + struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack; + struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack; + struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack; + + struct drm_dp_remote_i2c_read_ack_reply remote_i2c_read_ack; + struct drm_dp_remote_i2c_read_nak_reply remote_i2c_read_nack; + struct drm_dp_remote_i2c_write_ack_reply remote_i2c_write_ack; + + struct drm_dp_query_stream_enc_status_ack_reply enc_status; + } u; +}; + +/* msg is queued to be put into a slot */ +#define DRM_DP_SIDEBAND_TX_QUEUED 0 +/* msg has started transmitting on a slot - still on msgq */ +#define DRM_DP_SIDEBAND_TX_START_SEND 1 +/* msg has finished transmitting on a slot - removed from msgq only in slot */ +#define DRM_DP_SIDEBAND_TX_SENT 2 +/* msg has received a response - removed from slot */ +#define DRM_DP_SIDEBAND_TX_RX 3 +#define DRM_DP_SIDEBAND_TX_TIMEOUT 4 + +struct drm_dp_sideband_msg_tx { + u8 msg[256]; + u8 chunk[48]; + u8 cur_offset; + u8 cur_len; + struct drm_dp_mst_branch *dst; + struct list_head next; + int seqno; + int state; + bool path_msg; + struct drm_dp_sideband_msg_reply_body reply; +}; + +/* sideband msg handler */ +struct drm_dp_mst_topology_mgr; +struct drm_dp_mst_topology_cbs { + /* create a connector for a port */ + struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path); + /* + * Checks for any pending MST interrupts, passing them to MST core for + * processing, the same way an HPD IRQ pulse handler would do this. + * If provided MST core calls this callback from a poll-waiting loop + * when waiting for MST down message replies. The driver is expected + * to guard against a race between this callback and the driver's HPD + * IRQ pulse handler. + */ + void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr); +}; + +#define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base) + +/** + * struct drm_dp_mst_atomic_payload - Atomic state struct for an MST payload + * + * The primary atomic state structure for a given MST payload. Stores information like current + * bandwidth allocation, intended action for this payload, etc. + */ +struct drm_dp_mst_atomic_payload { + /** @port: The MST port assigned to this payload */ + struct drm_dp_mst_port *port; + + /** + * @vc_start_slot: The time slot that this payload starts on. Because payload start slots + * can't be determined ahead of time, the contents of this value are UNDEFINED at atomic + * check time. This shouldn't usually matter, as the start slot should never be relevant for + * atomic state computations. + * + * Since this value is determined at commit time instead of check time, this value is + * protected by the MST helpers ensuring that async commits operating on the given topology + * never run in parallel. In the event that a driver does need to read this value (e.g. to + * inform hardware of the starting timeslot for a payload), the driver may either: + * + * * Read this field during the atomic commit after + * drm_dp_mst_atomic_wait_for_dependencies() has been called, which will ensure the + * previous MST states payload start slots have been copied over to the new state. Note + * that a new start slot won't be assigned/removed from this payload until + * drm_dp_add_payload_part1()/drm_dp_remove_payload_part2() have been called. + * * Acquire the MST modesetting lock, and then wait for any pending MST-related commits to + * get committed to hardware by calling drm_crtc_commit_wait() on each of the + * &drm_crtc_commit structs in &drm_dp_mst_topology_state.commit_deps. + * + * If neither of the two above solutions suffice (e.g. the driver needs to read the start + * slot in the middle of an atomic commit without waiting for some reason), then drivers + * should cache this value themselves after changing payloads. + */ + s8 vc_start_slot; + + /** @vcpi: The Virtual Channel Payload Identifier */ + u8 vcpi; + /** + * @time_slots: + * The number of timeslots allocated to this payload from the source DP Tx to + * the immediate downstream DP Rx + */ + int time_slots; + /** @pbn: The payload bandwidth for this payload */ + int pbn; + + /** @delete: Whether or not we intend to delete this payload during this atomic commit */ + bool delete : 1; + /** @dsc_enabled: Whether or not this payload has DSC enabled */ + bool dsc_enabled : 1; + + /** @payload_allocation_status: The allocation status of this payload */ + enum drm_dp_mst_payload_allocation payload_allocation_status; + + /** @next: The list node for this payload */ + struct list_head next; +}; + +/** + * struct drm_dp_mst_topology_state - DisplayPort MST topology atomic state + * + * This struct represents the atomic state of the toplevel DisplayPort MST manager + */ +struct drm_dp_mst_topology_state { + /** @base: Base private state for atomic */ + struct drm_private_state base; + + /** @mgr: The topology manager */ + struct drm_dp_mst_topology_mgr *mgr; + + /** + * @pending_crtc_mask: A bitmask of all CRTCs this topology state touches, drivers may + * modify this to add additional dependencies if needed. + */ + u32 pending_crtc_mask; + /** + * @commit_deps: A list of all CRTC commits affecting this topology, this field isn't + * populated until drm_dp_mst_atomic_wait_for_dependencies() is called. + */ + struct drm_crtc_commit **commit_deps; + /** @num_commit_deps: The number of CRTC commits in @commit_deps */ + size_t num_commit_deps; + + /** @payload_mask: A bitmask of allocated VCPIs, used for VCPI assignments */ + u32 payload_mask; + /** @payloads: The list of payloads being created/destroyed in this state */ + struct list_head payloads; + + /** @total_avail_slots: The total number of slots this topology can handle (63 or 64) */ + u8 total_avail_slots; + /** @start_slot: The first usable time slot in this topology (1 or 0) */ + u8 start_slot; + + /** + * @pbn_div: The current PBN divisor for this topology. The driver is expected to fill this + * out itself. + */ + fixed20_12 pbn_div; +}; + +#define to_dp_mst_topology_mgr(x) container_of(x, struct drm_dp_mst_topology_mgr, base) + +/** + * struct drm_dp_mst_topology_mgr - DisplayPort MST manager + * + * This struct represents the toplevel displayport MST topology manager. + * There should be one instance of this for every MST capable DP connector + * on the GPU. + */ +struct drm_dp_mst_topology_mgr { + /** + * @base: Base private object for atomic + */ + struct drm_private_obj base; + + /** + * @dev: device pointer for adding i2c devices etc. + */ + struct drm_device *dev; + /** + * @cbs: callbacks for connector addition and destruction. + */ + const struct drm_dp_mst_topology_cbs *cbs; + /** + * @max_dpcd_transaction_bytes: maximum number of bytes to read/write + * in one go. + */ + int max_dpcd_transaction_bytes; + /** + * @aux: AUX channel for the DP MST connector this topolgy mgr is + * controlling. + */ + struct drm_dp_aux *aux; + /** + * @max_payloads: maximum number of payloads the GPU can generate. + */ + int max_payloads; + /** + * @conn_base_id: DRM connector ID this mgr is connected to. Only used + * to build the MST connector path value. + */ + int conn_base_id; + + /** + * @up_req_recv: Message receiver state for up requests. + */ + struct drm_dp_sideband_msg_rx up_req_recv; + + /** + * @down_rep_recv: Message receiver state for replies to down + * requests. + */ + struct drm_dp_sideband_msg_rx down_rep_recv; + + /** + * @lock: protects @mst_state, @mst_primary, @dpcd, and + * @payload_id_table_cleared. + */ + struct mutex lock; + + /** + * @probe_lock: Prevents @work and @up_req_work, the only writers of + * &drm_dp_mst_port.mstb and &drm_dp_mst_branch.ports, from racing + * while they update the topology. + */ + struct mutex probe_lock; + + /** + * @mst_state: If this manager is enabled for an MST capable port. False + * if no MST sink/branch devices is connected. + */ + bool mst_state : 1; + + /** + * @payload_id_table_cleared: Whether or not we've cleared the payload + * ID table for @mst_primary. Protected by @lock. + */ + bool payload_id_table_cleared : 1; + + /** + * @reset_rx_state: The down request's reply and up request message + * receiver state must be reset, after the topology manager got + * removed. Protected by @lock. + */ + bool reset_rx_state : 1; + + /** + * @payload_count: The number of currently active payloads in hardware. This value is only + * intended to be used internally by MST helpers for payload tracking, and is only safe to + * read/write from the atomic commit (not check) context. + */ + u8 payload_count; + + /** + * @next_start_slot: The starting timeslot to use for new VC payloads. This value is used + * internally by MST helpers for payload tracking, and is only safe to read/write from the + * atomic commit (not check) context. + */ + u8 next_start_slot; + + /** + * @mst_primary: Pointer to the primary/first branch device. + */ + struct drm_dp_mst_branch *mst_primary; + + /** + * @dpcd: Cache of DPCD for primary port. + */ + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + /** + * @sink_count: Sink count from DEVICE_SERVICE_IRQ_VECTOR_ESI0. + */ + u8 sink_count; + + /** + * @funcs: Atomic helper callbacks + */ + const struct drm_private_state_funcs *funcs; + + /** + * @qlock: protects @tx_msg_downq and &drm_dp_sideband_msg_tx.state + */ + struct mutex qlock; + + /** + * @tx_msg_downq: List of pending down requests + */ + struct list_head tx_msg_downq; + + /** + * @tx_waitq: Wait to queue stall for the tx worker. + */ + wait_queue_head_t tx_waitq; + /** + * @work: Probe work. + */ + struct work_struct work; + /** + * @tx_work: Sideband transmit worker. This can nest within the main + * @work worker for each transaction @work launches. + */ + struct work_struct tx_work; + + /** + * @destroy_port_list: List of to be destroyed connectors. + */ + struct list_head destroy_port_list; + /** + * @destroy_branch_device_list: List of to be destroyed branch + * devices. + */ + struct list_head destroy_branch_device_list; + /** + * @delayed_destroy_lock: Protects @destroy_port_list and + * @destroy_branch_device_list. + */ + struct mutex delayed_destroy_lock; + + /** + * @delayed_destroy_wq: Workqueue used for delayed_destroy_work items. + * A dedicated WQ makes it possible to drain any requeued work items + * on it. + */ + struct workqueue_struct *delayed_destroy_wq; + + /** + * @delayed_destroy_work: Work item to destroy MST port and branch + * devices, needed to avoid locking inversion. + */ + struct work_struct delayed_destroy_work; + + /** + * @up_req_list: List of pending up requests from the topology that + * need to be processed, in chronological order. + */ + struct list_head up_req_list; + /** + * @up_req_lock: Protects @up_req_list + */ + struct mutex up_req_lock; + /** + * @up_req_work: Work item to process up requests received from the + * topology. Needed to avoid blocking hotplug handling and sideband + * transmissions. + */ + struct work_struct up_req_work; + +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) + /** + * @topology_ref_history_lock: protects + * &drm_dp_mst_port.topology_ref_history and + * &drm_dp_mst_branch.topology_ref_history. + */ + struct mutex topology_ref_history_lock; +#endif +}; + +int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, + struct drm_device *dev, struct drm_dp_aux *aux, + int max_dpcd_transaction_bytes, + int max_payloads, int conn_base_id); + +void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr); + +/** + * enum drm_dp_mst_mode - sink's MST mode capability + */ +enum drm_dp_mst_mode { + /** + * @DRM_DP_SST: The sink does not support MST nor single stream sideband + * messaging. + */ + DRM_DP_SST, + /** + * @DRM_DP_MST: Sink supports MST, more than one stream and single + * stream sideband messaging. + */ + DRM_DP_MST, + /** + * @DRM_DP_SST_SIDEBAND_MSG: Sink supports only one stream and single + * stream sideband messaging. + */ + DRM_DP_SST_SIDEBAND_MSG, +}; + +enum drm_dp_mst_mode drm_dp_read_mst_cap(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]); +int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state); + +int drm_dp_mst_hpd_irq_handle_event(struct drm_dp_mst_topology_mgr *mgr, + const u8 *esi, + u8 *ack, + bool *handled); +void drm_dp_mst_hpd_irq_send_new_request(struct drm_dp_mst_topology_mgr *mgr); + +int +drm_dp_mst_detect_port(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); + +const struct drm_edid *drm_dp_mst_edid_read(struct drm_connector *connector, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); +struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); + +fixed20_12 drm_dp_get_vc_payload_bw(int link_rate, int link_lane_count); + +int drm_dp_calc_pbn_mode(int clock, int bpp); + +void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap); + +int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_atomic_payload *payload); +int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_atomic_payload *payload); +void drm_dp_remove_payload_part1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_atomic_payload *payload); +void drm_dp_remove_payload_part2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + const struct drm_dp_mst_atomic_payload *old_payload, + struct drm_dp_mst_atomic_payload *new_payload); + +int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_dump_topology(struct seq_file *m, + struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr); +int __must_check +drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr, + bool sync); + +ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux, + unsigned int offset, void *buffer, size_t size); +ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux, + unsigned int offset, void *buffer, size_t size); + +int drm_dp_mst_connector_late_register(struct drm_connector *connector, + struct drm_dp_mst_port *port); +void drm_dp_mst_connector_early_unregister(struct drm_connector *connector, + struct drm_dp_mst_port *port); + +struct drm_dp_mst_topology_state * +drm_atomic_get_mst_topology_state(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr); +struct drm_dp_mst_topology_state * +drm_atomic_get_old_mst_topology_state(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr); +struct drm_dp_mst_topology_state * +drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr); +struct drm_dp_mst_atomic_payload * +drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state, + struct drm_dp_mst_port *port); +bool drm_dp_mst_port_downstream_of_parent(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + struct drm_dp_mst_port *parent); +int __must_check +drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, int pbn); +int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, + struct drm_dp_mst_port *port, + int pbn, bool enable); +int __must_check +drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr); +int __must_check +drm_dp_atomic_release_time_slots(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); +void drm_dp_mst_atomic_wait_for_dependencies(struct drm_atomic_state *state); +int __must_check drm_dp_mst_atomic_setup_commit(struct drm_atomic_state *state); +int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, bool power_up); +int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + struct drm_dp_query_stream_enc_status_ack_reply *status); +int __must_check drm_dp_mst_atomic_check_mgr(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_port **failing_port); +int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state); +int __must_check drm_dp_mst_root_conn_atomic_check(struct drm_connector_state *new_conn_state, + struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port); +void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); + +static inline +bool drm_dp_mst_port_is_logical(struct drm_dp_mst_port *port) +{ + return port->port_num >= DP_MST_LOGICAL_PORT_0; +} + +struct drm_dp_aux *drm_dp_mst_aux_for_parent(struct drm_dp_mst_port *port); +struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port); + +static inline struct drm_dp_mst_topology_state * +to_drm_dp_mst_topology_state(struct drm_private_state *state) +{ + return container_of(state, struct drm_dp_mst_topology_state, base); +} + +extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs; + +/** + * __drm_dp_mst_state_iter_get - private atomic state iterator function for + * macro-internal use + * @state: &struct drm_atomic_state pointer + * @mgr: pointer to the &struct drm_dp_mst_topology_mgr iteration cursor + * @old_state: optional pointer to the old &struct drm_dp_mst_topology_state + * iteration cursor + * @new_state: optional pointer to the new &struct drm_dp_mst_topology_state + * iteration cursor + * @i: int iteration cursor, for macro-internal use + * + * Used by for_each_oldnew_mst_mgr_in_state(), + * for_each_old_mst_mgr_in_state(), and for_each_new_mst_mgr_in_state(). Don't + * call this directly. + * + * Returns: + * True if the current &struct drm_private_obj is a &struct + * drm_dp_mst_topology_mgr, false otherwise. + */ +static inline bool +__drm_dp_mst_state_iter_get(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr **mgr, + struct drm_dp_mst_topology_state **old_state, + struct drm_dp_mst_topology_state **new_state, + int i) +{ + struct __drm_private_objs_state *objs_state = &state->private_objs[i]; + + if (objs_state->ptr->funcs != &drm_dp_mst_topology_state_funcs) + return false; + + *mgr = to_dp_mst_topology_mgr(objs_state->ptr); + if (old_state) + *old_state = to_dp_mst_topology_state(objs_state->old_state); + if (new_state) + *new_state = to_dp_mst_topology_state(objs_state->new_state); + + return true; +} + +/** + * for_each_oldnew_mst_mgr_in_state - iterate over all DP MST topology + * managers in an atomic update + * @__state: &struct drm_atomic_state pointer + * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor + * @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old + * state + * @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new + * state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all DRM DP MST topology managers in an atomic update, + * tracking both old and new state. This is useful in places where the state + * delta needs to be considered, for example in atomic check functions. + */ +#define for_each_oldnew_mst_mgr_in_state(__state, mgr, old_state, new_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i))) + +/** + * for_each_old_mst_mgr_in_state - iterate over all DP MST topology managers + * in an atomic update + * @__state: &struct drm_atomic_state pointer + * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor + * @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old + * state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all DRM DP MST topology managers in an atomic update, + * tracking only the old state. This is useful in disable functions, where we + * need the old state the hardware is still in. + */ +#define for_each_old_mst_mgr_in_state(__state, mgr, old_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i))) + +/** + * for_each_new_mst_mgr_in_state - iterate over all DP MST topology managers + * in an atomic update + * @__state: &struct drm_atomic_state pointer + * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor + * @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new + * state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all DRM DP MST topology managers in an atomic update, + * tracking only the new state. This is useful in enable functions, where we + * need the new state the hardware should be in when the atomic commit + * operation has completed. + */ +#define for_each_new_mst_mgr_in_state(__state, mgr, new_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i))) + +#endif diff --git a/include/drm/display/drm_dp_tunnel.h b/include/drm/display/drm_dp_tunnel.h new file mode 100644 index 000000000000..87212c847915 --- /dev/null +++ b/include/drm/display/drm_dp_tunnel.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __DRM_DP_TUNNEL_H__ +#define __DRM_DP_TUNNEL_H__ + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/types.h> + +struct drm_dp_aux; + +struct drm_device; + +struct drm_atomic_state; +struct drm_dp_tunnel_mgr; +struct drm_dp_tunnel_state; + +struct ref_tracker; + +struct drm_dp_tunnel_ref { + struct drm_dp_tunnel *tunnel; + struct ref_tracker *tracker; +}; + +#ifdef CONFIG_DRM_DISPLAY_DP_TUNNEL + +struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker); + +void +drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker); + +static inline void drm_dp_tunnel_ref_get(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_ref *tunnel_ref) +{ + tunnel_ref->tunnel = drm_dp_tunnel_get(tunnel, &tunnel_ref->tracker); +} + +static inline void drm_dp_tunnel_ref_put(struct drm_dp_tunnel_ref *tunnel_ref) +{ + drm_dp_tunnel_put(tunnel_ref->tunnel, &tunnel_ref->tracker); + tunnel_ref->tunnel = NULL; +} + +struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux); +int drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel); +bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw); +int drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel); + +void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux); + +int drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel); + +const char *drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_old_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw); +int drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask); + +int drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask); + +int drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state); + +struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count); +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr); + +#else + +static inline struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker) +{ + return NULL; +} + +static inline void +drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker) {} + +static inline void drm_dp_tunnel_ref_get(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_ref *tunnel_ref) {} + +static inline void drm_dp_tunnel_ref_put(struct drm_dp_tunnel_ref *tunnel_ref) {} + +static inline struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int +drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel) +{ + return false; +} + +static inline int +drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel) +{ + return -1; +} + +static inline int +drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel) {} + +static inline int +drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int +drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int +drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return -1; +} + +static inline const char * +drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel) +{ + return NULL; +} + +static inline struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int +drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state) +{ + return 0; +} + +static inline struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr) {} + +#endif /* CONFIG_DRM_DISPLAY_DP_TUNNEL */ + +#endif /* __DRM_DP_TUNNEL_H__ */ diff --git a/include/drm/drm_dsc.h b/include/drm/display/drm_dsc.h index d03f1b83421a..bbbe7438473d 100644 --- a/include/drm/drm_dsc.h +++ b/include/drm/display/drm_dsc.h @@ -8,7 +8,7 @@ #ifndef DRM_DSC_H_ #define DRM_DSC_H_ -#include <drm/drm_dp_helper.h> +#include <drm/display/drm_dp.h> /* VESA Display Stream Compression DSC 1.2 constants */ #define DSC_NUM_BUF_RANGES 15 @@ -40,120 +40,238 @@ #define DSC_PPS_RC_RANGE_MINQP_SHIFT 11 #define DSC_PPS_RC_RANGE_MAXQP_SHIFT 6 #define DSC_PPS_NATIVE_420_SHIFT 1 -#define DSC_1_2_MAX_LINEBUF_DEPTH_BITS 16 -#define DSC_1_2_MAX_LINEBUF_DEPTH_VAL 0 -#define DSC_1_1_MAX_LINEBUF_DEPTH_BITS 13 -/* Configuration for a single Rate Control model range */ +/** + * struct drm_dsc_rc_range_parameters - DSC Rate Control range parameters + * + * This defines different rate control parameters used by the DSC engine + * to compress the frame. + */ struct drm_dsc_rc_range_parameters { - /* Min Quantization Parameters allowed for this range */ + /** + * @range_min_qp: Min Quantization Parameters allowed for this range + */ u8 range_min_qp; - /* Max Quantization Parameters allowed for this range */ + /** + * @range_max_qp: Max Quantization Parameters allowed for this range + */ u8 range_max_qp; - /* Bits/group offset to apply to target for this group */ + /** + * @range_bpg_offset: + * Bits/group offset to apply to target for this group + */ u8 range_bpg_offset; }; +/** + * struct drm_dsc_config - Parameters required to configure DSC + * + * Driver populates this structure with all the parameters required + * to configure the display stream compression on the source. + */ struct drm_dsc_config { - /* Bits / component for previous reconstructed line buffer */ + /** + * @line_buf_depth: + * Bits per component for previous reconstructed line buffer + */ u8 line_buf_depth; - /* Bits per component to code (must be 8, 10, or 12) */ + /** + * @bits_per_component: Bits per component to code (8/10/12) + */ u8 bits_per_component; - /* - * Flag indicating to do RGB - YCoCg conversion - * and back (should be 1 for RGB input) + /** + * @convert_rgb: + * Flag to indicate if RGB - YCoCg conversion is needed + * True if RGB input, False if YCoCg input */ bool convert_rgb; + /** + * @slice_count: Number fo slices per line used by the DSC encoder + */ u8 slice_count; - /* Slice Width */ + /** + * @slice_width: Width of each slice in pixels + */ u16 slice_width; - /* Slice Height */ + /** + * @slice_height: Slice height in pixels + */ u16 slice_height; - /* - * 4:2:2 enable mode (from PPS, 4:2:2 conversion happens - * outside of DSC encode/decode algorithm) + /** + * @simple_422: True if simple 4_2_2 mode is enabled else False + */ + bool simple_422; + /** + * @pic_width: Width of the input display frame in pixels */ - bool enable422; - /* Picture Width */ u16 pic_width; - /* Picture Height */ + /** + * @pic_height: Vertical height of the input display frame + */ u16 pic_height; - /* Offset to bits/group used by RC to determine QP adjustment */ + /** + * @rc_tgt_offset_high: + * Offset to bits/group used by RC to determine QP adjustment + */ u8 rc_tgt_offset_high; - /* Offset to bits/group used by RC to determine QP adjustment */ + /** + * @rc_tgt_offset_low: + * Offset to bits/group used by RC to determine QP adjustment + */ u8 rc_tgt_offset_low; - /* Bits/pixel target << 4 (ie., 4 fractional bits) */ + /** + * @bits_per_pixel: + * Target bits per pixel with 4 fractional bits, bits_per_pixel << 4 + */ u16 bits_per_pixel; - /* - * Factor to determine if an edge is present based - * on the bits produced + /** + * @rc_edge_factor: + * Factor to determine if an edge is present based on the bits produced */ u8 rc_edge_factor; - /* Slow down incrementing once the range reaches this value */ + /** + * @rc_quant_incr_limit1: + * Slow down incrementing once the range reaches this value + */ u8 rc_quant_incr_limit1; - /* Slow down incrementing once the range reaches this value */ + /** + * @rc_quant_incr_limit0: + * Slow down incrementing once the range reaches this value + */ u8 rc_quant_incr_limit0; - /* Number of pixels to delay the initial transmission */ + /** + * @initial_xmit_delay: + * Number of pixels to delay the initial transmission + */ u16 initial_xmit_delay; - /* Number of pixels to delay the VLD on the decoder,not including SSM */ + /** + * @initial_dec_delay: + * Initial decoder delay, number of pixel times that the decoder + * accumulates data in its rate buffer before starting to decode + * and output pixels. + */ u16 initial_dec_delay; - /* Block prediction enable */ + /** + * @block_pred_enable: + * True if block prediction is used to code any groups within the + * picture. False if BP not used + */ bool block_pred_enable; - /* Bits/group offset to use for first line of the slice */ + /** + * @first_line_bpg_offset: + * Number of additional bits allocated for each group on the first + * line of slice. + */ u8 first_line_bpg_offset; - /* Value to use for RC model offset at slice start */ + /** + * @initial_offset: Value to use for RC model offset at slice start + */ u16 initial_offset; - /* Thresholds defining each of the buffer ranges */ + /** + * @rc_buf_thresh: Thresholds defining each of the buffer ranges + */ u16 rc_buf_thresh[DSC_NUM_BUF_RANGES - 1]; - /* Parameters for each of the RC ranges */ + /** + * @rc_range_params: + * Parameters for each of the RC ranges defined in + * &struct drm_dsc_rc_range_parameters + */ struct drm_dsc_rc_range_parameters rc_range_params[DSC_NUM_BUF_RANGES]; - /* Total size of RC model */ + /** + * @rc_model_size: Total size of RC model + */ u16 rc_model_size; - /* Minimum QP where flatness information is sent */ + /** + * @flatness_min_qp: Minimum QP where flatness information is sent + */ u8 flatness_min_qp; - /* Maximum QP where flatness information is sent */ + /** + * @flatness_max_qp: Maximum QP where flatness information is sent + */ u8 flatness_max_qp; - /* Initial value for scale factor */ + /** + * @initial_scale_value: Initial value for the scale factor + */ u8 initial_scale_value; - /* Decrement scale factor every scale_decrement_interval groups */ + /** + * @scale_decrement_interval: + * Specifies number of group times between decrementing the scale factor + * at beginning of a slice. + */ u16 scale_decrement_interval; - /* Increment scale factor every scale_increment_interval groups */ + /** + * @scale_increment_interval: + * Number of group times between incrementing the scale factor value + * used at the beginning of a slice. + */ u16 scale_increment_interval; - /* Non-first line BPG offset to use */ + /** + * @nfl_bpg_offset: Non first line BPG offset to be used + */ u16 nfl_bpg_offset; - /* BPG offset used to enforce slice bit */ + /** + * @slice_bpg_offset: BPG offset used to enforce slice bit + */ u16 slice_bpg_offset; - /* Final RC linear transformation offset value */ + /** + * @final_offset: Final RC linear transformation offset value + */ u16 final_offset; - /* Enable on-off VBR (ie., disable stuffing bits) */ + /** + * @vbr_enable: True if VBR mode is enabled, false if disabled + */ bool vbr_enable; - /* Mux word size (in bits) for SSM mode */ + /** + * @mux_word_size: Mux word size (in bits) for SSM mode + */ u8 mux_word_size; - /* - * The (max) size in bytes of the "chunks" that are - * used in slice multiplexing + /** + * @slice_chunk_size: + * The (max) size in bytes of the "chunks" that are used in slice + * multiplexing. */ u16 slice_chunk_size; - /* Rate Control buffer siz in bits */ + /** + * @rc_bits: Rate control buffer size in bits + */ u16 rc_bits; - /* DSC Minor Version */ + /** + * @dsc_version_minor: DSC minor version + */ u8 dsc_version_minor; - /* DSC Major version */ + /** + * @dsc_version_major: DSC major version + */ u8 dsc_version_major; - /* Native 4:2:2 support */ + /** + * @native_422: True if Native 4:2:2 supported, else false + */ bool native_422; - /* Native 4:2:0 support */ + /** + * @native_420: True if Native 4:2:0 supported else false. + */ bool native_420; - /* Additional bits/grp for seconnd line of slice for native 4:2:0 */ + /** + * @second_line_bpg_offset: + * Additional bits/grp for seconnd line of slice for native 4:2:0 + */ u8 second_line_bpg_offset; - /* Num of bits deallocated for each grp that is not in second line of slice */ + /** + * @nsl_bpg_offset: + * Num of bits deallocated for each grp that is not in second line of + * slice + */ u16 nsl_bpg_offset; - /* Offset adj fr second line in Native 4:2:0 mode */ + /** + * @second_line_offset_adj: + * Offset adjustment for second line in Native 4:2:0 mode + */ u16 second_line_offset_adj; }; /** - * struct picture_parameter_set - Represents 128 bytes of Picture Parameter Set + * struct drm_dsc_picture_parameter_set - Represents 128 bytes of + * Picture Parameter Set * * The VESA DSC standard defines picture parameter set (PPS) which display * stream compression encoders must communicate to decoders. @@ -468,18 +586,17 @@ struct drm_dsc_picture_parameter_set { * This structure represents the DSC PPS infoframe required to send the Picture * Parameter Set metadata required before enabling VESA Display Stream * Compression. This is based on the DP Secondary Data Packet structure and - * comprises of SDP Header as defined in drm_dp_helper.h and PPS payload. + * comprises of SDP Header as defined &struct dp_sdp_header in drm_dp_helper.h + * and PPS payload defined in &struct drm_dsc_picture_parameter_set. * - * @pps_header: Header for PPS as per DP SDP header format + * @pps_header: Header for PPS as per DP SDP header format of type + * &struct dp_sdp_header * @pps_payload: PPS payload fields as per DSC specification Table 4-1 + * as represented in &struct drm_dsc_picture_parameter_set */ struct drm_dsc_pps_infoframe { struct dp_sdp_header pps_header; struct drm_dsc_picture_parameter_set pps_payload; } __packed; -void drm_dsc_dp_pps_header_init(struct drm_dsc_pps_infoframe *pps_sdp); -void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp, - const struct drm_dsc_config *dsc_cfg); - #endif /* _DRM_DSC_H_ */ diff --git a/include/drm/display/drm_dsc_helper.h b/include/drm/display/drm_dsc_helper.h new file mode 100644 index 000000000000..2c2b9033f60f --- /dev/null +++ b/include/drm/display/drm_dsc_helper.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: MIT + * Copyright (C) 2018 Intel Corp. + * + * Authors: + * Manasi Navare <manasi.d.navare@intel.com> + */ + +#ifndef DRM_DSC_HELPER_H_ +#define DRM_DSC_HELPER_H_ + +#include <drm/display/drm_dsc.h> + +enum drm_dsc_params_type { + DRM_DSC_1_2_444, + DRM_DSC_1_1_PRE_SCR, /* legacy params from DSC 1.1 */ + DRM_DSC_1_2_422, + DRM_DSC_1_2_420, +}; + +struct drm_printer; + +void drm_dsc_dp_pps_header_init(struct dp_sdp_header *pps_header); +int drm_dsc_dp_rc_buffer_size(u8 rc_buffer_block_size, u8 rc_buffer_size); +void drm_dsc_pps_payload_pack(struct drm_dsc_picture_parameter_set *pps_sdp, + const struct drm_dsc_config *dsc_cfg); +void drm_dsc_set_const_params(struct drm_dsc_config *vdsc_cfg); +void drm_dsc_set_rc_buf_thresh(struct drm_dsc_config *vdsc_cfg); +int drm_dsc_setup_rc_params(struct drm_dsc_config *vdsc_cfg, enum drm_dsc_params_type type); +int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg); +u8 drm_dsc_initial_scale_value(const struct drm_dsc_config *dsc); +u32 drm_dsc_flatness_det_thresh(const struct drm_dsc_config *dsc); +u32 drm_dsc_get_bpp_int(const struct drm_dsc_config *vdsc_cfg); +void drm_dsc_dump_config(struct drm_printer *p, int indent, const struct drm_dsc_config *cfg); + +#endif /* _DRM_DSC_HELPER_H_ */ + diff --git a/include/drm/drm_hdcp.h b/include/drm/display/drm_hdcp.h index a6de09c5e47f..96a99b1377c0 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/display/drm_hdcp.h @@ -6,11 +6,14 @@ * Sean Paul <seanpaul@chromium.org> */ -#ifndef _DRM_HDCP_H_INCLUDED_ -#define _DRM_HDCP_H_INCLUDED_ +#ifndef _DRM_HDCP_H_ +#define _DRM_HDCP_H_ + +#include <linux/types.h> /* Period of hdcp checks (to ensure we're still authenticated) */ #define DRM_HDCP_CHECK_PERIOD_MS (128 * 16) +#define DRM_HDCP2_CHECK_PERIOD_MS 500 /* Shared lengths/masks between HDMI/DVI/DisplayPort */ #define DRM_HDCP_AN_LEN 8 @@ -26,6 +29,9 @@ /* Slave address for the HDCP registers in the receiver */ #define DRM_HDCP_DDC_ADDR 0x3A +/* Value to use at the end of the SHA-1 bytestream used for repeaters */ +#define DRM_HDCP_SHA1_TERMINATOR 0x80 + /* HDCP register offsets for HDMI/DVI devices */ #define DRM_HDCP_DDC_BKSV 0x00 #define DRM_HDCP_DDC_RI_PRIME 0x08 @@ -66,7 +72,6 @@ #define HDCP_2_2_REP_SEND_ACK 15 #define HDCP_2_2_REP_STREAM_MANAGE 16 #define HDCP_2_2_REP_STREAM_READY 17 -#define HDCP_2_2_ERRATA_DP_STREAM_TYPE 50 #define HDCP_2_2_RTX_LEN 8 #define HDCP_2_2_RRX_LEN 8 @@ -96,11 +101,11 @@ /* Following Macros take a byte at a time for bit(s) masking */ /* - * TODO: This has to be changed for DP MST, as multiple stream on - * same port is possible. - * For HDCP2.2 on HDMI and DP SST this value is always 1. + * TODO: HDCP_2_2_MAX_CONTENT_STREAMS_CNT is based upon actual + * H/W MST streams capacity. + * This required to be moved out to platform specific header. */ -#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 1 +#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 4 #define HDCP_2_2_TXCAP_MASK_LEN 2 #define HDCP_2_2_RXCAPS_LEN 3 #define HDCP_2_2_RX_REPEATER(x) ((x) & BIT(0)) @@ -217,18 +222,16 @@ struct hdcp2_rep_stream_ready { u8 m_prime[HDCP_2_2_MPRIME_LEN]; } __packed; -struct hdcp2_dp_errata_stream_type { - u8 msg_id; - u8 stream_type; -} __packed; - /* HDCP2.2 TIMEOUTs in mSec */ #define HDCP_2_2_CERT_TIMEOUT_MS 100 +#define HDCP_2_2_DP_CERT_READ_TIMEOUT_MS 110 #define HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS 1000 #define HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS 200 +#define HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS 7 #define HDCP_2_2_PAIRING_TIMEOUT_MS 200 +#define HDCP_2_2_DP_PAIRING_READ_TIMEOUT_MS 5 #define HDCP_2_2_HDMI_LPRIME_TIMEOUT_MS 20 -#define HDCP_2_2_DP_LPRIME_TIMEOUT_MS 7 +#define HDCP_2_2_DP_LPRIME_TIMEOUT_MS 16 #define HDCP_2_2_RECVID_LIST_TIMEOUT_MS 3000 #define HDCP_2_2_STREAM_READY_TIMEOUT_MS 100 @@ -250,4 +253,46 @@ struct hdcp2_dp_errata_stream_type { #define HDCP_2_2_HDMI_RXSTATUS_READY(x) ((x) & BIT(2)) #define HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(x) ((x) & BIT(3)) +/* + * Helper functions to convert 24bit big endian hdcp sequence number to + * host format and back + */ +static inline +u32 drm_hdcp_be24_to_cpu(const u8 seq_num[HDCP_2_2_SEQ_NUM_LEN]) +{ + return (u32)(seq_num[2] | seq_num[1] << 8 | seq_num[0] << 16); +} + +static inline +void drm_hdcp_cpu_to_be24(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN], u32 val) +{ + seq_num[0] = val >> 16; + seq_num[1] = val >> 8; + seq_num[2] = val; +} + +#define DRM_HDCP_SRM_GEN1_MAX_BYTES (5 * 1024) +#define DRM_HDCP_1_4_SRM_ID 0x8 +#define DRM_HDCP_SRM_ID_MASK (0xF << 4) +#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_1_4_DCP_SIG_SIZE 40 +#define DRM_HDCP_2_SRM_ID 0x9 +#define DRM_HDCP_2_INDICATOR 0x1 +#define DRM_HDCP_2_INDICATOR_MASK 0xF +#define DRM_HDCP_2_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_2_DCP_SIG_SIZE 384 +#define DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ 4 +#define DRM_HDCP_2_KSV_COUNT_2_LSBITS(byte) (((byte) & 0xC0) >> 6) + +struct hdcp_srm_header { + u8 srm_id; + u8 reserved; + __be16 srm_version; + u8 srm_gen_no; +} __packed; + +/* Content Type classification for HDCP2.2 vs others */ +#define DRM_MODE_HDCP_CONTENT_TYPE0 0 +#define DRM_MODE_HDCP_CONTENT_TYPE1 1 + #endif diff --git a/include/drm/display/drm_hdcp_helper.h b/include/drm/display/drm_hdcp_helper.h new file mode 100644 index 000000000000..8aaf87bf2735 --- /dev/null +++ b/include/drm/display/drm_hdcp_helper.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2017 Google, Inc. + * + * Authors: + * Sean Paul <seanpaul@chromium.org> + */ + +#ifndef _DRM_HDCP_HELPER_H_INCLUDED_ +#define _DRM_HDCP_HELPER_H_INCLUDED_ + +#include <drm/display/drm_hdcp.h> + +struct drm_device; +struct drm_connector; + +int drm_hdcp_check_ksvs_revoked(struct drm_device *dev, u8 *ksvs, u32 ksv_count); +int drm_connector_attach_content_protection_property(struct drm_connector *connector, + bool hdcp_content_type); +void drm_hdcp_update_content_protection(struct drm_connector *connector, u64 val); + +#endif diff --git a/include/drm/display/drm_hdmi_audio_helper.h b/include/drm/display/drm_hdmi_audio_helper.h new file mode 100644 index 000000000000..44d910bdc72d --- /dev/null +++ b/include/drm/display/drm_hdmi_audio_helper.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_DISPLAY_HDMI_AUDIO_HELPER_H_ +#define DRM_DISPLAY_HDMI_AUDIO_HELPER_H_ + +#include <linux/types.h> + +struct drm_connector; +struct drm_connector_hdmi_audio_funcs; + +struct device; + +int drm_connector_hdmi_audio_init(struct drm_connector *connector, + struct device *hdmi_codec_dev, + const struct drm_connector_hdmi_audio_funcs *funcs, + unsigned int max_i2s_playback_channels, + u64 i2s_formats, + bool spdif_playback, + int sound_dai_port); +void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector, + bool plugged); + +#endif diff --git a/include/drm/display/drm_hdmi_cec_helper.h b/include/drm/display/drm_hdmi_cec_helper.h new file mode 100644 index 000000000000..fd8f4d2f02c1 --- /dev/null +++ b/include/drm/display/drm_hdmi_cec_helper.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_DISPLAY_HDMI_CEC_HELPER +#define DRM_DISPLAY_HDMI_CEC_HELPER + +#include <linux/types.h> + +struct drm_connector; + +struct cec_msg; +struct device; + +struct drm_connector_hdmi_cec_funcs { + /** + * @init: perform hardware-specific initialization before registering the CEC adapter + */ + int (*init)(struct drm_connector *connector); + + /** + * @uninit: perform hardware-specific teardown for the CEC adapter + */ + void (*uninit)(struct drm_connector *connector); + + /** + * @enable: enable or disable CEC adapter + */ + int (*enable)(struct drm_connector *connector, bool enable); + + /** + * @log_addr: set adapter's logical address, can be called multiple + * times if adapter supports several LAs + */ + int (*log_addr)(struct drm_connector *connector, u8 logical_addr); + + /** + * @transmit: start transmission of the specified CEC message + */ + int (*transmit)(struct drm_connector *connector, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); +}; + +int drmm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_funcs *funcs, + const char *name, + u8 available_las, + struct device *dev); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector, + u8 status); + +#if IS_ENABLED(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER) +int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev); +#else +static inline int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev) +{ + return 0; +} +#endif + +#endif diff --git a/include/drm/display/drm_hdmi_helper.h b/include/drm/display/drm_hdmi_helper.h new file mode 100644 index 000000000000..09145c9ee9fc --- /dev/null +++ b/include/drm/display/drm_hdmi_helper.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_HDMI_HELPER +#define DRM_HDMI_HELPER + +#include <linux/hdmi.h> + +struct drm_connector; +struct drm_connector_state; +struct drm_display_mode; + +void +drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame, + const struct drm_connector_state *conn_state); + +void +drm_hdmi_avi_infoframe_bars(struct hdmi_avi_infoframe *frame, + const struct drm_connector_state *conn_state); + +int +drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame, + const struct drm_connector_state *conn_state); + +void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, + const struct drm_connector_state *conn_state); + +unsigned long long +drm_hdmi_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc, enum hdmi_colorspace fmt); + +void +drm_hdmi_acr_get_n_cts(unsigned long long tmds_char_rate, + unsigned int sample_rate, + unsigned int *out_n, + unsigned int *out_cts); + +#endif diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h new file mode 100644 index 000000000000..2349c0d0f00f --- /dev/null +++ b/include/drm/display/drm_hdmi_state_helper.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_HDMI_STATE_HELPER_H_ +#define DRM_HDMI_STATE_HELPER_H_ + +struct drm_atomic_state; +struct drm_connector; +struct drm_connector_state; +struct drm_display_mode; +struct hdmi_audio_infoframe; + +enum drm_connector_status; + +void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, + struct drm_connector_state *new_conn_state); + +int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, + struct drm_atomic_state *state); + +int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector, + struct hdmi_audio_infoframe *frame); +int drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector); +int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, + struct drm_atomic_state *state); +void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector, + enum drm_connector_status status); +void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector); + +enum drm_mode_status +drm_hdmi_connector_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode); + +#endif // DRM_HDMI_STATE_HELPER_H_ diff --git a/include/drm/drm_scdc_helper.h b/include/drm/display/drm_scdc.h index f92eb2094d6b..3d58f37e8ed8 100644 --- a/include/drm/drm_scdc_helper.h +++ b/include/drm/display/drm_scdc.h @@ -21,11 +21,8 @@ * DEALINGS IN THE SOFTWARE. */ -#ifndef DRM_SCDC_HELPER_H -#define DRM_SCDC_HELPER_H - -#include <linux/i2c.h> -#include <linux/types.h> +#ifndef DRM_SCDC_H +#define DRM_SCDC_H #define SCDC_SINK_VERSION 0x01 @@ -50,9 +47,9 @@ #define SCDC_READ_REQUEST_ENABLE (1 << 0) #define SCDC_STATUS_FLAGS_0 0x40 -#define SCDC_CH2_LOCK (1 < 3) -#define SCDC_CH1_LOCK (1 < 2) -#define SCDC_CH0_LOCK (1 < 1) +#define SCDC_CH2_LOCK (1 << 3) +#define SCDC_CH1_LOCK (1 << 2) +#define SCDC_CH0_LOCK (1 << 1) #define SCDC_CH_LOCK_MASK (SCDC_CH2_LOCK | SCDC_CH1_LOCK | SCDC_CH0_LOCK) #define SCDC_CLOCK_DETECT (1 << 0) @@ -88,49 +85,4 @@ #define SCDC_MANUFACTURER_SPECIFIC 0xde #define SCDC_MANUFACTURER_SPECIFIC_SIZE 34 -ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, - size_t size); -ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, - const void *buffer, size_t size); - -/** - * drm_scdc_readb - read a single byte from SCDC - * @adapter: I2C adapter - * @offset: offset of register to read - * @value: return location for the register value - * - * Reads a single byte from SCDC. This is a convenience wrapper around the - * drm_scdc_read() function. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -static inline int drm_scdc_readb(struct i2c_adapter *adapter, u8 offset, - u8 *value) -{ - return drm_scdc_read(adapter, offset, value, sizeof(*value)); -} - -/** - * drm_scdc_writeb - write a single byte to SCDC - * @adapter: I2C adapter - * @offset: offset of register to read - * @value: return location for the register value - * - * Writes a single byte to SCDC. This is a convenience wrapper around the - * drm_scdc_write() function. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -static inline int drm_scdc_writeb(struct i2c_adapter *adapter, u8 offset, - u8 value) -{ - return drm_scdc_write(adapter, offset, &value, sizeof(value)); -} - -bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter); - -bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable); -bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set); #endif diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h new file mode 100644 index 000000000000..34600476a1b9 --- /dev/null +++ b/include/drm/display/drm_scdc_helper.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_SCDC_HELPER_H +#define DRM_SCDC_HELPER_H + +#include <linux/types.h> + +#include <drm/display/drm_scdc.h> + +struct drm_connector; +struct i2c_adapter; + +ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, + size_t size); +ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, + const void *buffer, size_t size); + +/** + * drm_scdc_readb - read a single byte from SCDC + * @adapter: I2C adapter + * @offset: offset of register to read + * @value: return location for the register value + * + * Reads a single byte from SCDC. This is a convenience wrapper around the + * drm_scdc_read() function. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_scdc_readb(struct i2c_adapter *adapter, u8 offset, + u8 *value) +{ + return drm_scdc_read(adapter, offset, value, sizeof(*value)); +} + +/** + * drm_scdc_writeb - write a single byte to SCDC + * @adapter: I2C adapter + * @offset: offset of register to read + * @value: return location for the register value + * + * Writes a single byte to SCDC. This is a convenience wrapper around the + * drm_scdc_write() function. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_scdc_writeb(struct i2c_adapter *adapter, u8 offset, + u8 value) +{ + return drm_scdc_write(adapter, offset, &value, sizeof(value)); +} + +bool drm_scdc_get_scrambling_status(struct drm_connector *connector); + +bool drm_scdc_set_scrambling(struct drm_connector *connector, bool enable); +bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, bool set); + +#endif diff --git a/include/drm/drmP.h b/include/drm/drmP.h deleted file mode 100644 index bdb0d5548f39..000000000000 --- a/include/drm/drmP.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Internal Header for the Direct Rendering Manager - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * Copyright (c) 2009-2010, Code Aurora Forum. - * All rights reserved. - * - * Author: Rickard E. (Rik) Faith <faith@valinux.com> - * Author: Gareth Hughes <gareth@valinux.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _DRM_P_H_ -#define _DRM_P_H_ - -#include <linux/agp_backend.h> -#include <linux/cdev.h> -#include <linux/dma-mapping.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/highmem.h> -#include <linux/idr.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/jiffies.h> -#include <linux/kernel.h> -#include <linux/kref.h> -#include <linux/miscdevice.h> -#include <linux/mm.h> -#include <linux/mutex.h> -#include <linux/platform_device.h> -#include <linux/poll.h> -#include <linux/ratelimit.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/vmalloc.h> -#include <linux/workqueue.h> -#include <linux/dma-fence.h> -#include <linux/module.h> - -#include <asm/mman.h> -#include <asm/pgalloc.h> -#include <linux/uaccess.h> - -#include <uapi/drm/drm.h> -#include <uapi/drm/drm_mode.h> - -#include <drm/drm_agpsupport.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fourcc.h> -#include <drm/drm_hashtab.h> -#include <drm/drm_mm.h> -#include <drm/drm_os_linux.h> -#include <drm/drm_sarea.h> -#include <drm/drm_drv.h> -#include <drm/drm_prime.h> -#include <drm/drm_print.h> -#include <drm/drm_pci.h> -#include <drm/drm_file.h> -#include <drm/drm_debugfs.h> -#include <drm/drm_ioctl.h> -#include <drm/drm_sysfs.h> -#include <drm/drm_vblank.h> -#include <drm/drm_irq.h> -#include <drm/drm_device.h> - -struct module; - -struct device_node; -struct videomode; -struct reservation_object; -struct dma_buf_attachment; - -struct pci_dev; -struct pci_controller; - -#define DRM_IF_VERSION(maj, min) (maj << 16 | min) - -#define DRM_SWITCH_POWER_ON 0 -#define DRM_SWITCH_POWER_OFF 1 -#define DRM_SWITCH_POWER_CHANGING 2 -#define DRM_SWITCH_POWER_DYNAMIC_OFF 3 - -/* returns true if currently okay to sleep */ -static inline bool drm_can_sleep(void) -{ - if (in_atomic() || in_dbg_master() || irqs_disabled()) - return false; - return true; -} - -#if defined(CONFIG_DRM_DEBUG_SELFTEST_MODULE) -#define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) EXPORT_SYMBOL(x) -#else -#define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) -#endif - -#endif diff --git a/include/drm/drm_accel.h b/include/drm/drm_accel.h new file mode 100644 index 000000000000..20a665ec6f16 --- /dev/null +++ b/include/drm/drm_accel.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2022 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#ifndef DRM_ACCEL_H_ +#define DRM_ACCEL_H_ + +#include <drm/drm_file.h> + +#define ACCEL_MAJOR 261 +#define ACCEL_MAX_MINORS 256 + +/** + * DRM_ACCEL_FOPS - Default drm accelerators file operations + * + * This macro provides a shorthand for setting the accelerator file ops in the + * &file_operations structure. If all you need are the default ops, use + * DEFINE_DRM_ACCEL_FOPS instead. + */ +#define DRM_ACCEL_FOPS \ + .open = accel_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek, \ + .mmap = drm_gem_mmap, \ + .fop_flags = FOP_UNSIGNED_OFFSET + +/** + * DEFINE_DRM_ACCEL_FOPS() - macro to generate file operations for accelerators drivers + * @name: name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations for accelerators based + * drivers, which can be assigned to &drm_driver.fops. Note that this structure + * cannot be shared between drivers, because it contains a reference to the + * current module using THIS_MODULE. + * + * Note that the declaration is already marked as static - if you need a + * non-static version of this you're probably doing it wrong and will break the + * THIS_MODULE reference by accident. + */ +#define DEFINE_DRM_ACCEL_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + DRM_ACCEL_FOPS,\ + } + +#if IS_ENABLED(CONFIG_DRM_ACCEL) + +extern struct xarray accel_minors_xa; + +void accel_core_exit(void); +int accel_core_init(void); +void accel_set_device_instance_params(struct device *kdev, int index); +int accel_open(struct inode *inode, struct file *filp); +void accel_debugfs_register(struct drm_device *dev); + +#else + +static inline void accel_core_exit(void) +{ +} + +static inline int __init accel_core_init(void) +{ + /* Return 0 to allow drm_core_init to complete successfully */ + return 0; +} + +static inline void accel_set_device_instance_params(struct device *kdev, int index) +{ +} + +static inline void accel_debugfs_register(struct drm_device *dev) +{ +} + +#endif /* IS_ENABLED(CONFIG_DRM_ACCEL) */ + +#endif /* DRM_ACCEL_H_ */ diff --git a/include/drm/drm_agpsupport.h b/include/drm/drm_agpsupport.h deleted file mode 100644 index b05e462276d5..000000000000 --- a/include/drm/drm_agpsupport.h +++ /dev/null @@ -1,149 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _DRM_AGPSUPPORT_H_ -#define _DRM_AGPSUPPORT_H_ - -#include <linux/agp_backend.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/mm.h> -#include <linux/mutex.h> -#include <linux/types.h> -#include <uapi/drm/drm.h> - -struct drm_device; -struct drm_file; - -struct drm_agp_head { - struct agp_kern_info agp_info; - struct list_head memory; - unsigned long mode; - struct agp_bridge_data *bridge; - int enabled; - int acquired; - unsigned long base; - int agp_mtrr; - int cant_use_aperture; - unsigned long page_mask; -}; - -#if IS_ENABLED(CONFIG_AGP) - -void drm_free_agp(struct agp_memory * handle, int pages); -int drm_bind_agp(struct agp_memory * handle, unsigned int start); -int drm_unbind_agp(struct agp_memory * handle); -struct agp_memory *drm_agp_bind_pages(struct drm_device *dev, - struct page **pages, - unsigned long num_pages, - uint32_t gtt_offset, - uint32_t type); - -struct drm_agp_head *drm_agp_init(struct drm_device *dev); -void drm_legacy_agp_clear(struct drm_device *dev); -int drm_agp_acquire(struct drm_device *dev); -int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_release(struct drm_device *dev); -int drm_agp_release_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode); -int drm_agp_enable_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info); -int drm_agp_info_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request); -int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request); -int drm_agp_free_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request); -int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request); -int drm_agp_bind_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -#else /* CONFIG_AGP */ - -static inline void drm_free_agp(struct agp_memory * handle, int pages) -{ -} - -static inline int drm_bind_agp(struct agp_memory * handle, unsigned int start) -{ - return -ENODEV; -} - -static inline int drm_unbind_agp(struct agp_memory * handle) -{ - return -ENODEV; -} - -static inline struct agp_memory *drm_agp_bind_pages(struct drm_device *dev, - struct page **pages, - unsigned long num_pages, - uint32_t gtt_offset, - uint32_t type) -{ - return NULL; -} - -static inline struct drm_agp_head *drm_agp_init(struct drm_device *dev) -{ - return NULL; -} - -static inline void drm_legacy_agp_clear(struct drm_device *dev) -{ -} - -static inline int drm_agp_acquire(struct drm_device *dev) -{ - return -ENODEV; -} - -static inline int drm_agp_release(struct drm_device *dev) -{ - return -ENODEV; -} - -static inline int drm_agp_enable(struct drm_device *dev, - struct drm_agp_mode mode) -{ - return -ENODEV; -} - -static inline int drm_agp_info(struct drm_device *dev, - struct drm_agp_info *info) -{ - return -ENODEV; -} - -static inline int drm_agp_alloc(struct drm_device *dev, - struct drm_agp_buffer *request) -{ - return -ENODEV; -} - -static inline int drm_agp_free(struct drm_device *dev, - struct drm_agp_buffer *request) -{ - return -ENODEV; -} - -static inline int drm_agp_unbind(struct drm_device *dev, - struct drm_agp_binding *request) -{ - return -ENODEV; -} - -static inline int drm_agp_bind(struct drm_device *dev, - struct drm_agp_binding *request) -{ - return -ENODEV; -} - -#endif /* CONFIG_AGP */ - -#endif /* _DRM_AGPSUPPORT_H_ */ diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f9b35834c45d..43783891d359 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -30,12 +30,13 @@ #include <drm/drm_crtc.h> #include <drm/drm_util.h> +#include <drm/drm_colorop.h> /** * struct drm_crtc_commit - track modeset commits on a CRTC * * This structure is used to track pending modeset changes and atomic commit on - * a per-CRTC basis. Since updating the list should never block this structure + * a per-CRTC basis. Since updating the list should never block, this structure * is reference counted to allow waiters to safely wait on an event to complete, * without holding any locks. * @@ -60,12 +61,14 @@ * wait for flip_done <---- * clean up atomic state * - * The important bit to know is that cleanup_done is the terminal event, but the - * ordering between flip_done and hw_done is entirely up to the specific driver + * The important bit to know is that &cleanup_done is the terminal event, but the + * ordering between &flip_done and &hw_done is entirely up to the specific driver * and modeset state change. * * For an implementation of how to use this look at * drm_atomic_helper_setup_commit() from the atomic helper library. + * + * See also drm_crtc_commit_wait(). */ struct drm_crtc_commit { /** @@ -92,6 +95,9 @@ struct drm_crtc_commit { * commit is sent to userspace, or when an out-fence is singalled. Note * that for most hardware, in most cases this happens after @hw_done is * signalled. + * + * Completion of this stage is signalled implicitly by calling + * drm_crtc_send_vblank_event() on &drm_crtc_state.event. */ struct completion flip_done; @@ -100,13 +106,16 @@ struct drm_crtc_commit { * * Will be signalled when all hw register changes for this commit have * been written out. Especially when disabling a pipe this can be much - * later than than @flip_done, since that can signal already when the + * later than @flip_done, since that can signal already when the * screen goes black, whereas to fully shut down a pipe more register * I/O is required. * * Note that this does not need to include separately reference-counted * resources like backing storage buffer pinning, or runtime pm * management. + * + * Drivers should call drm_atomic_helper_commit_hw_done() to signal + * completion of this stage. */ struct completion hw_done; @@ -118,6 +127,9 @@ struct drm_crtc_commit { * a vblank wait completed it might be a bit later. This completion is * useful to throttle updates and avoid hardware updates getting ahead * of the buffer cleanup too much. + * + * Drivers should call drm_atomic_helper_commit_cleanup_done() to signal + * completion of this stage. */ struct completion cleanup_done; @@ -139,21 +151,58 @@ struct drm_crtc_commit { /** * @abort_completion: * - * A flag that's set after drm_atomic_helper_setup_commit takes a second - * reference for the completion of $drm_crtc_state.event. It's used by - * the free code to remove the second reference if commit fails. + * A flag that's set after drm_atomic_helper_setup_commit() takes a + * second reference for the completion of $drm_crtc_state.event. It's + * used by the free code to remove the second reference if commit fails. */ bool abort_completion; }; +struct __drm_colorops_state { + struct drm_colorop *ptr; + struct drm_colorop_state *state, *old_state, *new_state; +}; + struct __drm_planes_state { struct drm_plane *ptr; - struct drm_plane_state *state, *old_state, *new_state; + + /** + * @state_to_destroy: + * + * Used to track the @drm_plane_state we will need to free when + * tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_plane_state *state_to_destroy; + + struct drm_plane_state *old_state, *new_state; }; struct __drm_crtcs_state { struct drm_crtc *ptr; - struct drm_crtc_state *state, *old_state, *new_state; + + /** + * @state_to_destroy: + * + * Used to track the @drm_crtc_state we will need to free when + * tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_crtc_state *state_to_destroy; + + struct drm_crtc_state *old_state, *new_state; /** * @commit: @@ -171,7 +220,24 @@ struct __drm_crtcs_state { struct __drm_connnectors_state { struct drm_connector *ptr; - struct drm_connector_state *state, *old_state, *new_state; + + /** + * @state_to_destroy: + * + * Used to track the @drm_connector_state we will need to free + * when tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_connector_state *state_to_destroy; + + struct drm_connector_state *old_state, *new_state; + /** * @out_fence_ptr: * @@ -192,7 +258,7 @@ struct drm_private_state; * private objects. The structure itself is used as a vtable to identify the * associated private object type. Each private object type that needs to be * added to the atomic states is expected to have an implementation of these - * hooks and pass a pointer to it's drm_private_state_funcs struct to + * hooks and pass a pointer to its drm_private_state_funcs struct to * drm_atomic_get_private_obj_state(). */ struct drm_private_state_funcs { @@ -216,6 +282,18 @@ struct drm_private_state_funcs { */ void (*atomic_destroy_state)(struct drm_private_obj *obj, struct drm_private_state *state); + + /** + * @atomic_print_state: + * + * If driver subclasses &struct drm_private_state, it should implement + * this optional hook for printing additional driver specific state. + * + * Do not call this directly, use drm_atomic_private_obj_print_state() + * instead. + */ + void (*atomic_print_state)(struct drm_printer *p, + const struct drm_private_state *state); }; /** @@ -228,9 +306,51 @@ struct drm_private_state_funcs { * Currently only tracks the state update functions and the opaque driver * private state itself, but in the future might also track which * &drm_modeset_lock is required to duplicate and update this object's state. + * + * All private objects must be initialized before the DRM device they are + * attached to is registered to the DRM subsystem (call to drm_dev_register()) + * and should stay around until this DRM device is unregistered (call to + * drm_dev_unregister()). In other words, private objects lifetime is tied + * to the DRM device lifetime. This implies that: + * + * 1/ all calls to drm_atomic_private_obj_init() must be done before calling + * drm_dev_register() + * 2/ all calls to drm_atomic_private_obj_fini() must be done after calling + * drm_dev_unregister() + * + * If that private object is used to store a state shared by multiple + * CRTCs, proper care must be taken to ensure that non-blocking commits are + * properly ordered to avoid a use-after-free issue. + * + * Indeed, assuming a sequence of two non-blocking &drm_atomic_commit on two + * different &drm_crtc using different &drm_plane and &drm_connector, so with no + * resources shared, there's no guarantee on which commit is going to happen + * first. However, the second &drm_atomic_commit will consider the first + * &drm_private_obj its old state, and will be in charge of freeing it whenever + * the second &drm_atomic_commit is done. + * + * If the first &drm_atomic_commit happens after it, it will consider its + * &drm_private_obj the new state and will be likely to access it, resulting in + * an access to a freed memory region. Drivers should store (and get a reference + * to) the &drm_crtc_commit structure in our private state in + * &drm_mode_config_helper_funcs.atomic_commit_setup, and then wait for that + * commit to complete as the first step of + * &drm_mode_config_helper_funcs.atomic_commit_tail, similar to + * drm_atomic_helper_wait_for_dependencies(). */ struct drm_private_obj { /** + * @head: List entry used to attach a private object to a &drm_device + * (queued to &drm_mode_config.privobj_list). + */ + struct list_head head; + + /** + * @lock: Modeset lock to protect the state object. + */ + struct drm_modeset_lock lock; + + /** * @state: Current atomic state for this driver private object. */ struct drm_private_state *state; @@ -245,43 +365,112 @@ struct drm_private_obj { }; /** + * drm_for_each_privobj() - private object iterator + * + * @privobj: pointer to the current private object. Updated after each + * iteration + * @dev: the DRM device we want get private objects from + * + * Allows one to iterate over all private objects attached to @dev + */ +#define drm_for_each_privobj(privobj, dev) \ + list_for_each_entry(privobj, &(dev)->mode_config.privobj_list, head) + +/** * struct drm_private_state - base struct for driver private object state - * @state: backpointer to global drm_atomic_state * - * Currently only contains a backpointer to the overall atomic update, but in - * the future also might hold synchronization information similar to e.g. - * &drm_crtc.commit. + * Currently only contains a backpointer to the overall atomic update, + * and the relevant private object but in the future also might hold + * synchronization information similar to e.g. &drm_crtc.commit. */ struct drm_private_state { + /** + * @state: backpointer to global drm_atomic_state + */ struct drm_atomic_state *state; + + /** + * @obj: backpointer to the private object + */ + struct drm_private_obj *obj; }; struct __drm_private_objs_state { struct drm_private_obj *ptr; - struct drm_private_state *state, *old_state, *new_state; + + /** + * @state_to_destroy: + * + * Used to track the @drm_private_state we will need to free + * when tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_private_state *state_to_destroy; + + struct drm_private_state *old_state, *new_state; }; /** - * struct drm_atomic_state - the global state object for atomic updates - * @ref: count of all references to this state (will not be freed until zero) - * @dev: parent DRM device - * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics - * @async_update: hint for asynchronous plane update - * @planes: pointer to array of structures with per-plane data - * @crtcs: pointer to array of CRTC pointers - * @num_connector: size of the @connectors and @connector_states arrays - * @connectors: pointer to array of structures with per-connector data - * @num_private_objs: size of the @private_objs array - * @private_objs: pointer to array of private object pointers - * @acquire_ctx: acquire context for this atomic modeset state update + * struct drm_atomic_state - Atomic commit structure + * + * This structure is the kernel counterpart of @drm_mode_atomic and represents + * an atomic commit that transitions from an old to a new display state. It + * contains all the objects affected by the atomic commit and both the new + * state structures and pointers to the old state structures for + * these. * * States are added to an atomic update by calling drm_atomic_get_crtc_state(), * drm_atomic_get_plane_state(), drm_atomic_get_connector_state(), or for * private state structures, drm_atomic_get_private_obj_state(). + * + * NOTE: struct drm_atomic_state first started as a single collection of + * entities state pointers (drm_plane_state, drm_crtc_state, etc.). + * + * At atomic_check time, you could get the state about to be committed + * from drm_atomic_state, and the one currently running from the + * entities state pointer (drm_crtc.state, for example). After the call + * to drm_atomic_helper_swap_state(), the entities state pointer would + * contain the state previously checked, and the drm_atomic_state + * structure the old state. + * + * Over time, and in order to avoid confusion, drm_atomic_state has + * grown to have both the old state (ie, the state we replace) and the + * new state (ie, the state we want to apply). Those names are stable + * during the commit process, which makes it easier to reason about. + * + * You can still find some traces of that evolution through some hooks + * or callbacks taking a drm_atomic_state parameter called names like + * "old_state". This doesn't necessarily mean that the previous + * drm_atomic_state is passed, but rather that this used to be the state + * collection we were replacing after drm_atomic_helper_swap_state(), + * but the variable name was never updated. + * + * Some atomic operations implementations followed a similar process. We + * first started to pass the entity state only. However, it was pretty + * cumbersome for drivers, and especially CRTCs, to retrieve the states + * of other components. Thus, we switched to passing the whole + * drm_atomic_state as a parameter to those operations. Similarly, the + * transition isn't complete yet, and one might still find atomic + * operations taking a drm_atomic_state pointer, or a component state + * pointer. The former is the preferred form. */ struct drm_atomic_state { + /** + * @ref: + * + * Count of all references to this update (will not be freed until zero). + */ struct kref ref; + /** + * @dev: Parent DRM Device. + */ struct drm_device *dev; /** @@ -289,19 +478,136 @@ struct drm_atomic_state { * * Allow full modeset. This is used by the ATOMIC IOCTL handler to * implement the DRM_MODE_ATOMIC_ALLOW_MODESET flag. Drivers should - * never consult this flag, instead looking at the output of - * drm_atomic_crtc_needs_modeset(). + * generally not consult this flag, but instead look at the output of + * drm_atomic_crtc_needs_modeset(). The detailed rules are: + * + * - Drivers must not consult @allow_modeset in the atomic commit path. + * Use drm_atomic_crtc_needs_modeset() instead. + * + * - Drivers must consult @allow_modeset before adding unrelated struct + * drm_crtc_state to this commit by calling + * drm_atomic_get_crtc_state(). See also the warning in the + * documentation for that function. + * + * - Drivers must never change this flag, it is under the exclusive + * control of userspace. + * + * - Drivers may consult @allow_modeset in the atomic check path, if + * they have the choice between an optimal hardware configuration + * which requires a modeset, and a less optimal configuration which + * can be committed without a modeset. An example would be suboptimal + * scanout FIFO allocation resulting in increased idle power + * consumption. This allows userspace to avoid flickering and delays + * for the normal composition loop at reasonable cost. */ bool allow_modeset : 1; + /** + * @legacy_cursor_update: + * + * Hint to enforce legacy cursor IOCTL semantics. + * + * WARNING: This is thoroughly broken and pretty much impossible to + * implement correctly. Drivers must ignore this and should instead + * implement &drm_plane_helper_funcs.atomic_async_check and + * &drm_plane_helper_funcs.atomic_async_commit hooks. New users of this + * flag are not allowed. + */ bool legacy_cursor_update : 1; + + /** + * @async_update: hint for asynchronous plane update + */ bool async_update : 1; + + /** + * @duplicated: + * + * Indicates whether or not this atomic state was duplicated using + * drm_atomic_helper_duplicate_state(). Drivers and atomic helpers + * should use this to fixup normal inconsistencies in duplicated + * states. + */ + bool duplicated : 1; + + /** + * @checked: + * + * Indicates the state has been checked and thus must no longer + * be mutated. For internal use only, do not consult from drivers. + */ + bool checked : 1; + + /** + * @plane_color_pipeline: + * + * Indicates whether this atomic state originated with a client that + * set the DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE. + * + * Drivers and helper functions should use this to ignore legacy + * properties that are incompatible with the drm_plane COLOR_PIPELINE + * behavior, such as: + * + * - COLOR_RANGE + * - COLOR_ENCODING + * + * or any other driver-specific properties that might affect pixel + * values. + */ + bool plane_color_pipeline : 1; + + /** + * @colorops: + * + * Pointer to array of @drm_colorop and @drm_colorop_state part of this + * update. + */ + struct __drm_colorops_state *colorops; + + /** + * @planes: + * + * Pointer to array of @drm_plane and @drm_plane_state part of this + * update. + */ struct __drm_planes_state *planes; + + /** + * @crtcs: + * + * Pointer to array of @drm_crtc and @drm_crtc_state part of this + * update. + */ struct __drm_crtcs_state *crtcs; + + /** + * @num_connector: size of the @connectors array + */ int num_connector; + + /** + * @connectors: + * + * Pointer to array of @drm_connector and @drm_connector_state part of + * this update. + */ struct __drm_connnectors_state *connectors; + + /** + * @num_private_objs: size of the @private_objs array + */ int num_private_objs; + + /** + * @private_objs: + * + * Pointer to array of @drm_private_obj and @drm_private_obj_state part + * of this update. + */ struct __drm_private_objs_state *private_objs; + /** + * @acquire_ctx: acquire context for this atomic modeset state update + */ struct drm_modeset_acquire_ctx *acquire_ctx; /** @@ -309,9 +615,9 @@ struct drm_atomic_state { * * Used for signaling unbound planes/connectors. * When a connector or plane is not bound to any CRTC, it's still important - * to preserve linearity to prevent the atomic states from being freed to early. + * to preserve linearity to prevent the atomic states from being freed too early. * - * This commit (if set) is not bound to any crtc, but will be completed when + * This commit (if set) is not bound to any CRTC, but will be completed when * drm_atomic_helper_commit_hw_done() is called. */ struct drm_crtc_commit *fake_commit; @@ -354,6 +660,8 @@ static inline void drm_crtc_commit_put(struct drm_crtc_commit *commit) kref_put(&commit->ref, __drm_crtc_commit_free); } +int drm_crtc_commit_wait(struct drm_crtc_commit *commit); + struct drm_atomic_state * __must_check drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); @@ -396,11 +704,15 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, struct drm_plane_state * __must_check drm_atomic_get_plane_state(struct drm_atomic_state *state, struct drm_plane *plane); +struct drm_colorop_state * +drm_atomic_get_colorop_state(struct drm_atomic_state *state, + struct drm_colorop *colorop); struct drm_connector_state * __must_check drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); -void drm_atomic_private_obj_init(struct drm_private_obj *obj, +void drm_atomic_private_obj_init(struct drm_device *dev, + struct drm_private_obj *obj, struct drm_private_state *state, const struct drm_private_state_funcs *funcs); void drm_atomic_private_obj_fini(struct drm_private_obj *obj); @@ -408,73 +720,60 @@ void drm_atomic_private_obj_fini(struct drm_private_obj *obj); struct drm_private_state * __must_check drm_atomic_get_private_obj_state(struct drm_atomic_state *state, struct drm_private_obj *obj); +struct drm_private_state * +drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state, + struct drm_private_obj *obj); +struct drm_private_state * +drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state, + struct drm_private_obj *obj); + +struct drm_connector * +drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state, + struct drm_encoder *encoder); +struct drm_connector * +drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state, + struct drm_encoder *encoder); +struct drm_connector * +drm_atomic_get_connector_for_encoder(const struct drm_encoder *encoder, + struct drm_modeset_acquire_ctx *ctx); + +struct drm_crtc * +drm_atomic_get_old_crtc_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder); +struct drm_crtc * +drm_atomic_get_new_crtc_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder); /** - * drm_atomic_get_existing_crtc_state - get crtc state, if it exists + * drm_atomic_get_old_crtc_state - get old CRTC state, if it exists * @state: global atomic state object - * @crtc: crtc to grab - * - * This function returns the crtc state for the given crtc, or NULL - * if the crtc is not part of the global atomic state. + * @crtc: CRTC to grab * - * This function is deprecated, @drm_atomic_get_old_crtc_state or - * @drm_atomic_get_new_crtc_state should be used instead. + * This function returns the old CRTC state for the given CRTC, or + * NULL if the CRTC is not part of the global atomic state. */ static inline struct drm_crtc_state * -drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, - struct drm_crtc *crtc) -{ - return state->crtcs[drm_crtc_index(crtc)].state; -} - -/** - * drm_atomic_get_old_crtc_state - get old crtc state, if it exists - * @state: global atomic state object - * @crtc: crtc to grab - * - * This function returns the old crtc state for the given crtc, or - * NULL if the crtc is not part of the global atomic state. - */ -static inline struct drm_crtc_state * -drm_atomic_get_old_crtc_state(struct drm_atomic_state *state, +drm_atomic_get_old_crtc_state(const struct drm_atomic_state *state, struct drm_crtc *crtc) { return state->crtcs[drm_crtc_index(crtc)].old_state; } /** - * drm_atomic_get_new_crtc_state - get new crtc state, if it exists + * drm_atomic_get_new_crtc_state - get new CRTC state, if it exists * @state: global atomic state object - * @crtc: crtc to grab + * @crtc: CRTC to grab * - * This function returns the new crtc state for the given crtc, or - * NULL if the crtc is not part of the global atomic state. + * This function returns the new CRTC state for the given CRTC, or + * NULL if the CRTC is not part of the global atomic state. */ static inline struct drm_crtc_state * -drm_atomic_get_new_crtc_state(struct drm_atomic_state *state, +drm_atomic_get_new_crtc_state(const struct drm_atomic_state *state, struct drm_crtc *crtc) { return state->crtcs[drm_crtc_index(crtc)].new_state; } /** - * drm_atomic_get_existing_plane_state - get plane state, if it exists - * @state: global atomic state object - * @plane: plane to grab - * - * This function returns the plane state for the given plane, or NULL - * if the plane is not part of the global atomic state. - * - * This function is deprecated, @drm_atomic_get_old_plane_state or - * @drm_atomic_get_new_plane_state should be used instead. - */ -static inline struct drm_plane_state * -drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, - struct drm_plane *plane) -{ - return state->planes[drm_plane_index(plane)].state; -} - -/** * drm_atomic_get_old_plane_state - get plane state, if it exists * @state: global atomic state object * @plane: plane to grab @@ -483,7 +782,7 @@ drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, * NULL if the plane is not part of the global atomic state. */ static inline struct drm_plane_state * -drm_atomic_get_old_plane_state(struct drm_atomic_state *state, +drm_atomic_get_old_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { return state->planes[drm_plane_index(plane)].old_state; @@ -498,33 +797,40 @@ drm_atomic_get_old_plane_state(struct drm_atomic_state *state, * NULL if the plane is not part of the global atomic state. */ static inline struct drm_plane_state * -drm_atomic_get_new_plane_state(struct drm_atomic_state *state, +drm_atomic_get_new_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { return state->planes[drm_plane_index(plane)].new_state; } /** - * drm_atomic_get_existing_connector_state - get connector state, if it exists + * drm_atomic_get_old_colorop_state - get colorop state, if it exists * @state: global atomic state object - * @connector: connector to grab - * - * This function returns the connector state for the given connector, - * or NULL if the connector is not part of the global atomic state. + * @colorop: colorop to grab * - * This function is deprecated, @drm_atomic_get_old_connector_state or - * @drm_atomic_get_new_connector_state should be used instead. + * This function returns the old colorop state for the given colorop, or + * NULL if the colorop is not part of the global atomic state. */ -static inline struct drm_connector_state * -drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, - struct drm_connector *connector) +static inline struct drm_colorop_state * +drm_atomic_get_old_colorop_state(struct drm_atomic_state *state, + struct drm_colorop *colorop) { - int index = drm_connector_index(connector); - - if (index >= state->num_connector) - return NULL; + return state->colorops[drm_colorop_index(colorop)].old_state; +} - return state->connectors[index].state; +/** + * drm_atomic_get_new_colorop_state - get colorop state, if it exists + * @state: global atomic state object + * @colorop: colorop to grab + * + * This function returns the new colorop state for the given colorop, or + * NULL if the colorop is not part of the global atomic state. + */ +static inline struct drm_colorop_state * +drm_atomic_get_new_colorop_state(struct drm_atomic_state *state, + struct drm_colorop *colorop) +{ + return state->colorops[drm_colorop_index(colorop)].new_state; } /** @@ -536,7 +842,7 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, * or NULL if the connector is not part of the global atomic state. */ static inline struct drm_connector_state * -drm_atomic_get_old_connector_state(struct drm_atomic_state *state, +drm_atomic_get_old_connector_state(const struct drm_atomic_state *state, struct drm_connector *connector) { int index = drm_connector_index(connector); @@ -556,7 +862,7 @@ drm_atomic_get_old_connector_state(struct drm_atomic_state *state, * or NULL if the connector is not part of the global atomic state. */ static inline struct drm_connector_state * -drm_atomic_get_new_connector_state(struct drm_atomic_state *state, +drm_atomic_get_new_connector_state(const struct drm_atomic_state *state, struct drm_connector *connector) { int index = drm_connector_index(connector); @@ -572,11 +878,11 @@ drm_atomic_get_new_connector_state(struct drm_atomic_state *state, * @state: global atomic state object * @plane: plane to grab * - * This function returns the plane state for the given plane, either from - * @state, or if the plane isn't part of the atomic state update, from @plane. - * This is useful in atomic check callbacks, when drivers need to peek at, but - * not change, state of other planes, since it avoids threading an error code - * back up the call chain. + * This function returns the plane state for the given plane, either the + * new plane state from @state, or if the plane isn't part of the atomic + * state update, from @plane. This is useful in atomic check callbacks, + * when drivers need to peek at, but not change, state of other planes, + * since it avoids threading an error code back up the call chain. * * WARNING: * @@ -594,21 +900,33 @@ drm_atomic_get_new_connector_state(struct drm_atomic_state *state, * Read-only pointer to the current plane state. */ static inline const struct drm_plane_state * -__drm_atomic_get_current_plane_state(struct drm_atomic_state *state, +__drm_atomic_get_current_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { - if (state->planes[drm_plane_index(plane)].state) - return state->planes[drm_plane_index(plane)].state; + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_new_plane_state(state, plane); + if (plane_state) + return plane_state; + /* + * If the plane isn't part of the state, fallback to the currently active one. + */ return plane->state; } int __must_check +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder); +int __must_check drm_atomic_add_affected_connectors(struct drm_atomic_state *state, struct drm_crtc *crtc); int __must_check drm_atomic_add_affected_planes(struct drm_atomic_state *state, struct drm_crtc *crtc); +int __must_check +drm_atomic_add_affected_colorops(struct drm_atomic_state *state, + struct drm_plane *plane); int __must_check drm_atomic_check_only(struct drm_atomic_state *state); int __must_check drm_atomic_commit(struct drm_atomic_state *state); @@ -636,6 +954,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->connectors[__i].ptr && \ ((connector) = (__state)->connectors[__i].ptr, \ + (void)(connector) /* Only to avoid unused-but-set-variable warning */, \ (old_connector_state) = (__state)->connectors[__i].old_state, \ (new_connector_state) = (__state)->connectors[__i].new_state, 1)) @@ -657,6 +976,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->connectors[__i].ptr && \ ((connector) = (__state)->connectors[__i].ptr, \ + (void)(connector) /* Only to avoid unused-but-set-variable warning */, \ (old_connector_state) = (__state)->connectors[__i].old_state, 1)) /** @@ -677,7 +997,9 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->connectors[__i].ptr && \ ((connector) = (__state)->connectors[__i].ptr, \ - (new_connector_state) = (__state)->connectors[__i].new_state, 1)) + (void)(connector) /* Only to avoid unused-but-set-variable warning */, \ + (new_connector_state) = (__state)->connectors[__i].new_state, \ + (void)(new_connector_state) /* Only to avoid unused-but-set-variable warning */, 1)) /** * for_each_oldnew_crtc_in_state - iterate over all CRTCs in an atomic update @@ -697,8 +1019,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->crtcs[__i].ptr && \ ((crtc) = (__state)->crtcs[__i].ptr, \ + (void)(crtc) /* Only to avoid unused-but-set-variable warning */, \ (old_crtc_state) = (__state)->crtcs[__i].old_state, \ - (new_crtc_state) = (__state)->crtcs[__i].new_state, 1)) + (void)(old_crtc_state) /* Only to avoid unused-but-set-variable warning */, \ + (new_crtc_state) = (__state)->crtcs[__i].new_state, \ + (void)(new_crtc_state) /* Only to avoid unused-but-set-variable warning */, 1)) /** * for_each_old_crtc_in_state - iterate over all CRTCs in an atomic update @@ -717,6 +1042,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->crtcs[__i].ptr && \ ((crtc) = (__state)->crtcs[__i].ptr, \ + (void)(crtc) /* Only to avoid unused-but-set-variable warning */, \ (old_crtc_state) = (__state)->crtcs[__i].old_state, 1)) /** @@ -736,7 +1062,52 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->crtcs[__i].ptr && \ ((crtc) = (__state)->crtcs[__i].ptr, \ - (new_crtc_state) = (__state)->crtcs[__i].new_state, 1)) + (void)(crtc) /* Only to avoid unused-but-set-variable warning */, \ + (new_crtc_state) = (__state)->crtcs[__i].new_state, \ + (void)(new_crtc_state) /* Only to avoid unused-but-set-variable warning */, 1)) + +/** + * for_each_oldnew_colorop_in_state - iterate over all colorops in an atomic update + * @__state: &struct drm_atomic_state pointer + * @colorop: &struct drm_colorop iteration cursor + * @old_colorop_state: &struct drm_colorop_state iteration cursor for the old state + * @new_colorop_state: &struct drm_colorop_state iteration cursor for the new state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all colorops in an atomic update, tracking both old and + * new state. This is useful in places where the state delta needs to be + * considered, for example in atomic check functions. + */ +#define for_each_oldnew_colorop_in_state(__state, colorop, old_colorop_state, \ + new_colorop_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->dev->mode_config.num_colorop; \ + (__i)++) \ + for_each_if ((__state)->colorops[__i].ptr && \ + ((colorop) = (__state)->colorops[__i].ptr, \ + (void)(colorop) /* Only to avoid unused-but-set-variable warning */, \ + (old_colorop_state) = (__state)->colorops[__i].old_state,\ + (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) + +/** + * for_each_new_colorop_in_state - iterate over all colorops in an atomic update + * @__state: &struct drm_atomic_state pointer + * @colorop: &struct drm_colorop iteration cursor + * @new_colorop_state: &struct drm_colorop_state iteration cursor for the new state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all colorops in an atomic update, tracking new state. This is + * useful in places where the state delta needs to be considered, for example in + * atomic check functions. + */ +#define for_each_new_colorop_in_state(__state, colorop, new_colorop_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->dev->mode_config.num_colorop; \ + (__i)++) \ + for_each_if ((__state)->colorops[__i].ptr && \ + ((colorop) = (__state)->colorops[__i].ptr, \ + (void)(colorop) /* Only to avoid unused-but-set-variable warning */, \ + (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) /** * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update @@ -756,6 +1127,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->planes[__i].ptr && \ ((plane) = (__state)->planes[__i].ptr, \ + (void)(plane) /* Only to avoid unused-but-set-variable warning */, \ (old_plane_state) = (__state)->planes[__i].old_state,\ (new_plane_state) = (__state)->planes[__i].new_state, 1)) @@ -782,6 +1154,22 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (new_plane_state) = (__state)->planes[__i].new_state, 1)) /** + * for_each_new_plane_in_state_reverse - other than only tracking new state, + * it's the same as for_each_oldnew_plane_in_state_reverse + * @__state: &struct drm_atomic_state pointer + * @plane: &struct drm_plane iteration cursor + * @new_plane_state: &struct drm_plane_state iteration cursor for the new state + * @__i: int iteration cursor, for macro-internal use + */ +#define for_each_new_plane_in_state_reverse(__state, plane, new_plane_state, __i) \ + for ((__i) = ((__state)->dev->mode_config.num_total_plane - 1); \ + (__i) >= 0; \ + (__i)--) \ + for_each_if ((__state)->planes[__i].ptr && \ + ((plane) = (__state)->planes[__i].ptr, \ + (new_plane_state) = (__state)->planes[__i].new_state, 1)) + +/** * for_each_old_plane_in_state - iterate over all planes in an atomic update * @__state: &struct drm_atomic_state pointer * @plane: &struct drm_plane iteration cursor @@ -816,7 +1204,9 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if ((__state)->planes[__i].ptr && \ ((plane) = (__state)->planes[__i].ptr, \ - (new_plane_state) = (__state)->planes[__i].new_state, 1)) + (void)(plane) /* Only to avoid unused-but-set-variable warning */, \ + (new_plane_state) = (__state)->planes[__i].new_state, \ + (void)(new_plane_state) /* Only to avoid unused-but-set-variable warning */, 1)) /** * for_each_oldnew_private_obj_in_state - iterate over all private objects in an atomic update @@ -871,6 +1261,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); for ((__i) = 0; \ (__i) < (__state)->num_private_objs && \ ((obj) = (__state)->private_objs[__i].ptr, \ + (void)(obj) /* Only to avoid unused-but-set-variable warning */, \ (new_obj_state) = (__state)->private_objs[__i].new_state, 1); \ (__i)++) @@ -900,4 +1291,92 @@ drm_atomic_crtc_needs_modeset(const struct drm_crtc_state *state) state->connectors_changed; } +/** + * drm_atomic_crtc_effectively_active - compute whether CRTC is actually active + * @state: &drm_crtc_state for the CRTC + * + * When in self refresh mode, the crtc_state->active value will be false, since + * the CRTC is off. However in some cases we're interested in whether the CRTC + * is active, or effectively active (ie: it's connected to an active display). + * In these cases, use this function instead of just checking active. + */ +static inline bool +drm_atomic_crtc_effectively_active(const struct drm_crtc_state *state) +{ + return state->active || state->self_refresh_active; +} + +/** + * struct drm_bus_cfg - bus configuration + * + * This structure stores the configuration of a physical bus between two + * components in an output pipeline, usually between two bridges, an encoder + * and a bridge, or a bridge and a connector. + * + * The bus configuration is stored in &drm_bridge_state separately for the + * input and output buses, as seen from the point of view of each bridge. The + * bus configuration of a bridge output is usually identical to the + * configuration of the next bridge's input, but may differ if the signals are + * modified between the two bridges, for instance by an inverter on the board. + * The input and output configurations of a bridge may differ if the bridge + * modifies the signals internally, for instance by performing format + * conversion, or modifying signals polarities. + */ +struct drm_bus_cfg { + /** + * @format: format used on this bus (one of the MEDIA_BUS_FMT_* format) + * + * This field should not be directly modified by drivers + * (drm_atomic_bridge_chain_select_bus_fmts() takes care of the bus + * format negotiation). + */ + u32 format; + + /** + * @flags: DRM_BUS_* flags used on this bus + */ + u32 flags; +}; + +/** + * struct drm_bridge_state - Atomic bridge state object + */ +struct drm_bridge_state { + /** + * @base: inherit from &drm_private_state + */ + struct drm_private_state base; + + /** + * @bridge: the bridge this state refers to + */ + struct drm_bridge *bridge; + + /** + * @input_bus_cfg: input bus configuration + */ + struct drm_bus_cfg input_bus_cfg; + + /** + * @output_bus_cfg: output bus configuration + */ + struct drm_bus_cfg output_bus_cfg; +}; + +static inline struct drm_bridge_state * +drm_priv_to_bridge_state(struct drm_private_state *priv) +{ + return container_of(priv, struct drm_bridge_state, base); +} + +struct drm_bridge_state * +drm_atomic_get_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge); +struct drm_bridge_state * +drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state, + struct drm_bridge *bridge); +struct drm_bridge_state * +drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state, + struct drm_bridge *bridge); + #endif /* DRM_ATOMIC_H_ */ diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 58214be3bf3d..53382fe93537 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -34,12 +34,23 @@ #include <drm/drm_atomic_state_helper.h> #include <drm/drm_util.h> +/* + * Drivers that don't allow primary plane scaling may pass this macro in place + * of the min/max scale parameters of the plane-state checker function. + * + * Due to src being in 16.16 fixed point and dest being in integer pixels, + * 1<<16 represents no scaling. + */ +#define DRM_PLANE_NO_SCALING (1<<16) + struct drm_atomic_state; struct drm_private_obj; struct drm_private_state; int drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_atomic_state *state); +int drm_atomic_helper_check_wb_connector_state(struct drm_connector *connector, + struct drm_atomic_state *state); int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state, const struct drm_crtc_state *crtc_state, int min_scale, @@ -48,6 +59,7 @@ int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state, bool can_update_disabled); int drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state); +int drm_atomic_helper_check_crtc_primary_plane(struct drm_crtc_state *crtc_state); int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_helper_commit_tail(struct drm_atomic_state *state); @@ -74,6 +86,9 @@ void drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, struct drm_atomic_state *old_state); +void +drm_atomic_helper_calc_timestamping_constants(struct drm_atomic_state *state); + void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, @@ -81,6 +96,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, int drm_atomic_helper_prepare_planes(struct drm_device *dev, struct drm_atomic_state *state); +void drm_atomic_helper_unprepare_planes(struct drm_device *dev, + struct drm_atomic_state *state); #define DRM_PLANE_COMMIT_ACTIVE_ONLY BIT(0) #define DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET BIT(1) @@ -117,15 +134,13 @@ int drm_atomic_helper_update_plane(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); int drm_atomic_helper_disable_plane(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); -int __drm_atomic_helper_disable_plane(struct drm_plane *plane, - struct drm_plane_state *plane_state); int drm_atomic_helper_set_config(struct drm_mode_set *set, struct drm_modeset_acquire_ctx *ctx); -int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state); int drm_atomic_helper_disable_all(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx); +int drm_atomic_helper_reset_crtc(struct drm_crtc *crtc, + struct drm_modeset_acquire_ctx *ctx); void drm_atomic_helper_shutdown(struct drm_device *dev); struct drm_atomic_state * drm_atomic_helper_duplicate_state(struct drm_device *dev, @@ -148,15 +163,11 @@ int drm_atomic_helper_page_flip_target( uint32_t flags, uint32_t target, struct drm_modeset_acquire_ctx *ctx); -int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, - uint32_t size, - struct drm_modeset_acquire_ctx *ctx); /** * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC * @plane: the loop cursor - * @crtc: the crtc whose planes are iterated + * @crtc: the CRTC whose planes are iterated * * This iterates over the current state, useful (for example) when applying * atomic state after it has been checked and swapped. To iterate over the @@ -168,9 +179,9 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, drm_for_each_plane_mask(plane, (crtc)->dev, (crtc)->state->plane_mask) /** - * drm_crtc_atomic_state_for_each_plane - iterate over attached planes in new state + * drm_atomic_crtc_state_for_each_plane - iterate over attached planes in new state * @plane: the loop cursor - * @crtc_state: the incoming crtc-state + * @crtc_state: the incoming CRTC state * * Similar to drm_crtc_for_each_plane(), but iterates the planes that will be * attached if the specified state is applied. Useful during for example @@ -181,10 +192,10 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask) /** - * drm_crtc_atomic_state_for_each_plane_state - iterate over attached planes in new state + * drm_atomic_crtc_state_for_each_plane_state - iterate over attached planes in new state * @plane: the loop cursor * @plane_state: loop cursor for the plane's state, must be const - * @crtc_state: the incoming crtc-state + * @crtc_state: the incoming CRTC state * * Similar to drm_crtc_for_each_plane(), but iterates the planes that will be * attached if the specified state is applied. Useful during for example @@ -193,7 +204,7 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, * * Compared to just drm_atomic_crtc_state_for_each_plane() this also fills in a * const plane_state. This is useful when a driver just wants to peek at other - * active planes on this crtc, but does not need to change it. + * active planes on this CRTC, but does not need to change it. */ #define drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) \ drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask) \ @@ -202,6 +213,32 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, plane))) /** + * drm_atomic_plane_enabling - check whether a plane is being enabled + * @old_plane_state: old atomic plane state + * @new_plane_state: new atomic plane state + * + * Checks the atomic state of a plane to determine whether it's being enabled + * or not. This also WARNs if it detects an invalid state (both CRTC and FB + * need to either both be NULL or both be non-NULL). + * + * RETURNS: + * True if the plane is being enabled, false otherwise. + */ +static inline bool drm_atomic_plane_enabling(struct drm_plane_state *old_plane_state, + struct drm_plane_state *new_plane_state) +{ + /* + * When enabling a plane, CRTC and FB should always be set together. + * Anything else should be considered a bug in the atomic core, so we + * gently warn about it. + */ + WARN_ON((!new_plane_state->crtc && new_plane_state->fb) || + (new_plane_state->crtc && !new_plane_state->fb)); + + return !old_plane_state->crtc && new_plane_state->crtc; +} + +/** * drm_atomic_plane_disabling - check whether a plane is being disabled * @old_plane_state: old atomic plane state * @new_plane_state: new atomic plane state @@ -228,4 +265,12 @@ drm_atomic_plane_disabling(struct drm_plane_state *old_plane_state, return old_plane_state->crtc && !new_plane_state->crtc; } +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 66c92cbd8e16..b9740edb2658 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -26,6 +26,9 @@ #include <linux/types.h> +struct drm_atomic_state; +struct drm_bridge; +struct drm_bridge_state; struct drm_crtc; struct drm_crtc_state; struct drm_plane; @@ -37,6 +40,10 @@ struct drm_private_state; struct drm_modeset_acquire_ctx; struct drm_device; +void __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *state, + struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, + struct drm_crtc_state *state); void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, struct drm_crtc_state *state); @@ -46,6 +53,8 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); +void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *state, + struct drm_plane *plane); void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *state); void drm_atomic_helper_plane_reset(struct drm_plane *plane); @@ -57,9 +66,15 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); +void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, + struct drm_connector *connector); void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector); +int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, + struct drm_atomic_state *state); +void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connector); void __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, struct drm_connector_state *state); @@ -71,3 +86,14 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, struct drm_private_state *state); + +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); +struct drm_bridge_state * +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge); +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, + struct drm_bridge_state *state); +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state); +struct drm_bridge_state * +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); diff --git a/include/drm/drm_atomic_uapi.h b/include/drm/drm_atomic_uapi.h index 8cec52ad1277..436315523326 100644 --- a/include/drm/drm_atomic_uapi.h +++ b/include/drm/drm_atomic_uapi.h @@ -37,6 +37,7 @@ struct drm_crtc; struct drm_connector_state; struct dma_fence; struct drm_framebuffer; +struct drm_colorop; int __must_check drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, @@ -49,8 +50,8 @@ drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, struct drm_crtc *crtc); void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, struct drm_framebuffer *fb); -void drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state, - struct dma_fence *fence); +void drm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state, + struct drm_colorop *colorop); int __must_check drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, struct drm_crtc *crtc); diff --git a/include/drm/drm_audio_component.h b/include/drm/drm_audio_component.h index 4923b00328c1..5a4cd1fa8e2a 100644 --- a/include/drm/drm_audio_component.h +++ b/include/drm/drm_audio_component.h @@ -4,7 +4,11 @@ #ifndef _DRM_AUDIO_COMPONENT_H_ #define _DRM_AUDIO_COMPONENT_H_ +#include <linux/completion.h> +#include <linux/types.h> + struct drm_audio_component; +struct device; /** * struct drm_audio_component_ops - Ops implemented by DRM driver, called by hda driver @@ -18,14 +22,17 @@ struct drm_audio_component_ops { * @get_power: get the POWER_DOMAIN_AUDIO power well * * Request the power well to be turned on. + * + * Returns a wakeref cookie to be passed back to the corresponding + * call to @put_power. */ - void (*get_power)(struct device *); + unsigned long (*get_power)(struct device *); /** * @put_power: put the POWER_DOMAIN_AUDIO power well * * Allow the power well to be turned off. */ - void (*put_power)(struct device *); + void (*put_power)(struct device *, unsigned long); /** * @codec_wake_override: Enable/disable codec wake signal */ @@ -113,6 +120,10 @@ struct drm_audio_component { * @audio_ops: Ops implemented by hda driver, called by DRM driver */ const struct drm_audio_component_audio_ops *audio_ops; + /** + * @master_bind_complete: completion held during component master binding + */ + struct completion master_bind_complete; }; #endif /* _DRM_AUDIO_COMPONENT_H_ */ diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index 86bff9841b54..50131383ed81 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -1,3 +1,6 @@ +#ifndef _DRM_AUTH_H_ +#define _DRM_AUTH_H_ + /* * Internal Header for the Direct Rendering Manager * @@ -25,39 +28,18 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef _DRM_AUTH_H_ -#define _DRM_AUTH_H_ +#include <linux/idr.h> +#include <linux/kref.h> +#include <linux/wait.h> -/* - * Legacy DRI1 locking data structure. Only here instead of in drm_legacy.h for - * include ordering reasons. - * - * DO NOT USE. - */ -struct drm_lock_data { - struct drm_hw_lock *hw_lock; - struct drm_file *file_priv; - wait_queue_head_t lock_queue; - unsigned long lock_time; - spinlock_t spinlock; - uint32_t kernel_waiters; - uint32_t user_waiters; - int idle_has_lock; -}; +struct drm_file; /** * struct drm_master - drm master structure * * @refcount: Refcount for this master object. * @dev: Link back to the DRM device - * @lock: DRI1 lock information. * @driver_priv: Pointer to driver-private information. - * @lessor: Lease holder - * @lessee_id: id for lessees. Owners always have id 0 - * @lessee_list: other lessees of the same master - * @lessees: drm_masters leasing from this one - * @leases: Objects leased to this drm_master. - * @lessee_idr: All lessees under this owner (only used where lessor == NULL) * * Note that master structures are only relevant for the legacy/primary device * nodes, hence there can only be one per device, not one per drm_minor. @@ -80,24 +62,75 @@ struct drm_master { * &drm_device.master_mutex. */ struct idr magic_map; - struct drm_lock_data lock; void *driver_priv; - /* Tree of display resource leases, each of which is a drm_master struct - * All of these get activated simultaneously, so drm_device master points - * at the top of the tree (for which lessor is NULL). Protected by - * &drm_device.mode_config.idr_mutex. + /** + * @lessor: + * + * Lease grantor, only set if this &struct drm_master represents a + * lessee holding a lease of objects from @lessor. Full owners of the + * device have this set to NULL. + * + * The lessor does not change once it's set in drm_lease_create(), and + * each lessee holds a reference to its lessor that it releases upon + * being destroyed in drm_lease_destroy(). + * + * See also the :ref:`section on display resource leasing + * <drm_leasing>`. */ - struct drm_master *lessor; + + /** + * @lessee_id: + * + * ID for lessees. Owners (i.e. @lessor is NULL) always have ID 0. + * Protected by &drm_device.mode_config's &drm_mode_config.idr_mutex. + */ int lessee_id; + + /** + * @lessee_list: + * + * List entry of lessees of @lessor, where they are linked to @lessees. + * Not used for owners. Protected by &drm_device.mode_config's + * &drm_mode_config.idr_mutex. + */ struct list_head lessee_list; + + /** + * @lessees: + * + * List of drm_masters leasing from this one. Protected by + * &drm_device.mode_config's &drm_mode_config.idr_mutex. + * + * This list is empty if no leases have been granted, or if all lessees + * have been destroyed. Since lessors are referenced by all their + * lessees, this master cannot be destroyed unless the list is empty. + */ struct list_head lessees; + + /** + * @leases: + * + * Objects leased to this drm_master. Protected by + * &drm_device.mode_config's &drm_mode_config.idr_mutex. + * + * Objects are leased all together in drm_lease_create(), and are + * removed all together when the lease is revoked. + */ struct idr leases; + + /** + * @lessee_idr: + * + * All lessees under this owner (only used where @lessor is NULL). + * Protected by &drm_device.mode_config's &drm_mode_config.idr_mutex. + */ struct idr lessee_idr; }; struct drm_master *drm_master_get(struct drm_master *master); +struct drm_master *drm_file_get_master(struct drm_file *file_priv); void drm_master_put(struct drm_master **master); bool drm_is_current_master(struct drm_file *fpriv); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index bd850747ce54..0ff7ab4aa868 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -23,14 +23,40 @@ #ifndef __DRM_BRIDGE_H__ #define __DRM_BRIDGE_H__ -#include <linux/list.h> +#include <linux/cleanup.h> #include <linux/ctype.h> +#include <linux/list.h> +#include <linux/mutex.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> +struct cec_msg; +struct device_node; + struct drm_bridge; struct drm_bridge_timings; +struct drm_connector; +struct drm_display_info; +struct drm_minor; struct drm_panel; +struct edid; +struct hdmi_codec_daifmt; +struct hdmi_codec_params; +struct i2c_adapter; + +/** + * enum drm_bridge_attach_flags - Flags for &drm_bridge_funcs.attach + */ +enum drm_bridge_attach_flags { + /** + * @DRM_BRIDGE_ATTACH_NO_CONNECTOR: When this flag is set the bridge + * shall not create a drm_connector. + */ + DRM_BRIDGE_ATTACH_NO_CONNECTOR = BIT(0), +}; /** * struct drm_bridge_funcs - drm_bridge control functions @@ -40,15 +66,27 @@ struct drm_bridge_funcs { * @attach: * * This callback is invoked whenever our bridge is being attached to a - * &drm_encoder. + * &drm_encoder. The flags argument tunes the behaviour of the attach + * operation (see DRM_BRIDGE_ATTACH_*). * - * The attach callback is optional. + * The @attach callback is optional. * * RETURNS: * * Zero on success, error code on failure. */ - int (*attach)(struct drm_bridge *bridge); + int (*attach)(struct drm_bridge *bridge, struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags); + + /** + * @destroy: + * + * This callback is invoked when the bridge is about to be + * deallocated. + * + * The @destroy callback is optional. + */ + void (*destroy)(struct drm_bridge *bridge); /** * @detach: @@ -56,7 +94,7 @@ struct drm_bridge_funcs { * This callback is invoked whenever our bridge is being detached from a * &drm_encoder. * - * The detach callback is optional. + * The @detach callback is optional. */ void (*detach)(struct drm_bridge *bridge); @@ -76,14 +114,14 @@ struct drm_bridge_funcs { * atomic helpers to validate modes supplied by userspace in * drm_atomic_helper_check_modeset(). * - * This function is optional. + * The @mode_valid callback is optional. * * NOTE: * * Since this function is both called from the check phase of an atomic * commit, and the mode validation in the probe paths it is not allowed * to look at anything else but the passed-in mode, and validate it - * against configuration-invariant hardward constraints. Any further + * against configuration-invariant hardware constraints. Any further * limits which depend upon the configuration can only be checked in * @mode_fixup. * @@ -92,6 +130,7 @@ struct drm_bridge_funcs { * drm_mode_status Enum */ enum drm_mode_status (*mode_valid)(struct drm_bridge *bridge, + const struct drm_display_info *info, const struct drm_display_mode *mode); /** @@ -108,7 +147,9 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. * - * The mode_fixup callback is optional. + * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup() + * is not called when &drm_bridge_funcs.atomic_check() is implemented, + * so only one of them should be provided. * * NOTE: * @@ -135,37 +176,80 @@ struct drm_bridge_funcs { /** * @disable: * - * This callback should disable the bridge. It is called right before - * the preceding element in the display pipe is disabled. If the - * preceding element is a bridge this means it's called before that - * bridge's @disable vfunc. If the preceding element is a &drm_encoder - * it's called right before the &drm_encoder_helper_funcs.disable, - * &drm_encoder_helper_funcs.prepare or &drm_encoder_helper_funcs.dpms - * hook. + * The @disable callback should disable the bridge. * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. * - * The disable callback is optional. + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is disabled via one of: + * + * - &drm_bridge_funcs.disable + * - &drm_bridge_funcs.atomic_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms. + * + * The @disable callback is optional. + * + * NOTE: + * + * This is deprecated, do not use! + * New drivers shall use &drm_bridge_funcs.atomic_disable. */ void (*disable)(struct drm_bridge *bridge); /** * @post_disable: * - * This callback should disable the bridge. It is called right after the - * preceding element in the display pipe is disabled. If the preceding - * element is a bridge this means it's called after that bridge's - * @post_disable function. If the preceding element is a &drm_encoder - * it's called right after the encoder's - * &drm_encoder_helper_funcs.disable, &drm_encoder_helper_funcs.prepare - * or &drm_encoder_helper_funcs.dpms hook. - * * The bridge must assume that the display pipe (i.e. clocks and timing - * singals) feeding it is no longer running when this callback is - * called. + * signals) feeding this bridge is no longer running when the + * @post_disable is called. + * + * This callback should perform all the actions required by the hardware + * after it has stopped receiving signals from the preceding element. * - * The post_disable callback is optional. + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is post-disabled (unless marked otherwise by the + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.post_disable + * - &drm_bridge_funcs.atomic_post_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms + * + * The @post_disable callback is optional. + * + * NOTE: + * + * This is deprecated, do not use! + * New drivers shall use &drm_bridge_funcs.atomic_post_disable. */ void (*post_disable)(struct drm_bridge *bridge); @@ -191,52 +275,721 @@ struct drm_bridge_funcs { * * NOTE: * - * If a need arises to store and access modes adjusted for other - * locations than the connection between the CRTC and the first bridge, - * the DRM framework will have to be extended with DRM bridge states. + * This is deprecated, do not use! + * New drivers shall set their mode in the + * &drm_bridge_funcs.atomic_enable operation. */ void (*mode_set)(struct drm_bridge *bridge, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode); /** * @pre_enable: * - * This callback should enable the bridge. It is called right before - * the preceding element in the display pipe is enabled. If the - * preceding element is a bridge this means it's called before that - * bridge's @pre_enable function. If the preceding element is a - * &drm_encoder it's called right before the encoder's - * &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or - * &drm_encoder_helper_funcs.dpms hook. - * * The display pipe (i.e. clocks and timing signals) feeding this bridge - * will not yet be running when this callback is called. The bridge must - * not enable the display link feeding the next bridge in the chain (if - * there is one) when this callback is called. + * will not yet be running when the @pre_enable is called. + * + * This callback should perform all the necessary actions to prepare the + * bridge to accept signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is pre-enabled (unless marked otherwise by + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.pre_enable + * - &drm_bridge_funcs.atomic_pre_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit * - * The pre_enable callback is optional. + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - &drm_encoder_helper_funcs.commit + * + * The @pre_enable callback is optional. + * + * NOTE: + * + * This is deprecated, do not use! + * New drivers shall use &drm_bridge_funcs.atomic_pre_enable. */ void (*pre_enable)(struct drm_bridge *bridge); /** * @enable: * - * This callback should enable the bridge. It is called right after - * the preceding element in the display pipe is enabled. If the - * preceding element is a bridge this means it's called after that - * bridge's @enable function. If the preceding element is a - * &drm_encoder it's called right after the encoder's - * &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or - * &drm_encoder_helper_funcs.dpms hook. + * The @enable callback should enable the bridge. * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is running when this callback is called. This * callback must enable the display link feeding the next bridge in the * chain if there is one. * - * The enable callback is optional. + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is enabled via one of: + * + * - &drm_bridge_funcs.enable + * - &drm_bridge_funcs.atomic_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - drm_encoder_helper_funcs.commit + * + * The @enable callback is optional. + * + * NOTE: + * + * This is deprecated, do not use! + * New drivers shall use &drm_bridge_funcs.atomic_enable. */ void (*enable)(struct drm_bridge *bridge); + + /** + * @atomic_pre_enable: + * + * The display pipe (i.e. clocks and timing signals) feeding this bridge + * will not yet be running when the @atomic_pre_enable is called. + * + * This callback should perform all the necessary actions to prepare the + * bridge to accept signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is pre-enabled (unless marked otherwise by + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.pre_enable + * - &drm_bridge_funcs.atomic_pre_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - &drm_encoder_helper_funcs.commit + * + * The @atomic_pre_enable callback is optional. + */ + void (*atomic_pre_enable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * The @atomic_enable callback should enable the bridge. + * + * The bridge can assume that the display pipe (i.e. clocks and timing + * signals) feeding it is running when this callback is called. This + * callback must enable the display link feeding the next bridge in the + * chain if there is one. + * + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is enabled via one of: + * + * - &drm_bridge_funcs.enable + * - &drm_bridge_funcs.atomic_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - drm_encoder_helper_funcs.commit + * + * The @atomic_enable callback is optional. + */ + void (*atomic_enable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + /** + * @atomic_disable: + * + * The @atomic_disable callback should disable the bridge. + * + * The bridge can assume that the display pipe (i.e. clocks and timing + * signals) feeding it is still running when this callback is called. + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is disabled via one of: + * + * - &drm_bridge_funcs.disable + * - &drm_bridge_funcs.atomic_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms. + * + * The @atomic_disable callback is optional. + */ + void (*atomic_disable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_post_disable: + * + * The bridge must assume that the display pipe (i.e. clocks and timing + * signals) feeding this bridge is no longer running when the + * @atomic_post_disable is called. + * + * This callback should perform all the actions required by the hardware + * after it has stopped receiving signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is post-disabled (unless marked otherwise by the + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.post_disable + * - &drm_bridge_funcs.atomic_post_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms + * + * The @atomic_post_disable callback is optional. + */ + void (*atomic_post_disable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_duplicate_state: + * + * Duplicate the current bridge state object (which is guaranteed to be + * non-NULL). + * + * The atomic_duplicate_state hook is mandatory if the bridge + * implements any of the atomic hooks, and should be left unassigned + * otherwise. For bridges that don't subclass &drm_bridge_state, the + * drm_atomic_helper_bridge_duplicate_state() helper function shall be + * used to implement this hook. + * + * RETURNS: + * A valid drm_bridge_state object or NULL if the allocation fails. + */ + struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge); + + /** + * @atomic_destroy_state: + * + * Destroy a bridge state object previously allocated by + * &drm_bridge_funcs.atomic_duplicate_state(). + * + * The atomic_destroy_state hook is mandatory if the bridge implements + * any of the atomic hooks, and should be left unassigned otherwise. + * For bridges that don't subclass &drm_bridge_state, the + * drm_atomic_helper_bridge_destroy_state() helper function shall be + * used to implement this hook. + */ + void (*atomic_destroy_state)(struct drm_bridge *bridge, + struct drm_bridge_state *state); + + /** + * @atomic_get_output_bus_fmts: + * + * Return the supported bus formats on the output end of a bridge. + * The returned array must be allocated with kmalloc() and will be + * freed by the caller. If the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). + * + * This method is only called on the last element of the bridge chain + * as part of the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will + * fall back to &drm_connector.display_info.bus_formats[0] if + * &drm_connector.display_info.num_bus_formats > 0, + * or to MEDIA_BUS_FMT_FIXED otherwise. + */ + u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); + + /** + * @atomic_get_input_bus_fmts: + * + * Return the supported bus formats on the input end of a bridge for + * a specific output bus format. + * + * The returned array must be allocated with kmalloc() and will be + * freed by the caller. If the allocation fails, NULL should be + * returned. num_input_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). When the format is not supported NULL should be + * returned and num_input_fmts should be set to 0. + * + * This method is called on all elements of the bridge chain as part of + * the bus format negotiation process that happens in + * drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will bypass + * bus format negotiation on this element of the bridge without + * failing, and the previous element in the chain will be passed + * MEDIA_BUS_FMT_FIXED as its output bus format. + * + * Bridge drivers that need to support being linked to bridges that are + * not supporting bus format negotiation should handle the + * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a + * sensible default value or extracting this information from somewhere + * else (FW property, &drm_display_mode, &drm_display_info, ...) + * + * Note: Even if input format selection on the first bridge has no + * impact on the negotiation process (bus format negotiation stops once + * we reach the first element of the chain), drivers are expected to + * return accurate input formats as the input format may be used to + * configure the CRTC output appropriately. + */ + u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + + /** + * @atomic_check: + * + * This method is responsible for checking bridge state correctness. + * It can also check the state of the surrounding components in chain + * to make sure the whole pipeline can work properly. + * + * &drm_bridge_funcs.atomic_check() hooks are called in reverse + * order (from the last to the first bridge). + * + * This method is optional. &drm_bridge_funcs.mode_fixup() is not + * called when &drm_bridge_funcs.atomic_check() is implemented, so only + * one of them should be provided. + * + * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or + * &drm_bridge_state.output_bus_cfg.flags it should happen in + * this function. By default the &drm_bridge_state.output_bus_cfg.flags + * field is set to the next bridge + * &drm_bridge_state.input_bus_cfg.flags value or + * &drm_connector.display_info.bus_flags if the bridge is the last + * element in the chain. + * + * RETURNS: + * zero if the check passed, a negative error code otherwise. + */ + int (*atomic_check)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); + + /** + * @atomic_reset: + * + * Reset the bridge to a predefined state (or retrieve its current + * state) and return a &drm_bridge_state object matching this state. + * This function is called at attach time. + * + * The atomic_reset hook is mandatory if the bridge implements any of + * the atomic hooks, and should be left unassigned otherwise. For + * bridges that don't subclass &drm_bridge_state, the + * drm_atomic_helper_bridge_reset() helper function shall be used to + * implement this hook. + * + * Note that the atomic_reset() semantics is not exactly matching the + * reset() semantics found on other components (connector, plane, ...). + * + * 1. The reset operation happens when the bridge is attached, not when + * drm_mode_config_reset() is called + * 2. It's meant to be used exclusively on bridges that have been + * converted to the ATOMIC API + * + * RETURNS: + * A valid drm_bridge_state object in case of success, an ERR_PTR() + * giving the reason of the failure otherwise. + */ + struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); + + /** + * @detect: + * + * Check if anything is attached to the bridge output. + * + * This callback is optional, if not implemented the bridge will be + * considered as always having a component attached to its output. + * Bridges that implement this callback shall set the + * DRM_BRIDGE_OP_DETECT flag in their &drm_bridge->ops. + * + * RETURNS: + * + * drm_connector_status indicating the bridge output status. + */ + enum drm_connector_status (*detect)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @get_modes: + * + * Fill all modes currently valid for the sink into the &drm_connector + * with drm_mode_probed_add(). + * + * The @get_modes callback is mostly intended to support non-probeable + * displays such as many fixed panels. Bridges that support reading + * EDID shall leave @get_modes unimplemented and implement the + * &drm_bridge_funcs->edid_read callback instead. + * + * This callback is optional. Bridges that implement it shall set the + * DRM_BRIDGE_OP_MODES flag in their &drm_bridge->ops. + * + * The connector parameter shall be used for the sole purpose of + * filling modes, and shall not be stored internally by bridge drivers + * for future usage. + * + * RETURNS: + * + * The number of modes added by calling drm_mode_probed_add(). + */ + int (*get_modes)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @edid_read: + * + * Read the EDID data of the connected display. + * + * The @edid_read callback is the preferred way of reporting mode + * information for a display connected to the bridge output. Bridges + * that support reading EDID shall implement this callback and leave + * the @get_modes callback unimplemented. + * + * The caller of this operation shall first verify the output + * connection status and refrain from reading EDID from a disconnected + * output. + * + * This callback is optional. Bridges that implement it shall set the + * DRM_BRIDGE_OP_EDID flag in their &drm_bridge->ops. + * + * The connector parameter shall be used for the sole purpose of EDID + * retrieval, and shall not be stored internally by bridge drivers for + * future usage. + * + * RETURNS: + * + * An edid structure newly allocated with drm_edid_alloc() or returned + * from drm_edid_read() family of functions on success, or NULL + * otherwise. The caller is responsible for freeing the returned edid + * structure with drm_edid_free(). + */ + const struct drm_edid *(*edid_read)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @hpd_notify: + * + * Notify the bridge of hot plug detection. + * + * This callback is optional, it may be implemented by bridges that + * need to be notified of display connection or disconnection for + * internal reasons. One use case is to reset the internal state of CEC + * controllers for HDMI bridges. + */ + void (*hpd_notify)(struct drm_bridge *bridge, + enum drm_connector_status status); + + /** + * @hpd_enable: + * + * Enable hot plug detection. From now on the bridge shall call + * drm_bridge_hpd_notify() each time a change is detected in the output + * connection status, until hot plug detection gets disabled with + * @hpd_disable. + * + * This callback is optional and shall only be implemented by bridges + * that support hot-plug notification without polling. Bridges that + * implement it shall also implement the @hpd_disable callback and set + * the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops. + */ + void (*hpd_enable)(struct drm_bridge *bridge); + + /** + * @hpd_disable: + * + * Disable hot plug detection. Once this function returns the bridge + * shall not call drm_bridge_hpd_notify() when a change in the output + * connection status occurs. + * + * This callback is optional and shall only be implemented by bridges + * that support hot-plug notification without polling. Bridges that + * implement it shall also implement the @hpd_enable callback and set + * the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops. + */ + void (*hpd_disable)(struct drm_bridge *bridge); + + /** + * @hdmi_tmds_char_rate_valid: + * + * Check whether a particular TMDS character rate is supported by the + * driver. + * + * This callback is optional and should only be implemented by the + * bridges that take part in the HDMI connector implementation. Bridges + * that implement it shall set the DRM_BRIDGE_OP_HDMI flag in their + * &drm_bridge->ops. + * + * Returns: + * + * Either &drm_mode_status.MODE_OK or one of the failure reasons + * in &enum drm_mode_status. + */ + enum drm_mode_status + (*hdmi_tmds_char_rate_valid)(const struct drm_bridge *bridge, + const struct drm_display_mode *mode, + unsigned long long tmds_rate); + + /** + * @hdmi_clear_infoframe: + * + * This callback clears the infoframes in the hardware during commit. + * It will be called multiple times, once for every disabled infoframe + * type. + * + * This callback is optional but it must be implemented by bridges that + * set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops. + */ + int (*hdmi_clear_infoframe)(struct drm_bridge *bridge, + enum hdmi_infoframe_type type); + /** + * @hdmi_write_infoframe: + * + * Program the infoframe into the hardware. It will be called multiple + * times, once for every updated infoframe type. + * + * This callback is optional but it must be implemented by bridges that + * set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops. + */ + int (*hdmi_write_infoframe)(struct drm_bridge *bridge, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len); + + /** + * @hdmi_audio_startup: + * + * Called when ASoC starts an audio stream setup. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_audio_startup)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @hdmi_audio_prepare: + * Configures HDMI-encoder for audio stream. Can be called multiple + * times for each setup. + * + * This callback is optional but it must be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_audio_prepare)(struct drm_bridge *bridge, + struct drm_connector *connector, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); + + /** + * @hdmi_audio_shutdown: + * + * Shut down the audio stream. + * + * This callback is optional but it must be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + void (*hdmi_audio_shutdown)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @hdmi_audio_mute_stream: + * + * Mute/unmute HDMI audio stream. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_audio_mute_stream)(struct drm_bridge *bridge, + struct drm_connector *connector, + bool enable, int direction); + + /** + * @hdmi_cec_init: + * + * Initialize CEC part of the bridge. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER flag in their + * &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_cec_init)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @hdmi_cec_enable: + * + * Enable or disable the CEC adapter inside the bridge. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER flag in their + * &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_cec_enable)(struct drm_bridge *bridge, bool enable); + + /** + * @hdmi_cec_log_addr: + * + * Set the logical address of the CEC adapter inside the bridge. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER flag in their + * &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_cec_log_addr)(struct drm_bridge *bridge, u8 logical_addr); + + /** + * @hdmi_cec_transmit: + * + * Transmit the message using the CEC adapter inside the bridge. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER flag in their + * &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*hdmi_cec_transmit)(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + + /** + * @dp_audio_startup: + * + * Called when ASoC starts a DisplayPort audio stream setup. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_DP_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*dp_audio_startup)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @dp_audio_prepare: + * Configures DisplayPort audio stream. Can be called multiple + * times for each setup. + * + * This callback is optional but it must be implemented by bridges that + * set the @DRM_BRIDGE_OP_DP_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*dp_audio_prepare)(struct drm_bridge *bridge, + struct drm_connector *connector, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); + + /** + * @dp_audio_shutdown: + * + * Shut down the DisplayPort audio stream. + * + * This callback is optional but it must be implemented by bridges that + * set the @DRM_BRIDGE_OP_DP_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + void (*dp_audio_shutdown)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @dp_audio_mute_stream: + * + * Mute/unmute DisplayPort audio stream. + * + * This callback is optional, it can be implemented by bridges that + * set the @DRM_BRIDGE_OP_DP_AUDIO flag in their &drm_bridge->ops. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*dp_audio_mute_stream)(struct drm_bridge *bridge, + struct drm_connector *connector, + bool enable, int direction); + + /** + * @debugfs_init: + * + * Allows bridges to create bridge-specific debugfs files. + */ + void (*debugfs_init)(struct drm_bridge *bridge, struct dentry *root); }; /** @@ -244,14 +997,13 @@ struct drm_bridge_funcs { */ struct drm_bridge_timings { /** - * @sampling_edge: + * @input_bus_flags: * - * Tells whether the bridge samples the digital input signal - * from the display engine on the positive or negative edge of the - * clock, this should reuse the DRM_BUS_FLAG_PIXDATA_[POS|NEG]EDGE - * bitwise flags from the DRM connector (bit 2 and 3 valid). + * Tells what additional settings for the pixel data on the bus + * this bridge requires (like pixel signal polarity). See also + * &drm_display_info->bus_flags. */ - u32 sampling_edge; + u32 input_bus_flags; /** * @setup_time_ps: * @@ -266,22 +1018,109 @@ struct drm_bridge_timings { * input signal after the clock edge. */ u32 hold_time_ps; + /** + * @dual_link: + * + * True if the bus operates in dual-link mode. The exact meaning is + * dependent on the bus type. For LVDS buses, this indicates that even- + * and odd-numbered pixels are received on separate links. + */ + bool dual_link; +}; + +/** + * enum drm_bridge_ops - Bitmask of operations supported by the bridge + */ +enum drm_bridge_ops { + /** + * @DRM_BRIDGE_OP_DETECT: The bridge can detect displays connected to + * its output. Bridges that set this flag shall implement the + * &drm_bridge_funcs->detect callback. + */ + DRM_BRIDGE_OP_DETECT = BIT(0), + /** + * @DRM_BRIDGE_OP_EDID: The bridge can retrieve the EDID of the display + * connected to its output. Bridges that set this flag shall implement + * the &drm_bridge_funcs->edid_read callback. + */ + DRM_BRIDGE_OP_EDID = BIT(1), + /** + * @DRM_BRIDGE_OP_HPD: The bridge can detect hot-plug and hot-unplug + * without requiring polling. Bridges that set this flag shall + * implement the &drm_bridge_funcs->hpd_enable and + * &drm_bridge_funcs->hpd_disable callbacks if they support enabling + * and disabling hot-plug detection dynamically. + */ + DRM_BRIDGE_OP_HPD = BIT(2), + /** + * @DRM_BRIDGE_OP_MODES: The bridge can retrieve the modes supported + * by the display at its output. This does not include reading EDID + * which is separately covered by @DRM_BRIDGE_OP_EDID. Bridges that set + * this flag shall implement the &drm_bridge_funcs->get_modes callback. + */ + DRM_BRIDGE_OP_MODES = BIT(3), + /** + * @DRM_BRIDGE_OP_HDMI: The bridge provides HDMI connector operations, + * including infoframes support. Bridges that set this flag must + * implement the &drm_bridge_funcs->write_infoframe callback. + * + * Note: currently there can be at most one bridge in a chain that sets + * this bit. This is to simplify corresponding glue code in connector + * drivers. + */ + DRM_BRIDGE_OP_HDMI = BIT(4), + /** + * @DRM_BRIDGE_OP_HDMI_AUDIO: The bridge provides HDMI audio operations. + * Bridges that set this flag must implement the + * &drm_bridge_funcs->hdmi_audio_prepare and + * &drm_bridge_funcs->hdmi_audio_shutdown callbacks. + * + * Note: currently there can be at most one bridge in a chain that sets + * this bit. This is to simplify corresponding glue code in connector + * drivers. Also it is not possible to have a bridge in the chain that + * sets @DRM_BRIDGE_OP_DP_AUDIO if there is a bridge that sets this + * flag. + */ + DRM_BRIDGE_OP_HDMI_AUDIO = BIT(5), + /** + * @DRM_BRIDGE_OP_DP_AUDIO: The bridge provides DisplayPort audio operations. + * Bridges that set this flag must implement the + * &drm_bridge_funcs->dp_audio_prepare and + * &drm_bridge_funcs->dp_audio_shutdown callbacks. + * + * Note: currently there can be at most one bridge in a chain that sets + * this bit. This is to simplify corresponding glue code in connector + * drivers. Also it is not possible to have a bridge in the chain that + * sets @DRM_BRIDGE_OP_HDMI_AUDIO if there is a bridge that sets this + * flag. + */ + DRM_BRIDGE_OP_DP_AUDIO = BIT(6), + /** + * @DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER: The bridge requires CEC notifier + * to be present. + */ + DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER = BIT(7), + /** + * @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER: The bridge requires CEC adapter + * to be present. + */ + DRM_BRIDGE_OP_HDMI_CEC_ADAPTER = BIT(8), }; /** * struct drm_bridge - central DRM bridge control structure */ struct drm_bridge { + /** @base: inherit from &drm_private_object */ + struct drm_private_obj base; /** @dev: DRM device this bridge belongs to */ struct drm_device *dev; /** @encoder: encoder to which this bridge is connected */ struct drm_encoder *encoder; - /** @next: the next bridge in the encoder chain */ - struct drm_bridge *next; -#ifdef CONFIG_OF + /** @chain_node: used to form a bridge chain */ + struct list_head chain_node; /** @of_node: device node pointer to the bridge */ struct device_node *of_node; -#endif /** @list: to keep track of all added bridges */ struct list_head list; /** @@ -292,36 +1131,469 @@ struct drm_bridge { const struct drm_bridge_timings *timings; /** @funcs: control functions */ const struct drm_bridge_funcs *funcs; + + /** + * @container: Pointer to the private driver struct embedding this + * @struct drm_bridge. + */ + void *container; + + /** + * @refcount: reference count of users referencing this bridge. + */ + struct kref refcount; + /** @driver_private: pointer to the bridge driver's internal context */ void *driver_private; + /** @ops: bitmask of operations supported by the bridge */ + enum drm_bridge_ops ops; + /** + * @type: Type of the connection at the bridge output + * (DRM_MODE_CONNECTOR_*). For bridges at the end of this chain this + * identifies the type of connected display. + */ + int type; + /** + * @interlace_allowed: Indicate that the bridge can handle interlaced + * modes. + */ + bool interlace_allowed; + /** + * @ycbcr_420_allowed: Indicate that the bridge can handle YCbCr 420 + * output. + */ + bool ycbcr_420_allowed; + /** + * @pre_enable_prev_first: The bridge requires that the prev + * bridge @pre_enable function is called before its @pre_enable, + * and conversely for post_disable. This is most frequently a + * requirement for DSI devices which need the host to be initialised + * before the peripheral. + */ + bool pre_enable_prev_first; + /** + * @support_hdcp: Indicate that the bridge supports HDCP. + */ + bool support_hdcp; + /** + * @ddc: Associated I2C adapter for DDC access, if any. + */ + struct i2c_adapter *ddc; + + /** + * @vendor: Vendor of the product to be used for the SPD InfoFrame + * generation. This is required if @DRM_BRIDGE_OP_HDMI is set. + */ + const char *vendor; + + /** + * @product: Name of the product to be used for the SPD InfoFrame + * generation. This is required if @DRM_BRIDGE_OP_HDMI is set. + */ + const char *product; + + /** + * @supported_formats: Bitmask of @hdmi_colorspace listing supported + * output formats. This is only relevant if @DRM_BRIDGE_OP_HDMI is set. + */ + unsigned int supported_formats; + + /** + * @max_bpc: Maximum bits per char the HDMI bridge supports. Allowed + * values are 8, 10 and 12. This is only relevant if + * @DRM_BRIDGE_OP_HDMI is set. + */ + unsigned int max_bpc; + + /** + * @hdmi_cec_dev: device to be used as a containing device for CEC + * functions. + */ + struct device *hdmi_cec_dev; + + /** + * @hdmi_audio_dev: device to be used as a parent for the HDMI Codec if + * either of @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO is set. + */ + struct device *hdmi_audio_dev; + + /** + * @hdmi_audio_max_i2s_playback_channels: maximum number of playback + * I2S channels for the @DRM_BRIDGE_OP_HDMI_AUDIO or + * @DRM_BRIDGE_OP_DP_AUDIO. + */ + int hdmi_audio_max_i2s_playback_channels; + + /** + * @hdmi_audio_i2s_formats: supported I2S formats, optional. The + * default is to allow all formats supported by the corresponding I2S + * bus driver. This is only used for bridges setting + * @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO. + */ + u64 hdmi_audio_i2s_formats; + + /** + * @hdmi_audio_spdif_playback: set if this bridge has S/PDIF playback + * port for @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO. + */ + unsigned int hdmi_audio_spdif_playback : 1; + + /** + * @hdmi_audio_dai_port: sound DAI port for either of + * @DRM_BRIDGE_OP_HDMI_AUDIO and @DRM_BRIDGE_OP_DP_AUDIO, -1 if it is + * not used. + */ + int hdmi_audio_dai_port; + + /** + * @hdmi_cec_adapter_name: the name of the adapter to register + */ + const char *hdmi_cec_adapter_name; + + /** + * @hdmi_cec_available_las: number of logical addresses, CEC_MAX_LOG_ADDRS if unset + */ + u8 hdmi_cec_available_las; + + /** private: */ + /** + * @hpd_mutex: Protects the @hpd_cb and @hpd_data fields. + */ + struct mutex hpd_mutex; + /** + * @hpd_cb: Hot plug detection callback, registered with + * drm_bridge_hpd_enable(). + */ + void (*hpd_cb)(void *data, enum drm_connector_status status); + /** + * @hpd_data: Private data passed to the Hot plug detection callback + * @hpd_cb. + */ + void *hpd_data; }; +static inline struct drm_bridge * +drm_priv_to_bridge(struct drm_private_obj *priv) +{ + return container_of(priv, struct drm_bridge, base); +} + +struct drm_bridge *drm_bridge_get(struct drm_bridge *bridge); +void drm_bridge_put(struct drm_bridge *bridge); + +/* Cleanup action for use with __free() */ +DEFINE_FREE(drm_bridge_put, struct drm_bridge *, if (_T) drm_bridge_put(_T)) + +void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset, + const struct drm_bridge_funcs *funcs); + +/** + * devm_drm_bridge_alloc - Allocate and initialize a bridge + * @dev: struct device of the bridge device + * @type: the type of the struct which contains struct &drm_bridge + * @member: the name of the &drm_bridge within @type + * @funcs: callbacks for this bridge + * + * The reference count of the returned bridge is initialized to 1. This + * reference will be automatically dropped via devm (by calling + * drm_bridge_put()) when @dev is removed. + * + * Returns: + * Pointer to new bridge, or ERR_PTR on failure. + */ +#define devm_drm_bridge_alloc(dev, type, member, funcs) \ + ((type *)__devm_drm_bridge_alloc(dev, sizeof(type), \ + offsetof(type, member), funcs)) + void drm_bridge_add(struct drm_bridge *bridge); +int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); -struct drm_bridge *of_drm_find_bridge(struct device_node *np); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, - struct drm_bridge *previous); + struct drm_bridge *previous, + enum drm_bridge_attach_flags flags); -bool drm_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_mode *mode); -void drm_bridge_disable(struct drm_bridge *bridge); -void drm_bridge_post_disable(struct drm_bridge *bridge); -void drm_bridge_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -void drm_bridge_pre_enable(struct drm_bridge *bridge); -void drm_bridge_enable(struct drm_bridge *bridge); +#ifdef CONFIG_OF +struct drm_bridge *of_drm_find_bridge(struct device_node *np); +#else +static inline struct drm_bridge *of_drm_find_bridge(struct device_node *np) +{ + return NULL; +} +#endif + +static inline bool drm_bridge_is_last(struct drm_bridge *bridge) +{ + return list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain); +} + +/** + * drm_bridge_get_current_state() - Get the current bridge state + * @bridge: bridge object + * + * This function must be called with the modeset lock held. + * + * RETURNS: + * + * The current bridge state, or NULL if there is none. + */ +static inline struct drm_bridge_state * +drm_bridge_get_current_state(struct drm_bridge *bridge) +{ + if (!bridge) + return NULL; + + /* + * Only atomic bridges will have bridge->base initialized by + * drm_atomic_private_obj_init(), so we need to make sure we're + * working with one before we try to use the lock. + */ + if (!bridge->funcs || !bridge->funcs->atomic_reset) + return NULL; + + drm_modeset_lock_assert_held(&bridge->base.lock); + + if (!bridge->base.state) + return NULL; + + return drm_priv_to_bridge_state(bridge->base.state); +} + +/** + * drm_bridge_get_next_bridge() - Get the next bridge in the chain + * @bridge: bridge object + * + * The caller is responsible of having a reference to @bridge via + * drm_bridge_get() or equivalent. This function leaves the refcount of + * @bridge unmodified. + * + * The refcount of the returned bridge is incremented. Use drm_bridge_put() + * when done with it. + * + * RETURNS: + * the next bridge in the chain after @bridge, or NULL if @bridge is the last. + */ +static inline struct drm_bridge * +drm_bridge_get_next_bridge(struct drm_bridge *bridge) +{ + if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain)) + return NULL; + + return drm_bridge_get(list_next_entry(bridge, chain_node)); +} + +/** + * drm_bridge_get_prev_bridge() - Get the previous bridge in the chain + * @bridge: bridge object + * + * The caller is responsible of having a reference to @bridge via + * drm_bridge_get() or equivalent. This function leaves the refcount of + * @bridge unmodified. + * + * The refcount of the returned bridge is incremented. Use drm_bridge_put() + * when done with it. + * + * RETURNS: + * the previous bridge in the chain, or NULL if @bridge is the first. + */ +static inline struct drm_bridge * +drm_bridge_get_prev_bridge(struct drm_bridge *bridge) +{ + if (list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) + return NULL; + + return drm_bridge_get(list_prev_entry(bridge, chain_node)); +} + +/** + * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain + * @encoder: encoder object + * + * The refcount of the returned bridge is incremented. Use drm_bridge_put() + * when done with it. + * + * RETURNS: + * the first bridge in the chain, or NULL if @encoder has no bridge attached + * to it. + */ +static inline struct drm_bridge * +drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder) +{ + return drm_bridge_get(list_first_entry_or_null(&encoder->bridge_chain, + struct drm_bridge, chain_node)); +} + +/** + * drm_bridge_chain_get_last_bridge() - Get the last bridge in the chain + * @encoder: encoder object + * + * The refcount of the returned bridge is incremented. Use drm_bridge_put() + * when done with it. + * + * RETURNS: + * the last bridge in the chain, or NULL if @encoder has no bridge attached + * to it. + */ +static inline struct drm_bridge * +drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder) +{ + return drm_bridge_get(list_last_entry_or_null(&encoder->bridge_chain, + struct drm_bridge, chain_node)); +} + +/** + * drm_bridge_get_next_bridge_and_put - Get the next bridge in the chain + * and put the previous + * @bridge: bridge object + * + * Same as drm_bridge_get_next_bridge() but additionally puts the @bridge. + * + * RETURNS: + * the next bridge in the chain after @bridge, or NULL if @bridge is the last. + */ +static inline struct drm_bridge * +drm_bridge_get_next_bridge_and_put(struct drm_bridge *bridge) +{ + struct drm_bridge *next = drm_bridge_get_next_bridge(bridge); + + drm_bridge_put(bridge); + + return next; +} + +/** + * drm_for_each_bridge_in_chain_scoped - iterate over all bridges attached + * to an encoder + * @encoder: the encoder to iterate bridges on + * @bridge: a bridge pointer updated to point to the current bridge at each + * iteration + * + * Iterate over all bridges present in the bridge chain attached to @encoder. + * + * Automatically gets/puts the bridge reference while iterating, and puts + * the reference even if returning or breaking in the middle of the loop. + */ +#define drm_for_each_bridge_in_chain_scoped(encoder, bridge) \ + for (struct drm_bridge *bridge __free(drm_bridge_put) = \ + drm_bridge_chain_get_first_bridge(encoder); \ + bridge; \ + bridge = drm_bridge_get_next_bridge_and_put(bridge)) + +/** + * drm_for_each_bridge_in_chain_from - iterate over all bridges starting + * from the given bridge + * @first_bridge: the bridge to start from + * @bridge: a bridge pointer updated to point to the current bridge at each + * iteration + * + * Iterate over all bridges in the encoder chain starting from + * @first_bridge, included. + * + * Automatically gets/puts the bridge reference while iterating, and puts + * the reference even if returning or breaking in the middle of the loop. + */ +#define drm_for_each_bridge_in_chain_from(first_bridge, bridge) \ + for (struct drm_bridge *bridge __free(drm_bridge_put) = \ + drm_bridge_get(first_bridge); \ + bridge; \ + bridge = drm_bridge_get_next_bridge_and_put(bridge)) + +enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode); +void drm_bridge_chain_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode); + +int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); +void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); + +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + +enum drm_connector_status +drm_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector); +int drm_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector); +const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector); +void drm_bridge_hpd_enable(struct drm_bridge *bridge, + void (*cb)(void *data, + enum drm_connector_status status), + void *data); +void drm_bridge_hpd_disable(struct drm_bridge *bridge); +void drm_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status); #ifdef CONFIG_DRM_PANEL_BRIDGE -struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, - u32 connector_type); +bool drm_bridge_is_panel(const struct drm_bridge *bridge); +struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel); +struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, + u32 connector_type); void drm_panel_bridge_remove(struct drm_bridge *bridge); +int drm_panel_bridge_set_orientation(struct drm_connector *connector, + struct drm_bridge *bridge); struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, - struct drm_panel *panel, - u32 connector_type); + struct drm_panel *panel); +struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, + struct drm_panel *panel, + u32 connector_type); +struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, + struct drm_panel *panel); +struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); +#else +static inline bool drm_bridge_is_panel(const struct drm_bridge *bridge) +{ + return false; +} + +static inline int drm_panel_bridge_set_orientation(struct drm_connector *connector, + struct drm_bridge *bridge) +{ + return -EINVAL; +} #endif +#if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL_BRIDGE) +struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node, + u32 port, u32 endpoint); +struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, + u32 port, u32 endpoint); +#else +static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, + struct device_node *node, + u32 port, + u32 endpoint) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, + struct device_node *node, + u32 port, + u32 endpoint) +{ + return ERR_PTR(-ENODEV); +} +#endif + +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge); + +void drm_bridge_debugfs_params(struct dentry *root); +void drm_bridge_debugfs_encoder_params(struct dentry *root, struct drm_encoder *encoder); + #endif diff --git a/include/drm/drm_bridge_connector.h b/include/drm/drm_bridge_connector.h new file mode 100644 index 000000000000..69630815fb09 --- /dev/null +++ b/include/drm/drm_bridge_connector.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#ifndef __DRM_BRIDGE_CONNECTOR_H__ +#define __DRM_BRIDGE_CONNECTOR_H__ + +struct drm_connector; +struct drm_device; +struct drm_encoder; + +struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, + struct drm_encoder *encoder); + +#endif /* __DRM_BRIDGE_CONNECTOR_H__ */ diff --git a/include/drm/drm_bridge_helper.h b/include/drm/drm_bridge_helper.h new file mode 100644 index 000000000000..6c35b479ec2a --- /dev/null +++ b/include/drm/drm_bridge_helper.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef __DRM_BRIDGE_HELPER_H_ +#define __DRM_BRIDGE_HELPER_H_ + +struct drm_bridge; +struct drm_modeset_acquire_ctx; + +int drm_bridge_helper_reset_crtc(struct drm_bridge *bridge, + struct drm_modeset_acquire_ctx *ctx); + +#endif // __DRM_BRIDGE_HELPER_H_ diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h new file mode 100644 index 000000000000..b909fa8f810a --- /dev/null +++ b/include/drm/drm_buddy.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2021 Intel Corporation + */ + +#ifndef __DRM_BUDDY_H__ +#define __DRM_BUDDY_H__ + +#include <linux/bitops.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/rbtree.h> + +struct drm_printer; + +#define DRM_BUDDY_RANGE_ALLOCATION BIT(0) +#define DRM_BUDDY_TOPDOWN_ALLOCATION BIT(1) +#define DRM_BUDDY_CONTIGUOUS_ALLOCATION BIT(2) +#define DRM_BUDDY_CLEAR_ALLOCATION BIT(3) +#define DRM_BUDDY_CLEARED BIT(4) +#define DRM_BUDDY_TRIM_DISABLE BIT(5) + +struct drm_buddy_block { +#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12) +#define DRM_BUDDY_HEADER_STATE GENMASK_ULL(11, 10) +#define DRM_BUDDY_ALLOCATED (1 << 10) +#define DRM_BUDDY_FREE (2 << 10) +#define DRM_BUDDY_SPLIT (3 << 10) +#define DRM_BUDDY_HEADER_CLEAR GENMASK_ULL(9, 9) +/* Free to be used, if needed in the future */ +#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6) +#define DRM_BUDDY_HEADER_ORDER GENMASK_ULL(5, 0) + u64 header; + + struct drm_buddy_block *left; + struct drm_buddy_block *right; + struct drm_buddy_block *parent; + + void *private; /* owned by creator */ + + /* + * While the block is allocated by the user through drm_buddy_alloc*, + * the user has ownership of the link, for example to maintain within + * a list, if so desired. As soon as the block is freed with + * drm_buddy_free* ownership is given back to the mm. + */ + union { + struct rb_node rb; + struct list_head link; + }; + + struct list_head tmp_link; +}; + +/* Order-zero must be at least SZ_4K */ +#define DRM_BUDDY_MAX_ORDER (63 - 12) + +/* + * Binary Buddy System. + * + * Locking should be handled by the user, a simple mutex around + * drm_buddy_alloc* and drm_buddy_free* should suffice. + */ +struct drm_buddy { + /* Maintain a free list for each order. */ + struct rb_root **free_trees; + + /* + * Maintain explicit binary tree(s) to track the allocation of the + * address space. This gives us a simple way of finding a buddy block + * and performing the potentially recursive merge step when freeing a + * block. Nodes are either allocated or free, in which case they will + * also exist on the respective free list. + */ + struct drm_buddy_block **roots; + + /* + * Anything from here is public, and remains static for the lifetime of + * the mm. Everything above is considered do-not-touch. + */ + unsigned int n_roots; + unsigned int max_order; + + /* Must be at least SZ_4K */ + u64 chunk_size; + u64 size; + u64 avail; + u64 clear_avail; +}; + +static inline u64 +drm_buddy_block_offset(const struct drm_buddy_block *block) +{ + return block->header & DRM_BUDDY_HEADER_OFFSET; +} + +static inline unsigned int +drm_buddy_block_order(struct drm_buddy_block *block) +{ + return block->header & DRM_BUDDY_HEADER_ORDER; +} + +static inline unsigned int +drm_buddy_block_state(struct drm_buddy_block *block) +{ + return block->header & DRM_BUDDY_HEADER_STATE; +} + +static inline bool +drm_buddy_block_is_allocated(struct drm_buddy_block *block) +{ + return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED; +} + +static inline bool +drm_buddy_block_is_clear(struct drm_buddy_block *block) +{ + return block->header & DRM_BUDDY_HEADER_CLEAR; +} + +static inline bool +drm_buddy_block_is_free(struct drm_buddy_block *block) +{ + return drm_buddy_block_state(block) == DRM_BUDDY_FREE; +} + +static inline bool +drm_buddy_block_is_split(struct drm_buddy_block *block) +{ + return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT; +} + +static inline u64 +drm_buddy_block_size(struct drm_buddy *mm, + struct drm_buddy_block *block) +{ + return mm->chunk_size << drm_buddy_block_order(block); +} + +int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size); + +void drm_buddy_fini(struct drm_buddy *mm); + +struct drm_buddy_block * +drm_get_buddy(struct drm_buddy_block *block); + +int drm_buddy_alloc_blocks(struct drm_buddy *mm, + u64 start, u64 end, u64 size, + u64 min_page_size, + struct list_head *blocks, + unsigned long flags); + +int drm_buddy_block_trim(struct drm_buddy *mm, + u64 *start, + u64 new_size, + struct list_head *blocks); + +void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear); + +void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block); + +void drm_buddy_free_list(struct drm_buddy *mm, + struct list_head *objects, + unsigned int flags); + +void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p); +void drm_buddy_block_print(struct drm_buddy *mm, + struct drm_buddy_block *block, + struct drm_printer *p); +#endif diff --git a/include/drm/drm_cache.h b/include/drm/drm_cache.h index bfe1639df02d..08e0e3ffad13 100644 --- a/include/drm/drm_cache.h +++ b/include/drm/drm_cache.h @@ -35,21 +35,54 @@ #include <linux/scatterlist.h> +struct iosys_map; + void drm_clflush_pages(struct page *pages[], unsigned long num_pages); void drm_clflush_sg(struct sg_table *st); void drm_clflush_virt_range(void *addr, unsigned long length); -u64 drm_get_max_iomem(void); +bool drm_need_swiotlb(int dma_bits); static inline bool drm_arch_can_wc_memory(void) { #if defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) return false; -#elif defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON3) +#elif defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + return false; +#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64) + /* + * The DRM driver stack is designed to work with cache coherent devices + * only, but permits an optimization to be enabled in some cases, where + * for some buffers, both the CPU and the GPU use uncached mappings, + * removing the need for DMA snooping and allocation in the CPU caches. + * + * The use of uncached GPU mappings relies on the correct implementation + * of the PCIe NoSnoop TLP attribute by the platform, otherwise the GPU + * will use cached mappings nonetheless. On x86 platforms, this does not + * seem to matter, as uncached CPU mappings will snoop the caches in any + * case. However, on ARM and arm64, enabling this optimization on a + * platform where NoSnoop is ignored results in loss of coherency, which + * breaks correct operation of the device. Since we have no way of + * detecting whether NoSnoop works or not, just disable this + * optimization entirely for ARM and arm64. + */ + return false; +#elif defined(CONFIG_LOONGARCH) + /* + * LoongArch maintains cache coherency in hardware, but its WUC attribute + * (Weak-ordered UnCached, which is similar to WC) is out of the scope of + * cache coherency machanism. This means WUC can only used for write-only + * memory regions. + */ return false; #else return true; #endif } +void drm_memcpy_init_early(void); + +void drm_memcpy_from_wc(struct iosys_map *dst, + const struct iosys_map *src, + unsigned long len); #endif diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 971bb7853776..c972a8a3385b 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -1,10 +1,16 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ #ifndef _DRM_CLIENT_H_ #define _DRM_CLIENT_H_ +#include <linux/iosys-map.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> #include <linux/types.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> + struct drm_client_dev; struct drm_device; struct drm_file; @@ -23,10 +29,20 @@ struct drm_client_funcs { struct module *owner; /** + * @free: + * + * Called when the client gets unregistered. Implementations should + * release all client-specific data and free the memory. + * + * This callback is optional. + */ + void (*free)(struct drm_client_dev *client); + + /** * @unregister: * * Called when &drm_device is unregistered. The client should respond by - * releasing it's resources using drm_client_release(). + * releasing its resources using drm_client_release(). * * This callback is optional. */ @@ -39,9 +55,16 @@ struct drm_client_funcs { * returns zero gets the privilege to restore and no more clients are * called. This callback is not called after @unregister has been called. * + * Note that the core does not guarantee exclusion against concurrent + * drm_open(). Clients need to ensure this themselves, for example by + * using drm_master_internal_acquire() and drm_master_internal_release(). + * + * If the caller passes force, the client should ignore any present DRM + * master and restore the display anyway. + * * This callback is optional. */ - int (*restore)(struct drm_client_dev *client); + int (*restore)(struct drm_client_dev *client, bool force); /** * @hotplug: @@ -52,6 +75,24 @@ struct drm_client_funcs { * This callback is optional. */ int (*hotplug)(struct drm_client_dev *client); + + /** + * @suspend: + * + * Called when suspending the device. + * + * This callback is optional. + */ + int (*suspend)(struct drm_client_dev *client); + + /** + * @resume: + * + * Called when resuming the device from suspend. + * + * This callback is optional. + */ + int (*resume)(struct drm_client_dev *client); }; /** @@ -85,16 +126,45 @@ struct drm_client_dev { * @file: DRM file */ struct drm_file *file; + + /** + * @modeset_mutex: Protects @modesets. + */ + struct mutex modeset_mutex; + + /** + * @modesets: CRTC configurations + */ + struct drm_mode_set *modesets; + + /** + * @suspended: + * + * The client has been suspended. + */ + bool suspended; + + /** + * @hotplug_pending: + * + * A hotplug event has been received while the client was suspended. + * Try again on resume. + */ + bool hotplug_pending; + + /** + * @hotplug_failed: + * + * Set by client hotplug helpers if the hotplugging failed + * before. It is usually not tried again. + */ + bool hotplug_failed; }; int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, const char *name, const struct drm_client_funcs *funcs); void drm_client_release(struct drm_client_dev *client); -void drm_client_add(struct drm_client_dev *client); - -void drm_client_dev_unregister(struct drm_device *dev); -void drm_client_dev_hotplug(struct drm_device *dev); -void drm_client_dev_restore(struct drm_device *dev); +void drm_client_register(struct drm_client_dev *client); /** * struct drm_client_buffer - DRM client buffer @@ -106,24 +176,18 @@ struct drm_client_buffer { struct drm_client_dev *client; /** - * @handle: Buffer handle - */ - u32 handle; - - /** - * @pitch: Buffer pitch - */ - u32 pitch; - - /** * @gem: GEM object backing this buffer + * + * FIXME: The DRM framebuffer holds a reference on its GEM + * buffer objects. Do not use this field in new code and + * update existing users. */ struct drm_gem_object *gem; /** - * @vaddr: Virtual address for the buffer + * @map: Virtual address for the buffer */ - void *vaddr; + struct iosys_map map; /** * @fb: DRM framebuffer @@ -132,9 +196,47 @@ struct drm_client_buffer { }; struct drm_client_buffer * -drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format); -void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); +drm_client_buffer_create_dumb(struct drm_client_dev *client, u32 width, u32 height, u32 format); +void drm_client_buffer_delete(struct drm_client_buffer *buffer); +int drm_client_buffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect); +int drm_client_buffer_vmap_local(struct drm_client_buffer *buffer, + struct iosys_map *map_copy); +void drm_client_buffer_vunmap_local(struct drm_client_buffer *buffer); +int drm_client_buffer_vmap(struct drm_client_buffer *buffer, + struct iosys_map *map); +void drm_client_buffer_vunmap(struct drm_client_buffer *buffer); + +int drm_client_modeset_create(struct drm_client_dev *client); +void drm_client_modeset_free(struct drm_client_dev *client); +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height); +bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation); +int drm_client_modeset_check(struct drm_client_dev *client); +int drm_client_modeset_commit_locked(struct drm_client_dev *client); +int drm_client_modeset_commit(struct drm_client_dev *client); +int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); +int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned int crtc_index); + +/** + * drm_client_for_each_modeset() - Iterate over client modesets + * @modeset: &drm_mode_set loop cursor + * @client: DRM client + */ +#define drm_client_for_each_modeset(modeset, client) \ + for (({ lockdep_assert_held(&(client)->modeset_mutex); }), \ + modeset = (client)->modesets; modeset->crtc; modeset++) -int drm_client_debugfs_init(struct drm_minor *minor); +/** + * drm_client_for_each_connector_iter - connector_list iterator macro + * @connector: &struct drm_connector pointer used as cursor + * @iter: &struct drm_connector_list_iter + * + * This iterates the connectors that are useable for internal clients (excludes + * writeback connectors). + * + * For more info see drm_for_each_connector_iter(). + */ +#define drm_client_for_each_connector_iter(connector, iter) \ + drm_for_each_connector_iter(connector, iter) \ + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) #endif diff --git a/include/drm/drm_client_event.h b/include/drm/drm_client_event.h new file mode 100644 index 000000000000..79369c755bc9 --- /dev/null +++ b/include/drm/drm_client_event.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ + +#ifndef _DRM_CLIENT_EVENT_H_ +#define _DRM_CLIENT_EVENT_H_ + +#include <linux/types.h> + +struct drm_device; + +#if defined(CONFIG_DRM_CLIENT) +void drm_client_dev_unregister(struct drm_device *dev); +void drm_client_dev_hotplug(struct drm_device *dev); +void drm_client_dev_restore(struct drm_device *dev, bool force); +void drm_client_dev_suspend(struct drm_device *dev); +void drm_client_dev_resume(struct drm_device *dev); +#else +static inline void drm_client_dev_unregister(struct drm_device *dev) +{ } +static inline void drm_client_dev_hotplug(struct drm_device *dev) +{ } +static inline void drm_client_dev_restore(struct drm_device *dev, bool force) +{ } +static inline void drm_client_dev_suspend(struct drm_device *dev) +{ } +static inline void drm_client_dev_resume(struct drm_device *dev) +{ } +#endif + +#endif diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h index 90ef9996d9a4..5140691f476a 100644 --- a/include/drm/drm_color_mgmt.h +++ b/include/drm/drm_color_mgmt.h @@ -24,12 +24,49 @@ #define __DRM_COLOR_MGMT_H__ #include <linux/ctype.h> +#include <linux/math64.h> #include <drm/drm_property.h> struct drm_crtc; struct drm_plane; -uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision); +/** + * drm_color_lut_extract - clamp and round LUT entries + * @user_input: input value + * @bit_precision: number of bits the hw LUT supports + * + * Extract a degamma/gamma LUT value provided by user (in the form of + * &drm_color_lut entries) and round it to the precision supported by the + * hardware, following OpenGL int<->float conversion rules + * (see eg. OpenGL 4.6 specification - 2.3.5 Fixed-Point Data Conversions). + */ +static inline u32 drm_color_lut_extract(u32 user_input, int bit_precision) +{ + if (bit_precision > 16) + return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(user_input, (1 << bit_precision) - 1), + (1 << 16) - 1); + else + return DIV_ROUND_CLOSEST(user_input * ((1 << bit_precision) - 1), + (1 << 16) - 1); +} + +/** + * drm_color_lut32_extract - clamp and round LUT entries + * @user_input: input value + * @bit_precision: number of bits the hw LUT supports + * + * Extract U0.bit_precision from a U0.32 LUT value. + * + */ +static inline u32 drm_color_lut32_extract(u32 user_input, int bit_precision) +{ + u64 max = (bit_precision >= 64) ? ~0ULL : (1ULL << bit_precision) - 1; + + return DIV_ROUND_CLOSEST_ULL((u64)user_input * max, + (1ULL << 32) - 1); +} + +u64 drm_color_ctm_s31_32_to_qm_n(u64 user_input, u32 m, u32 n); void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, uint degamma_lut_size, @@ -51,6 +88,18 @@ static inline int drm_color_lut_size(const struct drm_property_blob *blob) return blob->length / sizeof(struct drm_color_lut); } +/** + * drm_color_lut32_size - calculate the number of entries in the extended LUT + * @blob: blob containing the LUT + * + * Returns: + * The number of entries in the color LUT stored in @blob. + */ +static inline int drm_color_lut32_size(const struct drm_property_blob *blob) +{ + return blob->length / sizeof(struct drm_color_lut32); +} + enum drm_color_encoding { DRM_COLOR_YCBCR_BT601, DRM_COLOR_YCBCR_BT709, @@ -69,4 +118,61 @@ int drm_plane_create_color_properties(struct drm_plane *plane, u32 supported_ranges, enum drm_color_encoding default_encoding, enum drm_color_range default_range); + +/** + * enum drm_color_lut_tests - hw-specific LUT tests to perform + * + * The drm_color_lut_check() function takes a bitmask of the values here to + * determine which tests to apply to a userspace-provided LUT. + */ +enum drm_color_lut_tests { + /** + * @DRM_COLOR_LUT_EQUAL_CHANNELS: + * + * Checks whether the entries of a LUT all have equal values for the + * red, green, and blue channels. Intended for hardware that only + * accepts a single value per LUT entry and assumes that value applies + * to all three color components. + */ + DRM_COLOR_LUT_EQUAL_CHANNELS = BIT(0), + + /** + * @DRM_COLOR_LUT_NON_DECREASING: + * + * Checks whether the entries of a LUT are always flat or increasing + * (never decreasing). + */ + DRM_COLOR_LUT_NON_DECREASING = BIT(1), +}; + +int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests); + +/* + * Gamma-LUT programming + */ + +typedef void (*drm_crtc_set_lut_func)(struct drm_crtc *, unsigned int, u16, u16, u16); + +void drm_crtc_load_gamma_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma); +void drm_crtc_load_gamma_565_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma); +void drm_crtc_load_gamma_555_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma); + +void drm_crtc_fill_gamma_888(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma); +void drm_crtc_fill_gamma_565(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma); +void drm_crtc_fill_gamma_555(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma); + +/* + * Color-LUT programming + */ + +void drm_crtc_load_palette_8(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_palette); + +void drm_crtc_fill_palette_332(struct drm_crtc *crtc, drm_crtc_set_lut_func set_palette); +void drm_crtc_fill_palette_8(struct drm_crtc *crtc, drm_crtc_set_lut_func set_palette); + +int drm_color_lut32_check(const struct drm_property_blob *lut, u32 tests); #endif diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h new file mode 100644 index 000000000000..a3a32f9f918c --- /dev/null +++ b/include/drm/drm_colorop.h @@ -0,0 +1,464 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DRM_COLOROP_H__ +#define __DRM_COLOROP_H__ + +#include <drm/drm_mode_object.h> +#include <drm/drm_mode.h> +#include <drm/drm_property.h> + +/* DRM colorop flags */ +#define DRM_COLOROP_FLAG_ALLOW_BYPASS (1<<0) /* Allow bypass on the drm_colorop */ + +/** + * enum drm_colorop_curve_1d_type - type of 1D curve + * + * Describes a 1D curve to be applied by the DRM_COLOROP_1D_CURVE colorop. + */ +enum drm_colorop_curve_1d_type { + /** + * @DRM_COLOROP_1D_CURVE_SRGB_EOTF: + * + * enum string "sRGB EOTF" + * + * sRGB piece-wise electro-optical transfer function. Transfer + * characteristics as defined by IEC 61966-2-1 sRGB. Equivalent + * to H.273 TransferCharacteristics code point 13 with + * MatrixCoefficients set to 0. + */ + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + + /** + * @DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: + * + * enum string "sRGB Inverse EOTF" + * + * The inverse of &DRM_COLOROP_1D_CURVE_SRGB_EOTF + */ + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + + /** + * @DRM_COLOROP_1D_CURVE_PQ_125_EOTF: + * + * enum string "PQ 125 EOTF" + * + * The PQ transfer function, scaled by 125.0f, so that 10,000 + * nits correspond to 125.0f. + * + * Transfer characteristics of the PQ function as defined by + * SMPTE ST 2084 (2014) for 10-, 12-, 14-, and 16-bit systems + * and Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system, + * represented by H.273 TransferCharacteristics code point 16. + */ + DRM_COLOROP_1D_CURVE_PQ_125_EOTF, + + /** + * @DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: + * + * enum string "PQ 125 Inverse EOTF" + * + * The inverse of DRM_COLOROP_1D_CURVE_PQ_125_EOTF. + */ + DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF, + + /** + * @DRM_COLOROP_1D_CURVE_BT2020_INV_OETF: + * + * enum string "BT.2020 Inverse OETF" + * + * The inverse of &DRM_COLOROP_1D_CURVE_BT2020_OETF + */ + DRM_COLOROP_1D_CURVE_BT2020_INV_OETF, + + /** + * @DRM_COLOROP_1D_CURVE_BT2020_OETF: + * + * enum string "BT.2020 OETF" + * + * The BT.2020/BT.709 transfer function. The BT.709 and BT.2020 + * transfer functions are the same, the only difference is that + * BT.2020 is defined with more precision for 10 and 12-bit + * encodings. + * + * + */ + DRM_COLOROP_1D_CURVE_BT2020_OETF, + + /** + * @DRM_COLOROP_1D_CURVE_GAMMA22: + * + * enum string "Gamma 2.2" + * + * A gamma 2.2 power function. This applies a power curve with + * gamma value of 2.2 to the input values. + */ + DRM_COLOROP_1D_CURVE_GAMMA22, + + /** + * @DRM_COLOROP_1D_CURVE_GAMMA22_INV: + * + * enum string "Gamma 2.2 Inverse" + * + * The inverse of &DRM_COLOROP_1D_CURVE_GAMMA22 + */ + DRM_COLOROP_1D_CURVE_GAMMA22_INV, + /** + * @DRM_COLOROP_1D_CURVE_COUNT: + * + * enum value denoting the size of the enum + */ + DRM_COLOROP_1D_CURVE_COUNT +}; + +/** + * struct drm_colorop_state - mutable colorop state + */ +struct drm_colorop_state { + /** @colorop: backpointer to the colorop */ + struct drm_colorop *colorop; + + /* + * Color properties + * + * The following fields are not always valid, their usage depends + * on the colorop type. See their associated comment for more + * information. + */ + + /** + * @bypass: + * + * When the property BYPASS exists on this colorop, this stores + * the requested bypass state: true if colorop shall be bypassed, + * false if colorop is enabled. + */ + bool bypass; + + /** + * @curve_1d_type: + * + * Type of 1D curve. + */ + enum drm_colorop_curve_1d_type curve_1d_type; + + /** + * @multiplier: + * + * Multiplier to 'gain' the plane. Format is S31.32 sign-magnitude. + */ + uint64_t multiplier; + + /** + * @data: + * + * Data blob for any TYPE that requires such a blob. The + * interpretation of the blob is TYPE-specific. + * + * See the &drm_colorop_type documentation for how blob is laid + * out. + */ + struct drm_property_blob *data; + + /** @state: backpointer to global drm_atomic_state */ + struct drm_atomic_state *state; +}; + +/** + * struct drm_colorop - DRM color operation control structure + * + * A colorop represents one color operation. They can be chained via + * the 'next' pointer to build a color pipeline. + * + * Since colorops cannot stand-alone and are used to describe colorop + * operations on a plane they don't have their own locking mechanism but + * are locked and programmed along with their associated &drm_plane. + * + */ +struct drm_colorop { + /** @dev: parent DRM device */ + struct drm_device *dev; + + /** + * @head: + * + * List of all colorops on @dev, linked from &drm_mode_config.colorop_list. + * Invariant over the lifetime of @dev and therefore does not need + * locking. + */ + struct list_head head; + + /** + * @index: Position inside the mode_config.list, can be used as an array + * index. It is invariant over the lifetime of the colorop. + */ + unsigned int index; + + /** @base: base mode object */ + struct drm_mode_object base; + + /** + * @plane: + * + * The plane on which the colorop sits. A drm_colorop is always unique + * to a plane. + */ + struct drm_plane *plane; + + /** + * @state: + * + * Current atomic state for this colorop. + * + * This is protected by @mutex. Note that nonblocking atomic commits + * access the current colorop state without taking locks. + */ + struct drm_colorop_state *state; + + /* + * Color properties + * + * The following fields are not always valid, their usage depends + * on the colorop type. See their associated comment for more + * information. + */ + + /** @properties: property tracking for this colorop */ + struct drm_object_properties properties; + + /** + * @type: + * + * Read-only + * Type of color operation + */ + enum drm_colorop_type type; + + /** + * @next: + * + * Read-only + * Pointer to next drm_colorop in pipeline + */ + struct drm_colorop *next; + + /** + * @type_property: + * + * Read-only "TYPE" property for specifying the type of + * this color operation. The type is enum drm_colorop_type. + */ + struct drm_property *type_property; + + /** + * @bypass_property: + * + * Boolean property to control enablement of the color + * operation. Only present if DRM_COLOROP_FLAG_ALLOW_BYPASS + * flag is set. When present, setting bypass to "true" shall + * always be supported to allow compositors to quickly fall + * back to alternate methods of color processing. This is + * important since setting color operations can fail due to + * unique HW constraints. + */ + struct drm_property *bypass_property; + + /** + * @size: + * + * Number of entries of the custom LUT. This should be read-only. + */ + uint32_t size; + + /** + * @lut1d_interpolation: + * + * Read-only + * Interpolation for DRM_COLOROP_1D_LUT + */ + enum drm_colorop_lut1d_interpolation_type lut1d_interpolation; + + /** + * @lut3d_interpolation: + * + * Read-only + * Interpolation for DRM_COLOROP_3D_LUT + */ + enum drm_colorop_lut3d_interpolation_type lut3d_interpolation; + + /** + * @lut1d_interpolation_property: + * + * Read-only property for DRM_COLOROP_1D_LUT interpolation + */ + struct drm_property *lut1d_interpolation_property; + + /** + * @curve_1d_type_property: + * + * Sub-type for DRM_COLOROP_1D_CURVE type. + */ + struct drm_property *curve_1d_type_property; + + /** + * @multiplier_property: + * + * Multiplier property for plane gain + */ + struct drm_property *multiplier_property; + + /** + * @size_property: + * + * Size property for custom LUT from userspace. + */ + struct drm_property *size_property; + + /** + * @lut3d_interpolation_property: + * + * Read-only property for DRM_COLOROP_3D_LUT interpolation + */ + struct drm_property *lut3d_interpolation_property; + + /** + * @data_property: + * + * blob property for any TYPE that requires a blob of data, + * such as 1DLUT, CTM, 3DLUT, etc. + * + * The way this blob is interpreted depends on the TYPE of + * this + */ + struct drm_property *data_property; + + /** + * @next_property: + * + * Read-only property to next colorop in the pipeline + */ + struct drm_property *next_property; + +}; + +#define obj_to_colorop(x) container_of(x, struct drm_colorop, base) + +/** + * drm_colorop_find - look up a Colorop object from its ID + * @dev: DRM device + * @file_priv: drm file to check for lease against. + * @id: &drm_mode_object ID + * + * This can be used to look up a Colorop from its userspace ID. Only used by + * drivers for legacy IOCTLs and interface, nowadays extensions to the KMS + * userspace interface should be done using &drm_property. + */ +static inline struct drm_colorop *drm_colorop_find(struct drm_device *dev, + struct drm_file *file_priv, + uint32_t id) +{ + struct drm_mode_object *mo; + + mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_COLOROP); + return mo ? obj_to_colorop(mo) : NULL; +} + +void drm_colorop_pipeline_destroy(struct drm_device *dev); +void drm_colorop_cleanup(struct drm_colorop *colorop); + +int drm_plane_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, u64 supported_tfs, uint32_t flags); +int drm_plane_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, uint32_t lut_size, + enum drm_colorop_lut1d_interpolation_type interpolation, + uint32_t flags); +int drm_plane_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, uint32_t flags); +int drm_plane_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, uint32_t flags); +int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + uint32_t lut_size, + enum drm_colorop_lut3d_interpolation_type interpolation, + uint32_t flags); + +struct drm_colorop_state * +drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop); + +void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop, + struct drm_colorop_state *state); + +/** + * drm_colorop_reset - reset colorop atomic state + * @colorop: drm colorop + * + * Resets the atomic state for @colorop by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_colorop_reset(struct drm_colorop *colorop); + +/** + * drm_colorop_index - find the index of a registered colorop + * @colorop: colorop to find index for + * + * Given a registered colorop, return the index of that colorop within a DRM + * device's list of colorops. + */ +static inline unsigned int drm_colorop_index(const struct drm_colorop *colorop) +{ + return colorop->index; +} + +#define drm_for_each_colorop(colorop, dev) \ + list_for_each_entry(colorop, &(dev)->mode_config.colorop_list, head) + +/** + * drm_get_colorop_type_name - return a string for colorop type + * @type: colorop type to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_colorop_type_name(enum drm_colorop_type type); + +/** + * drm_get_colorop_curve_1d_type_name - return a string for 1D curve type + * @type: 1d curve type to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type type); + +const char * +drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_interpolation_type type); + +const char * +drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type); + +void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next); + +#endif /* __DRM_COLOROP_H__ */ diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 9be2181b3ed7..8f34f4b8183d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -27,8 +27,10 @@ #include <linux/llist.h> #include <linux/ctype.h> #include <linux/hdmi.h> +#include <linux/notifier.h> #include <drm/drm_mode_object.h> #include <drm/drm_util.h> +#include <drm/drm_property.h> #include <uapi/drm/drm_mode.h> @@ -36,11 +38,18 @@ struct drm_connector_helper_funcs; struct drm_modeset_acquire_ctx; struct drm_device; struct drm_crtc; +struct drm_display_mode; struct drm_encoder; +struct drm_panel; struct drm_property; struct drm_property_blob; struct drm_printer; +struct drm_privacy_screen; +struct drm_edid; struct edid; +struct hdmi_codec_daifmt; +struct hdmi_codec_params; +struct i2c_adapter; enum drm_connector_force { DRM_FORCE_UNSPECIFIED, @@ -83,7 +92,7 @@ enum drm_connector_status { }; /** - * enum drm_connector_registration_status - userspace registration status for + * enum drm_connector_registration_state - userspace registration status for * a &drm_connector * * This enum is used to track the status of initializing a connector and @@ -140,6 +149,77 @@ enum subpixel_order { }; /** + * enum drm_connector_tv_mode - Analog TV output mode + * + * This enum is used to indicate the TV output mode used on an analog TV + * connector. + * + * WARNING: The values of this enum is uABI since they're exposed in the + * "TV mode" connector property. + */ +enum drm_connector_tv_mode { + /** + * @DRM_MODE_TV_MODE_NTSC: CCIR System M (aka 525-lines) + * together with the NTSC Color Encoding. + */ + DRM_MODE_TV_MODE_NTSC, + + /** + * @DRM_MODE_TV_MODE_NTSC_443: Variant of + * @DRM_MODE_TV_MODE_NTSC. Uses a color subcarrier frequency + * of 4.43 MHz. + */ + DRM_MODE_TV_MODE_NTSC_443, + + /** + * @DRM_MODE_TV_MODE_NTSC_J: Variant of @DRM_MODE_TV_MODE_NTSC + * used in Japan. Uses a black level equals to the blanking + * level. + */ + DRM_MODE_TV_MODE_NTSC_J, + + /** + * @DRM_MODE_TV_MODE_PAL: CCIR System B together with the PAL + * color system. + */ + DRM_MODE_TV_MODE_PAL, + + /** + * @DRM_MODE_TV_MODE_PAL_M: CCIR System M (aka 525-lines) + * together with the PAL color encoding + */ + DRM_MODE_TV_MODE_PAL_M, + + /** + * @DRM_MODE_TV_MODE_PAL_N: CCIR System N together with the PAL + * color encoding. It uses 625 lines, but has a color subcarrier + * frequency of 3.58MHz, the SECAM color space, and narrower + * channels compared to most of the other PAL variants. + */ + DRM_MODE_TV_MODE_PAL_N, + + /** + * @DRM_MODE_TV_MODE_SECAM: CCIR System B together with the + * SECAM color system. + */ + DRM_MODE_TV_MODE_SECAM, + + /** + * @DRM_MODE_TV_MODE_MONOCHROME: Use timings appropriate to + * the DRM mode, including equalizing pulses for a 525-line + * or 625-line mode, with no pedestal or color encoding. + */ + DRM_MODE_TV_MODE_MONOCHROME, + + /** + * @DRM_MODE_TV_MODE_MAX: Number of analog TV output modes. + * + * Internal implementation detail; this is not uABI. + */ + DRM_MODE_TV_MODE_MAX, +}; + +/** * struct drm_scrambling: sink's scrambling support. */ struct drm_scrambling { @@ -174,6 +254,46 @@ struct drm_scdc { struct drm_scrambling scrambling; }; +/** + * struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink + * + * Describes the DSC support provided by HDMI 2.1 sink. + * The information is fetched fom additional HFVSDB blocks defined + * for HDMI 2.1. + */ +struct drm_hdmi_dsc_cap { + /** @v_1p2: flag for dsc1.2 version support by sink */ + bool v_1p2; + + /** @native_420: Does sink support DSC with 4:2:0 compression */ + bool native_420; + + /** + * @all_bpp: Does sink support all bpp with 4:4:4: or 4:2:2 + * compressed formats + */ + bool all_bpp; + + /** + * @bpc_supported: compressed bpc supported by sink : 10, 12 or 16 bpc + */ + u8 bpc_supported; + + /** @max_slices: maximum number of Horizontal slices supported by */ + u8 max_slices; + + /** @clk_per_slice : max pixel clock in MHz supported per slice */ + int clk_per_slice; + + /** @max_lanes : dsc max lanes supported for Fixed rate Link training */ + u8 max_lanes; + + /** @max_frl_rate_per_lane : maximum frl rate with DSC per lane */ + u8 max_frl_rate_per_lane; + + /** @total_chunk_kbytes: max size of chunks in KBs supported per line*/ + u8 total_chunk_kbytes; +}; /** * struct drm_hdmi_info - runtime information about the connected HDMI sink @@ -187,25 +307,31 @@ struct drm_hdmi_info { /** * @y420_vdb_modes: bitmap of modes which can support ycbcr420 - * output only (not normal RGB/YCBCR444/422 outputs). There are total - * 107 VICs defined by CEA-861-F spec, so the size is 128 bits to map - * upto 128 VICs; + * output only (not normal RGB/YCBCR444/422 outputs). The max VIC + * defined by the CEA-861-G spec is 219, so the size is 256 bits to map + * up to 256 VICs. */ - unsigned long y420_vdb_modes[BITS_TO_LONGS(128)]; + unsigned long y420_vdb_modes[BITS_TO_LONGS(256)]; /** * @y420_cmdb_modes: bitmap of modes which can support ycbcr420 - * output also, along with normal HDMI outputs. There are total 107 - * VICs defined by CEA-861-F spec, so the size is 128 bits to map upto - * 128 VICs; + * output also, along with normal HDMI outputs. The max VIC defined by + * the CEA-861-G spec is 219, so the size is 256 bits to map up to 256 + * VICs. */ - unsigned long y420_cmdb_modes[BITS_TO_LONGS(128)]; - - /** @y420_cmdb_map: bitmap of SVD index, to extraxt vcb modes */ - u64 y420_cmdb_map; + unsigned long y420_cmdb_modes[BITS_TO_LONGS(256)]; /** @y420_dc_modes: bitmap of deep color support index */ u8 y420_dc_modes; + + /** @max_frl_rate_per_lane: support fixed rate link */ + u8 max_frl_rate_per_lane; + + /** @max_lanes: supported by sink */ + u8 max_lanes; + + /** @dsc_cap: DSC capabilities of the sink */ + struct drm_hdmi_dsc_cap dsc_cap; }; /** @@ -254,6 +380,294 @@ enum drm_panel_orientation { }; /** + * enum drm_hdmi_broadcast_rgb - Broadcast RGB Selection for an HDMI @drm_connector + */ +enum drm_hdmi_broadcast_rgb { + /** + * @DRM_HDMI_BROADCAST_RGB_AUTO: The RGB range is selected + * automatically based on the mode. + */ + DRM_HDMI_BROADCAST_RGB_AUTO, + + /** + * @DRM_HDMI_BROADCAST_RGB_FULL: Full range RGB is forced. + */ + DRM_HDMI_BROADCAST_RGB_FULL, + + /** + * @DRM_HDMI_BROADCAST_RGB_LIMITED: Limited range RGB is forced. + */ + DRM_HDMI_BROADCAST_RGB_LIMITED, +}; + +const char * +drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_rgb); +const char * +drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt); + +/** + * struct drm_monitor_range_info - Panel's Monitor range in EDID for + * &drm_display_info + * + * This struct is used to store a frequency range supported by panel + * as parsed from EDID's detailed monitor range descriptor block. + * + * @min_vfreq: This is the min supported refresh rate in Hz from + * EDID's detailed monitor range. + * @max_vfreq: This is the max supported refresh rate in Hz from + * EDID's detailed monitor range + */ +struct drm_monitor_range_info { + u16 min_vfreq; + u16 max_vfreq; +}; + +/** + * struct drm_luminance_range_info - Panel's luminance range for + * &drm_display_info. Calculated using data in EDID + * + * This struct is used to store a luminance range supported by panel + * as calculated using data from EDID's static hdr metadata. + * + * @min_luminance: This is the min supported luminance value + * + * @max_luminance: This is the max supported luminance value + */ +struct drm_luminance_range_info { + u32 min_luminance; + u32 max_luminance; +}; + +/** + * enum drm_privacy_screen_status - privacy screen status + * + * This enum is used to track and control the state of the integrated privacy + * screen present on some display panels, via the "privacy-screen sw-state" + * and "privacy-screen hw-state" properties. Note the _LOCKED enum values + * are only valid for the "privacy-screen hw-state" property. + * + * @PRIVACY_SCREEN_DISABLED: + * The privacy-screen on the panel is disabled + * @PRIVACY_SCREEN_ENABLED: + * The privacy-screen on the panel is enabled + * @PRIVACY_SCREEN_DISABLED_LOCKED: + * The privacy-screen on the panel is disabled and locked (cannot be changed) + * @PRIVACY_SCREEN_ENABLED_LOCKED: + * The privacy-screen on the panel is enabled and locked (cannot be changed) + */ +enum drm_privacy_screen_status { + PRIVACY_SCREEN_DISABLED = 0, + PRIVACY_SCREEN_ENABLED, + PRIVACY_SCREEN_DISABLED_LOCKED, + PRIVACY_SCREEN_ENABLED_LOCKED, +}; + +/** + * enum drm_colorspace - color space + * + * This enum is a consolidated colorimetry list supported by HDMI and + * DP protocol standard. The respective connectors will register + * a property with the subset of this list (supported by that + * respective protocol). Userspace will set the colorspace through + * a colorspace property which will be created and exposed to + * userspace. + * + * DP definitions come from the DP v2.0 spec + * HDMI definitions come from the CTA-861-H spec + * + * @DRM_MODE_COLORIMETRY_DEFAULT: + * Driver specific behavior. + * @DRM_MODE_COLORIMETRY_NO_DATA: + * Driver specific behavior. + * @DRM_MODE_COLORIMETRY_SMPTE_170M_YCC: + * (HDMI) + * SMPTE ST 170M colorimetry format + * @DRM_MODE_COLORIMETRY_BT709_YCC: + * (HDMI, DP) + * ITU-R BT.709 colorimetry format + * @DRM_MODE_COLORIMETRY_XVYCC_601: + * (HDMI, DP) + * xvYCC601 colorimetry format + * @DRM_MODE_COLORIMETRY_XVYCC_709: + * (HDMI, DP) + * xvYCC709 colorimetry format + * @DRM_MODE_COLORIMETRY_SYCC_601: + * (HDMI, DP) + * sYCC601 colorimetry format + * @DRM_MODE_COLORIMETRY_OPYCC_601: + * (HDMI, DP) + * opYCC601 colorimetry format + * @DRM_MODE_COLORIMETRY_OPRGB: + * (HDMI, DP) + * opRGB colorimetry format + * @DRM_MODE_COLORIMETRY_BT2020_CYCC: + * (HDMI, DP) + * ITU-R BT.2020 Y'c C'bc C'rc (constant luminance) colorimetry format + * @DRM_MODE_COLORIMETRY_BT2020_RGB: + * (HDMI, DP) + * ITU-R BT.2020 R' G' B' colorimetry format + * @DRM_MODE_COLORIMETRY_BT2020_YCC: + * (HDMI, DP) + * ITU-R BT.2020 Y' C'b C'r colorimetry format + * @DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65: + * (HDMI) + * SMPTE ST 2113 P3D65 colorimetry format + * @DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER: + * (HDMI) + * SMPTE ST 2113 P3DCI colorimetry format + * @DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED: + * (DP) + * RGB wide gamut fixed point colorimetry format + * @DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT: + * (DP) + * RGB wide gamut floating point + * (scRGB (IEC 61966-2-2)) colorimetry format + * @DRM_MODE_COLORIMETRY_BT601_YCC: + * (DP) + * ITU-R BT.601 colorimetry format + * The DP spec does not say whether this is the 525 or the 625 + * line version. + * @DRM_MODE_COLORIMETRY_COUNT: + * Not a valid value; merely used four counting + */ +enum drm_colorspace { + /* For Default case, driver will set the colorspace */ + DRM_MODE_COLORIMETRY_DEFAULT = 0, + /* CEA 861 Normal Colorimetry options */ + DRM_MODE_COLORIMETRY_NO_DATA = 0, + DRM_MODE_COLORIMETRY_SMPTE_170M_YCC = 1, + DRM_MODE_COLORIMETRY_BT709_YCC = 2, + /* CEA 861 Extended Colorimetry Options */ + DRM_MODE_COLORIMETRY_XVYCC_601 = 3, + DRM_MODE_COLORIMETRY_XVYCC_709 = 4, + DRM_MODE_COLORIMETRY_SYCC_601 = 5, + DRM_MODE_COLORIMETRY_OPYCC_601 = 6, + DRM_MODE_COLORIMETRY_OPRGB = 7, + DRM_MODE_COLORIMETRY_BT2020_CYCC = 8, + DRM_MODE_COLORIMETRY_BT2020_RGB = 9, + DRM_MODE_COLORIMETRY_BT2020_YCC = 10, + /* Additional Colorimetry extension added as part of CTA 861.G */ + DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65 = 11, + DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER = 12, + /* Additional Colorimetry Options added for DP 1.4a VSC Colorimetry Format */ + DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED = 13, + DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT = 14, + DRM_MODE_COLORIMETRY_BT601_YCC = 15, + DRM_MODE_COLORIMETRY_COUNT +}; + +/** + * enum drm_bus_flags - bus_flags info for &drm_display_info + * + * This enum defines signal polarities and clock edge information for signals on + * a bus as bitmask flags. + * + * The clock edge information is conveyed by two sets of symbols, + * DRM_BUS_FLAGS_*_DRIVE_\* and DRM_BUS_FLAGS_*_SAMPLE_\*. When this enum is + * used to describe a bus from the point of view of the transmitter, the + * \*_DRIVE_\* flags should be used. When used from the point of view of the + * receiver, the \*_SAMPLE_\* flags should be used. The \*_DRIVE_\* and + * \*_SAMPLE_\* flags alias each other, with the \*_SAMPLE_POSEDGE and + * \*_SAMPLE_NEGEDGE flags being equal to \*_DRIVE_NEGEDGE and \*_DRIVE_POSEDGE + * respectively. This simplifies code as signals are usually sampled on the + * opposite edge of the driving edge. Transmitters and receivers may however + * need to take other signal timings into account to convert between driving + * and sample edges. + */ +enum drm_bus_flags { + /** + * @DRM_BUS_FLAG_DE_LOW: + * + * The Data Enable signal is active low + */ + DRM_BUS_FLAG_DE_LOW = BIT(0), + + /** + * @DRM_BUS_FLAG_DE_HIGH: + * + * The Data Enable signal is active high + */ + DRM_BUS_FLAG_DE_HIGH = BIT(1), + + /** + * @DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE: + * + * Data is driven on the rising edge of the pixel clock + */ + DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE = BIT(2), + + /** + * @DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE: + * + * Data is driven on the falling edge of the pixel clock + */ + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE = BIT(3), + + /** + * @DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE: + * + * Data is sampled on the rising edge of the pixel clock + */ + DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, + + /** + * @DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE: + * + * Data is sampled on the falling edge of the pixel clock + */ + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, + + /** + * @DRM_BUS_FLAG_DATA_MSB_TO_LSB: + * + * Data is transmitted MSB to LSB on the bus + */ + DRM_BUS_FLAG_DATA_MSB_TO_LSB = BIT(4), + + /** + * @DRM_BUS_FLAG_DATA_LSB_TO_MSB: + * + * Data is transmitted LSB to MSB on the bus + */ + DRM_BUS_FLAG_DATA_LSB_TO_MSB = BIT(5), + + /** + * @DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE: + * + * Sync signals are driven on the rising edge of the pixel clock + */ + DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE = BIT(6), + + /** + * @DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE: + * + * Sync signals are driven on the falling edge of the pixel clock + */ + DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE = BIT(7), + + /** + * @DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE: + * + * Sync signals are sampled on the rising edge of the pixel clock + */ + DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE = DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE, + + /** + * @DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE: + * + * Sync signals are sampled on the falling edge of the pixel clock + */ + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE = DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, + + /** + * @DRM_BUS_FLAG_SHARP_SIGNALS: + * + * Set if the Sharp-specific signals (SPL, CLS, PS, REV) must be used + */ + DRM_BUS_FLAG_SHARP_SIGNALS = BIT(8), +}; + +/** * struct drm_display_info - runtime data about the connected sink * * Describes a given display (e.g. CRT or flat panel) and its limitations. For @@ -266,26 +680,16 @@ enum drm_panel_orientation { */ struct drm_display_info { /** - * @name: Name of the display. - */ - char name[DRM_DISPLAY_INFO_LEN]; - - /** * @width_mm: Physical width in mm. */ - unsigned int width_mm; + unsigned int width_mm; + /** * @height_mm: Physical height in mm. */ unsigned int height_mm; /** - * @pixel_clock: Maximum pixel clock supported by the sink, in units of - * 100Hz. This mismatches the clock in &drm_display_mode (which is in - * kHZ), because that's what the EDID uses as base unit. - */ - unsigned int pixel_clock; - /** * @bpc: Maximum bits per color channel. Used by HDMI and DP outputs. */ unsigned int bpc; @@ -296,9 +700,9 @@ struct drm_display_info { enum subpixel_order subpixel_order; #define DRM_COLOR_FORMAT_RGB444 (1<<0) -#define DRM_COLOR_FORMAT_YCRCB444 (1<<1) -#define DRM_COLOR_FORMAT_YCRCB422 (1<<2) -#define DRM_COLOR_FORMAT_YCRCB420 (1<<3) +#define DRM_COLOR_FORMAT_YCBCR444 (1<<1) +#define DRM_COLOR_FORMAT_YCBCR422 (1<<2) +#define DRM_COLOR_FORMAT_YCBCR420 (1<<3) /** * @panel_orientation: Read only connector property for built-in panels, @@ -328,24 +732,10 @@ struct drm_display_info { */ unsigned int num_bus_formats; -#define DRM_BUS_FLAG_DE_LOW (1<<0) -#define DRM_BUS_FLAG_DE_HIGH (1<<1) -/* drive data on pos. edge */ -#define DRM_BUS_FLAG_PIXDATA_POSEDGE (1<<2) -/* drive data on neg. edge */ -#define DRM_BUS_FLAG_PIXDATA_NEGEDGE (1<<3) -/* data is transmitted MSB to LSB on the bus */ -#define DRM_BUS_FLAG_DATA_MSB_TO_LSB (1<<4) -/* data is transmitted LSB to MSB on the bus */ -#define DRM_BUS_FLAG_DATA_LSB_TO_MSB (1<<5) -/* drive sync on pos. edge */ -#define DRM_BUS_FLAG_SYNC_POSEDGE (1<<6) -/* drive sync on neg. edge */ -#define DRM_BUS_FLAG_SYNC_NEGEDGE (1<<7) - /** * @bus_flags: Additional information (like pixel signal polarity) for - * the pixel data on the bus, using DRM_BUS_FLAGS\_ defines. + * the pixel data on the bus, using &enum drm_bus_flags values + * DRM_BUS_FLAGS\_. */ u32 bus_flags; @@ -361,15 +751,43 @@ struct drm_display_info { bool dvi_dual; /** + * @is_hdmi: True if the sink is an HDMI device. + * + * This field shall be used instead of calling + * drm_detect_hdmi_monitor() when possible. + */ + bool is_hdmi; + + /** + * @has_audio: True if the sink supports audio. + * + * This field shall be used instead of calling + * drm_detect_monitor_audio() when possible. + */ + bool has_audio; + + /** * @has_hdmi_infoframe: Does the sink support the HDMI infoframe? */ bool has_hdmi_infoframe; /** - * @edid_hdmi_dc_modes: Mask of supported hdmi deep color modes. Even - * more stuff redundant with @bus_formats. + * @rgb_quant_range_selectable: Does the sink support selecting + * the RGB quantization range? */ - u8 edid_hdmi_dc_modes; + bool rgb_quant_range_selectable; + + /** + * @edid_hdmi_rgb444_dc_modes: Mask of supported hdmi deep color modes + * in RGB 4:4:4. Even more stuff redundant with @bus_formats. + */ + u8 edid_hdmi_rgb444_dc_modes; + + /** + * @edid_hdmi_ycbcr444_dc_modes: Mask of supported hdmi deep color + * modes in YCbCr 4:4:4. Even more stuff redundant with @bus_formats. + */ + u8 edid_hdmi_ycbcr444_dc_modes; /** * @cea_rev: CEA revision of the HDMI sink. @@ -382,9 +800,67 @@ struct drm_display_info { struct drm_hdmi_info hdmi; /** + * @hdr_sink_metadata: HDR Metadata Information read from sink + */ + struct hdr_sink_metadata hdr_sink_metadata; + + /** * @non_desktop: Non desktop display (HMD). */ bool non_desktop; + + /** + * @monitor_range: Frequency range supported by monitor range descriptor + */ + struct drm_monitor_range_info monitor_range; + + /** + * @luminance_range: Luminance range supported by panel + */ + struct drm_luminance_range_info luminance_range; + + /** + * @mso_stream_count: eDP Multi-SST Operation (MSO) stream count from + * the DisplayID VESA vendor block. 0 for conventional Single-Stream + * Transport (SST), or 2 or 4 MSO streams. + */ + u8 mso_stream_count; + + /** + * @mso_pixel_overlap: eDP MSO segment pixel overlap, 0-8 pixels. + */ + u8 mso_pixel_overlap; + + /** + * @max_dsc_bpp: Maximum DSC target bitrate, if it is set to 0 the + * monitor's default value is used instead. + */ + u32 max_dsc_bpp; + + /** + * @vics: Array of vics_len VICs. Internal to EDID parsing. + */ + u8 *vics; + + /** + * @vics_len: Number of elements in vics. Internal to EDID parsing. + */ + int vics_len; + + /** + * @quirks: EDID based quirks. DRM core and drivers can query the + * @drm_edid_quirk quirks using drm_edid_has_quirk(), the rest of + * the quirks also tracked here are internal to EDID parsing. + */ + u32 quirks; + + /** + * @source_physical_address: Source Physical Address from HDMI + * Vendor-Specific Data Block, for CEC usage. + * + * Defaults to CEC_PHYS_ADDR_INVALID (0xffff). + */ + u16 source_physical_address; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, @@ -392,13 +868,39 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, unsigned int num_formats); /** + * struct drm_connector_tv_margins - TV connector related margins + * + * Describes the margins in pixels to put around the image on TV + * connectors to deal with overscan. + */ +struct drm_connector_tv_margins { + /** + * @bottom: Bottom margin in pixels. + */ + unsigned int bottom; + + /** + * @left: Left margin in pixels. + */ + unsigned int left; + + /** + * @right: Right margin in pixels. + */ + unsigned int right; + + /** + * @top: Top margin in pixels. + */ + unsigned int top; +}; + +/** * struct drm_tv_connector_state - TV connector related states - * @subconnector: selected subconnector - * @margins: margins - * @margins.left: left margin - * @margins.right: right margin - * @margins.top: top margin - * @margins.bottom: bottom margin + * @select_subconnector: selected subconnector + * @subconnector: detected subconnector + * @margins: TV margins + * @legacy_mode: Legacy TV mode, driver specific value * @mode: TV mode * @brightness: brightness in percent * @contrast: contrast in percent @@ -408,13 +910,10 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, * @hue: hue in percent */ struct drm_tv_connector_state { + enum drm_mode_subconnector select_subconnector; enum drm_mode_subconnector subconnector; - struct { - unsigned int left; - unsigned int right; - unsigned int top; - unsigned int bottom; - } margins; + struct drm_connector_tv_margins margins; + unsigned int legacy_mode; unsigned int mode; unsigned int brightness; unsigned int contrast; @@ -425,6 +924,82 @@ struct drm_tv_connector_state { }; /** + * struct drm_connector_hdmi_infoframe - HDMI Infoframe container + */ +struct drm_connector_hdmi_infoframe { + /** + * @data: HDMI Infoframe structure + */ + union hdmi_infoframe data; + + /** + * @set: Is the content of @data valid? + */ + bool set; +}; + +/* + * struct drm_connector_hdmi_state - HDMI state container + */ +struct drm_connector_hdmi_state { + /** + * @broadcast_rgb: Connector property to pass the + * Broadcast RGB selection value. + */ + enum drm_hdmi_broadcast_rgb broadcast_rgb; + + /** + * @infoframes: HDMI Infoframes matching that state + */ + struct { + /** + * @avi: AVI Infoframes structure matching our + * state. + */ + struct drm_connector_hdmi_infoframe avi; + + /** + * @hdr_drm: DRM (Dynamic Range and Mastering) + * Infoframes structure matching our state. + */ + struct drm_connector_hdmi_infoframe hdr_drm; + + /** + * @spd: SPD Infoframes structure matching our + * state. + */ + struct drm_connector_hdmi_infoframe spd; + + /** + * @vendor: HDMI Vendor Infoframes structure + * matching our state. + */ + struct drm_connector_hdmi_infoframe hdmi; + } infoframes; + + /** + * @is_limited_range: Is the output supposed to use a limited + * RGB Quantization Range or not? + */ + bool is_limited_range; + + /** + * @output_bpc: Bits per color channel to output. + */ + unsigned int output_bpc; + + /** + * @output_format: Pixel format to output in. + */ + enum hdmi_colorspace output_format; + + /** + * @tmds_char_rate: TMDS Character Rate, in Hz. + */ + unsigned long long tmds_char_rate; +}; + +/** * struct drm_connector_state - mutable connector state */ struct drm_connector_state { @@ -445,6 +1020,15 @@ struct drm_connector_state { * Used by the atomic helpers to select the encoder, through the * &drm_connector_helper_funcs.atomic_best_encoder or * &drm_connector_helper_funcs.best_encoder callbacks. + * + * This is also used in the atomic helpers to map encoders to their + * current and previous connectors, see + * drm_atomic_get_old_connector_for_encoder() and + * drm_atomic_get_new_connector_for_encoder(). + * + * NOTE: Atomic drivers must fill this out (either themselves or through + * helpers), for otherwise the GETCONNECTOR and GETENCODER IOCTLs will + * not return correct data to userspace. */ struct drm_encoder *best_encoder; @@ -468,6 +1052,20 @@ struct drm_connector_state { struct drm_tv_connector_state tv; /** + * @self_refresh_aware: + * + * This tracks whether a connector is aware of the self refresh state. + * It should be set to true for those connector implementations which + * understand the self refresh state. This is needed since the crtc + * registers the self refresh helpers and it doesn't know if the + * connectors downstream have implemented self refresh entry/exit. + * + * Drivers should set this to true in atomic_check if they know how to + * handle self_refresh requests. + */ + bool self_refresh_aware; + + /** * @picture_aspect_ratio: Connector property to control the * HDMI infoframe aspect ratio setting. * @@ -485,6 +1083,12 @@ struct drm_connector_state { unsigned int content_type; /** + * @hdcp_content_type: Connector property to pass the type of + * protected content. This is most commonly used for HDCP. + */ + unsigned int hdcp_content_type; + + /** * @scaling_mode: Connector property to control the * upscaling, mostly used for built-in panels. */ @@ -497,6 +1101,13 @@ struct drm_connector_state { unsigned int content_protection; /** + * @colorspace: State variable for Connector property to request + * colorspace change on Sink. This is most commonly used to switch + * to wider color gamuts like BT2020. + */ + enum drm_colorspace colorspace; + + /** * @writeback_job: Writeback job for writeback connectors * * Holds the framebuffer and out-fence for a writeback connector. As @@ -520,6 +1131,168 @@ struct drm_connector_state { * and the connector bpc limitations obtained from edid. */ u8 max_bpc; + + /** + * @privacy_screen_sw_state: See :ref:`Standard Connector + * Properties<standard_connector_properties>` + */ + enum drm_privacy_screen_status privacy_screen_sw_state; + + /** + * @hdr_output_metadata: + * DRM blob property for HDR output metadata + */ + struct drm_property_blob *hdr_output_metadata; + + /** + * @hdmi: HDMI-related variable and properties. Filled by + * @drm_atomic_helper_connector_hdmi_check(). + */ + struct drm_connector_hdmi_state hdmi; +}; + +struct drm_connector_hdmi_audio_funcs { + /** + * @startup: + * + * Called when ASoC starts an audio stream setup. The + * @startup() is optional. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*startup)(struct drm_connector *connector); + + /** + * @prepare: + * Configures HDMI-encoder for audio stream. Can be called + * multiple times for each setup. Mandatory. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*prepare)(struct drm_connector *connector, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); + + /** + * @shutdown: + * + * Shut down the audio stream. Mandatory. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + void (*shutdown)(struct drm_connector *connector); + + /** + * @mute_stream: + * + * Mute/unmute HDMI audio stream. The @mute_stream callback is + * optional. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*mute_stream)(struct drm_connector *connector, + bool enable, int direction); +}; + +void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector); +void drm_connector_cec_phys_addr_set(struct drm_connector *connector); + +/** + * struct drm_connector_cec_funcs - drm_hdmi_connector control functions + */ +struct drm_connector_cec_funcs { + /** + * @phys_addr_invalidate: mark CEC physical address as invalid + * + * The callback to mark CEC physical address as invalid, abstracting + * the operation. + */ + void (*phys_addr_invalidate)(struct drm_connector *connector); + + /** + * @phys_addr_set: set CEC physical address + * + * The callback to set CEC physical address, abstracting the operation. + */ + void (*phys_addr_set)(struct drm_connector *connector, u16 addr); +}; + +/** + * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions + */ +struct drm_connector_hdmi_funcs { + /** + * @tmds_char_rate_valid: + * + * This callback is invoked at atomic_check time to figure out + * whether a particular TMDS character rate is supported by the + * driver. + * + * The @tmds_char_rate_valid callback is optional. + * + * Returns: + * + * Either &drm_mode_status.MODE_OK or one of the failure reasons + * in &enum drm_mode_status. + */ + enum drm_mode_status + (*tmds_char_rate_valid)(const struct drm_connector *connector, + const struct drm_display_mode *mode, + unsigned long long tmds_rate); + + /** + * @clear_infoframe: + * + * This callback is invoked through + * @drm_atomic_helper_connector_hdmi_update_infoframes during a + * commit to clear the infoframes into the hardware. It will be + * called multiple times, once for every disabled infoframe + * type. + * + * The @clear_infoframe callback is optional. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*clear_infoframe)(struct drm_connector *connector, + enum hdmi_infoframe_type type); + + /** + * @write_infoframe: + * + * This callback is invoked through + * @drm_atomic_helper_connector_hdmi_update_infoframes during a + * commit to program the infoframes into the hardware. It will + * be called multiple times, once for every updated infoframe + * type. + * + * The @write_infoframe callback is mandatory. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*write_infoframe)(struct drm_connector *connector, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len); + + /** + * @read_edid: + * + * This callback is used by the framework as a replacement for reading + * the EDID from connector->ddc. It is still recommended to provide + * connector->ddc instead of implementing this callback. Returned EDID + * should be freed via the drm_edid_free(). + * + * The @read_edid callback is optional. + * + * Returns: + * Valid EDID on success, NULL in case of failure. + */ + const struct drm_edid *(*read_edid)(struct drm_connector *connector); }; /** @@ -582,6 +1355,11 @@ struct drm_connector_funcs { * locks to avoid races with concurrent modeset changes need to use * &drm_connector_helper_funcs.detect_ctx instead. * + * Also note that this callback can be called no matter the + * state the connector is in. Drivers that need the underlying + * device to be powered to perform the detection will first need + * to make sure it's been properly enabled. + * * RETURNS: * * drm_connector_status indicating the connector's status. @@ -813,21 +1591,295 @@ struct drm_connector_funcs { */ void (*atomic_print_state)(struct drm_printer *p, const struct drm_connector_state *state); + + /** + * @oob_hotplug_event: + * + * This will get called when a hotplug-event for a drm-connector + * has been received from a source outside the display driver / device. + */ + void (*oob_hotplug_event)(struct drm_connector *connector, + enum drm_connector_status status); + + /** + * @debugfs_init: + * + * Allows connectors to create connector-specific debugfs files. + */ + void (*debugfs_init)(struct drm_connector *connector, struct dentry *root); }; -/* mode specified on the command line */ +/** + * struct drm_cmdline_mode - DRM Mode passed through the kernel command-line + * + * Each connector can have an initial mode with additional options + * passed through the kernel command line. This structure allows to + * express those parameters and will be filled by the command-line + * parser. + */ struct drm_cmdline_mode { + /** + * @name: + * + * Name of the mode. + */ + char name[DRM_DISPLAY_MODE_LEN]; + + /** + * @specified: + * + * Has a mode been read from the command-line? + */ bool specified; + + /** + * @refresh_specified: + * + * Did the mode have a preferred refresh rate? + */ bool refresh_specified; + + /** + * @bpp_specified: + * + * Did the mode have a preferred BPP? + */ bool bpp_specified; - int xres, yres; + + /** + * @pixel_clock: + * + * Pixel Clock in kHz. Optional. + */ + unsigned int pixel_clock; + + /** + * @xres: + * + * Active resolution on the X axis, in pixels. + */ + int xres; + + /** + * @yres: + * + * Active resolution on the Y axis, in pixels. + */ + int yres; + + /** + * @bpp: + * + * Bits per pixels for the mode. + */ int bpp; + + /** + * @refresh: + * + * Refresh rate, in Hertz. + */ int refresh; + + /** + * @rb: + * + * Do we need to use reduced blanking? + */ bool rb; + + /** + * @interlace: + * + * The mode is interlaced. + */ bool interlace; + + /** + * @cvt: + * + * The timings will be calculated using the VESA Coordinated + * Video Timings instead of looking up the mode from a table. + */ bool cvt; + + /** + * @margins: + * + * Add margins to the mode calculation (1.8% of xres rounded + * down to 8 pixels and 1.8% of yres). + */ bool margins; + + /** + * @force: + * + * Ignore the hotplug state of the connector, and force its + * state to one of the DRM_FORCE_* values. + */ enum drm_connector_force force; + + /** + * @rotation_reflection: + * + * Initial rotation and reflection of the mode setup from the + * command line. See DRM_MODE_ROTATE_* and + * DRM_MODE_REFLECT_*. The only rotations supported are + * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180. + */ + unsigned int rotation_reflection; + + /** + * @panel_orientation: + * + * drm-connector "panel orientation" property override value, + * DRM_MODE_PANEL_ORIENTATION_UNKNOWN if not set. + */ + enum drm_panel_orientation panel_orientation; + + /** + * @tv_margins: TV margins to apply to the mode. + */ + struct drm_connector_tv_margins tv_margins; + + /** + * @tv_mode: TV mode standard. See DRM_MODE_TV_MODE_*. + */ + enum drm_connector_tv_mode tv_mode; + + /** + * @tv_mode_specified: + * + * Did the mode have a preferred TV mode? + */ + bool tv_mode_specified; +}; + +/** + * struct drm_connector_hdmi_audio - DRM gemeric HDMI Codec-related structure + * + * HDMI drivers usually incorporate a HDMI Codec. This structure expresses the + * generic HDMI Codec as used by the DRM HDMI Codec framework. + */ +struct drm_connector_hdmi_audio { + /** + * @funcs: + * + * Implementation of the HDMI codec functionality to be used by the DRM + * HDMI Codec framework. + */ + const struct drm_connector_hdmi_audio_funcs *funcs; + + /** + * @codec_pdev: + * + * Platform device created to hold the HDMI Codec. It will be + * automatically unregistered during drm_connector_cleanup(). + */ + struct platform_device *codec_pdev; + + /** + * @lock: + * + * Mutex to protect @last_state, @plugged_cb and @plugged_cb_dev. + */ + struct mutex lock; + + /** + * @plugged_cb: + * + * Callback to be called when the HDMI sink get plugged to or unplugged + * from this connector. This is assigned by the framework when + * requested by the ASoC code. + */ + void (*plugged_cb)(struct device *dev, bool plugged); + + /** + * @plugged_cb_dev: + * + * The data for @plugged_cb(). It is being provided by the ASoC. + */ + struct device *plugged_cb_dev; + + /** + * @last_state: + * + * Last plugged state recored by the framework. It is used to correctly + * report the state to @plugged_cb(). + */ + bool last_state; + + /** + * @dai_port: + * + * The port in DT that is used for the Codec DAI. + */ + int dai_port; +}; + +/* + * struct drm_connector_hdmi - DRM Connector HDMI-related structure + */ +struct drm_connector_hdmi { +#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8 + /** + * @vendor: HDMI Controller Vendor Name + */ + unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] __nonstring; + +#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16 + /** + * @product: HDMI Controller Product Name + */ + unsigned char product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] __nonstring; + + /** + * @supported_formats: Bitmask of @hdmi_colorspace + * supported by the controller. + */ + unsigned long supported_formats; + + /** + * @funcs: HDMI connector Control Functions + */ + const struct drm_connector_hdmi_funcs *funcs; + + /** + * @infoframes: Current Infoframes output by the connector + */ + struct { + /** + * @lock: Mutex protecting against concurrent access to + * the infoframes, most notably between KMS and ALSA. + */ + struct mutex lock; + + /** + * @audio: Current Audio Infoframes structure. Protected + * by @lock. + */ + struct drm_connector_hdmi_infoframe audio; + } infoframes; +}; + +/** + * struct drm_connector_cec - DRM Connector CEC-related structure + */ +struct drm_connector_cec { + /** + * @mutex: protects all fields in this structure. + */ + struct mutex mutex; + + /** + * @funcs: CEC Control Functions + */ + const struct drm_connector_cec_funcs *funcs; + + /** + * @data: CEC implementation-specific data + */ + void *data; }; /** @@ -845,6 +1897,14 @@ struct drm_connector { struct device *kdev; /** @attr: sysfs attributes */ struct device_attribute *attr; + /** + * @fwnode: associated fwnode supplied by platform firmware + * + * Drivers can set this to associate a fwnode with a connector, drivers + * are expected to get a reference on the fwnode when setting this. + * drm_connector_cleanup() will call fwnode_handle_put() on this. + */ + struct fwnode_handle *fwnode; /** * @head: @@ -856,6 +1916,14 @@ struct drm_connector { */ struct list_head head; + /** + * @global_connector_list_entry: + * + * Connector entry in the global connector-list, used by + * drm_connector_find_by_fwnode(). + */ + struct list_head global_connector_list_entry; + /** @base: base KMS object */ struct drm_mode_object base; @@ -906,7 +1974,7 @@ struct drm_connector { /** * @ycbcr_420_allowed : This bool indicates if this connector is * capable of handling YCBCR 420 output. While parsing the EDID - * blocks, its very helpful to know, if the source is capable of + * blocks it's very helpful to know if the source is capable of * handling YCBCR 420 outputs. */ bool ycbcr_420_allowed; @@ -957,8 +2025,12 @@ struct drm_connector { /** * @edid_blob_ptr: DRM property containing EDID if present. Protected by - * &drm_mode_config.mutex. This should be updated only by calling + * &drm_mode_config.mutex. + * + * This must be updated only by calling drm_edid_connector_update() or * drm_connector_update_edid_property(). + * + * This must not be used by drivers directly. */ struct drm_property_blob *edid_blob_ptr; @@ -983,10 +2055,10 @@ struct drm_connector { struct drm_property *vrr_capable_property; /** - * @content_protection_property: DRM ENUM property for content - * protection. See drm_connector_attach_content_protection_property(). + * @colorspace_property: Connector property to set the suitable + * colorspace supported by the sink. */ - struct drm_property *content_protection_property; + struct drm_property *colorspace_property; /** * @path_blob_ptr: @@ -997,11 +2069,40 @@ struct drm_connector { struct drm_property_blob *path_blob_ptr; /** + * @max_bpc: Maximum bits per color channel the connector supports. + */ + unsigned int max_bpc; + + /** * @max_bpc_property: Default connector property for the max bpc to be * driven out of the connector. */ struct drm_property *max_bpc_property; + /** @privacy_screen: drm_privacy_screen for this connector, or NULL. */ + struct drm_privacy_screen *privacy_screen; + + /** @privacy_screen_notifier: privacy-screen notifier_block */ + struct notifier_block privacy_screen_notifier; + + /** + * @privacy_screen_sw_state_property: Optional atomic property for the + * connector to control the integrated privacy screen. + */ + struct drm_property *privacy_screen_sw_state_property; + + /** + * @privacy_screen_hw_state_property: Optional atomic property for the + * connector to report the actual integrated privacy screen state. + */ + struct drm_property *privacy_screen_hw_state_property; + + /** + * @broadcast_rgb_property: Connector property to set the + * Broadcast RGB selection to output with. + */ + struct drm_property *broadcast_rgb_property; + #define DRM_CONNECTOR_POLL_HPD (1 << 0) #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) @@ -1044,15 +2145,29 @@ struct drm_connector { struct drm_cmdline_mode cmdline_mode; /** @force: a DRM_FORCE_<foo> state for forced mode sets */ enum drm_connector_force force; - /** @override_edid: has the EDID been overwritten through debugfs for testing? */ - bool override_edid; -#define DRM_CONNECTOR_MAX_ENCODER 3 /** - * @encoder_ids: Valid encoders for this connector. Please only use - * drm_connector_for_each_possible_encoder() to enumerate these. + * @edid_override: Override EDID set via debugfs. + * + * Do not modify or access outside of the drm_edid_override_* family of + * functions. */ - uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; + const struct drm_edid *edid_override; + + /** + * @edid_override_mutex: Protect access to edid_override. + */ + struct mutex edid_override_mutex; + + /** @epoch_counter: used to detect any other changes in connector, besides status */ + u64 epoch_counter; + + /** + * @possible_encoders: Bit mask of encoders that can drive this + * connector, drm_encoder_index() determines the index into the bitfield + * and the bits are set with drm_connector_attach_encoder(). + */ + u32 possible_encoders; /** * @encoder: Currently bound encoder driving this connector, if any. @@ -1063,8 +2178,11 @@ struct drm_connector { struct drm_encoder *encoder; #define MAX_ELD_BYTES 128 - /** @eld: EDID-like data, if present */ + /** @eld: EDID-like data, if present, protected by @eld_mutex */ uint8_t eld[MAX_ELD_BYTES]; + /** @eld_mutex: protection for concurrenct access to @eld */ + struct mutex eld_mutex; + /** @latency_present: AV delay info from ELD, if found */ bool latency_present[2]; /** @@ -1077,6 +2195,18 @@ struct drm_connector { * [0]: progressive, [1]: interlaced */ int audio_latency[2]; + + /** + * @ddc: associated ddc adapter. + * A connector usually has its associated ddc adapter. If a driver uses + * this field, then an appropriate symbolic link is created in connector + * sysfs directory to make it easy for the user to tell which i2c + * adapter is for a particular display. + * + * The field should be set by calling drm_connector_init_with_ddc(). + */ + struct i2c_adapter *ddc; + /** * @null_edid_counter: track sinks that give us all zeros for the EDID. * Needed to workaround some HW bugs where we get all 0s @@ -1092,6 +2222,12 @@ struct drm_connector { * rev1.1 4.2.2.6 */ bool edid_corrupt; + /** + * @real_edid_checksum: real edid checksum for corrupted edid block. + * Required in Displayport 1.4 compliance testing + * rev1.1 4.2.2.6 + */ + u8 real_edid_checksum; /** @debugfs_entry: debugfs directory for this connector */ struct dentry *debugfs_entry; @@ -1154,6 +2290,21 @@ struct drm_connector { * &drm_mode_config.connector_free_work. */ struct llist_node free_node; + + /** + * @hdmi: HDMI-related variable and properties. + */ + struct drm_connector_hdmi hdmi; + + /** + * @hdmi_audio: HDMI codec properties and non-DRM state. + */ + struct drm_connector_hdmi_audio hdmi_audio; + + /** + * @cec: CEC-related data. + */ + struct drm_connector_cec cec; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) @@ -1162,8 +2313,33 @@ int drm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type); +int drm_connector_dynamic_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type, + struct i2c_adapter *ddc); +int drm_connector_init_with_ddc(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type, + struct i2c_adapter *ddc); +int drmm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type, + struct i2c_adapter *ddc); +int drmm_connector_hdmi_init(struct drm_device *dev, + struct drm_connector *connector, + const char *vendor, const char *product, + const struct drm_connector_funcs *funcs, + const struct drm_connector_hdmi_funcs *hdmi_funcs, + int connector_type, + struct i2c_adapter *ddc, + unsigned long supported_formats, + unsigned int max_bpc); void drm_connector_attach_edid_property(struct drm_connector *connector); int drm_connector_register(struct drm_connector *connector); +int drm_connector_dynamic_register(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector); int drm_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder); @@ -1239,32 +2415,50 @@ drm_connector_is_unregistered(struct drm_connector *connector) DRM_CONNECTOR_UNREGISTERED; } +void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, + enum drm_connector_status status); +const char *drm_get_connector_type_name(unsigned int connector_type); const char *drm_get_connector_status_name(enum drm_connector_status status); const char *drm_get_subpixel_order_name(enum subpixel_order order); const char *drm_get_dpms_name(int val); const char *drm_get_dvi_i_subconnector_name(int val); const char *drm_get_dvi_i_select_name(int val); +const char *drm_get_tv_mode_name(int val); const char *drm_get_tv_subconnector_name(int val); const char *drm_get_tv_select_name(int val); +const char *drm_get_dp_subconnector_name(int val); const char *drm_get_content_protection_name(int val); +const char *drm_get_hdcp_content_type_name(int val); + +int drm_get_tv_mode_from_name(const char *name, size_t len); int drm_mode_create_dvi_i_properties(struct drm_device *dev); +void drm_connector_attach_dp_subconnector_property(struct drm_connector *connector); + +int drm_mode_create_tv_margin_properties(struct drm_device *dev); +int drm_mode_create_tv_properties_legacy(struct drm_device *dev, + unsigned int num_modes, + const char * const modes[]); int drm_mode_create_tv_properties(struct drm_device *dev, - unsigned int num_modes, - const char * const modes[]); + unsigned int supported_tv_modes); +void drm_connector_attach_tv_margin_properties(struct drm_connector *conn); int drm_mode_create_scaling_mode_property(struct drm_device *dev); int drm_connector_attach_content_type_property(struct drm_connector *dev); int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, u32 scaling_mode_mask); int drm_connector_attach_vrr_capable_property( struct drm_connector *connector); -int drm_connector_attach_content_protection_property( - struct drm_connector *connector); +int drm_connector_attach_broadcast_rgb_property(struct drm_connector *connector); +int drm_connector_attach_colorspace_property(struct drm_connector *connector); +int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector); +bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state *old_state, + struct drm_connector_state *new_state); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); +int drm_mode_create_hdmi_colorspace_property(struct drm_connector *connector, + u32 supported_colorspaces); +int drm_mode_create_dp_colorspace_property(struct drm_connector *connector, + u32 supported_colorspaces); int drm_mode_create_content_type_property(struct drm_device *dev); -void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, - const struct drm_connector_state *conn_state); - int drm_mode_create_suggested_offset_properties(struct drm_device *dev); int drm_connector_set_path_property(struct drm_connector *connector, @@ -1276,10 +2470,23 @@ void drm_connector_set_link_status_property(struct drm_connector *connector, uint64_t link_status); void drm_connector_set_vrr_capable_property( struct drm_connector *connector, bool capable); -int drm_connector_init_panel_orientation_property( - struct drm_connector *connector, int width, int height); +int drm_connector_set_panel_orientation( + struct drm_connector *connector, + enum drm_panel_orientation panel_orientation); +int drm_connector_set_panel_orientation_with_quirk( + struct drm_connector *connector, + enum drm_panel_orientation panel_orientation, + int width, int height); +int drm_connector_set_orientation_from_panel( + struct drm_connector *connector, + struct drm_panel *panel); int drm_connector_attach_max_bpc_property(struct drm_connector *connector, int min, int max); +void drm_connector_create_privacy_screen_properties(struct drm_connector *conn); +void drm_connector_attach_privacy_screen_properties(struct drm_connector *conn); +void drm_connector_attach_privacy_screen_provider( + struct drm_connector *connector, struct drm_privacy_screen *priv); +void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state); /** * struct drm_tile_group - Tile group metadata @@ -1299,9 +2506,9 @@ struct drm_tile_group { }; struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - char topology[8]); + const char topology[8]); struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - char topology[8]); + const char topology[8]); void drm_mode_put_tile_group(struct drm_device *dev, struct drm_tile_group *tg); @@ -1313,6 +2520,11 @@ void drm_mode_put_tile_group(struct drm_device *dev, * drm_connector_list_iter_begin(), drm_connector_list_iter_end() and * drm_connector_list_iter_next() respectively the convenience macro * drm_for_each_connector_iter(). + * + * Note that the return value of drm_connector_list_iter_next() is only valid + * up to the next drm_connector_list_iter_next() or + * drm_connector_list_iter_end() call. If you want to use the connector later, + * then you need to grab your own reference first using drm_connector_get(). */ struct drm_connector_list_iter { /* private: */ @@ -1328,6 +2540,7 @@ void drm_connector_list_iter_end(struct drm_connector_list_iter *iter); bool drm_connector_has_possible_encoder(struct drm_connector *connector, struct drm_encoder *encoder); +const char *drm_get_colorspace_name(enum drm_colorspace colorspace); /** * drm_for_each_connector_iter - connector_list iterator macro @@ -1345,13 +2558,9 @@ bool drm_connector_has_possible_encoder(struct drm_connector *connector, * drm_connector_for_each_possible_encoder - iterate connector's possible encoders * @connector: &struct drm_connector pointer * @encoder: &struct drm_encoder pointer used as cursor - * @__i: int iteration cursor, for macro-internal use */ -#define drm_connector_for_each_possible_encoder(connector, encoder, __i) \ - for ((__i) = 0; (__i) < ARRAY_SIZE((connector)->encoder_ids) && \ - (connector)->encoder_ids[(__i)] != 0; (__i)++) \ - for_each_if((encoder) = \ - drm_encoder_find((connector)->dev, NULL, \ - (connector)->encoder_ids[(__i)])) \ +#define drm_connector_for_each_possible_encoder(connector, encoder) \ + drm_for_each_encoder_mask(encoder, (connector)->dev, \ + (connector)->possible_encoders) #endif diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 39c3900aab3c..66278ffeebd6 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -25,36 +25,24 @@ #ifndef __DRM_CRTC_H__ #define __DRM_CRTC_H__ -#include <linux/i2c.h> #include <linux/spinlock.h> #include <linux/types.h> -#include <linux/fb.h> -#include <linux/hdmi.h> -#include <linux/media-bus-format.h> -#include <uapi/drm/drm_mode.h> -#include <uapi/drm/drm_fourcc.h> #include <drm/drm_modeset_lock.h> -#include <drm/drm_rect.h> #include <drm/drm_mode_object.h> -#include <drm/drm_framebuffer.h> #include <drm/drm_modes.h> -#include <drm/drm_connector.h> -#include <drm/drm_property.h> -#include <drm/drm_bridge.h> -#include <drm/drm_edid.h> +#include <drm/drm_device.h> #include <drm/drm_plane.h> -#include <drm/drm_blend.h> -#include <drm/drm_color_mgmt.h> #include <drm/drm_debugfs_crc.h> #include <drm/drm_mode_config.h> +struct drm_connector; struct drm_device; +struct drm_framebuffer; struct drm_mode_set; struct drm_file; -struct drm_clip_rect; struct drm_printer; +struct drm_self_refresh_data; struct device_node; -struct dma_fence; struct edid; static inline int64_t U642I64(uint64_t val) @@ -78,7 +66,7 @@ struct drm_plane_helper_funcs; /** * struct drm_crtc_state - mutable CRTC state * - * Note that the distinction between @enable and @active is rather subtile: + * Note that the distinction between @enable and @active is rather subtle: * Flipping @active while @enable is set without changing anything else may * never return in a failure from the &drm_mode_config_funcs.atomic_check * callback. Userspace assumes that a DPMS On will always succeed. In other @@ -89,11 +77,6 @@ struct drm_plane_helper_funcs; * intended to indicate whether a full modeset is needed, rather than strictly * describing what has changed in a commit. See also: * drm_atomic_crtc_needs_modeset() - * - * WARNING: Transitional helpers (like drm_helper_crtc_mode_set() or - * drm_helper_crtc_mode_set_base()) do not maintain many of the derived control - * state like @plane_mask so drivers not converted over to atomic helpers should - * not rely on these being accurate! */ struct drm_crtc_state { /** @crtc: backpointer to the CRTC */ @@ -173,12 +156,25 @@ struct drm_crtc_state { * @no_vblank: * * Reflects the ability of a CRTC to send VBLANK events. This state - * usually depends on the pipeline configuration, and the main usuage - * is CRTCs feeding a writeback connector operating in oneshot mode. - * In this case the VBLANK event is only generated when a job is queued - * to the writeback connector, and we want the core to fake VBLANK - * events when this part of the pipeline hasn't changed but others had - * or when the CRTC and connectors are being disabled. + * usually depends on the pipeline configuration. If set to true, DRM + * atomic helpers will send out a fake VBLANK event during display + * updates after all hardware changes have been committed. This is + * implemented in drm_atomic_helper_fake_vblank(). + * + * One usage is for drivers and/or hardware without support for VBLANK + * interrupts. Such drivers typically do not initialize vblanking + * (i.e., call drm_vblank_init() with the number of CRTCs). For CRTCs + * without initialized vblanking, this field is set to true in + * drm_atomic_helper_check_modeset(), and a fake VBLANK event will be + * send out on each update of the display pipeline by + * drm_atomic_helper_fake_vblank(). + * + * Another usage is CRTCs feeding a writeback connector operating in + * oneshot mode. In this case the fake VBLANK event is only generated + * when a job is queued to the writeback connector, and we want the + * core to fake VBLANK events when this part of the pipeline hasn't + * changed but others had or when the CRTC and connectors are being + * disabled. * * __drm_atomic_helper_crtc_duplicate_state() will not reset the value * from the current state, the CRTC driver is then responsible for @@ -190,7 +186,7 @@ struct drm_crtc_state { * this case the driver will send the VBLANK event on its own when the * writeback job is complete. */ - bool no_vblank : 1; + bool no_vblank; /** * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to @@ -271,6 +267,10 @@ struct drm_crtc_state { * Lookup table for converting pixel data after the color conversion * matrix @ctm. See drm_crtc_enable_color_mgmt(). The blob (if not * NULL) is an array of &struct drm_color_lut. + * + * Note that for mostly historical reasons stemming from Xorg heritage, + * this is also used to store the color map (also sometimes color lut, + * CLUT or color palette) for indexed formats like DRM_FORMAT_C8. */ struct drm_property_blob *gamma_lut; @@ -283,12 +283,12 @@ struct drm_crtc_state { u32 target_vblank; /** - * @pageflip_flags: + * @async_flip: * - * DRM_MODE_PAGE_FLIP_* flags, as passed to the page flip ioctl. - * Zero in any other case. + * This is set when DRM_MODE_PAGE_FLIP_ASYNC is set in the legacy + * PAGE_FLIP IOCTL. It's not wired up for the atomic IOCTL itself yet. */ - u32 pageflip_flags; + bool async_flip; /** * @vrr_enabled: @@ -300,6 +300,35 @@ struct drm_crtc_state { bool vrr_enabled; /** + * @self_refresh_active: + * + * Used by the self refresh helpers to denote when a self refresh + * transition is occurring. This will be set on enable/disable callbacks + * when self refresh is being enabled or disabled. In some cases, it may + * not be desirable to fully shut off the crtc during self refresh. + * CRTC's can inspect this flag and determine the best course of action. + */ + bool self_refresh_active; + + /** + * @scaling_filter: + * + * Scaling filter to be applied + */ + enum drm_scaling_filter scaling_filter; + + /** + * @sharpness_strength: + * + * Used by the user to set the sharpness intensity. + * The value ranges from 0-255. + * Default value is 0 which disable the sharpness feature. + * Any value greater than 0 enables sharpening with the + * specified strength. + */ + u8 sharpness_strength; + + /** * @event: * * Optional pointer to a DRM event to signal upon completion of the @@ -323,7 +352,14 @@ struct drm_crtc_state { * - Events for disabled CRTCs are not allowed, and drivers can ignore * that case. * - * This can be handled by the drm_crtc_send_vblank_event() function, + * For very simple hardware without VBLANK interrupt, enabling + * &struct drm_crtc_state.no_vblank makes DRM's atomic commit helpers + * send a fake VBLANK event at the end of the display update after all + * hardware changes have been applied. See + * drm_atomic_helper_fake_vblank(). + * + * For more complex hardware this + * can be handled by the drm_crtc_send_vblank_event() function, * which the driver should call on the provided event upon completion of * the atomic commit. Note that if the driver supports vblank signalling * and timestamping the vblank counters and timestamps must agree with @@ -472,7 +508,7 @@ struct drm_crtc_funcs { /** * @destroy: * - * Clean up plane resources. This is only called at driver unload time + * Clean up CRTC resources. This is only called at driver unload time * through drm_mode_config_cleanup() since a CRTC cannot be hotplugged * in DRM. */ @@ -743,6 +779,9 @@ struct drm_crtc_funcs { * provided from the configured source. Drivers must accept an "auto" * source name that will select a default source for this CRTC. * + * This may trigger an atomic modeset commit if necessary, to enable CRC + * generation. + * * Note that "auto" can depend upon the current modeset configuration, * e.g. it could pick an encoder or output specific CRC sampling point. * @@ -754,6 +793,7 @@ struct drm_crtc_funcs { * 0 on success or a negative error code on failure. */ int (*set_crc_source)(struct drm_crtc *crtc, const char *source); + /** * @verify_crc_source: * @@ -851,6 +891,47 @@ struct drm_crtc_funcs { * new drivers as the replacement of &drm_driver.disable_vblank hook. */ void (*disable_vblank)(struct drm_crtc *crtc); + + /** + * @get_vblank_timestamp: + * + * Called by drm_get_last_vbltimestamp(). Should return a precise + * timestamp when the most recent vblank interval ended or will end. + * + * Specifically, the timestamp in @vblank_time should correspond as + * closely as possible to the time when the first video scanline of + * the video frame after the end of vblank will start scanning out, + * the time immediately after end of the vblank interval. If the + * @crtc is currently inside vblank, this will be a time in the future. + * If the @crtc is currently scanning out a frame, this will be the + * past start time of the current scanout. This is meant to adhere + * to the OpenML OML_sync_control extension specification. + * + * Parameters: + * + * crtc: + * CRTC for which timestamp should be returned. + * max_error: + * Maximum allowable timestamp error in nanoseconds. + * Implementation should strive to provide timestamp + * with an error of at most max_error nanoseconds. + * Returns true upper bound on error for timestamp. + * vblank_time: + * Target location for returned vblank timestamp. + * in_vblank_irq: + * True when called from drm_crtc_handle_vblank(). Some drivers + * need to apply some workarounds for gpu-specific vblank irq quirks + * if flag is set. + * + * Returns: + * + * True on success, false on failure, which means the core should + * fallback to a simple timestamp taken in drm_crtc_handle_vblank(). + */ + bool (*get_vblank_timestamp)(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq); }; /** @@ -958,11 +1039,12 @@ struct drm_crtc { * Programmed mode in hw, after adjustments for encoders, crtc, panel * scaling etc. Should only be used by legacy drivers, for high * precision vblank timestamps in - * drm_calc_vbltimestamp_from_scanoutpos(). + * drm_crtc_vblank_helper_get_vblank_timestamp(). * * Note that atomic drivers should not use this, but instead use * &drm_crtc_state.adjusted_mode. And for high-precision timestamps - * drm_calc_vbltimestamp_from_scanoutpos() used &drm_vblank_crtc.hwmode, + * drm_crtc_vblank_helper_get_vblank_timestamp() used + * &drm_vblank_crtc.hwmode, * which is filled out by calling drm_calc_timestamping_constants(). */ struct drm_display_mode hwmode; @@ -990,12 +1072,18 @@ struct drm_crtc { /** * @gamma_size: Size of legacy gamma ramp reported to userspace. Set up * by calling drm_mode_crtc_set_gamma_size(). + * + * Note that atomic drivers need to instead use + * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt(). */ uint32_t gamma_size; /** * @gamma_store: Gamma ramp values used by the legacy SETGAMMA and * GETGAMMA IOCTls. Set up by calling drm_mode_crtc_set_gamma_size(). + * + * Note that atomic drivers need to instead use + * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt(). */ uint16_t *gamma_store; @@ -1006,6 +1094,18 @@ struct drm_crtc { struct drm_object_properties properties; /** + * @scaling_filter_property: property to apply a particular filter while + * scaling. + */ + struct drm_property *scaling_filter_property; + + /** + * @sharpness_strength_property: property to apply + * the intensity of the sharpness requested. + */ + struct drm_property *sharpness_strength_property; + + /** * @state: * * Current atomic state for this CRTC. @@ -1044,14 +1144,12 @@ struct drm_crtc { */ spinlock_t commit_lock; -#ifdef CONFIG_DEBUG_FS /** * @debugfs_entry: * * Debugfs directory for this CRTC. */ struct dentry *debugfs_entry; -#endif /** * @crc: @@ -1087,6 +1185,13 @@ struct drm_crtc { * The name of the CRTC's fence timeline. */ char timeline_name[32]; + + /** + * @self_refresh_data: Holds the state for the self refresh helpers + * + * Initialized via drm_self_refresh_helper_init(). + */ + struct drm_self_refresh_data *self_refresh_data; }; /** @@ -1123,8 +1228,50 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_plane *cursor, const struct drm_crtc_funcs *funcs, const char *name, ...); + +__printf(6, 7) +int drmm_crtc_init_with_planes(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...); + void drm_crtc_cleanup(struct drm_crtc *crtc); +__printf(7, 8) +void *__drmm_crtc_alloc_with_planes(struct drm_device *dev, + size_t size, size_t offset, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...); + +/** + * drmm_crtc_alloc_with_planes - Allocate and initialize a new CRTC object with + * specified primary and cursor planes. + * @dev: DRM device + * @type: the type of the struct which contains struct &drm_crtc + * @member: the name of the &drm_crtc within @type. + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC + * @funcs: callbacks for the new CRTC + * @name: printf style format string for the CRTC name, or NULL for default name + * + * Allocates and initializes a new crtc object. Cleanup is automatically + * handled through registering drmm_crtc_cleanup() with drmm_add_action(). + * + * The @drm_crtc_funcs.destroy hook must be NULL. + * + * Returns: + * Pointer to new crtc, or ERR_PTR on failure. + */ +#define drmm_crtc_alloc_with_planes(dev, type, member, primary, cursor, funcs, name, ...) \ + ((type *)__drmm_crtc_alloc_with_planes(dev, sizeof(type), \ + offsetof(type, member), \ + primary, cursor, funcs, \ + name, ##__VA_ARGS__)) + /** * drm_crtc_index - find the index of a registered CRTC * @crtc: CRTC to find index for @@ -1149,9 +1296,6 @@ static inline uint32_t drm_crtc_mask(const struct drm_crtc *crtc) return 1 << drm_crtc_index(crtc); } -int drm_crtc_force_disable(struct drm_crtc *crtc); -int drm_crtc_force_disable_all(struct drm_device *dev); - int drm_mode_set_config_internal(struct drm_mode_set *set); struct drm_crtc *drm_crtc_from_index(struct drm_device *dev, int idx); @@ -1184,4 +1328,18 @@ static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, #define drm_for_each_crtc(crtc, dev) \ list_for_each_entry(crtc, &(dev)->mode_config.crtc_list, head) +/** + * drm_for_each_crtc_reverse - iterate over all CRTCs in reverse order + * @crtc: a &struct drm_crtc as the loop cursor + * @dev: the &struct drm_device + * + * Iterate over all CRTCs of @dev. + */ +#define drm_for_each_crtc_reverse(crtc, dev) \ + list_for_each_entry_reverse(crtc, &(dev)->mode_config.crtc_list, head) + +int drm_crtc_create_scaling_filter_property(struct drm_crtc *crtc, + unsigned int supported_filters); +bool drm_crtc_in_clone_mode(struct drm_crtc_state *crtc_state); +int drm_crtc_create_sharpness_strength_property(struct drm_crtc *crtc); #endif /* __DRM_CRTC_H__ */ diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index d65f034843ce..8c886fc46ef2 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -33,15 +33,17 @@ #ifndef __DRM_CRTC_HELPER_H__ #define __DRM_CRTC_HELPER_H__ -#include <linux/spinlock.h> #include <linux/types.h> -#include <linux/idr.h> -#include <linux/fb.h> - -#include <drm/drm_crtc.h> -#include <drm/drm_modeset_helper_vtables.h> -#include <drm/drm_modeset_helper.h> +struct drm_atomic_state; +struct drm_connector; +struct drm_crtc; +struct drm_device; +struct drm_display_mode; +struct drm_encoder; +struct drm_framebuffer; +struct drm_mode_set; +struct drm_modeset_acquire_ctx; void drm_helper_disable_unused_functions(struct drm_device *dev); int drm_crtc_helper_set_config(struct drm_mode_set *set, @@ -50,27 +52,14 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); +int drm_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state); bool drm_helper_crtc_in_use(struct drm_crtc *crtc); bool drm_helper_encoder_in_use(struct drm_encoder *encoder); int drm_helper_connector_dpms(struct drm_connector *connector, int mode); void drm_helper_resume_force_mode(struct drm_device *dev); - -/* drm_probe_helper.c */ -int drm_helper_probe_single_connector_modes(struct drm_connector - *connector, uint32_t maxX, - uint32_t maxY); -int drm_helper_probe_detect(struct drm_connector *connector, - struct drm_modeset_acquire_ctx *ctx, - bool force); -void drm_kms_helper_poll_init(struct drm_device *dev); -void drm_kms_helper_poll_fini(struct drm_device *dev); -bool drm_helper_hpd_irq_event(struct drm_device *dev); -void drm_kms_helper_hotplug_event(struct drm_device *dev); - -void drm_kms_helper_poll_disable(struct drm_device *dev); -void drm_kms_helper_poll_enable(struct drm_device *dev); -bool drm_kms_helper_is_poll_worker(void); +int drm_helper_force_disable_all(struct drm_device *dev); #endif diff --git a/include/drm/drm_damage_helper.h b/include/drm/drm_damage_helper.h index 4487660b26b8..a58cbcd11276 100644 --- a/include/drm/drm_damage_helper.h +++ b/include/drm/drm_damage_helper.h @@ -64,7 +64,6 @@ struct drm_atomic_helper_damage_iter { bool full_update; }; -void drm_plane_enable_fb_damage_clips(struct drm_plane *plane); void drm_atomic_helper_check_plane_damage(struct drm_atomic_state *state, struct drm_plane_state *plane_state); int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb, @@ -78,22 +77,8 @@ drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter, bool drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter, struct drm_rect *rect); - -/** - * drm_helper_get_plane_damage_clips - Returns damage clips in &drm_rect. - * @state: Plane state. - * - * Returns plane damage rectangles in internal &drm_rect. Currently &drm_rect - * can be obtained by simply typecasting &drm_mode_rect. This is because both - * are signed 32 and during drm_atomic_check_only() it is verified that damage - * clips are inside fb. - * - * Return: Clips in plane fb_damage_clips blob property. - */ -static inline struct drm_rect * -drm_helper_get_plane_damage_clips(const struct drm_plane_state *state) -{ - return (struct drm_rect *)drm_plane_get_damage_clips(state); -} +bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state, + const struct drm_plane_state *state, + struct drm_rect *rect); #endif diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index ac0f75df1ac9..ea8cba94208a 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -32,6 +32,24 @@ #ifndef _DRM_DEBUGFS_H_ #define _DRM_DEBUGFS_H_ +#include <linux/types.h> +#include <linux/seq_file.h> + +#include <drm/drm_gpuvm.h> + +/** + * DRM_DEBUGFS_GPUVA_INFO - &drm_info_list entry to dump a GPU VA space + * @show: the &drm_info_list's show callback + * @data: driver private data + * + * Drivers should use this macro to define a &drm_info_list entry to provide a + * debugfs file for dumping the GPU VA space regions and mappings. + * + * For each DRM GPU VA space drivers should call drm_debugfs_gpuva_info() from + * their @show callback. + */ +#define DRM_DEBUGFS_GPUVA_INFO(show, data) {"gpuvas", show, DRIVER_GEM_GPUVA, data} + /** * struct drm_info_list - debugfs info list entry * @@ -77,25 +95,103 @@ struct drm_info_node { struct dentry *dent; }; +/** + * struct drm_debugfs_info - debugfs info list entry + * + * This structure represents a debugfs file to be created by the drm + * core. + */ +struct drm_debugfs_info { + /** @name: File name */ + const char *name; + + /** + * @show: + * + * Show callback. &seq_file->private will be set to the &struct + * drm_debugfs_entry corresponding to the instance of this info + * on a given &struct drm_device. + */ + int (*show)(struct seq_file*, void*); + + /** @driver_features: Required driver features for this entry. */ + u32 driver_features; + + /** @data: Driver-private data, should not be device-specific. */ + void *data; +}; + +/** + * struct drm_debugfs_entry - Per-device debugfs node structure + * + * This structure represents a debugfs file, as an instantiation of a &struct + * drm_debugfs_info on a &struct drm_device. + */ +struct drm_debugfs_entry { + /** @dev: &struct drm_device for this node. */ + struct drm_device *dev; + + /** @file: Template for this node. */ + struct drm_debugfs_info file; + + /** @list: Linked list of all device nodes. */ + struct list_head list; +}; + #if defined(CONFIG_DEBUG_FS) -int drm_debugfs_create_files(const struct drm_info_list *files, - int count, struct dentry *root, - struct drm_minor *minor); -int drm_debugfs_remove_files(const struct drm_info_list *files, - int count, struct drm_minor *minor); +void drm_debugfs_create_files(const struct drm_info_list *files, + int count, struct dentry *root, + struct drm_minor *minor); +int drm_debugfs_remove_files(const struct drm_info_list *files, int count, + struct dentry *root, struct drm_minor *minor); + +void drm_debugfs_add_file(struct drm_device *dev, const char *name, + int (*show)(struct seq_file*, void*), void *data); + +void drm_debugfs_add_files(struct drm_device *dev, + const struct drm_debugfs_info *files, int count); + +int drm_debugfs_gpuva_info(struct seq_file *m, + struct drm_gpuvm *gpuvm); + +void drm_debugfs_clients_add(struct drm_file *file); +void drm_debugfs_clients_remove(struct drm_file *file); #else -static inline int drm_debugfs_create_files(const struct drm_info_list *files, +static inline void drm_debugfs_create_files(const struct drm_info_list *files, + int count, struct dentry *root, + struct drm_minor *minor) +{} + +static inline int drm_debugfs_remove_files(const struct drm_info_list *files, int count, struct dentry *root, struct drm_minor *minor) { return 0; } -static inline int drm_debugfs_remove_files(const struct drm_info_list *files, - int count, struct drm_minor *minor) +static inline void drm_debugfs_add_file(struct drm_device *dev, const char *name, + int (*show)(struct seq_file*, void*), + void *data) +{} + +static inline void drm_debugfs_add_files(struct drm_device *dev, + const struct drm_debugfs_info *files, + int count) +{} + +static inline int drm_debugfs_gpuva_info(struct seq_file *m, + struct drm_gpuvm *gpuvm) { return 0; } + +static inline void drm_debugfs_clients_add(struct drm_file *file) +{ +} + +static inline void drm_debugfs_clients_remove(struct drm_file *file) +{ +} #endif #endif /* _DRM_DEBUGFS_H_ */ diff --git a/include/drm/drm_debugfs_crc.h b/include/drm/drm_debugfs_crc.h index b225eeb30d05..1b4c98c2f838 100644 --- a/include/drm/drm_debugfs_crc.h +++ b/include/drm/drm_debugfs_crc.h @@ -22,13 +22,19 @@ #ifndef __DRM_DEBUGFS_CRC_H__ #define __DRM_DEBUGFS_CRC_H__ +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/wait.h> + +struct drm_crtc; + #define DRM_MAX_CRC_NR 10 /** * struct drm_crtc_crc_entry - entry describing a frame's content * @has_frame_counter: whether the source was able to provide a frame number * @frame: number of the frame this CRC is about, if @has_frame_counter is true - * @crc: array of values that characterize the frame + * @crcs: array of values that characterize the frame */ struct drm_crtc_crc_entry { bool has_frame_counter; diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 42411b3ea0c8..5af49c5c3778 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -5,18 +5,16 @@ #include <linux/kref.h> #include <linux/mutex.h> #include <linux/idr.h> +#include <linux/sched.h> -#include <drm/drm_hashtab.h> #include <drm/drm_mode_config.h> struct drm_driver; struct drm_minor; struct drm_master; -struct drm_device_dma; struct drm_vblank_crtc; -struct drm_sg_mem; -struct drm_local_map; struct drm_vma_offset_manager; +struct drm_vram_mm; struct drm_fb_helper; struct inode; @@ -24,25 +22,150 @@ struct inode; struct pci_dev; struct pci_controller; +/* + * Recovery methods for wedged device in order of less to more side-effects. + * To be used with drm_dev_wedged_event() as recovery @method. Callers can + * use any one, multiple (or'd) or none depending on their needs. + * + * Refer to "Device Wedging" chapter in Documentation/gpu/drm-uapi.rst for more + * details. + */ +#define DRM_WEDGE_RECOVERY_NONE BIT(0) /* optional telemetry collection */ +#define DRM_WEDGE_RECOVERY_REBIND BIT(1) /* unbind + bind driver */ +#define DRM_WEDGE_RECOVERY_BUS_RESET BIT(2) /* unbind + reset bus device + bind */ +#define DRM_WEDGE_RECOVERY_VENDOR BIT(3) /* vendor specific recovery method */ + /** - * DRM device structure. This structure represent a complete card that + * struct drm_wedge_task_info - information about the guilty task of a wedge dev + */ +struct drm_wedge_task_info { + /** @pid: pid of the task */ + pid_t pid; + /** @comm: command name of the task */ + char comm[TASK_COMM_LEN]; +}; + +/** + * enum switch_power_state - power state of drm device + */ + +enum switch_power_state { + /** @DRM_SWITCH_POWER_ON: Power state is ON */ + DRM_SWITCH_POWER_ON = 0, + + /** @DRM_SWITCH_POWER_OFF: Power state is OFF */ + DRM_SWITCH_POWER_OFF = 1, + + /** @DRM_SWITCH_POWER_CHANGING: Power state is changing */ + DRM_SWITCH_POWER_CHANGING = 2, + + /** @DRM_SWITCH_POWER_DYNAMIC_OFF: Suspended */ + DRM_SWITCH_POWER_DYNAMIC_OFF = 3, +}; + +/** + * struct drm_device - DRM device structure + * + * This structure represent a complete card that * may contain multiple heads. */ struct drm_device { - struct list_head legacy_dev_list;/**< list of devices per driver for stealth attach cleanup */ - int if_version; /**< Highest interface version set */ - - /** \name Lifetime Management */ - /*@{ */ - struct kref ref; /**< Object ref-count */ - struct device *dev; /**< Device structure of bus-device */ - struct drm_driver *driver; /**< DRM driver managing the device */ - void *dev_private; /**< DRM driver private data */ - struct drm_minor *primary; /**< Primary node */ - struct drm_minor *render; /**< Render node */ + /** @if_version: Highest interface version set */ + int if_version; + + /** @ref: Object ref-count */ + struct kref ref; + + /** @dev: Device structure of bus-device */ + struct device *dev; + + /** + * @dma_dev: + * + * Device for DMA operations. Only required if the device @dev + * cannot perform DMA by itself. Should be NULL otherwise. Call + * drm_dev_dma_dev() to get the DMA device instead of using this + * field directly. Call drm_dev_set_dma_dev() to set this field. + * + * DRM devices are sometimes bound to virtual devices that cannot + * perform DMA by themselves. Drivers should set this field to the + * respective DMA controller. + * + * Devices on USB and other peripheral busses also cannot perform + * DMA by themselves. The @dma_dev field should point the bus + * controller that does DMA on behalve of such a device. Required + * for importing buffers via dma-buf. + * + * If set, the DRM core automatically releases the reference on the + * device. + */ + struct device *dma_dev; + + /** + * @managed: + * + * Managed resources linked to the lifetime of this &drm_device as + * tracked by @ref. + */ + struct { + /** @managed.resources: managed resources list */ + struct list_head resources; + /** @managed.final_kfree: pointer for final kfree() call */ + void *final_kfree; + /** @managed.lock: protects @managed.resources */ + spinlock_t lock; + } managed; + + /** @driver: DRM driver managing the device */ + const struct drm_driver *driver; + + /** + * @dev_private: + * + * DRM driver private data. This is deprecated and should be left set to + * NULL. + * + * Instead of using this pointer it is recommended that drivers use + * devm_drm_dev_alloc() and embed struct &drm_device in their larger + * per-device structure. + */ + void *dev_private; + + /** + * @primary: + * + * Primary node. Drivers should not interact with this + * directly. debugfs interfaces can be registered with + * drm_debugfs_add_file(), and sysfs should be directly added on the + * hardware (and not character device node) struct device @dev. + */ + struct drm_minor *primary; + + /** + * @render: + * + * Render node. Drivers should not interact with this directly ever. + * Drivers should not expose any additional interfaces in debugfs or + * sysfs on this node. + */ + struct drm_minor *render; + + /** @accel: Compute Acceleration node */ + struct drm_minor *accel; + + /** + * @registered: + * + * Internally used by drm_dev_register() and drm_connector_register(). + */ bool registered; - /* currently active master for this device. Protected by master_mutex */ + /** + * @master: + * + * Currently active master for this device. + * Protected by &master_mutex + */ struct drm_master *master; /** @@ -63,85 +186,65 @@ struct drm_device { */ bool unplugged; - struct inode *anon_inode; /**< inode for private address-space */ - char *unique; /**< unique name of the device */ - /*@} */ + /** @anon_inode: inode for private address-space */ + struct inode *anon_inode; - /** \name Locks */ - /*@{ */ - struct mutex struct_mutex; /**< For others */ - struct mutex master_mutex; /**< For drm_minor::master and drm_file::is_master */ - /*@} */ + /** @unique: Unique name of the device */ + char *unique; - /** \name Usage Counters */ - /*@{ */ - int open_count; /**< Outstanding files open, protected by drm_global_mutex. */ - spinlock_t buf_lock; /**< For drm_device::buf_use and a few other things. */ - int buf_use; /**< Buffers in use -- cannot alloc */ - atomic_t buf_alloc; /**< Buffer allocation in progress */ - /*@} */ + /** + * @master_mutex: + * + * Lock for &drm_minor.master and &drm_file.is_master + */ + struct mutex master_mutex; + /** + * @open_count: + * + * Usage counter for outstanding files open, + * protected by drm_global_mutex + */ + atomic_t open_count; + + /** @filelist_mutex: Protects @filelist. */ struct mutex filelist_mutex; + /** + * @filelist: + * + * List of userspace clients, linked through &drm_file.lhead. + */ struct list_head filelist; /** * @filelist_internal: * - * List of open DRM files for in-kernel clients. Protected by @filelist_mutex. + * List of open DRM files for in-kernel clients. + * Protected by &filelist_mutex. */ struct list_head filelist_internal; /** * @clientlist_mutex: * - * Protects @clientlist access. + * Protects &clientlist access. */ struct mutex clientlist_mutex; /** * @clientlist: * - * List of in-kernel clients. Protected by @clientlist_mutex. + * List of in-kernel clients. Protected by &clientlist_mutex. */ struct list_head clientlist; - /** \name Memory management */ - /*@{ */ - struct list_head maplist; /**< Linked list of regions */ - struct drm_open_hash map_hash; /**< User token hash table for maps */ - - /** \name Context handle management */ - /*@{ */ - struct list_head ctxlist; /**< Linked list of context handles */ - struct mutex ctxlist_mutex; /**< For ctxlist */ - - struct idr ctx_idr; - - struct list_head vmalist; /**< List of vmas (for debugging) */ - - /*@} */ - - /** \name DMA support */ - /*@{ */ - struct drm_device_dma *dma; /**< Optional pointer for DMA support */ - /*@} */ - - /** \name Context support */ - /*@{ */ - - __volatile__ long context_flag; /**< Context swapping flag */ - int last_context; /**< Last current context */ - /*@} */ - /** - * @irq_enabled: + * @client_sysrq_list: * - * Indicates that interrupt handling is enabled, specifically vblank - * handling. Drivers which don't use drm_irq_install() need to set this - * to true manually. + * Entry into list of devices registered for sysrq. Allows in-kernel + * clients on this device to handle sysrq keys. */ - bool irq_enabled; - int irq; + struct list_head client_sysrq_list; /** * @vblank_disable_immediate: @@ -153,8 +256,9 @@ struct drm_device { * This can be set to true it the hardware has a working vblank counter * with high-precision timestamping (otherwise there are races) and the * driver uses drm_crtc_vblank_on() and drm_crtc_vblank_off() - * appropriately. See also @max_vblank_count and - * &drm_crtc_funcs.get_vblank_counter. + * appropriately. Also, see @max_vblank_count, + * &drm_crtc_funcs.get_vblank_counter and + * &drm_vblank_crtc_config.disable_immediate. */ bool vblank_disable_immediate; @@ -168,7 +272,16 @@ struct drm_device { */ struct drm_vblank_crtc *vblank; - spinlock_t vblank_time_lock; /**< Protects vblank count and time updates during vblank enable/disable */ + /** + * @vblank_time_lock: + * + * Protects vblank count and time updates during vblank enable/disable + */ + spinlock_t vblank_time_lock; + /** + * @vbl_lock: Top-level vblank references lock, wraps the low-level + * @vblank_time_lock. + */ spinlock_t vbl_lock; /** @@ -184,45 +297,54 @@ struct drm_device { * races and imprecision over longer time periods, hence exposing a * hardware vblank counter is always recommended. * - * If non-zeor, &drm_crtc_funcs.get_vblank_counter must be set. + * This is the statically configured device wide maximum. The driver + * can instead choose to use a runtime configurable per-crtc value + * &drm_vblank_crtc.max_vblank_count, in which case @max_vblank_count + * must be left at zero. See drm_crtc_set_max_vblank_count() on how + * to use the per-crtc value. + * + * If non-zero, &drm_crtc_funcs.get_vblank_counter must be set. */ - u32 max_vblank_count; /**< size of vblank counter register */ + u32 max_vblank_count; + + /** @vblank_event_list: List of vblank events */ + struct list_head vblank_event_list; /** - * List of events + * @event_lock: + * + * Protects @vblank_event_list and event delivery in + * general. See drm_send_event() and drm_send_event_locked(). */ - struct list_head vblank_event_list; spinlock_t event_lock; - /*@} */ - - struct drm_agp_head *agp; /**< AGP data */ + /** @num_crtcs: Number of CRTCs on this device */ + unsigned int num_crtcs; - struct pci_dev *pdev; /**< PCI device structure */ -#ifdef __alpha__ - struct pci_controller *hose; -#endif + /** @mode_config: Current mode config */ + struct drm_mode_config mode_config; - struct drm_sg_mem *sg; /**< Scatter gather memory */ - unsigned int num_crtcs; /**< Number of CRTCs on this device */ + /** @object_name_lock: GEM information */ + struct mutex object_name_lock; - struct { - int context; - struct drm_hw_lock *lock; - } sigdata; + /** @object_name_idr: GEM information */ + struct idr object_name_idr; - struct drm_local_map *agp_buffer_map; - unsigned int agp_buffer_token; + /** @vma_offset_manager: GEM information */ + struct drm_vma_offset_manager *vma_offset_manager; - struct drm_mode_config mode_config; /**< Current mode config */ + /** @vram_mm: VRAM MM memory manager */ + struct drm_vram_mm *vram_mm; - /** \name GEM information */ - /*@{ */ - struct mutex object_name_lock; - struct idr object_name_idr; - struct drm_vma_offset_manager *vma_offset_manager; - /*@} */ - int switch_power_state; + /** + * @switch_power_state: + * + * Power state of the client. + * Used by drivers supporting the switcheroo driver. + * The state is maintained in the + * &vga_switcheroo_client_ops.set_gpu_state callback + */ + enum switch_power_state switch_power_state; /** * @fb_helper: @@ -231,6 +353,32 @@ struct drm_device { * Set by drm_fb_helper_init() and cleared by drm_fb_helper_fini(). */ struct drm_fb_helper *fb_helper; + + /** + * @debugfs_root: + * + * Root directory for debugfs files. + */ + struct dentry *debugfs_root; }; +void drm_dev_set_dma_dev(struct drm_device *dev, struct device *dma_dev); + +/** + * drm_dev_dma_dev - returns the DMA device for a DRM device + * @dev: DRM device + * + * Returns the DMA device of the given DRM device. By default, this + * the DRM device's parent. See drm_dev_set_dma_dev(). + * + * Returns: + * A DMA-capable device for the DRM device. + */ +static inline struct device *drm_dev_dma_dev(struct drm_device *dev) +{ + if (dev->dma_dev) + return dev->dma_dev; + return dev->dev; +} + #endif diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h deleted file mode 100644 index c0d4df6a606f..000000000000 --- a/include/drm/drm_displayid.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright © 2014 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef DRM_DISPLAYID_H -#define DRM_DISPLAYID_H - -#define DATA_BLOCK_PRODUCT_ID 0x00 -#define DATA_BLOCK_DISPLAY_PARAMETERS 0x01 -#define DATA_BLOCK_COLOR_CHARACTERISTICS 0x02 -#define DATA_BLOCK_TYPE_1_DETAILED_TIMING 0x03 -#define DATA_BLOCK_TYPE_2_DETAILED_TIMING 0x04 -#define DATA_BLOCK_TYPE_3_SHORT_TIMING 0x05 -#define DATA_BLOCK_TYPE_4_DMT_TIMING 0x06 -#define DATA_BLOCK_VESA_TIMING 0x07 -#define DATA_BLOCK_CEA_TIMING 0x08 -#define DATA_BLOCK_VIDEO_TIMING_RANGE 0x09 -#define DATA_BLOCK_PRODUCT_SERIAL_NUMBER 0x0a -#define DATA_BLOCK_GP_ASCII_STRING 0x0b -#define DATA_BLOCK_DISPLAY_DEVICE_DATA 0x0c -#define DATA_BLOCK_INTERFACE_POWER_SEQUENCING 0x0d -#define DATA_BLOCK_TRANSFER_CHARACTERISTICS 0x0e -#define DATA_BLOCK_DISPLAY_INTERFACE 0x0f -#define DATA_BLOCK_STEREO_DISPLAY_INTERFACE 0x10 -#define DATA_BLOCK_TILED_DISPLAY 0x12 - -#define DATA_BLOCK_VENDOR_SPECIFIC 0x7f - -#define PRODUCT_TYPE_EXTENSION 0 -#define PRODUCT_TYPE_TEST 1 -#define PRODUCT_TYPE_PANEL 2 -#define PRODUCT_TYPE_MONITOR 3 -#define PRODUCT_TYPE_TV 4 -#define PRODUCT_TYPE_REPEATER 5 -#define PRODUCT_TYPE_DIRECT_DRIVE 6 - -struct displayid_hdr { - u8 rev; - u8 bytes; - u8 prod_id; - u8 ext_count; -} __packed; - -struct displayid_block { - u8 tag; - u8 rev; - u8 num_bytes; -} __packed; - -struct displayid_tiled_block { - struct displayid_block base; - u8 tile_cap; - u8 topo[3]; - u8 tile_size[4]; - u8 tile_pixel_bezel[5]; - u8 topology_id[8]; -} __packed; - -struct displayid_detailed_timings_1 { - u8 pixel_clock[3]; - u8 flags; - u8 hactive[2]; - u8 hblank[2]; - u8 hsync[2]; - u8 hsw[2]; - u8 vactive[2]; - u8 vblank[2]; - u8 vsync[2]; - u8 vsw[2]; -} __packed; - -struct displayid_detailed_timing_block { - struct displayid_block base; - struct displayid_detailed_timings_1 timings[0]; -}; -#endif diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h deleted file mode 100644 index 727af08e5ea6..000000000000 --- a/include/drm/drm_dp_mst_helper.h +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright © 2014 Red Hat. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ -#ifndef _DRM_DP_MST_HELPER_H_ -#define _DRM_DP_MST_HELPER_H_ - -#include <linux/types.h> -#include <drm/drm_dp_helper.h> -#include <drm/drm_atomic.h> - -struct drm_dp_mst_branch; - -/** - * struct drm_dp_vcpi - Virtual Channel Payload Identifier - * @vcpi: Virtual channel ID. - * @pbn: Payload Bandwidth Number for this channel - * @aligned_pbn: PBN aligned with slot size - * @num_slots: number of slots for this PBN - */ -struct drm_dp_vcpi { - int vcpi; - int pbn; - int aligned_pbn; - int num_slots; -}; - -/** - * struct drm_dp_mst_port - MST port - * @kref: reference count for this port. - * @port_num: port number - * @input: if this port is an input port. - * @mcs: message capability status - DP 1.2 spec. - * @ddps: DisplayPort Device Plug Status - DP 1.2 - * @pdt: Peer Device Type - * @ldps: Legacy Device Plug Status - * @dpcd_rev: DPCD revision of device on this port - * @num_sdp_streams: Number of simultaneous streams - * @num_sdp_stream_sinks: Number of stream sinks - * @available_pbn: Available bandwidth for this port. - * @next: link to next port on this branch device - * @mstb: branch device attach below this port - * @aux: i2c aux transport to talk to device connected to this port. - * @parent: branch device parent of this port - * @vcpi: Virtual Channel Payload info for this port. - * @connector: DRM connector this port is connected to. - * @mgr: topology manager this port lives under. - * - * This structure represents an MST port endpoint on a device somewhere - * in the MST topology. - */ -struct drm_dp_mst_port { - struct kref kref; - - u8 port_num; - bool input; - bool mcs; - bool ddps; - u8 pdt; - bool ldps; - u8 dpcd_rev; - u8 num_sdp_streams; - u8 num_sdp_stream_sinks; - uint16_t available_pbn; - struct list_head next; - struct drm_dp_mst_branch *mstb; /* pointer to an mstb if this port has one */ - struct drm_dp_aux aux; /* i2c bus for this port? */ - struct drm_dp_mst_branch *parent; - - struct drm_dp_vcpi vcpi; - struct drm_connector *connector; - struct drm_dp_mst_topology_mgr *mgr; - - /** - * @cached_edid: for DP logical ports - make tiling work by ensuring - * that the EDID for all connectors is read immediately. - */ - struct edid *cached_edid; - /** - * @has_audio: Tracks whether the sink connector to this port is - * audio-capable. - */ - bool has_audio; -}; - -/** - * struct drm_dp_mst_branch - MST branch device. - * @kref: reference count for this port. - * @rad: Relative Address to talk to this branch device. - * @lct: Link count total to talk to this branch device. - * @num_ports: number of ports on the branch. - * @msg_slots: one bit per transmitted msg slot. - * @ports: linked list of ports on this branch. - * @port_parent: pointer to the port parent, NULL if toplevel. - * @mgr: topology manager for this branch device. - * @tx_slots: transmission slots for this device. - * @last_seqno: last sequence number used to talk to this. - * @link_address_sent: if a link address message has been sent to this device yet. - * @guid: guid for DP 1.2 branch device. port under this branch can be - * identified by port #. - * - * This structure represents an MST branch device, there is one - * primary branch device at the root, along with any other branches connected - * to downstream port of parent branches. - */ -struct drm_dp_mst_branch { - struct kref kref; - u8 rad[8]; - u8 lct; - int num_ports; - - int msg_slots; - struct list_head ports; - - /* list of tx ops queue for this port */ - struct drm_dp_mst_port *port_parent; - struct drm_dp_mst_topology_mgr *mgr; - - /* slots are protected by mstb->mgr->qlock */ - struct drm_dp_sideband_msg_tx *tx_slots[2]; - int last_seqno; - bool link_address_sent; - - /* global unique identifier to identify branch devices */ - u8 guid[16]; -}; - - -/* sideband msg header - not bit struct */ -struct drm_dp_sideband_msg_hdr { - u8 lct; - u8 lcr; - u8 rad[8]; - bool broadcast; - bool path_msg; - u8 msg_len; - bool somt; - bool eomt; - bool seqno; -}; - -struct drm_dp_nak_reply { - u8 guid[16]; - u8 reason; - u8 nak_data; -}; - -struct drm_dp_link_address_ack_reply { - u8 guid[16]; - u8 nports; - struct drm_dp_link_addr_reply_port { - bool input_port; - u8 peer_device_type; - u8 port_number; - bool mcs; - bool ddps; - bool legacy_device_plug_status; - u8 dpcd_revision; - u8 peer_guid[16]; - u8 num_sdp_streams; - u8 num_sdp_stream_sinks; - } ports[16]; -}; - -struct drm_dp_remote_dpcd_read_ack_reply { - u8 port_number; - u8 num_bytes; - u8 bytes[255]; -}; - -struct drm_dp_remote_dpcd_write_ack_reply { - u8 port_number; -}; - -struct drm_dp_remote_dpcd_write_nak_reply { - u8 port_number; - u8 reason; - u8 bytes_written_before_failure; -}; - -struct drm_dp_remote_i2c_read_ack_reply { - u8 port_number; - u8 num_bytes; - u8 bytes[255]; -}; - -struct drm_dp_remote_i2c_read_nak_reply { - u8 port_number; - u8 nak_reason; - u8 i2c_nak_transaction; -}; - -struct drm_dp_remote_i2c_write_ack_reply { - u8 port_number; -}; - - -struct drm_dp_sideband_msg_rx { - u8 chunk[48]; - u8 msg[256]; - u8 curchunk_len; - u8 curchunk_idx; /* chunk we are parsing now */ - u8 curchunk_hdrlen; - u8 curlen; /* total length of the msg */ - bool have_somt; - bool have_eomt; - struct drm_dp_sideband_msg_hdr initial_hdr; -}; - -#define DRM_DP_MAX_SDP_STREAMS 16 -struct drm_dp_allocate_payload { - u8 port_number; - u8 number_sdp_streams; - u8 vcpi; - u16 pbn; - u8 sdp_stream_sink[DRM_DP_MAX_SDP_STREAMS]; -}; - -struct drm_dp_allocate_payload_ack_reply { - u8 port_number; - u8 vcpi; - u16 allocated_pbn; -}; - -struct drm_dp_connection_status_notify { - u8 guid[16]; - u8 port_number; - bool legacy_device_plug_status; - bool displayport_device_plug_status; - bool message_capability_status; - bool input_port; - u8 peer_device_type; -}; - -struct drm_dp_remote_dpcd_read { - u8 port_number; - u32 dpcd_address; - u8 num_bytes; -}; - -struct drm_dp_remote_dpcd_write { - u8 port_number; - u32 dpcd_address; - u8 num_bytes; - u8 *bytes; -}; - -#define DP_REMOTE_I2C_READ_MAX_TRANSACTIONS 4 -struct drm_dp_remote_i2c_read { - u8 num_transactions; - u8 port_number; - struct { - u8 i2c_dev_id; - u8 num_bytes; - u8 *bytes; - u8 no_stop_bit; - u8 i2c_transaction_delay; - } transactions[DP_REMOTE_I2C_READ_MAX_TRANSACTIONS]; - u8 read_i2c_device_id; - u8 num_bytes_read; -}; - -struct drm_dp_remote_i2c_write { - u8 port_number; - u8 write_i2c_device_id; - u8 num_bytes; - u8 *bytes; -}; - -/* this covers ENUM_RESOURCES, POWER_DOWN_PHY, POWER_UP_PHY */ -struct drm_dp_port_number_req { - u8 port_number; -}; - -struct drm_dp_enum_path_resources_ack_reply { - u8 port_number; - u16 full_payload_bw_number; - u16 avail_payload_bw_number; -}; - -/* covers POWER_DOWN_PHY, POWER_UP_PHY */ -struct drm_dp_port_number_rep { - u8 port_number; -}; - -struct drm_dp_query_payload { - u8 port_number; - u8 vcpi; -}; - -struct drm_dp_resource_status_notify { - u8 port_number; - u8 guid[16]; - u16 available_pbn; -}; - -struct drm_dp_query_payload_ack_reply { - u8 port_number; - u8 allocated_pbn; -}; - -struct drm_dp_sideband_msg_req_body { - u8 req_type; - union ack_req { - struct drm_dp_connection_status_notify conn_stat; - struct drm_dp_port_number_req port_num; - struct drm_dp_resource_status_notify resource_stat; - - struct drm_dp_query_payload query_payload; - struct drm_dp_allocate_payload allocate_payload; - - struct drm_dp_remote_dpcd_read dpcd_read; - struct drm_dp_remote_dpcd_write dpcd_write; - - struct drm_dp_remote_i2c_read i2c_read; - struct drm_dp_remote_i2c_write i2c_write; - } u; -}; - -struct drm_dp_sideband_msg_reply_body { - u8 reply_type; - u8 req_type; - union ack_replies { - struct drm_dp_nak_reply nak; - struct drm_dp_link_address_ack_reply link_addr; - struct drm_dp_port_number_rep port_number; - - struct drm_dp_enum_path_resources_ack_reply path_resources; - struct drm_dp_allocate_payload_ack_reply allocate_payload; - struct drm_dp_query_payload_ack_reply query_payload; - - struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack; - struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack; - struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack; - - struct drm_dp_remote_i2c_read_ack_reply remote_i2c_read_ack; - struct drm_dp_remote_i2c_read_nak_reply remote_i2c_read_nack; - struct drm_dp_remote_i2c_write_ack_reply remote_i2c_write_ack; - } u; -}; - -/* msg is queued to be put into a slot */ -#define DRM_DP_SIDEBAND_TX_QUEUED 0 -/* msg has started transmitting on a slot - still on msgq */ -#define DRM_DP_SIDEBAND_TX_START_SEND 1 -/* msg has finished transmitting on a slot - removed from msgq only in slot */ -#define DRM_DP_SIDEBAND_TX_SENT 2 -/* msg has received a response - removed from slot */ -#define DRM_DP_SIDEBAND_TX_RX 3 -#define DRM_DP_SIDEBAND_TX_TIMEOUT 4 - -struct drm_dp_sideband_msg_tx { - u8 msg[256]; - u8 chunk[48]; - u8 cur_offset; - u8 cur_len; - struct drm_dp_mst_branch *dst; - struct list_head next; - int seqno; - int state; - bool path_msg; - struct drm_dp_sideband_msg_reply_body reply; -}; - -/* sideband msg handler */ -struct drm_dp_mst_topology_mgr; -struct drm_dp_mst_topology_cbs { - /* create a connector for a port */ - struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path); - void (*register_connector)(struct drm_connector *connector); - void (*destroy_connector)(struct drm_dp_mst_topology_mgr *mgr, - struct drm_connector *connector); - void (*hotplug)(struct drm_dp_mst_topology_mgr *mgr); - -}; - -#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8) - -#define DP_PAYLOAD_LOCAL 1 -#define DP_PAYLOAD_REMOTE 2 -#define DP_PAYLOAD_DELETE_LOCAL 3 - -struct drm_dp_payload { - int payload_state; - int start_slot; - int num_slots; - int vcpi; -}; - -#define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base) - -struct drm_dp_mst_topology_state { - struct drm_private_state base; - int avail_slots; - struct drm_dp_mst_topology_mgr *mgr; -}; - -#define to_dp_mst_topology_mgr(x) container_of(x, struct drm_dp_mst_topology_mgr, base) - -/** - * struct drm_dp_mst_topology_mgr - DisplayPort MST manager - * - * This struct represents the toplevel displayport MST topology manager. - * There should be one instance of this for every MST capable DP connector - * on the GPU. - */ -struct drm_dp_mst_topology_mgr { - /** - * @base: Base private object for atomic - */ - struct drm_private_obj base; - - /** - * @dev: device pointer for adding i2c devices etc. - */ - struct drm_device *dev; - /** - * @cbs: callbacks for connector addition and destruction. - */ - const struct drm_dp_mst_topology_cbs *cbs; - /** - * @max_dpcd_transaction_bytes: maximum number of bytes to read/write - * in one go. - */ - int max_dpcd_transaction_bytes; - /** - * @aux: AUX channel for the DP MST connector this topolgy mgr is - * controlling. - */ - struct drm_dp_aux *aux; - /** - * @max_payloads: maximum number of payloads the GPU can generate. - */ - int max_payloads; - /** - * @conn_base_id: DRM connector ID this mgr is connected to. Only used - * to build the MST connector path value. - */ - int conn_base_id; - - /** - * @down_rep_recv: Message receiver state for down replies. This and - * @up_req_recv are only ever access from the work item, which is - * serialised. - */ - struct drm_dp_sideband_msg_rx down_rep_recv; - /** - * @up_req_recv: Message receiver state for up requests. This and - * @down_rep_recv are only ever access from the work item, which is - * serialised. - */ - struct drm_dp_sideband_msg_rx up_req_recv; - - /** - * @lock: protects mst state, primary, dpcd. - */ - struct mutex lock; - - /** - * @mst_state: If this manager is enabled for an MST capable port. False - * if no MST sink/branch devices is connected. - */ - bool mst_state; - /** - * @mst_primary: Pointer to the primary/first branch device. - */ - struct drm_dp_mst_branch *mst_primary; - - /** - * @dpcd: Cache of DPCD for primary port. - */ - u8 dpcd[DP_RECEIVER_CAP_SIZE]; - /** - * @sink_count: Sink count from DEVICE_SERVICE_IRQ_VECTOR_ESI0. - */ - u8 sink_count; - /** - * @pbn_div: PBN to slots divisor. - */ - int pbn_div; - - /** - * @funcs: Atomic helper callbacks - */ - const struct drm_private_state_funcs *funcs; - - /** - * @qlock: protects @tx_msg_downq, the &drm_dp_mst_branch.txslost and - * &drm_dp_sideband_msg_tx.state once they are queued - */ - struct mutex qlock; - /** - * @tx_msg_downq: List of pending down replies. - */ - struct list_head tx_msg_downq; - - /** - * @payload_lock: Protect payload information. - */ - struct mutex payload_lock; - /** - * @proposed_vcpis: Array of pointers for the new VCPI allocation. The - * VCPI structure itself is &drm_dp_mst_port.vcpi. - */ - struct drm_dp_vcpi **proposed_vcpis; - /** - * @payloads: Array of payloads. - */ - struct drm_dp_payload *payloads; - /** - * @payload_mask: Elements of @payloads actually in use. Since - * reallocation of active outputs isn't possible gaps can be created by - * disabling outputs out of order compared to how they've been enabled. - */ - unsigned long payload_mask; - /** - * @vcpi_mask: Similar to @payload_mask, but for @proposed_vcpis. - */ - unsigned long vcpi_mask; - - /** - * @tx_waitq: Wait to queue stall for the tx worker. - */ - wait_queue_head_t tx_waitq; - /** - * @work: Probe work. - */ - struct work_struct work; - /** - * @tx_work: Sideband transmit worker. This can nest within the main - * @work worker for each transaction @work launches. - */ - struct work_struct tx_work; - - /** - * @destroy_connector_list: List of to be destroyed connectors. - */ - struct list_head destroy_connector_list; - /** - * @destroy_connector_lock: Protects @connector_list. - */ - struct mutex destroy_connector_lock; - /** - * @destroy_connector_work: Work item to destroy connectors. Needed to - * avoid locking inversion. - */ - struct work_struct destroy_connector_work; -}; - -int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, - struct drm_device *dev, struct drm_dp_aux *aux, - int max_dpcd_transaction_bytes, - int max_payloads, int conn_base_id); - -void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr); - - -int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state); - - -int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled); - - -enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); - -bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port); -struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); - - -int drm_dp_calc_pbn_mode(int clock, int bpp); - - -bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, int pbn, int slots); - -int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); - - -void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); - - -void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port); - - -int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, - int pbn); - - -int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr); - - -int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr); - -int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr); - -void drm_dp_mst_dump_topology(struct seq_file *m, - struct drm_dp_mst_topology_mgr *mgr); - -void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr); -int __must_check -drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr); -struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state, - struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, - struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, int pbn); -int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, - struct drm_dp_mst_topology_mgr *mgr, - int slots); -int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, bool power_up); - -#endif diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 35af23f5fa0d..42fc085f986d 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -30,39 +30,151 @@ #include <linux/list.h> #include <linux/irqreturn.h> +#include <video/nomodeset.h> + #include <drm/drm_device.h> +struct dmem_cgroup_region; +struct drm_fb_helper; +struct drm_fb_helper_surface_size; struct drm_file; struct drm_gem_object; struct drm_master; struct drm_minor; +struct dma_buf; struct dma_buf_attachment; struct drm_display_mode; struct drm_mode_create_dumb; struct drm_printer; +struct sg_table; + +/** + * enum drm_driver_feature - feature flags + * + * See &drm_driver.driver_features, drm_device.driver_features and + * drm_core_check_feature(). + */ +enum drm_driver_feature { + /** + * @DRIVER_GEM: + * + * Driver use the GEM memory manager. This should be set for all modern + * drivers. + */ + DRIVER_GEM = BIT(0), + /** + * @DRIVER_MODESET: + * + * Driver supports mode setting interfaces (KMS). + */ + DRIVER_MODESET = BIT(1), + /** + * @DRIVER_RENDER: + * + * Driver supports dedicated render nodes. See also the :ref:`section on + * render nodes <drm_render_node>` for details. + */ + DRIVER_RENDER = BIT(3), + /** + * @DRIVER_ATOMIC: + * + * Driver supports the full atomic modesetting userspace API. Drivers + * which only use atomic internally, but do not support the full + * userspace API (e.g. not all properties converted to atomic, or + * multi-plane updates are not guaranteed to be tear-free) should not + * set this flag. + */ + DRIVER_ATOMIC = BIT(4), + /** + * @DRIVER_SYNCOBJ: + * + * Driver supports &drm_syncobj for explicit synchronization of command + * submission. + */ + DRIVER_SYNCOBJ = BIT(5), + /** + * @DRIVER_SYNCOBJ_TIMELINE: + * + * Driver supports the timeline flavor of &drm_syncobj for explicit + * synchronization of command submission. + */ + DRIVER_SYNCOBJ_TIMELINE = BIT(6), + /** + * @DRIVER_COMPUTE_ACCEL: + * + * Driver supports compute acceleration devices. This flag is mutually exclusive with + * @DRIVER_RENDER and @DRIVER_MODESET. Devices that support both graphics and compute + * acceleration should be handled by two drivers that are connected using auxiliary bus. + */ + DRIVER_COMPUTE_ACCEL = BIT(7), + /** + * @DRIVER_GEM_GPUVA: + * + * Driver supports user defined GPU VA bindings for GEM objects. + */ + DRIVER_GEM_GPUVA = BIT(8), + /** + * @DRIVER_CURSOR_HOTSPOT: + * + * Driver supports and requires cursor hotspot information in the + * cursor plane (e.g. cursor plane has to actually track the mouse + * cursor and the clients are required to set hotspot in order for + * the cursor planes to work correctly). + */ + DRIVER_CURSOR_HOTSPOT = BIT(9), -/* driver capabilities and requirements mask */ -#define DRIVER_USE_AGP 0x1 -#define DRIVER_LEGACY 0x2 -#define DRIVER_PCI_DMA 0x8 -#define DRIVER_SG 0x10 -#define DRIVER_HAVE_DMA 0x20 -#define DRIVER_HAVE_IRQ 0x40 -#define DRIVER_IRQ_SHARED 0x80 -#define DRIVER_GEM 0x1000 -#define DRIVER_MODESET 0x2000 -#define DRIVER_PRIME 0x4000 -#define DRIVER_RENDER 0x8000 -#define DRIVER_ATOMIC 0x10000 -#define DRIVER_KMS_LEGACY_CONTEXT 0x20000 -#define DRIVER_SYNCOBJ 0x40000 + /* IMPORTANT: Below are all the legacy flags, add new ones above. */ + + /** + * @DRIVER_USE_AGP: + * + * Set up DRM AGP support, see drm_agp_init(), the DRM core will manage + * AGP resources. New drivers don't need this. + */ + DRIVER_USE_AGP = BIT(25), + /** + * @DRIVER_LEGACY: + * + * Denote a legacy driver using shadow attach. Do not use. + */ + DRIVER_LEGACY = BIT(26), + /** + * @DRIVER_PCI_DMA: + * + * Driver is capable of PCI DMA, mapping of PCI DMA buffers to userspace + * will be enabled. Only for legacy drivers. Do not use. + */ + DRIVER_PCI_DMA = BIT(27), + /** + * @DRIVER_SG: + * + * Driver can perform scatter/gather DMA, allocation and mapping of + * scatter/gather buffers will be enabled. Only for legacy drivers. Do + * not use. + */ + DRIVER_SG = BIT(28), + + /** + * @DRIVER_HAVE_DMA: + * + * Driver supports DMA, the userspace DMA API will be supported. Only + * for legacy drivers. Do not use. + */ + DRIVER_HAVE_DMA = BIT(29), + /** + * @DRIVER_HAVE_IRQ: + * + * Legacy irq support. Only for legacy drivers. Do not use. + */ + DRIVER_HAVE_IRQ = BIT(30), +}; /** * struct drm_driver - DRM driver structure * - * This structure represent the common code for a family of cards. There will - * one drm_device for each card present in this family. It contains lots of - * vfunc entries, and a pile of those probably should be moved to more + * This structure represent the common code for a family of cards. There will be + * one &struct drm_device for each card present in this family. It contains lots + * of vfunc entries, and a pile of those probably should be moved to more * appropriate places like &drm_mode_config_funcs or into a new operations * structure for GEM drivers. */ @@ -70,13 +182,12 @@ struct drm_driver { /** * @load: * - * Backward-compatible driver callback to complete - * initialization steps after the driver is registered. For - * this reason, may suffer from race conditions and its use is - * deprecated for new drivers. It is therefore only supported - * for existing drivers not yet converted to the new scheme. - * See drm_dev_init() and drm_dev_register() for proper and - * race-free way to set up a &struct drm_device. + * Backward-compatible driver callback to complete initialization steps + * after the driver is registered. For this reason, may suffer from + * race conditions and its use is deprecated for new drivers. It is + * therefore only supported for existing drivers not yet converted to + * the new scheme. See devm_drm_dev_alloc() and drm_dev_register() for + * proper and race-free way to set up a &struct drm_device. * * This is deprecated, do not use! * @@ -121,34 +232,6 @@ struct drm_driver { void (*postclose) (struct drm_device *, struct drm_file *); /** - * @lastclose: - * - * Called when the last &struct drm_file has been closed and there's - * currently no userspace client for the &struct drm_device. - * - * Modern drivers should only use this to force-restore the fbdev - * framebuffer using drm_fb_helper_restore_fbdev_mode_unlocked(). - * Anything else would indicate there's something seriously wrong. - * Modern drivers can also use this to execute delayed power switching - * state changes, e.g. in conjunction with the :ref:`vga_switcheroo` - * infrastructure. - * - * This is called after @postclose hook has been called. - * - * NOTE: - * - * All legacy drivers use this callback to de-initialize the hardware. - * This is purely because of the shadow-attach model, where the DRM - * kernel driver does not really own the hardware. Instead ownershipe is - * handled with the help of userspace through an inheritedly racy dance - * to set/unset the VT into raw mode. - * - * Legacy drivers initialize the hardware in the @firstopen callback, - * which isn't even called for modern drivers. - */ - void (*lastclose) (struct drm_device *); - - /** * @unload: * * Reverse the effects of the driver load callback. Ideally, @@ -169,222 +252,21 @@ struct drm_driver { * @release: * * Optional callback for destroying device data after the final - * reference is released, i.e. the device is being destroyed. Drivers - * using this callback are responsible for calling drm_dev_fini() - * to finalize the device and then freeing the struct themselves. - */ - void (*release) (struct drm_device *); - - /** - * @get_vblank_counter: - * - * Driver callback for fetching a raw hardware vblank counter for the - * CRTC specified with the pipe argument. If a device doesn't have a - * hardware counter, the driver can simply leave the hook as NULL. - * The DRM core will account for missed vblank events while interrupts - * where disabled based on system timestamps. - * - * Wraparound handling and loss of events due to modesetting is dealt - * with in the DRM core code, as long as drivers call - * drm_crtc_vblank_off() and drm_crtc_vblank_on() when disabling or - * enabling a CRTC. - * - * This is deprecated and should not be used by new drivers. - * Use &drm_crtc_funcs.get_vblank_counter instead. - * - * Returns: - * - * Raw vblank counter value. - */ - u32 (*get_vblank_counter) (struct drm_device *dev, unsigned int pipe); - - /** - * @enable_vblank: - * - * Enable vblank interrupts for the CRTC specified with the pipe - * argument. - * - * This is deprecated and should not be used by new drivers. - * Use &drm_crtc_funcs.enable_vblank instead. - * - * Returns: - * - * Zero on success, appropriate errno if the given @crtc's vblank - * interrupt cannot be enabled. - */ - int (*enable_vblank) (struct drm_device *dev, unsigned int pipe); - - /** - * @disable_vblank: - * - * Disable vblank interrupts for the CRTC specified with the pipe - * argument. - * - * This is deprecated and should not be used by new drivers. - * Use &drm_crtc_funcs.disable_vblank instead. - */ - void (*disable_vblank) (struct drm_device *dev, unsigned int pipe); - - /** - * @get_scanout_position: - * - * Called by vblank timestamping code. - * - * Returns the current display scanout position from a crtc, and an - * optional accurate ktime_get() timestamp of when position was - * measured. Note that this is a helper callback which is only used if a - * driver uses drm_calc_vbltimestamp_from_scanoutpos() for the - * @get_vblank_timestamp callback. - * - * Parameters: - * - * dev: - * DRM device. - * pipe: - * Id of the crtc to query. - * in_vblank_irq: - * True when called from drm_crtc_handle_vblank(). Some drivers - * need to apply some workarounds for gpu-specific vblank irq quirks - * if flag is set. - * vpos: - * Target location for current vertical scanout position. - * hpos: - * Target location for current horizontal scanout position. - * stime: - * Target location for timestamp taken immediately before - * scanout position query. Can be NULL to skip timestamp. - * etime: - * Target location for timestamp taken immediately after - * scanout position query. Can be NULL to skip timestamp. - * mode: - * Current display timings. - * - * Returns vpos as a positive number while in active scanout area. - * Returns vpos as a negative number inside vblank, counting the number - * of scanlines to go until end of vblank, e.g., -1 means "one scanline - * until start of active scanout / end of vblank." - * - * Returns: - * - * True on success, false if a reliable scanout position counter could - * not be read out. - * - * FIXME: - * - * Since this is a helper to implement @get_vblank_timestamp, we should - * move it to &struct drm_crtc_helper_funcs, like all the other - * helper-internal hooks. - */ - bool (*get_scanout_position) (struct drm_device *dev, unsigned int pipe, - bool in_vblank_irq, int *vpos, int *hpos, - ktime_t *stime, ktime_t *etime, - const struct drm_display_mode *mode); - - /** - * @get_vblank_timestamp: - * - * Called by drm_get_last_vbltimestamp(). Should return a precise - * timestamp when the most recent VBLANK interval ended or will end. - * - * Specifically, the timestamp in @vblank_time should correspond as - * closely as possible to the time when the first video scanline of - * the video frame after the end of VBLANK will start scanning out, - * the time immediately after end of the VBLANK interval. If the - * @crtc is currently inside VBLANK, this will be a time in the future. - * If the @crtc is currently scanning out a frame, this will be the - * past start time of the current scanout. This is meant to adhere - * to the OpenML OML_sync_control extension specification. - * - * Paramters: - * - * dev: - * dev DRM device handle. - * pipe: - * crtc for which timestamp should be returned. - * max_error: - * Maximum allowable timestamp error in nanoseconds. - * Implementation should strive to provide timestamp - * with an error of at most max_error nanoseconds. - * Returns true upper bound on error for timestamp. - * vblank_time: - * Target location for returned vblank timestamp. - * in_vblank_irq: - * True when called from drm_crtc_handle_vblank(). Some drivers - * need to apply some workarounds for gpu-specific vblank irq quirks - * if flag is set. - * - * Returns: - * - * True on success, false on failure, which means the core should - * fallback to a simple timestamp taken in drm_crtc_handle_vblank(). + * reference is released, i.e. the device is being destroyed. * - * FIXME: - * - * We should move this hook to &struct drm_crtc_funcs like all the other - * vblank hooks. - */ - bool (*get_vblank_timestamp) (struct drm_device *dev, unsigned int pipe, - int *max_error, - ktime_t *vblank_time, - bool in_vblank_irq); - - /** - * @irq_handler: - * - * Interrupt handler called when using drm_irq_install(). Not used by - * drivers which implement their own interrupt handling. - */ - irqreturn_t(*irq_handler) (int irq, void *arg); - - /** - * @irq_preinstall: - * - * Optional callback used by drm_irq_install() which is called before - * the interrupt handler is registered. This should be used to clear out - * any pending interrupts (from e.g. firmware based drives) and reset - * the interrupt handling registers. - */ - void (*irq_preinstall) (struct drm_device *dev); - - /** - * @irq_postinstall: - * - * Optional callback used by drm_irq_install() which is called after - * the interrupt handler is registered. This should be used to enable - * interrupt generation in the hardware. - */ - int (*irq_postinstall) (struct drm_device *dev); - - /** - * @irq_uninstall: - * - * Optional callback used by drm_irq_uninstall() which is called before - * the interrupt handler is unregistered. This should be used to disable - * interrupt generation in the hardware. - */ - void (*irq_uninstall) (struct drm_device *dev); - - /** - * @master_create: - * - * Called whenever a new master is created. Only used by vmwgfx. - */ - int (*master_create)(struct drm_device *dev, struct drm_master *master); - - /** - * @master_destroy: - * - * Called whenever a master is destroyed. Only used by vmwgfx. + * This is deprecated, clean up all memory allocations associated with a + * &drm_device using drmm_add_action(), drmm_kmalloc() and related + * managed resources functions. */ - void (*master_destroy)(struct drm_device *dev, struct drm_master *master); + void (*release) (struct drm_device *); /** * @master_set: * * Called whenever the minor master is set. Only used by vmwgfx. */ - int (*master_set)(struct drm_device *dev, struct drm_file *file_priv, - bool from_open); + void (*master_set)(struct drm_device *dev, struct drm_file *file_priv, + bool from_open); /** * @master_drop: * @@ -397,107 +279,52 @@ struct drm_driver { * * Allows drivers to create driver-specific debugfs files. */ - int (*debugfs_init)(struct drm_minor *minor); - - /** - * @gem_free_object: deconstructor for drm_gem_objects - * - * This is deprecated and should not be used by new drivers. Use - * @gem_free_object_unlocked instead. - */ - void (*gem_free_object) (struct drm_gem_object *obj); - - /** - * @gem_free_object_unlocked: deconstructor for drm_gem_objects - * - * This is for drivers which are not encumbered with &drm_device.struct_mutex - * legacy locking schemes. Use this hook instead of @gem_free_object. - */ - void (*gem_free_object_unlocked) (struct drm_gem_object *obj); - - /** - * @gem_open_object: - * - * Driver hook called upon gem handle creation - */ - int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); - - /** - * @gem_close_object: - * - * Driver hook called upon gem handle release - */ - void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); - - /** - * @gem_print_info: - * - * If driver subclasses struct &drm_gem_object, it can implement this - * optional hook for printing additional driver specific info. - * - * drm_printf_indent() should be used in the callback passing it the - * indent argument. - * - * This callback is called from drm_gem_print_info(). - */ - void (*gem_print_info)(struct drm_printer *p, unsigned int indent, - const struct drm_gem_object *obj); + void (*debugfs_init)(struct drm_minor *minor); /** * @gem_create_object: constructor for gem objects * - * Hook for allocating the GEM object struct, for use by core - * helpers. + * Hook for allocating the GEM object struct, for use by the CMA + * and SHMEM GEM helpers. Returns a GEM object on success, or an + * ERR_PTR()-encoded error code otherwise. */ struct drm_gem_object *(*gem_create_object)(struct drm_device *dev, size_t size); - /* prime: */ /** * @prime_handle_to_fd: * - * export handle -> fd (see drm_gem_prime_handle_to_fd() helper) + * PRIME export function. Only used by vmwgfx. */ int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); /** * @prime_fd_to_handle: * - * import fd -> handle (see drm_gem_prime_fd_to_handle() helper) + * PRIME import function. Only used by vmwgfx. */ int (*prime_fd_to_handle)(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); - /** - * @gem_prime_export: - * - * export GEM -> dmabuf - * - * This defaults to drm_gem_prime_export() if not set. - */ - struct dma_buf * (*gem_prime_export)(struct drm_device *dev, - struct drm_gem_object *obj, int flags); + /** * @gem_prime_import: * - * import dmabuf -> GEM + * Import hook for GEM drivers. * * This defaults to drm_gem_prime_import() if not set. */ struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev, struct dma_buf *dma_buf); - int (*gem_prime_pin)(struct drm_gem_object *obj); - void (*gem_prime_unpin)(struct drm_gem_object *obj); - struct reservation_object * (*gem_prime_res_obj)( - struct drm_gem_object *obj); - struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); + /** + * @gem_prime_import_sg_table: + * + * Optional hook used by the PRIME helper functions + * drm_gem_prime_import() respectively drm_gem_prime_import_dev(). + */ struct drm_gem_object *(*gem_prime_import_sg_table)( struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt); - void *(*gem_prime_vmap)(struct drm_gem_object *obj); - void (*gem_prime_vunmap)(struct drm_gem_object *obj, void *vaddr); - int (*gem_prime_mmap)(struct drm_gem_object *obj, - struct vm_area_struct *vma); /** * @dumb_create: @@ -541,30 +368,29 @@ struct drm_driver { int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); + /** - * @dumb_destroy: + * @fbdev_probe: * - * This destroys the userspace handle for the given dumb backing storage buffer. - * Since buffer objects must be reference counted in the kernel a buffer object - * won't be immediately freed if a framebuffer modeset object still uses it. + * Allocates and initialize the fb_info structure for fbdev emulation. + * Furthermore it also needs to allocate the DRM framebuffer used to + * back the fbdev. * - * Called by the user via ioctl. - * - * The default implementation is drm_gem_dumb_destroy(). GEM based drivers - * must not overwrite this. + * This callback is mandatory for fbdev support. * * Returns: * - * Zero on success, negative errno on failure. + * 0 on success ot a negative error code otherwise. */ - int (*dumb_destroy)(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle); + int (*fbdev_probe)(struct drm_fb_helper *fbdev_helper, + struct drm_fb_helper_surface_size *sizes); /** - * @gem_vm_ops: Driver private ops for this object + * @show_fdinfo: + * + * Print device specific fdinfo. See Documentation/gpu/drm-usage-stats.rst. */ - const struct vm_operations_struct *gem_vm_ops; + void (*show_fdinfo)(struct drm_printer *p, struct drm_file *f); /** @major: driver major number */ int major; @@ -576,10 +402,13 @@ struct drm_driver { char *name; /** @desc: driver description */ char *desc; - /** @date: driver date */ - char *date; - /** @driver_features: driver features */ + /** + * @driver_features: + * Driver features, see &enum drm_driver_feature. Drivers can disable + * some features on a per-instance basis using + * &drm_device.driver_features. + */ u32 driver_features; /** @@ -602,29 +431,53 @@ struct drm_driver { * some examples. */ const struct file_operations *fops; - - /* Everything below here is for legacy driver, never use! */ - /* private: */ - - /* List of devices hanging off this driver with stealth attach. */ - struct list_head legacy_dev_list; - int (*firstopen) (struct drm_device *); - void (*preclose) (struct drm_device *, struct drm_file *file_priv); - int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv); - int (*dma_quiescent) (struct drm_device *); - int (*context_dtor) (struct drm_device *dev, int context); - int dev_priv_size; }; -extern unsigned int drm_debug; +void *__devm_drm_dev_alloc(struct device *parent, + const struct drm_driver *driver, + size_t size, size_t offset); + +struct dmem_cgroup_region * +drmm_cgroup_register_region(struct drm_device *dev, + const char *region_name, u64 size); -int drm_dev_init(struct drm_device *dev, - struct drm_driver *driver, - struct device *parent); -void drm_dev_fini(struct drm_device *dev); +/** + * devm_drm_dev_alloc - Resource managed allocation of a &drm_device instance + * @parent: Parent device object + * @driver: DRM driver + * @type: the type of the struct which contains struct &drm_device + * @member: the name of the &drm_device within @type. + * + * This allocates and initialize a new DRM device. No device registration is done. + * Call drm_dev_register() to advertice the device to user space and register it + * with other core subsystems. This should be done last in the device + * initialization sequence to make sure userspace can't access an inconsistent + * state. + * + * The initial ref-count of the object is 1. Use drm_dev_get() and + * drm_dev_put() to take and drop further ref-counts. + * + * It is recommended that drivers embed &struct drm_device into their own device + * structure. + * + * Note that this manages the lifetime of the resulting &drm_device + * automatically using devres. The DRM device initialized with this function is + * automatically put on driver detach using drm_dev_put(). + * + * RETURNS: + * Pointer to new DRM device, or ERR_PTR on failure. + */ +#define devm_drm_dev_alloc(parent, driver, type, member) \ + ((type *) __devm_drm_dev_alloc(parent, driver, sizeof(type), \ + offsetof(type, member))) -struct drm_device *drm_dev_alloc(struct drm_driver *driver, +struct drm_device *drm_dev_alloc(const struct drm_driver *driver, struct device *parent); + +void *__drm_dev_alloc(struct device *parent, + const struct drm_driver *driver, + size_t size, size_t offset); + int drm_dev_register(struct drm_device *dev, unsigned long flags); void drm_dev_unregister(struct drm_device *dev); @@ -634,6 +487,8 @@ void drm_put_dev(struct drm_device *dev); bool drm_dev_enter(struct drm_device *dev, int *idx); void drm_dev_exit(int idx); void drm_dev_unplug(struct drm_device *dev); +int drm_dev_wedged_event(struct drm_device *dev, unsigned long method, + struct drm_wedge_task_info *info); /** * drm_dev_is_unplugged - is a DRM device unplugged @@ -643,6 +498,10 @@ void drm_dev_unplug(struct drm_device *dev); * Unplugging itself is singalled through drm_dev_unplug(). If a device is * unplugged, these two functions guarantee that any store before calling * drm_dev_unplug() is visible to callers of this function after it completes + * + * WARNING: This function fundamentally races against drm_dev_unplug(). It is + * recommended that drivers instead use the underlying drm_dev_enter() and + * drm_dev_exit() function pairs. */ static inline bool drm_dev_is_unplugged(struct drm_device *dev) { @@ -657,18 +516,38 @@ static inline bool drm_dev_is_unplugged(struct drm_device *dev) } /** + * drm_core_check_all_features - check driver feature flags mask + * @dev: DRM device to check + * @features: feature flag(s) mask + * + * This checks @dev for driver features, see &drm_driver.driver_features, + * &drm_device.driver_features, and the various &enum drm_driver_feature flags. + * + * Returns true if all features in the @features mask are supported, false + * otherwise. + */ +static inline bool drm_core_check_all_features(const struct drm_device *dev, + u32 features) +{ + u32 supported = dev->driver->driver_features & dev->driver_features; + + return features && (supported & features) == features; +} + +/** * drm_core_check_feature - check driver feature flags * @dev: DRM device to check * @feature: feature flag * * This checks @dev for driver features, see &drm_driver.driver_features, - * &drm_device.driver_features, and the various DRIVER_\* flags. + * &drm_device.driver_features, and the various &enum drm_driver_feature flags. * * Returns true if the @feature is supported, false otherwise. */ -static inline bool drm_core_check_feature(struct drm_device *dev, u32 feature) +static inline bool drm_core_check_feature(const struct drm_device *dev, + enum drm_driver_feature feature) { - return dev->driver->driver_features & dev->driver_features & feature; + return drm_core_check_all_features(dev, feature); } /** @@ -686,7 +565,33 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) } -int drm_dev_set_unique(struct drm_device *dev, const char *name); +/* TODO: Inline drm_firmware_drivers_only() in all its callers. */ +static inline bool drm_firmware_drivers_only(void) +{ + return video_firmware_drivers_only(); +} + +#if defined(CONFIG_DEBUG_FS) +void drm_debugfs_dev_init(struct drm_device *dev); +void drm_debugfs_init_root(void); +void drm_debugfs_remove_root(void); +void drm_debugfs_bridge_params(void); +#else +static inline void drm_debugfs_dev_init(struct drm_device *dev) +{ +} + +static inline void drm_debugfs_init_root(void) +{ +} + +static inline void drm_debugfs_remove_root(void) +{ +} +static inline void drm_debugfs_bridge_params(void) +{ +} +#endif #endif diff --git a/include/drm/drm_dumb_buffers.h b/include/drm/drm_dumb_buffers.h new file mode 100644 index 000000000000..1f3a8236fb3d --- /dev/null +++ b/include/drm/drm_dumb_buffers.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef __DRM_DUMB_BUFFERS_H__ +#define __DRM_DUMB_BUFFERS_H__ + +struct drm_device; +struct drm_mode_create_dumb; + +int drm_mode_size_dumb(struct drm_device *dev, + struct drm_mode_create_dumb *args, + unsigned long hw_pitch_align, + unsigned long hw_size_align); + +#endif diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index e3c404833115..04f7a7f1f108 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -24,9 +24,15 @@ #define __DRM_EDID_H__ #include <linux/types.h> -#include <linux/hdmi.h> +enum hdmi_quantization_range; +struct drm_connector; struct drm_device; +struct drm_display_mode; +struct drm_edid; +struct drm_printer; +struct hdmi_avi_infoframe; +struct hdmi_vendor_infoframe; struct i2c_adapter; #define EDID_LENGTH 128 @@ -44,7 +50,7 @@ struct est_timings { u8 t1; u8 t2; u8 mfg_rsvd; -} __attribute__((packed)); +} __packed; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ #define EDID_TIMING_ASPECT_SHIFT 6 @@ -57,7 +63,7 @@ struct est_timings { struct std_timing { u8 hsize; /* need to multiply by 8 then add 248 */ u8 vfreq_aspect; -} __attribute__((packed)); +} __packed; #define DRM_EDID_PT_HSYNC_POSITIVE (1 << 1) #define DRM_EDID_PT_VSYNC_POSITIVE (1 << 2) @@ -83,12 +89,32 @@ struct detailed_pixel_timing { u8 hborder; u8 vborder; u8 misc; -} __attribute__((packed)); +} __packed; /* If it's not pixel timing, it'll be one of the below */ struct detailed_data_string { u8 str[13]; -} __attribute__((packed)); +} __packed; + +#define DRM_EDID_RANGE_OFFSET_MIN_VFREQ (1 << 0) /* 1.4 */ +#define DRM_EDID_RANGE_OFFSET_MAX_VFREQ (1 << 1) /* 1.4 */ +#define DRM_EDID_RANGE_OFFSET_MIN_HFREQ (1 << 2) /* 1.4 */ +#define DRM_EDID_RANGE_OFFSET_MAX_HFREQ (1 << 3) /* 1.4 */ + +#define DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG 0x00 /* 1.3 */ +#define DRM_EDID_RANGE_LIMITS_ONLY_FLAG 0x01 /* 1.4 */ +#define DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG 0x02 /* 1.3 */ +#define DRM_EDID_CVT_SUPPORT_FLAG 0x04 /* 1.4 */ + +#define DRM_EDID_CVT_FLAGS_STANDARD_BLANKING (1 << 3) +#define DRM_EDID_CVT_FLAGS_REDUCED_BLANKING (1 << 4) + +enum drm_edid_quirk { + /* Do a dummy read before DPCD accesses, to prevent corruption. */ + DRM_EDID_QUIRK_DP_DPCD_PROBE, + + DRM_EDID_QUIRK_NUM, +}; struct detailed_data_monitor_range { u8 min_vfreq; @@ -105,7 +131,7 @@ struct detailed_data_monitor_range { __le16 m; u8 k; u8 j; /* need to divide by 2 */ - } __attribute__((packed)) gtf2; + } __packed gtf2; struct { u8 version; u8 data1; /* high 6 bits: extra clock resolution */ @@ -114,27 +140,27 @@ struct detailed_data_monitor_range { u8 flags; /* preferred aspect and blanking support */ u8 supported_scalings; u8 preferred_refresh; - } __attribute__((packed)) cvt; - } formula; -} __attribute__((packed)); + } __packed cvt; + } __packed formula; +} __packed; struct detailed_data_wpindex { u8 white_yx_lo; /* Lower 2 bits each */ u8 white_x_hi; u8 white_y_hi; u8 gamma; /* need to divide by 100 then add 1 */ -} __attribute__((packed)); +} __packed; struct detailed_data_color_point { u8 windex1; u8 wpindex1[3]; u8 windex2; u8 wpindex2[3]; -} __attribute__((packed)); +} __packed; struct cvt_timing { u8 code[3]; -} __attribute__((packed)); +} __packed; struct detailed_non_pixel { u8 pad1; @@ -148,8 +174,8 @@ struct detailed_non_pixel { struct detailed_data_wpindex color; struct std_timing timings[6]; struct cvt_timing cvt[4]; - } data; -} __attribute__((packed)); + } __packed data; +} __packed; #define EDID_DETAIL_EST_TIMINGS 0xf7 #define EDID_DETAIL_CVT_3BYTE 0xf8 @@ -166,8 +192,8 @@ struct detailed_timing { union { struct detailed_pixel_timing pixel_data; struct detailed_non_pixel other_data; - } data; -} __attribute__((packed)); + } __packed data; +} __packed; #define DRM_EDID_INPUT_SERRATION_VSYNC (1 << 0) #define DRM_EDID_INPUT_SYNC_ON_GREEN (1 << 1) @@ -176,23 +202,26 @@ struct detailed_timing { #define DRM_EDID_INPUT_BLANK_TO_BLACK (1 << 4) #define DRM_EDID_INPUT_VIDEO_LEVEL (3 << 5) #define DRM_EDID_INPUT_DIGITAL (1 << 7) -#define DRM_EDID_DIGITAL_DEPTH_MASK (7 << 4) -#define DRM_EDID_DIGITAL_DEPTH_UNDEF (0 << 4) -#define DRM_EDID_DIGITAL_DEPTH_6 (1 << 4) -#define DRM_EDID_DIGITAL_DEPTH_8 (2 << 4) -#define DRM_EDID_DIGITAL_DEPTH_10 (3 << 4) -#define DRM_EDID_DIGITAL_DEPTH_12 (4 << 4) -#define DRM_EDID_DIGITAL_DEPTH_14 (5 << 4) -#define DRM_EDID_DIGITAL_DEPTH_16 (6 << 4) -#define DRM_EDID_DIGITAL_DEPTH_RSVD (7 << 4) -#define DRM_EDID_DIGITAL_TYPE_UNDEF (0) -#define DRM_EDID_DIGITAL_TYPE_DVI (1) -#define DRM_EDID_DIGITAL_TYPE_HDMI_A (2) -#define DRM_EDID_DIGITAL_TYPE_HDMI_B (3) -#define DRM_EDID_DIGITAL_TYPE_MDDI (4) -#define DRM_EDID_DIGITAL_TYPE_DP (5) - -#define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 0) +#define DRM_EDID_DIGITAL_DEPTH_MASK (7 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_UNDEF (0 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_6 (1 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_8 (2 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_10 (3 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_12 (4 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_14 (5 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_16 (6 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_RSVD (7 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_MASK (7 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_UNDEF (0 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_DVI (1 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_HDMI_A (2 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_HDMI_B (3 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_MDDI (4 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_DP (5 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_DFP_1_X (1 << 0) /* 1.3 */ + +#define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 0) /* 1.2 */ +#define DRM_EDID_FEATURE_CONTINUOUS_FREQ (1 << 0) /* 1.4 */ #define DRM_EDID_FEATURE_PREFERRED_TIMING (1 << 1) #define DRM_EDID_FEATURE_STANDARD_COLOR (1 << 2) /* If analog */ @@ -221,72 +250,57 @@ struct detailed_timing { DRM_EDID_YCBCR420_DC_36 | \ DRM_EDID_YCBCR420_DC_30) -/* ELD Header Block */ -#define DRM_ELD_HEADER_BLOCK_SIZE 4 - -#define DRM_ELD_VER 0 -# define DRM_ELD_VER_SHIFT 3 -# define DRM_ELD_VER_MASK (0x1f << 3) -# define DRM_ELD_VER_CEA861D (2 << 3) /* supports 861D or below */ -# define DRM_ELD_VER_CANNED (0x1f << 3) - -#define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ - -/* ELD Baseline Block for ELD_Ver == 2 */ -#define DRM_ELD_CEA_EDID_VER_MNL 4 -# define DRM_ELD_CEA_EDID_VER_SHIFT 5 -# define DRM_ELD_CEA_EDID_VER_MASK (7 << 5) -# define DRM_ELD_CEA_EDID_VER_NONE (0 << 5) -# define DRM_ELD_CEA_EDID_VER_CEA861 (1 << 5) -# define DRM_ELD_CEA_EDID_VER_CEA861A (2 << 5) -# define DRM_ELD_CEA_EDID_VER_CEA861BCD (3 << 5) -# define DRM_ELD_MNL_SHIFT 0 -# define DRM_ELD_MNL_MASK (0x1f << 0) - -#define DRM_ELD_SAD_COUNT_CONN_TYPE 5 -# define DRM_ELD_SAD_COUNT_SHIFT 4 -# define DRM_ELD_SAD_COUNT_MASK (0xf << 4) -# define DRM_ELD_CONN_TYPE_SHIFT 2 -# define DRM_ELD_CONN_TYPE_MASK (3 << 2) -# define DRM_ELD_CONN_TYPE_HDMI (0 << 2) -# define DRM_ELD_CONN_TYPE_DP (1 << 2) -# define DRM_ELD_SUPPORTS_AI (1 << 1) -# define DRM_ELD_SUPPORTS_HDCP (1 << 0) - -#define DRM_ELD_AUD_SYNCH_DELAY 6 /* in units of 2 ms */ -# define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */ - -#define DRM_ELD_SPEAKER 7 -# define DRM_ELD_SPEAKER_MASK 0x7f -# define DRM_ELD_SPEAKER_RLRC (1 << 6) -# define DRM_ELD_SPEAKER_FLRC (1 << 5) -# define DRM_ELD_SPEAKER_RC (1 << 4) -# define DRM_ELD_SPEAKER_RLR (1 << 3) -# define DRM_ELD_SPEAKER_FC (1 << 2) -# define DRM_ELD_SPEAKER_LFE (1 << 1) -# define DRM_ELD_SPEAKER_FLR (1 << 0) - -#define DRM_ELD_PORT_ID 8 /* offsets 8..15 inclusive */ -# define DRM_ELD_PORT_ID_LEN 8 - -#define DRM_ELD_MANUFACTURER_NAME0 16 -#define DRM_ELD_MANUFACTURER_NAME1 17 - -#define DRM_ELD_PRODUCT_CODE0 18 -#define DRM_ELD_PRODUCT_CODE1 19 - -#define DRM_ELD_MONITOR_NAME_STRING 20 /* offsets 20..(20+mnl-1) inclusive */ - -#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad)) +/* HDMI 2.1 additional fields */ +#define DRM_EDID_MAX_FRL_RATE_MASK 0xf0 +#define DRM_EDID_FAPA_START_LOCATION (1 << 0) +#define DRM_EDID_ALLM (1 << 1) +#define DRM_EDID_FVA (1 << 2) + +/* Deep Color specific */ +#define DRM_EDID_DC_30BIT_420 (1 << 0) +#define DRM_EDID_DC_36BIT_420 (1 << 1) +#define DRM_EDID_DC_48BIT_420 (1 << 2) + +/* VRR specific */ +#define DRM_EDID_CNMVRR (1 << 3) +#define DRM_EDID_CINEMA_VRR (1 << 4) +#define DRM_EDID_MDELTA (1 << 5) +#define DRM_EDID_VRR_MAX_UPPER_MASK 0xc0 +#define DRM_EDID_VRR_MAX_LOWER_MASK 0xff +#define DRM_EDID_VRR_MIN_MASK 0x3f + +/* DSC specific */ +#define DRM_EDID_DSC_10BPC (1 << 0) +#define DRM_EDID_DSC_12BPC (1 << 1) +#define DRM_EDID_DSC_16BPC (1 << 2) +#define DRM_EDID_DSC_ALL_BPP (1 << 3) +#define DRM_EDID_DSC_NATIVE_420 (1 << 6) +#define DRM_EDID_DSC_1P2 (1 << 7) +#define DRM_EDID_DSC_MAX_FRL_RATE_MASK 0xf0 +#define DRM_EDID_DSC_MAX_SLICES 0xf +#define DRM_EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f + +struct drm_edid_product_id { + __be16 manufacturer_name; + __le16 product_code; + __le32 serial_number; + u8 week_of_manufacture; + u8 year_of_manufacture; +} __packed; struct edid { u8 header[8]; /* Vendor & product info */ - u8 mfg_id[2]; - u8 prod_code[2]; - u32 serial; /* FIXME: byte order */ - u8 mfg_week; - u8 mfg_year; + union { + struct drm_edid_product_id product_id; + struct { + u8 mfg_id[2]; + u8 prod_code[2]; + u32 serial; /* FIXME: byte order */ + u8 mfg_week; + u8 mfg_year; + } __packed; + } __packed; /* EDID version */ u8 version; u8 revision; @@ -298,7 +312,7 @@ struct edid { u8 features; /* Color characteristics */ u8 red_green_lo; - u8 black_white_lo; + u8 blue_white_lo; u8 red_x; u8 red_y; u8 green_x; @@ -317,7 +331,20 @@ struct edid { u8 extensions; /* Checksum */ u8 checksum; -} __attribute__((packed)); +} __packed; + +/* EDID matching */ +struct drm_edid_ident { + /* ID encoded by drm_edid_encode_panel_id() */ + u32 panel_id; + const char *name; +}; + +#define DRM_EDID_IDENT_INIT(_vend_chr_0, _vend_chr_1, _vend_chr_2, _product_id, _name) \ +{ \ + .panel_id = drm_edid_encode_panel_id(_vend_chr_0, _vend_chr_1, _vend_chr_2, _product_id), \ + .name = _name, \ +} #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) @@ -329,163 +356,139 @@ struct cea_sad { u8 byte2; /* meaning depends on format */ }; -struct drm_encoder; -struct drm_connector; -struct drm_display_mode; - -int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads); -int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb); +int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads); +int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb); int drm_av_sync_delay(struct drm_connector *connector, const struct drm_display_mode *mode); -#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE -struct edid *drm_load_edid_firmware(struct drm_connector *connector); -int __drm_set_edid_firmware_path(const char *path); -int __drm_get_edid_firmware_path(char *buf, size_t bufsize); -#else -static inline struct edid * -drm_load_edid_firmware(struct drm_connector *connector) -{ - return ERR_PTR(-ENOENT); -} -#endif - int drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, - const struct drm_display_mode *mode, - bool is_hdmi2_sink); + const struct drm_connector *connector, + const struct drm_display_mode *mode); int drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, - struct drm_connector *connector, + const struct drm_connector *connector, const struct drm_display_mode *mode); + void drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame, + const struct drm_connector *connector, const struct drm_display_mode *mode, - enum hdmi_quantization_range rgb_quant_range, - bool rgb_quant_range_selectable, - bool is_hdmi2_sink); + enum hdmi_quantization_range rgb_quant_range); /** - * drm_eld_mnl - Get ELD monitor name length in bytes. - * @eld: pointer to an eld memory structure with mnl set + * drm_edid_decode_mfg_id - Decode the manufacturer ID + * @mfg_id: The manufacturer ID + * @vend: A 4-byte buffer to store the 3-letter vendor string plus a '\0' + * termination */ -static inline int drm_eld_mnl(const uint8_t *eld) +static inline const char *drm_edid_decode_mfg_id(u16 mfg_id, char vend[4]) { - return (eld[DRM_ELD_CEA_EDID_VER_MNL] & DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; -} + vend[0] = '@' + ((mfg_id >> 10) & 0x1f); + vend[1] = '@' + ((mfg_id >> 5) & 0x1f); + vend[2] = '@' + ((mfg_id >> 0) & 0x1f); + vend[3] = '\0'; -/** - * drm_eld_sad - Get ELD SAD structures. - * @eld: pointer to an eld memory structure with sad_count set - */ -static inline const uint8_t *drm_eld_sad(const uint8_t *eld) -{ - unsigned int ver, mnl; - - ver = (eld[DRM_ELD_VER] & DRM_ELD_VER_MASK) >> DRM_ELD_VER_SHIFT; - if (ver != 2 && ver != 31) - return NULL; - - mnl = drm_eld_mnl(eld); - if (mnl > 16) - return NULL; - - return eld + DRM_ELD_CEA_SAD(mnl, 0); -} - -/** - * drm_eld_sad_count - Get ELD SAD count. - * @eld: pointer to an eld memory structure with sad_count set - */ -static inline int drm_eld_sad_count(const uint8_t *eld) -{ - return (eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_SAD_COUNT_MASK) >> - DRM_ELD_SAD_COUNT_SHIFT; + return vend; } /** - * drm_eld_calc_baseline_block_size - Calculate baseline block size in bytes - * @eld: pointer to an eld memory structure with mnl and sad_count set + * drm_edid_encode_panel_id - Encode an ID for matching against drm_edid_get_panel_id() + * @vend_chr_0: First character of the vendor string. + * @vend_chr_1: Second character of the vendor string. + * @vend_chr_2: Third character of the vendor string. + * @product_id: The 16-bit product ID. * - * This is a helper for determining the payload size of the baseline block, in - * bytes, for e.g. setting the Baseline_ELD_Len field in the ELD header block. - */ -static inline int drm_eld_calc_baseline_block_size(const uint8_t *eld) -{ - return DRM_ELD_MONITOR_NAME_STRING - DRM_ELD_HEADER_BLOCK_SIZE + - drm_eld_mnl(eld) + drm_eld_sad_count(eld) * 3; -} - -/** - * drm_eld_size - Get ELD size in bytes - * @eld: pointer to a complete eld memory structure + * This is a macro so that it can be calculated at compile time and used + * as an initializer. * - * The returned value does not include the vendor block. It's vendor specific, - * and comprises of the remaining bytes in the ELD memory buffer after - * drm_eld_size() bytes of header and baseline block. + * For instance: + * drm_edid_encode_panel_id('B', 'O', 'E', 0x2d08) => 0x09e52d08 * - * The returned value is guaranteed to be a multiple of 4. + * Return: a 32-bit ID per panel. */ -static inline int drm_eld_size(const uint8_t *eld) -{ - return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; -} +#define drm_edid_encode_panel_id(vend_chr_0, vend_chr_1, vend_chr_2, product_id) \ + ((((u32)(vend_chr_0) - '@') & 0x1f) << 26 | \ + (((u32)(vend_chr_1) - '@') & 0x1f) << 21 | \ + (((u32)(vend_chr_2) - '@') & 0x1f) << 16 | \ + ((product_id) & 0xffff)) /** - * drm_eld_get_spk_alloc - Get speaker allocation - * @eld: pointer to an ELD memory structure + * drm_edid_decode_panel_id - Decode a panel ID from drm_edid_encode_panel_id() + * @panel_id: The panel ID to decode. + * @vend: A 4-byte buffer to store the 3-letter vendor string plus a '\0' + * termination + * @product_id: The product ID will be returned here. * - * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER - * field definitions to identify speakers. + * For instance, after: + * drm_edid_decode_panel_id(0x09e52d08, vend, &product_id) + * These will be true: + * vend[0] = 'B' + * vend[1] = 'O' + * vend[2] = 'E' + * vend[3] = '\0' + * product_id = 0x2d08 */ -static inline u8 drm_eld_get_spk_alloc(const uint8_t *eld) +static inline void drm_edid_decode_panel_id(u32 panel_id, char vend[4], u16 *product_id) { - return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK; -} - -/** - * drm_eld_get_conn_type - Get device type hdmi/dp connected - * @eld: pointer to an ELD memory structure - * - * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to - * identify the display type connected. - */ -static inline u8 drm_eld_get_conn_type(const uint8_t *eld) -{ - return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK; + *product_id = (u16)(panel_id & 0xffff); + drm_edid_decode_mfg_id(panel_id >> 16, vend); } bool drm_probe_ddc(struct i2c_adapter *adapter); -struct edid *drm_do_get_edid(struct drm_connector *connector, - int (*get_edid_block)(void *data, u8 *buf, unsigned int block, - size_t len), - void *data); struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); struct edid *drm_get_edid_switcheroo(struct drm_connector *connector, struct i2c_adapter *adapter); struct edid *drm_edid_duplicate(const struct edid *edid); int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); +int drm_edid_override_connector_update(struct drm_connector *connector); u8 drm_match_cea_mode(const struct drm_display_mode *to_match); -enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code); -bool drm_detect_hdmi_monitor(struct edid *edid); -bool drm_detect_monitor_audio(struct edid *edid); -bool drm_rgb_quant_range_selectable(struct edid *edid); +bool drm_detect_hdmi_monitor(const struct edid *edid); +bool drm_detect_monitor_audio(const struct edid *edid); enum hdmi_quantization_range drm_default_rgb_quant_range(const struct drm_display_mode *mode); int drm_add_modes_noedid(struct drm_connector *connector, - int hdisplay, int vdisplay); -void drm_set_preferred_mode(struct drm_connector *connector, - int hpref, int vpref); + unsigned int hdisplay, unsigned int vdisplay); -int drm_edid_header_is_valid(const u8 *raw_edid); -bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, - bool *edid_corrupt); +int drm_edid_header_is_valid(const void *edid); bool drm_edid_is_valid(struct edid *edid); -void drm_edid_get_monitor_name(struct edid *edid, char *name, +void drm_edid_get_monitor_name(const struct edid *edid, char *name, int buflen); struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, bool rb); +struct drm_display_mode * +drm_display_mode_from_cea_vic(struct drm_device *dev, + u8 video_code); + +/* Interface based on struct drm_edid */ +const struct drm_edid *drm_edid_alloc(const void *edid, size_t size); +const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid); +void drm_edid_free(const struct drm_edid *drm_edid); +bool drm_edid_valid(const struct drm_edid *drm_edid); +const struct edid *drm_edid_raw(const struct drm_edid *drm_edid); +const struct drm_edid *drm_edid_read(struct drm_connector *connector); +const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, + struct i2c_adapter *adapter); +const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, + int (*read_block)(void *context, u8 *buf, unsigned int block, size_t len), + void *context); +const struct drm_edid *drm_edid_read_base_block(struct i2c_adapter *adapter); +const struct drm_edid *drm_edid_read_switcheroo(struct drm_connector *connector, + struct i2c_adapter *adapter); +int drm_edid_connector_update(struct drm_connector *connector, + const struct drm_edid *edid); +int drm_edid_connector_add_modes(struct drm_connector *connector); +bool drm_edid_is_digital(const struct drm_edid *drm_edid); +void drm_edid_get_product_id(const struct drm_edid *drm_edid, + struct drm_edid_product_id *id); +void drm_edid_print_product_id(struct drm_printer *p, + const struct drm_edid_product_id *id, bool raw); +u32 drm_edid_get_panel_id(const struct drm_edid *drm_edid); +bool drm_edid_match(const struct drm_edid *drm_edid, + const struct drm_edid_ident *ident); +bool drm_edid_has_quirk(struct drm_connector *connector, enum drm_edid_quirk quirk); + #endif /* __DRM_EDID_H__ */ diff --git a/include/drm/drm_eld.h b/include/drm/drm_eld.h new file mode 100644 index 000000000000..0a88d10b28b0 --- /dev/null +++ b/include/drm/drm_eld.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __DRM_ELD_H__ +#define __DRM_ELD_H__ + +#include <linux/types.h> + +struct cea_sad; + +/* ELD Header Block */ +#define DRM_ELD_HEADER_BLOCK_SIZE 4 + +#define DRM_ELD_VER 0 +# define DRM_ELD_VER_SHIFT 3 +# define DRM_ELD_VER_MASK (0x1f << 3) +# define DRM_ELD_VER_CEA861D (2 << 3) /* supports 861D or below */ +# define DRM_ELD_VER_CANNED (0x1f << 3) + +#define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ + +/* ELD Baseline Block for ELD_Ver == 2 */ +#define DRM_ELD_CEA_EDID_VER_MNL 4 +# define DRM_ELD_CEA_EDID_VER_SHIFT 5 +# define DRM_ELD_CEA_EDID_VER_MASK (7 << 5) +# define DRM_ELD_CEA_EDID_VER_NONE (0 << 5) +# define DRM_ELD_CEA_EDID_VER_CEA861 (1 << 5) +# define DRM_ELD_CEA_EDID_VER_CEA861A (2 << 5) +# define DRM_ELD_CEA_EDID_VER_CEA861BCD (3 << 5) +# define DRM_ELD_MNL_SHIFT 0 +# define DRM_ELD_MNL_MASK (0x1f << 0) + +#define DRM_ELD_SAD_COUNT_CONN_TYPE 5 +# define DRM_ELD_SAD_COUNT_SHIFT 4 +# define DRM_ELD_SAD_COUNT_MASK (0xf << 4) +# define DRM_ELD_CONN_TYPE_SHIFT 2 +# define DRM_ELD_CONN_TYPE_MASK (3 << 2) +# define DRM_ELD_CONN_TYPE_HDMI (0 << 2) +# define DRM_ELD_CONN_TYPE_DP (1 << 2) +# define DRM_ELD_SUPPORTS_AI (1 << 1) +# define DRM_ELD_SUPPORTS_HDCP (1 << 0) + +#define DRM_ELD_AUD_SYNCH_DELAY 6 /* in units of 2 ms */ +# define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */ + +#define DRM_ELD_SPEAKER 7 +# define DRM_ELD_SPEAKER_MASK 0x7f +# define DRM_ELD_SPEAKER_RLRC (1 << 6) +# define DRM_ELD_SPEAKER_FLRC (1 << 5) +# define DRM_ELD_SPEAKER_RC (1 << 4) +# define DRM_ELD_SPEAKER_RLR (1 << 3) +# define DRM_ELD_SPEAKER_FC (1 << 2) +# define DRM_ELD_SPEAKER_LFE (1 << 1) +# define DRM_ELD_SPEAKER_FLR (1 << 0) + +#define DRM_ELD_PORT_ID 8 /* offsets 8..15 inclusive */ +# define DRM_ELD_PORT_ID_LEN 8 + +#define DRM_ELD_MANUFACTURER_NAME0 16 +#define DRM_ELD_MANUFACTURER_NAME1 17 + +#define DRM_ELD_PRODUCT_CODE0 18 +#define DRM_ELD_PRODUCT_CODE1 19 + +#define DRM_ELD_MONITOR_NAME_STRING 20 /* offsets 20..(20+mnl-1) inclusive */ + +#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad)) + +/** + * drm_eld_mnl - Get ELD monitor name length in bytes. + * @eld: pointer to an eld memory structure with mnl set + */ +static inline int drm_eld_mnl(const u8 *eld) +{ + return (eld[DRM_ELD_CEA_EDID_VER_MNL] & DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; +} + +int drm_eld_sad_get(const u8 *eld, int sad_index, struct cea_sad *cta_sad); +int drm_eld_sad_set(u8 *eld, int sad_index, const struct cea_sad *cta_sad); + +/** + * drm_eld_sad - Get ELD SAD structures. + * @eld: pointer to an eld memory structure with sad_count set + */ +static inline const u8 *drm_eld_sad(const u8 *eld) +{ + unsigned int ver, mnl; + + ver = (eld[DRM_ELD_VER] & DRM_ELD_VER_MASK) >> DRM_ELD_VER_SHIFT; + if (ver != 2 && ver != 31) + return NULL; + + mnl = drm_eld_mnl(eld); + if (mnl > 16) + return NULL; + + return eld + DRM_ELD_CEA_SAD(mnl, 0); +} + +/** + * drm_eld_sad_count - Get ELD SAD count. + * @eld: pointer to an eld memory structure with sad_count set + */ +static inline int drm_eld_sad_count(const u8 *eld) +{ + return (eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_SAD_COUNT_MASK) >> + DRM_ELD_SAD_COUNT_SHIFT; +} + +/** + * drm_eld_calc_baseline_block_size - Calculate baseline block size in bytes + * @eld: pointer to an eld memory structure with mnl and sad_count set + * + * This is a helper for determining the payload size of the baseline block, in + * bytes, for e.g. setting the Baseline_ELD_Len field in the ELD header block. + */ +static inline int drm_eld_calc_baseline_block_size(const u8 *eld) +{ + return DRM_ELD_MONITOR_NAME_STRING - DRM_ELD_HEADER_BLOCK_SIZE + + drm_eld_mnl(eld) + drm_eld_sad_count(eld) * 3; +} + +/** + * drm_eld_size - Get ELD size in bytes + * @eld: pointer to a complete eld memory structure + * + * The returned value does not include the vendor block. It's vendor specific, + * and comprises of the remaining bytes in the ELD memory buffer after + * drm_eld_size() bytes of header and baseline block. + * + * The returned value is guaranteed to be a multiple of 4. + */ +static inline int drm_eld_size(const u8 *eld) +{ + return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; +} + +/** + * drm_eld_get_spk_alloc - Get speaker allocation + * @eld: pointer to an ELD memory structure + * + * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER + * field definitions to identify speakers. + */ +static inline u8 drm_eld_get_spk_alloc(const u8 *eld) +{ + return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK; +} + +/** + * drm_eld_get_conn_type - Get device type hdmi/dp connected + * @eld: pointer to an ELD memory structure + * + * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to + * identify the display type connected. + */ +static inline u8 drm_eld_get_conn_type(const u8 *eld) +{ + return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK; +} + +#endif /* __DRM_ELD_H__ */ diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 70cfca03d812..977a9381c8ba 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -60,7 +60,7 @@ struct drm_encoder_funcs { * @late_register: * * This optional hook can be used to register additional userspace - * interfaces attached to the encoder like debugfs interfaces. + * interfaces attached to the encoder. * It is called late in the driver load sequence from drm_dev_register(). * Everything added from this callback should be unregistered in * the early_unregister callback. @@ -81,6 +81,13 @@ struct drm_encoder_funcs { * before data structures are torndown. */ void (*early_unregister)(struct drm_encoder *encoder); + + /** + * @debugfs_init: + * + * Allows encoders to create encoder-specific debugfs files. + */ + void (*debugfs_init)(struct drm_encoder *encoder, struct dentry *root); }; /** @@ -89,8 +96,7 @@ struct drm_encoder_funcs { * @head: list management * @base: base KMS object * @name: human readable name, can be overwritten by the driver - * @bridge: bridge associated to the encoder - * @funcs: control functions + * @funcs: control functions, can be NULL for simple managed encoders * @helper_private: mid-layer private data * * CRTCs drive pixels to encoders, which convert them into signals @@ -140,9 +146,9 @@ struct drm_encoder { * @possible_crtcs: Bitmask of potential CRTC bindings, using * drm_crtc_index() as the index into the bitfield. The driver must set * the bits for all &drm_crtc objects this encoder can be connected to - * before calling drm_encoder_init(). + * before calling drm_dev_register(). * - * In reality almost every driver gets this wrong. + * You will get a WARN if you get this wrong in the driver. * * Note that since CRTC objects can't be hotplugged the assigned indices * are stable and hence known before registering all objects. @@ -154,12 +160,16 @@ struct drm_encoder { * using drm_encoder_index() as the index into the bitfield. The driver * must set the bits for all &drm_encoder objects which can clone a * &drm_crtc together with this encoder before calling - * drm_encoder_init(). Drivers should set the bit representing the + * drm_dev_register(). Drivers should set the bit representing the * encoder itself, too. Cloning bits should be set such that when two * encoders can be used in a cloned configuration, they both should have * each another bits set. * - * In reality almost every driver gets this wrong. + * As an exception to the above rule if the driver doesn't implement + * any cloning it can leave @possible_clones set to 0. The core will + * automagically fix this up by setting the bit for the encoder itself. + * + * You will get a WARN if you get this wrong in the driver. * * Note that since encoder objects can't be hotplugged the assigned indices * are stable and hence known before registering all objects. @@ -172,9 +182,22 @@ struct drm_encoder { * &drm_connector_state.crtc. */ struct drm_crtc *crtc; - struct drm_bridge *bridge; + + /** + * @bridge_chain: Bridges attached to this encoder. Drivers shall not + * access this field directly. + */ + struct list_head bridge_chain; + const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private; + + /** + * @debugfs_entry: + * + * Debugfs directory for this CRTC. + */ + struct dentry *debugfs_entry; }; #define obj_to_encoder(x) container_of(x, struct drm_encoder, base) @@ -185,6 +208,60 @@ int drm_encoder_init(struct drm_device *dev, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...); +__printf(5, 6) +int drmm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...); + +__printf(6, 7) +void *__drmm_encoder_alloc(struct drm_device *dev, + size_t size, size_t offset, + const struct drm_encoder_funcs *funcs, + int encoder_type, + const char *name, ...); + +/** + * drmm_encoder_alloc - Allocate and initialize an encoder + * @dev: drm device + * @type: the type of the struct which contains struct &drm_encoder + * @member: the name of the &drm_encoder within @type + * @funcs: callbacks for this encoder (optional) + * @encoder_type: user visible type of the encoder + * @name: printf style format string for the encoder name, or NULL for default name + * + * Allocates and initializes an encoder. Encoder should be subclassed as part of + * driver encoder objects. Cleanup is automatically handled through registering + * drm_encoder_cleanup() with drmm_add_action(). + * + * The @drm_encoder_funcs.destroy hook must be NULL. + * + * Returns: + * Pointer to new encoder, or ERR_PTR on failure. + */ +#define drmm_encoder_alloc(dev, type, member, funcs, encoder_type, name, ...) \ + ((type *)__drmm_encoder_alloc(dev, sizeof(type), \ + offsetof(type, member), funcs, \ + encoder_type, name, ##__VA_ARGS__)) + +/** + * drmm_plain_encoder_alloc - Allocate and initialize an encoder + * @dev: drm device + * @funcs: callbacks for this encoder (optional) + * @encoder_type: user visible type of the encoder + * @name: printf style format string for the encoder name, or NULL for default name + * + * This is a simplified version of drmm_encoder_alloc(), which only allocates + * and returns a struct drm_encoder instance, with no subclassing. + * + * Returns: + * Pointer to the new drm_encoder struct, or ERR_PTR on failure. + */ +#define drmm_plain_encoder_alloc(dev, funcs, encoder_type, name, ...) \ + ((struct drm_encoder *) \ + __drmm_encoder_alloc(dev, sizeof(struct drm_encoder), \ + 0, funcs, encoder_type, name, ##__VA_ARGS__)) + /** * drm_encoder_index - find the index of a registered encoder * @encoder: encoder to find index for @@ -198,7 +275,7 @@ static inline unsigned int drm_encoder_index(const struct drm_encoder *encoder) } /** - * drm_encoder_mask - find the mask of a registered ENCODER + * drm_encoder_mask - find the mask of a registered encoder * @encoder: encoder to find mask for * * Given a registered encoder, return the mask bit of that encoder for an diff --git a/include/drm/drm_encoder_slave.h b/include/drm/drm_encoder_slave.h deleted file mode 100644 index 1107b4b1c599..000000000000 --- a/include/drm/drm_encoder_slave.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __DRM_ENCODER_SLAVE_H__ -#define __DRM_ENCODER_SLAVE_H__ - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_encoder.h> - -/** - * struct drm_encoder_slave_funcs - Entry points exposed by a slave encoder driver - * @set_config: Initialize any encoder-specific modesetting parameters. - * The meaning of the @params parameter is implementation - * dependent. It will usually be a structure with DVO port - * data format settings or timings. It's not required for - * the new parameters to take effect until the next mode - * is set. - * - * Most of its members are analogous to the function pointers in - * &drm_encoder_helper_funcs and they can optionally be used to - * initialize the latter. Connector-like methods (e.g. @get_modes and - * @set_property) will typically be wrapped around and only be called - * if the encoder is the currently selected one for the connector. - */ -struct drm_encoder_slave_funcs { - void (*set_config)(struct drm_encoder *encoder, - void *params); - - void (*destroy)(struct drm_encoder *encoder); - void (*dpms)(struct drm_encoder *encoder, int mode); - void (*save)(struct drm_encoder *encoder); - void (*restore)(struct drm_encoder *encoder); - bool (*mode_fixup)(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); - int (*mode_valid)(struct drm_encoder *encoder, - struct drm_display_mode *mode); - void (*mode_set)(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); - - enum drm_connector_status (*detect)(struct drm_encoder *encoder, - struct drm_connector *connector); - int (*get_modes)(struct drm_encoder *encoder, - struct drm_connector *connector); - int (*create_resources)(struct drm_encoder *encoder, - struct drm_connector *connector); - int (*set_property)(struct drm_encoder *encoder, - struct drm_connector *connector, - struct drm_property *property, - uint64_t val); - -}; - -/** - * struct drm_encoder_slave - Slave encoder struct - * @base: DRM encoder object. - * @slave_funcs: Slave encoder callbacks. - * @slave_priv: Slave encoder private data. - * @bus_priv: Bus specific data. - * - * A &drm_encoder_slave has two sets of callbacks, @slave_funcs and the - * ones in @base. The former are never actually called by the common - * CRTC code, it's just a convenience for splitting the encoder - * functions in an upper, GPU-specific layer and a (hopefully) - * GPU-agnostic lower layer: It's the GPU driver responsibility to - * call the slave methods when appropriate. - * - * drm_i2c_encoder_init() provides a way to get an implementation of - * this. - */ -struct drm_encoder_slave { - struct drm_encoder base; - - const struct drm_encoder_slave_funcs *slave_funcs; - void *slave_priv; - void *bus_priv; -}; -#define to_encoder_slave(x) container_of((x), struct drm_encoder_slave, base) - -int drm_i2c_encoder_init(struct drm_device *dev, - struct drm_encoder_slave *encoder, - struct i2c_adapter *adap, - const struct i2c_board_info *info); - - -/** - * struct drm_i2c_encoder_driver - * - * Describes a device driver for an encoder connected to the GPU - * through an I2C bus. In addition to the entry points in @i2c_driver - * an @encoder_init function should be provided. It will be called to - * give the driver an opportunity to allocate any per-encoder data - * structures and to initialize the @slave_funcs and (optionally) - * @slave_priv members of @encoder. - */ -struct drm_i2c_encoder_driver { - struct i2c_driver i2c_driver; - - int (*encoder_init)(struct i2c_client *client, - struct drm_device *dev, - struct drm_encoder_slave *encoder); - -}; -#define to_drm_i2c_encoder_driver(x) container_of((x), \ - struct drm_i2c_encoder_driver, \ - i2c_driver) - -/** - * drm_i2c_encoder_get_client - Get the I2C client corresponding to an encoder - */ -static inline struct i2c_client *drm_i2c_encoder_get_client(struct drm_encoder *encoder) -{ - return (struct i2c_client *)to_encoder_slave(encoder)->bus_priv; -} - -/** - * drm_i2c_encoder_register - Register an I2C encoder driver - * @owner: Module containing the driver. - * @driver: Driver to be registered. - */ -static inline int drm_i2c_encoder_register(struct module *owner, - struct drm_i2c_encoder_driver *driver) -{ - return i2c_register_driver(owner, &driver->i2c_driver); -} - -/** - * drm_i2c_encoder_unregister - Unregister an I2C encoder driver - * @driver: Driver to be unregistered. - */ -static inline void drm_i2c_encoder_unregister(struct drm_i2c_encoder_driver *driver) -{ - i2c_del_driver(&driver->i2c_driver); -} - -void drm_i2c_encoder_destroy(struct drm_encoder *encoder); - - -/* - * Wrapper fxns which can be plugged in to drm_encoder_helper_funcs: - */ - -void drm_i2c_encoder_dpms(struct drm_encoder *encoder, int mode); -bool drm_i2c_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -void drm_i2c_encoder_prepare(struct drm_encoder *encoder); -void drm_i2c_encoder_commit(struct drm_encoder *encoder); -void drm_i2c_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -enum drm_connector_status drm_i2c_encoder_detect(struct drm_encoder *encoder, - struct drm_connector *connector); -void drm_i2c_encoder_save(struct drm_encoder *encoder); -void drm_i2c_encoder_restore(struct drm_encoder *encoder); - - -#endif diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h new file mode 100644 index 000000000000..aa786b828a0a --- /dev/null +++ b/include/drm/drm_exec.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +#ifndef __DRM_EXEC_H__ +#define __DRM_EXEC_H__ + +#include <linux/compiler.h> +#include <linux/ww_mutex.h> + +#define DRM_EXEC_INTERRUPTIBLE_WAIT BIT(0) +#define DRM_EXEC_IGNORE_DUPLICATES BIT(1) + +struct drm_gem_object; + +/** + * struct drm_exec - Execution context + */ +struct drm_exec { + /** + * @flags: Flags to control locking behavior + */ + u32 flags; + + /** + * @ticket: WW ticket used for acquiring locks + */ + struct ww_acquire_ctx ticket; + + /** + * @num_objects: number of objects locked + */ + unsigned int num_objects; + + /** + * @max_objects: maximum objects in array + */ + unsigned int max_objects; + + /** + * @objects: array of the locked objects + */ + struct drm_gem_object **objects; + + /** + * @contended: contended GEM object we backed off for + */ + struct drm_gem_object *contended; + + /** + * @prelocked: already locked GEM object due to contention + */ + struct drm_gem_object *prelocked; +}; + +/** + * drm_exec_obj() - Return the object for a give drm_exec index + * @exec: Pointer to the drm_exec context + * @index: The index. + * + * Return: Pointer to the locked object corresponding to @index if + * index is within the number of locked objects. NULL otherwise. + */ +static inline struct drm_gem_object * +drm_exec_obj(struct drm_exec *exec, unsigned long index) +{ + return index < exec->num_objects ? exec->objects[index] : NULL; +} + +/** + * drm_exec_for_each_locked_object - iterate over all the locked objects + * @exec: drm_exec object + * @index: unsigned long index for the iteration + * @obj: the current GEM object + * + * Iterate over all the locked GEM objects inside the drm_exec object. + */ +#define drm_exec_for_each_locked_object(exec, index, obj) \ + for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index)) + +/** + * drm_exec_for_each_locked_object_reverse - iterate over all the locked + * objects in reverse locking order + * @exec: drm_exec object + * @index: unsigned long index for the iteration + * @obj: the current GEM object + * + * Iterate over all the locked GEM objects inside the drm_exec object in + * reverse locking order. Note that @index may go below zero and wrap, + * but that will be caught by drm_exec_obj(), returning a NULL object. + */ +#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \ + for ((index) = (exec)->num_objects - 1; \ + ((obj) = drm_exec_obj(exec, index)); --(index)) + +/** + * drm_exec_until_all_locked - loop until all GEM objects are locked + * @exec: drm_exec object + * + * Core functionality of the drm_exec object. Loops until all GEM objects are + * locked and no more contention exists. At the beginning of the loop it is + * guaranteed that no GEM object is locked. + * + * Since labels can't be defined local to the loops body we use a jump pointer + * to make sure that the retry is only used from within the loops body. + */ +#define drm_exec_until_all_locked(exec) \ +__PASTE(__drm_exec_, __LINE__): \ + for (void *__drm_exec_retry_ptr; ({ \ + __drm_exec_retry_ptr = &&__PASTE(__drm_exec_, __LINE__);\ + (void)__drm_exec_retry_ptr; \ + drm_exec_cleanup(exec); \ + });) + +/** + * drm_exec_retry_on_contention - restart the loop to grap all locks + * @exec: drm_exec object + * + * Control flow helper to continue when a contention was detected and we need to + * clean up and re-start the loop to prepare all GEM objects. + */ +#define drm_exec_retry_on_contention(exec) \ + do { \ + if (unlikely(drm_exec_is_contended(exec))) \ + goto *__drm_exec_retry_ptr; \ + } while (0) + +/** + * drm_exec_is_contended - check for contention + * @exec: drm_exec object + * + * Returns true if the drm_exec object has run into some contention while + * locking a GEM object and needs to clean up. + */ +static inline bool drm_exec_is_contended(struct drm_exec *exec) +{ + return !!exec->contended; +} + +void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr); +void drm_exec_fini(struct drm_exec *exec); +bool drm_exec_cleanup(struct drm_exec *exec); +int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj); +void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj); +int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj, + unsigned int num_fences); +int drm_exec_prepare_array(struct drm_exec *exec, + struct drm_gem_object **objects, + unsigned int num_objects, + unsigned int num_fences); + +#endif diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h deleted file mode 100644 index 8dbbe1eece1b..000000000000 --- a/include/drm/drm_fb_cma_helper.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __DRM_FB_CMA_HELPER_H__ -#define __DRM_FB_CMA_HELPER_H__ - -struct drm_fbdev_cma; -struct drm_gem_cma_object; - -struct drm_fb_helper_surface_size; -struct drm_framebuffer_funcs; -struct drm_fb_helper_funcs; -struct drm_framebuffer; -struct drm_fb_helper; -struct drm_device; -struct drm_file; -struct drm_mode_fb_cmd2; -struct drm_plane; -struct drm_plane_state; - -int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, - unsigned int max_conn_count); -void drm_fb_cma_fbdev_fini(struct drm_device *dev); - -struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int max_conn_count); -void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); - -void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); -void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma); - -struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, - unsigned int plane); - -dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, - struct drm_plane_state *state, - unsigned int plane); - -#endif - diff --git a/include/drm/drm_fb_dma_helper.h b/include/drm/drm_fb_dma_helper.h new file mode 100644 index 000000000000..c950732c6d36 --- /dev/null +++ b/include/drm/drm_fb_dma_helper.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DRM_FB_DMA_HELPER_H__ +#define __DRM_FB_DMA_HELPER_H__ + +#include <linux/types.h> + +struct drm_device; +struct drm_framebuffer; +struct drm_plane; +struct drm_plane_state; +struct drm_scanout_buffer; + +struct drm_gem_dma_object *drm_fb_dma_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane); + +dma_addr_t drm_fb_dma_get_gem_addr(struct drm_framebuffer *fb, + struct drm_plane_state *state, + unsigned int plane); + +void drm_fb_dma_sync_non_coherent(struct drm_device *drm, + struct drm_plane_state *old_state, + struct drm_plane_state *state); + +int drm_fb_dma_get_scanout_buffer(struct drm_plane *plane, + struct drm_scanout_buffer *sb); + +#endif + diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index bb9acea61369..dd9a18f8de5a 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -30,28 +30,12 @@ #ifndef DRM_FB_HELPER_H #define DRM_FB_HELPER_H +struct drm_clip_rect; struct drm_fb_helper; -#include <drm/drm_client.h> -#include <drm/drm_crtc.h> -#include <drm/drm_device.h> -#include <linux/kgdb.h> - -enum mode_set_atomic { - LEAVE_ATOMIC_MODE_SET, - ENTER_ATOMIC_MODE_SET, -}; - -struct drm_fb_offset { - int x, y; -}; +#include <linux/fb.h> -struct drm_fb_helper_crtc { - struct drm_mode_set mode_set; - struct drm_display_mode *desired_mode; - int x, y; - int rotation; -}; +#include <drm/drm_client.h> /** * struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size @@ -67,10 +51,8 @@ struct drm_fb_helper_crtc { * according to the largest width/height (so it is large enough for all CRTCs * to scanout). But the fbdev width/height is sized to the minimum width/ * height of all the displays. This ensures that fbcon fits on the smallest - * of the attached displays. - * - * So what is passed to drm_fb_helper_fill_var() should be fb_width/fb_height, - * rather than the surface size. + * of the attached displays. fb_width/fb_height is used by + * drm_fb_helper_fill_info() to fill out the &fb_info.var structure. */ struct drm_fb_helper_surface_size { u32 fb_width; @@ -88,65 +70,58 @@ struct drm_fb_helper_surface_size { */ struct drm_fb_helper_funcs { /** - * @fb_probe: - * - * Driver callback to allocate and initialize the fbdev info structure. - * Furthermore it also needs to allocate the DRM framebuffer used to - * back the fbdev. + * @fb_dirty: * - * This callback is mandatory. + * Driver callback to update the framebuffer memory. If set, fbdev + * emulation will invoke this callback in regular intervals after + * the framebuffer has been written. * - * RETURNS: + * This callback is optional. * - * The driver should return 0 on success and a negative error code on - * failure. + * Returns: + * 0 on success, or an error code otherwise. */ - int (*fb_probe)(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes); + int (*fb_dirty)(struct drm_fb_helper *helper, struct drm_clip_rect *clip); /** - * @initial_config: - * - * Driver callback to setup an initial fbdev display configuration. - * Drivers can use this callback to tell the fbdev emulation what the - * preferred initial configuration is. This is useful to implement - * smooth booting where the fbdev (and subsequently all userspace) never - * changes the mode, but always inherits the existing configuration. + * @fb_restore: * - * This callback is optional. + * Driver callback to restore internal fbdev state. If set, fbdev + * emulation will invoke this callback after restoring the display + * mode. * - * RETURNS: + * Only for i915. Do not use in new code. * - * The driver should return true if a suitable initial configuration has - * been filled out and false when the fbdev helper should fall back to - * the default probing logic. + * TODO: Fix i915 to not require this callback. */ - bool (*initial_config)(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height); -}; + void (*fb_restore)(struct drm_fb_helper *helper); -struct drm_fb_helper_connector { - struct drm_connector *connector; + /** + * @fb_set_suspend: + * + * Driver callback to suspend or resume, if set, fbdev emulation will + * invoke this callback during suspend and resume. Driver should call + * fb_set_suspend() from their implementation. If not set, fbdev + * emulation will invoke fb_set_suspend() directly. + * + * Only for i915. Do not use in new code. + * + * TODO: Fix i915 to not require this callback. + */ + void (*fb_set_suspend)(struct drm_fb_helper *helper, bool suspend); }; /** * struct drm_fb_helper - main structure to emulate fbdev on top of KMS * @fb: Scanout framebuffer object * @dev: DRM device - * @crtc_count: number of possible CRTCs - * @crtc_info: per-CRTC helper state (mode, x/y offset, etc) - * @connector_count: number of connected connectors - * @connector_info_alloc_count: size of connector_info * @funcs: driver callbacks for fb helper - * @fbdev: emulated fbdev device info struct + * @info: emulated fbdev device info struct * @pseudo_palette: fake palette of 16 colors - * @dirty_clip: clip rectangle used with deferred_io to accumulate damage to - * the screen buffer - * @dirty_lock: spinlock protecting @dirty_clip - * @dirty_work: worker used to flush the framebuffer + * @damage_clip: clip rectangle used with deferred_io to accumulate damage to + * the screen buffer + * @damage_lock: spinlock protecting @damage_clip + * @damage_work: worker used to flush the framebuffer * @resume_work: worker used during resume if the console lock is already taken * * This is the main structure used by the fbdev helpers. Drivers supporting @@ -171,30 +146,12 @@ struct drm_fb_helper { struct drm_framebuffer *fb; struct drm_device *dev; - int crtc_count; - struct drm_fb_helper_crtc *crtc_info; - int connector_count; - int connector_info_alloc_count; - /** - * @sw_rotations: - * Bitmask of all rotations requested for panel-orientation which - * could not be handled in hardware. If only one bit is set - * fbdev->fbcon_rotate_hint gets set to the requested rotation. - */ - int sw_rotations; - /** - * @connector_info: - * - * Array of per-connector information. Do not iterate directly, but use - * drm_fb_helper_for_each_connector. - */ - struct drm_fb_helper_connector **connector_info; const struct drm_fb_helper_funcs *funcs; - struct fb_info *fbdev; + struct fb_info *info; u32 pseudo_palette[17]; - struct drm_clip_rect dirty_clip; - spinlock_t dirty_lock; - struct work_struct dirty_work; + struct drm_clip_rect damage_clip; + spinlock_t damage_lock; + struct work_struct damage_work; struct work_struct resume_work; /** @@ -247,6 +204,18 @@ struct drm_fb_helper { * See also: @deferred_setup */ int preferred_bpp; + +#ifdef CONFIG_FB_DEFERRED_IO + /** + * @fbdefio: + * + * Temporary storage for the driver's FB deferred I/O handler. If the + * driver uses the DRM fbdev emulation layer, this is set by the core + * to a generic deferred I/O handler if a driver is preferring to use + * a shadow buffer. + */ + struct fb_deferred_io fbdefio; +#endif }; static inline struct drm_fb_helper * @@ -273,9 +242,10 @@ drm_fb_helper_from_client(struct drm_client_dev *client) #ifdef CONFIG_DRM_FBDEV_EMULATION void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, + unsigned int preferred_bpp, const struct drm_fb_helper_funcs *funcs); -int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *helper, int max_conn); +void drm_fb_helper_unprepare(struct drm_fb_helper *fb_helper); +int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper); void drm_fb_helper_fini(struct drm_fb_helper *helper); int drm_fb_helper_blank(int blank, struct fb_info *info); int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, @@ -284,39 +254,20 @@ int drm_fb_helper_set_par(struct fb_info *info); int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info); -int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper); - -struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper); -void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper); -void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, - uint32_t fb_width, uint32_t fb_height); -void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, - uint32_t depth); - -void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper); +int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, + bool force); -void drm_fb_helper_deferred_io(struct fb_info *info, - struct list_head *pagelist); -int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper); +void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper); +void drm_fb_helper_fill_info(struct fb_info *info, + struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes); -ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf, - size_t count, loff_t *ppos); -ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos); +void drm_fb_helper_damage_range(struct fb_info *info, off_t off, size_t len); +void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height); -void drm_fb_helper_sys_fillrect(struct fb_info *info, - const struct fb_fillrect *rect); -void drm_fb_helper_sys_copyarea(struct fb_info *info, - const struct fb_copyarea *area); -void drm_fb_helper_sys_imageblit(struct fb_info *info, - const struct fb_image *image); - -void drm_fb_helper_cfb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect); -void drm_fb_helper_cfb_copyarea(struct fb_info *info, - const struct fb_copyarea *area); -void drm_fb_helper_cfb_imageblit(struct fb_info *info, - const struct fb_image *image); +#ifdef CONFIG_FB_DEFERRED_IO +void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist); +#endif void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend); void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, @@ -328,43 +279,23 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg); int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); -int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); +int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper); int drm_fb_helper_debug_enter(struct fb_info *info); int drm_fb_helper_debug_leave(struct fb_info *info); -struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height); -struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn); - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector); - -int drm_fb_helper_fbdev_setup(struct drm_device *dev, - struct drm_fb_helper *fb_helper, - const struct drm_fb_helper_funcs *funcs, - unsigned int preferred_bpp, - unsigned int max_conn_count); -void drm_fb_helper_fbdev_teardown(struct drm_device *dev); - -void drm_fb_helper_lastclose(struct drm_device *dev); -void drm_fb_helper_output_poll_changed(struct drm_device *dev); - -int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_surface_size *sizes); -int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp); #else static inline void drm_fb_helper_prepare(struct drm_device *dev, - struct drm_fb_helper *helper, - const struct drm_fb_helper_funcs *funcs) + struct drm_fb_helper *helper, + unsigned int preferred_bpp, + const struct drm_fb_helper_funcs *funcs) +{ +} + +static inline void drm_fb_helper_unprepare(struct drm_fb_helper *fb_helper) { } static inline int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *helper, - int max_conn) + struct drm_fb_helper *helper) { /* So drivers can use it to free the struct */ helper->dev = dev; @@ -407,24 +338,14 @@ drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) return 0; } -static inline struct fb_info * -drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) +static inline void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper) { - return NULL; } -static inline void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper) -{ -} - -static inline void drm_fb_helper_fill_var(struct fb_info *info, - struct drm_fb_helper *fb_helper, - uint32_t fb_width, uint32_t fb_height) -{ -} - -static inline void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, - uint32_t depth) +static inline void +drm_fb_helper_fill_info(struct fb_info *info, + struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) { } @@ -440,63 +361,12 @@ static inline int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, return 0; } -static inline void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) -{ -} - +#ifdef CONFIG_FB_DEFERRED_IO static inline void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagelist) { } - -static inline int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper) -{ - return -ENODEV; -} - -static inline ssize_t drm_fb_helper_sys_read(struct fb_info *info, - char __user *buf, size_t count, - loff_t *ppos) -{ - return -ENODEV; -} - -static inline ssize_t drm_fb_helper_sys_write(struct fb_info *info, - const char __user *buf, - size_t count, loff_t *ppos) -{ - return -ENODEV; -} - -static inline void drm_fb_helper_sys_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ -} - -static inline void drm_fb_helper_sys_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ -} - -static inline void drm_fb_helper_sys_imageblit(struct fb_info *info, - const struct fb_image *image) -{ -} - -static inline void drm_fb_helper_cfb_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ -} - -static inline void drm_fb_helper_cfb_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ -} - -static inline void drm_fb_helper_cfb_imageblit(struct fb_info *info, - const struct fb_image *image) -{ -} +#endif static inline void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend) @@ -513,14 +383,7 @@ static inline int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) return 0; } -static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, - int bpp_sel) -{ - return 0; -} - -static inline int -drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) +static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) { return 0; } @@ -534,119 +397,6 @@ static inline int drm_fb_helper_debug_leave(struct fb_info *info) { return 0; } - -static inline struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height) -{ - return NULL; -} - -static inline struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, - int width, int height) -{ - return NULL; -} - -static inline int -drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - -static inline int -drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - -static inline int -drm_fb_helper_fbdev_setup(struct drm_device *dev, - struct drm_fb_helper *fb_helper, - const struct drm_fb_helper_funcs *funcs, - unsigned int preferred_bpp, - unsigned int max_conn_count) -{ - /* So drivers can use it to free the struct */ - dev->fb_helper = fb_helper; - - return 0; -} - -static inline void drm_fb_helper_fbdev_teardown(struct drm_device *dev) -{ - dev->fb_helper = NULL; -} - -static inline void drm_fb_helper_lastclose(struct drm_device *dev) -{ -} - -static inline void drm_fb_helper_output_poll_changed(struct drm_device *dev) -{ -} - -static inline int -drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_surface_size *sizes) -{ - return 0; -} - -static inline int -drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) -{ - return 0; -} - #endif -/** - * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers - * @a: memory range, users of which are to be removed - * @name: requesting driver name - * @primary: also kick vga16fb if present - * - * This function removes framebuffer devices (initialized by firmware/bootloader) - * which use memory range described by @a. If @a is NULL all such devices are - * removed. - */ -static inline int -drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, - const char *name, bool primary) -{ -#if IS_REACHABLE(CONFIG_FB) - return remove_conflicting_framebuffers(a, name, primary); -#else - return 0; -#endif -} - -/** - * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices - * @pdev: PCI device - * @resource_id: index of PCI BAR configuring framebuffer memory - * @name: requesting driver name - * - * This function removes framebuffer devices (eg. initialized by firmware) - * using memory range configured for @pdev's BAR @resource_id. - * - * The function assumes that PCI device with shadowed ROM drives a primary - * display and so kicks out vga16fb. - */ -static inline int -drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, - int resource_id, - const char *name) -{ -#if IS_REACHABLE(CONFIG_FB) - return remove_conflicting_pci_framebuffers(pdev, resource_id, name); -#else - return 0; -#endif -} - #endif diff --git a/include/drm/drm_fbdev_dma.h b/include/drm/drm_fbdev_dma.h new file mode 100644 index 000000000000..fb3f2a9aa01a --- /dev/null +++ b/include/drm/drm_fbdev_dma.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_FBDEV_DMA_H +#define DRM_FBDEV_DMA_H + +struct drm_fb_helper; +struct drm_fb_helper_surface_size; + +#ifdef CONFIG_DRM_FBDEV_EMULATION +int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes); + +#define DRM_FBDEV_DMA_DRIVER_OPS \ + .fbdev_probe = drm_fbdev_dma_driver_fbdev_probe +#else +#define DRM_FBDEV_DMA_DRIVER_OPS \ + .fbdev_probe = NULL +#endif + +#endif diff --git a/include/drm/drm_fbdev_shmem.h b/include/drm/drm_fbdev_shmem.h new file mode 100644 index 000000000000..2fc708964d75 --- /dev/null +++ b/include/drm/drm_fbdev_shmem.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_FBDEV_SHMEM_H +#define DRM_FBDEV_SHMEM_H + +struct drm_fb_helper; +struct drm_fb_helper_surface_size; + +#ifdef CONFIG_DRM_FBDEV_EMULATION +int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes); + +#define DRM_FBDEV_SHMEM_DRIVER_OPS \ + .fbdev_probe = drm_fbdev_shmem_driver_fbdev_probe +#else +#define DRM_FBDEV_SHMEM_DRIVER_OPS \ + .fbdev_probe = NULL +#endif + +#endif diff --git a/include/drm/drm_fbdev_ttm.h b/include/drm/drm_fbdev_ttm.h new file mode 100644 index 000000000000..ad4a10bb4c78 --- /dev/null +++ b/include/drm/drm_fbdev_ttm.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_FBDEV_TTM_H +#define DRM_FBDEV_TTM_H + +#include <linux/stddef.h> + +struct drm_fb_helper; +struct drm_fb_helper_surface_size; + +#ifdef CONFIG_DRM_FBDEV_EMULATION +int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes); + +#define DRM_FBDEV_TTM_DRIVER_OPS \ + .fbdev_probe = drm_fbdev_ttm_driver_fbdev_probe +#else +#define DRM_FBDEV_TTM_DRIVER_OPS \ + .fbdev_probe = NULL +#endif + +#endif diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 84ac79219e4c..1a3018e4a537 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -32,6 +32,7 @@ #include <linux/types.h> #include <linux/completion.h> +#include <linux/idr.h> #include <uapi/drm/drm.h> @@ -40,20 +41,28 @@ struct dma_fence; struct drm_file; struct drm_device; +struct drm_printer; struct device; +struct file; + +extern struct xarray drm_minors_xa; /* * FIXME: Not sure we want to have drm_minor here in the end, but to avoid * header include loops we need it here for now. */ -/* Note that the order of this enum is ABI (it determines +/* Note that the values of this enum are ABI (it determines * /dev/dri/renderD* numbers). + * + * Setting DRM_MINOR_ACCEL to 32 gives enough space for more drm minors to + * be implemented before we hit any future */ enum drm_minor_type { - DRM_MINOR_PRIMARY, - DRM_MINOR_CONTROL, - DRM_MINOR_RENDER, + DRM_MINOR_PRIMARY = 0, + DRM_MINOR_CONTROL = 1, + DRM_MINOR_RENDER = 2, + DRM_MINOR_ACCEL = 32, }; /** @@ -68,14 +77,12 @@ enum drm_minor_type { struct drm_minor { /* private: */ int index; /* Minor device number */ - int type; /* Control or render */ + int type; /* Control or render or accel */ struct device *kdev; /* Linux device */ struct drm_device *dev; + struct dentry *debugfs_symlink; struct dentry *debugfs_root; - - struct list_head debugfs_list; - struct mutex debugfs_lock; /* Protects debugfs_list. */ }; /** @@ -200,6 +207,24 @@ struct drm_file { bool writeback_connectors; /** + * @plane_color_pipeline: + * + * True if client understands plane color pipelines + */ + bool plane_color_pipeline; + + /** + * @was_master: + * + * This client has or had, master capability. Protected by struct + * &drm_device.master_mutex. + * + * This is used to ensure that CAP_SYS_ADMIN is not enforced, if the + * client is or was master in the past. + */ + bool was_master; + + /** * @is_master: * * This client is the creator of @master. Protected by struct @@ -211,19 +236,57 @@ struct drm_file { bool is_master; /** + * @supports_virtualized_cursor_plane: + * + * This client is capable of handling the cursor plane with the + * restrictions imposed on it by the virtualized drivers. + * + * This implies that the cursor plane has to behave like a cursor + * i.e. track cursor movement. It also requires setting of the + * hotspot properties by the client on the cursor plane. + */ + bool supports_virtualized_cursor_plane; + + /** * @master: * - * Master this node is currently associated with. Only relevant if - * drm_is_primary_client() returns true. Note that this only - * matches &drm_device.master if the master is the currently active one. + * Master this node is currently associated with. Protected by struct + * &drm_device.master_mutex, and serialized by @master_lookup_lock. + * + * Only relevant if drm_is_primary_client() returns true. Note that + * this only matches &drm_device.master if the master is the currently + * active one. + * + * To update @master, both &drm_device.master_mutex and + * @master_lookup_lock need to be held, therefore holding either of + * them is safe and enough for the read side. + * + * When dereferencing this pointer, either hold struct + * &drm_device.master_mutex for the duration of the pointer's use, or + * use drm_file_get_master() if struct &drm_device.master_mutex is not + * currently held and there is no other need to hold it. This prevents + * @master from being freed during use. * * See also @authentication and @is_master and the :ref:`section on * primary nodes and authentication <drm_primary_node>`. */ struct drm_master *master; - /** @pid: Process that opened this file. */ - struct pid *pid; + /** @master_lookup_lock: Serializes @master. */ + spinlock_t master_lookup_lock; + + /** + * @pid: Process that is using this file. + * + * Must only be dereferenced under a rcu_read_lock or equivalent. + * + * Updates are guarded with dev->filelist_mutex and reference must be + * dropped after a RCU grace period to accommodate lockless readers. + */ + struct pid __rcu *pid; + + /** @client_id: A unique id for fdinfo */ + u64 client_id; /** @magic: Authentication magic, see @authenticated. */ drm_magic_t magic; @@ -244,6 +307,9 @@ struct drm_file { * * Mapping of mm object handles to object pointers. Used by the GEM * subsystem. Protected by @table_lock. + * + * Note that allocated entries might be NULL as a transient state when + * creating or deleting a handle. */ struct idr object_idr; @@ -333,8 +399,24 @@ struct drm_file { */ struct drm_prime_file_private prime; - /* private: */ - unsigned long lock_count; /* DRI1 legacy lock count */ + /** + * @client_name: + * + * Userspace-provided name; useful for accounting and debugging. + */ + const char *client_name; + + /** + * @client_name_lock: Protects @client_name. + */ + struct mutex client_name_lock; + + /** + * @debugfs_client: + * + * debugfs directory for each client under a drm node. + */ + struct dentry *debugfs_client; }; /** @@ -366,10 +448,35 @@ static inline bool drm_is_render_client(const struct drm_file *file_priv) return file_priv->minor->type == DRM_MINOR_RENDER; } +/** + * drm_is_accel_client - is this an open file of the compute acceleration node + * @file_priv: DRM file + * + * Returns true if this is an open file of the compute acceleration node, i.e. + * &drm_file.minor of @file_priv is a accel minor. + * + * See also :doc:`Introduction to compute accelerators subsystem + * </accel/introduction>`. + */ +static inline bool drm_is_accel_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_ACCEL; +} + +__printf(2, 3) +void drm_file_err(struct drm_file *file_priv, const char *fmt, ...); + +void drm_file_update_pid(struct drm_file *); + +struct drm_minor *drm_minor_acquire(struct xarray *minors_xa, unsigned int minor_id); +void drm_minor_release(struct drm_minor *minor); + int drm_open(struct inode *inode, struct file *filp); +int drm_open_helper(struct file *filp, struct drm_minor *minor); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); int drm_release(struct inode *inode, struct file *filp); +int drm_release_noglobal(struct inode *inode, struct file *filp); __poll_t drm_poll(struct file *filp, struct poll_table_struct *wait); int drm_event_reserve_init_locked(struct drm_device *dev, struct drm_file *file_priv, @@ -383,5 +490,44 @@ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p); void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e); void drm_send_event(struct drm_device *dev, struct drm_pending_event *e); +void drm_send_event_timestamp_locked(struct drm_device *dev, + struct drm_pending_event *e, + ktime_t timestamp); + +/** + * struct drm_memory_stats - GEM object stats associated + * @shared: Total size of GEM objects shared between processes + * @private: Total size of GEM objects + * @resident: Total size of GEM objects backing pages + * @purgeable: Total size of GEM objects that can be purged (resident and not active) + * @active: Total size of GEM objects active on one or more engines + * + * Used by drm_print_memory_stats() + */ +struct drm_memory_stats { + u64 shared; + u64 private; + u64 resident; + u64 purgeable; + u64 active; +}; + +enum drm_gem_object_status; + +int drm_memory_stats_is_zero(const struct drm_memory_stats *stats); +void drm_fdinfo_print_size(struct drm_printer *p, + const char *prefix, + const char *stat, + const char *region, + u64 sz); +void drm_print_memory_stats(struct drm_printer *p, + const struct drm_memory_stats *stats, + enum drm_gem_object_status supported_status, + const char *region); + +void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file); +void drm_show_fdinfo(struct seq_file *m, struct file *f); + +struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags); #endif /* _DRM_FILE_H_ */ diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 553210c02ee0..33de514a5221 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -26,6 +26,8 @@ #define DRM_FIXED_H #include <linux/math64.h> +#include <linux/types.h> +#include <linux/wordpart.h> typedef union dfixed { u32 full; @@ -76,6 +78,23 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) #define DRM_FIXED_EPSILON 1LL #define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON) +/** + * @drm_sm2fixp + * + * Convert a 1.31.32 signed-magnitude fixed point to 32.32 + * 2s-complement fixed point + * + * @return s64 2s-complement fixed point + */ +static inline s64 drm_sm2fixp(__u64 a) +{ + if ((a & (1LL << 63))) { + return -(a & 0x7fffffffffffffffll); + } else { + return a; + } +} + static inline s64 drm_int2fixp(int a) { return ((s64)a) << DRM_FIXED_POINT; @@ -86,9 +105,14 @@ static inline int drm_fixp2int(s64 a) return ((s64)a) >> DRM_FIXED_POINT; } +static inline int drm_fixp2int_round(s64 a) +{ + return drm_fixp2int(a + DRM_FIXED_ONE / 2); +} + static inline int drm_fixp2int_ceil(s64 a) { - if (a > 0) + if (a >= 0) return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE); else return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE); @@ -208,4 +232,27 @@ static inline s64 drm_fixp_exp(s64 x) return sum; } +static inline int fxp_q4_from_int(int val_int) +{ + return val_int << 4; +} + +static inline int fxp_q4_to_int(int val_q4) +{ + return val_q4 >> 4; +} + +static inline int fxp_q4_to_int_roundup(int val_q4) +{ + return (val_q4 + 0xf) >> 4; +} + +static inline int fxp_q4_to_frac(int val_q4) +{ + return val_q4 & 0xf; +} + +#define FXP_Q4_FMT "%d.%04d" +#define FXP_Q4_ARGS(val_q4) fxp_q4_to_int(val_q4), (fxp_q4_to_frac(val_q4) * 625) + #endif diff --git a/include/drm/drm_flip_work.h b/include/drm/drm_flip_work.h index 21c3d512d25c..1eef3283a109 100644 --- a/include/drm/drm_flip_work.h +++ b/include/drm/drm_flip_work.h @@ -31,11 +31,10 @@ /** * DOC: flip utils * - * Util to queue up work to run from work-queue context after flip/vblank. + * Utility to queue up work to run from work-queue context after flip/vblank. * Typically this can be used to defer unref of framebuffer's, cursor - * bo's, etc until after vblank. The APIs are all thread-safe. - * Moreover, drm_flip_work_queue_task and drm_flip_work_queue can be called - * in atomic context. + * bo's, etc until after vblank. The APIs are all thread-safe. Moreover, + * drm_flip_work_commit() can be called in atomic context. */ struct drm_flip_work; @@ -52,16 +51,6 @@ struct drm_flip_work; typedef void (*drm_flip_func_t)(struct drm_flip_work *work, void *val); /** - * struct drm_flip_task - flip work task - * @node: list entry element - * @data: data to pass to &drm_flip_work.func - */ -struct drm_flip_task { - struct list_head node; - void *data; -}; - -/** * struct drm_flip_work - flip work queue * @name: debug name * @func: callback fxn called for each committed item @@ -79,9 +68,6 @@ struct drm_flip_work { spinlock_t lock; }; -struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags); -void drm_flip_work_queue_task(struct drm_flip_work *work, - struct drm_flip_task *task); void drm_flip_work_queue(struct drm_flip_work *work, void *val); void drm_flip_work_commit(struct drm_flip_work *work, struct workqueue_struct *wq); diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h new file mode 100644 index 000000000000..2b5c1aef80b0 --- /dev/null +++ b/include/drm/drm_format_helper.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Noralf Trønnes + */ + +#ifndef __LINUX_DRM_FORMAT_HELPER_H +#define __LINUX_DRM_FORMAT_HELPER_H + +#include <linux/types.h> + +struct drm_device; +struct drm_format_info; +struct drm_framebuffer; +struct drm_rect; + +struct iosys_map; + +/** + * struct drm_format_conv_state - Stores format-conversion state + * + * DRM helpers for format conversion store temporary state in + * struct drm_xfrm_buf. The buffer's resources can be reused + * among multiple conversion operations. + * + * All fields are considered private. + */ +struct drm_format_conv_state { + /* private: */ + struct { + void *mem; + size_t size; + bool preallocated; + } tmp; +}; + +#define __DRM_FORMAT_CONV_STATE_INIT(_mem, _size, _preallocated) { \ + .tmp = { \ + .mem = (_mem), \ + .size = (_size), \ + .preallocated = (_preallocated), \ + } \ + } + +/** + * DRM_FORMAT_CONV_STATE_INIT - Initializer for struct drm_format_conv_state + * + * Initializes an instance of struct drm_format_conv_state to default values. + */ +#define DRM_FORMAT_CONV_STATE_INIT \ + __DRM_FORMAT_CONV_STATE_INIT(NULL, 0, false) + +/** + * DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED - Initializer for struct drm_format_conv_state + * @_mem: The preallocated memory area + * @_size: The number of bytes in _mem + * + * Initializes an instance of struct drm_format_conv_state to preallocated + * storage. The caller is responsible for releasing the provided memory range. + */ +#define DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED(_mem, _size) \ + __DRM_FORMAT_CONV_STATE_INIT(_mem, _size, true) + +void drm_format_conv_state_init(struct drm_format_conv_state *state); +void drm_format_conv_state_copy(struct drm_format_conv_state *state, + const struct drm_format_conv_state *old_state); +void *drm_format_conv_state_reserve(struct drm_format_conv_state *state, + size_t new_size, gfp_t flags); +void drm_format_conv_state_release(struct drm_format_conv_state *state); + +unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format, + const struct drm_rect *clip); + +void drm_fb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip); +void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, bool cached, + struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_rgb565be(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_xrgb1555(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_argb1555(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_bgrx8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_xrgb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, + struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_argb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, + struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_gray8(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_argb8888_to_argb4444(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); + +void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); + +void drm_fb_xrgb8888_to_gray2(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); + +#endif /* __LINUX_DRM_FORMAT_HELPER_H */ diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index bcb389f04618..471784426857 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -22,9 +22,15 @@ #ifndef __DRM_FOURCC_H__ #define __DRM_FOURCC_H__ +#include <linux/math.h> #include <linux/types.h> #include <uapi/drm/drm_fourcc.h> +/** + * DRM_FORMAT_MAX_PLANES - maximum number of planes a DRM format can have + */ +#define DRM_FORMAT_MAX_PLANES 4u + /* * DRM formats are little endian. Define host endian variants for the * most common formats here, to reduce the #ifdefs needed in drivers. @@ -48,7 +54,6 @@ #endif struct drm_device; -struct drm_mode_fb_cmd2; /** * struct drm_format_info - information about a DRM format @@ -78,7 +83,7 @@ struct drm_format_info { * triplet @char_per_block, @block_w, @block_h for better * describing the pixel format. */ - u8 cpp[3]; + u8 cpp[DRM_FORMAT_MAX_PLANES]; /** * @char_per_block: @@ -104,7 +109,7 @@ struct drm_format_info { * information from their drm_mode_config.get_format_info hook * if they want the core to be validating the pitch. */ - u8 char_per_block[3]; + u8 char_per_block[DRM_FORMAT_MAX_PLANES]; }; /** @@ -113,7 +118,7 @@ struct drm_format_info { * Block width in pixels, this is intended to be accessed through * drm_format_info_block_width() */ - u8 block_w[3]; + u8 block_w[DRM_FORMAT_MAX_PLANES]; /** * @block_h: @@ -121,7 +126,7 @@ struct drm_format_info { * Block height in pixels, this is intended to be accessed through * drm_format_info_block_height() */ - u8 block_h[3]; + u8 block_h[DRM_FORMAT_MAX_PLANES]; /** @hsub: Horizontal chroma subsampling factor */ u8 hsub; @@ -133,36 +138,187 @@ struct drm_format_info { /** @is_yuv: Is it a YUV format? */ bool is_yuv; + + /** @is_color_indexed: Is it a color-indexed format? */ + bool is_color_indexed; }; /** - * struct drm_format_name_buf - name of a DRM format - * @str: string buffer containing the format name + * drm_format_info_is_yuv_packed - check that the format info matches a YUV + * format with data laid in a single plane + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a packed YUV format. */ -struct drm_format_name_buf { - char str[32]; -}; +static inline bool +drm_format_info_is_yuv_packed(const struct drm_format_info *info) +{ + return info->is_yuv && info->num_planes == 1; +} + +/** + * drm_format_info_is_yuv_semiplanar - check that the format info matches a YUV + * format with data laid in two planes (luminance and chrominance) + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a semiplanar YUV format. + */ +static inline bool +drm_format_info_is_yuv_semiplanar(const struct drm_format_info *info) +{ + return info->is_yuv && info->num_planes == 2; +} + +/** + * drm_format_info_is_yuv_planar - check that the format info matches a YUV + * format with data laid in three planes (one for each YUV component) + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a planar YUV format. + */ +static inline bool +drm_format_info_is_yuv_planar(const struct drm_format_info *info) +{ + return info->is_yuv && info->num_planes == 3; +} + +/** + * drm_format_info_is_yuv_sampling_410 - check that the format info matches a + * YUV format with 4:1:0 sub-sampling + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a YUV format with 4:1:0 + * sub-sampling. + */ +static inline bool +drm_format_info_is_yuv_sampling_410(const struct drm_format_info *info) +{ + return info->is_yuv && info->hsub == 4 && info->vsub == 4; +} + +/** + * drm_format_info_is_yuv_sampling_411 - check that the format info matches a + * YUV format with 4:1:1 sub-sampling + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a YUV format with 4:1:1 + * sub-sampling. + */ +static inline bool +drm_format_info_is_yuv_sampling_411(const struct drm_format_info *info) +{ + return info->is_yuv && info->hsub == 4 && info->vsub == 1; +} + +/** + * drm_format_info_is_yuv_sampling_420 - check that the format info matches a + * YUV format with 4:2:0 sub-sampling + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a YUV format with 4:2:0 + * sub-sampling. + */ +static inline bool +drm_format_info_is_yuv_sampling_420(const struct drm_format_info *info) +{ + return info->is_yuv && info->hsub == 2 && info->vsub == 2; +} + +/** + * drm_format_info_is_yuv_sampling_422 - check that the format info matches a + * YUV format with 4:2:2 sub-sampling + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a YUV format with 4:2:2 + * sub-sampling. + */ +static inline bool +drm_format_info_is_yuv_sampling_422(const struct drm_format_info *info) +{ + return info->is_yuv && info->hsub == 2 && info->vsub == 1; +} + +/** + * drm_format_info_is_yuv_sampling_444 - check that the format info matches a + * YUV format with 4:4:4 sub-sampling + * @info: format info + * + * Returns: + * A boolean indicating whether the format info matches a YUV format with 4:4:4 + * sub-sampling. + */ +static inline bool +drm_format_info_is_yuv_sampling_444(const struct drm_format_info *info) +{ + return info->is_yuv && info->hsub == 1 && info->vsub == 1; +} + +/** + * drm_format_info_plane_width - width of the plane given the first plane + * @info: pixel format info + * @width: width of the first plane + * @plane: plane index + * + * Returns: + * The width of @plane, given that the width of the first plane is @width. + */ +static inline +int drm_format_info_plane_width(const struct drm_format_info *info, int width, + int plane) +{ + if (!info || plane >= info->num_planes) + return 0; + + if (plane == 0) + return width; + + return DIV_ROUND_UP(width, info->hsub); +} + +/** + * drm_format_info_plane_height - height of the plane given the first plane + * @info: pixel format info + * @height: height of the first plane + * @plane: plane index + * + * Returns: + * The height of @plane, given that the height of the first plane is @height. + */ +static inline +int drm_format_info_plane_height(const struct drm_format_info *info, int height, + int plane) +{ + if (!info || plane >= info->num_planes) + return 0; + + if (plane == 0) + return height; + + return DIV_ROUND_UP(height, info->vsub); +} const struct drm_format_info *__drm_format_info(u32 format); const struct drm_format_info *drm_format_info(u32 format); const struct drm_format_info * drm_get_format_info(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd); + u32 pixel_format, u64 modifier); uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth); -int drm_format_num_planes(uint32_t format); -int drm_format_plane_cpp(uint32_t format, int plane); -int drm_format_horz_chroma_subsampling(uint32_t format); -int drm_format_vert_chroma_subsampling(uint32_t format); -int drm_format_plane_width(int width, uint32_t format, int plane); -int drm_format_plane_height(int height, uint32_t format, int plane); +uint32_t drm_driver_color_mode_format(struct drm_device *dev, unsigned int color_mode); unsigned int drm_format_info_block_width(const struct drm_format_info *info, int plane); unsigned int drm_format_info_block_height(const struct drm_format_info *info, int plane); +unsigned int drm_format_info_bpp(const struct drm_format_info *info, int plane); uint64_t drm_format_info_min_pitch(const struct drm_format_info *info, int plane, unsigned int buffer_width); -const char *drm_get_format_name(uint32_t format, struct drm_format_name_buf *buf); #endif /* __DRM_FOURCC_H__ */ diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index c94acedfb08e..38b24fc8978d 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -23,13 +23,19 @@ #ifndef __DRM_FRAMEBUFFER_H__ #define __DRM_FRAMEBUFFER_H__ -#include <linux/list.h> +#include <linux/bits.h> #include <linux/ctype.h> +#include <linux/list.h> +#include <linux/sched.h> + +#include <drm/drm_fourcc.h> #include <drm/drm_mode_object.h> -struct drm_framebuffer; -struct drm_file; +struct drm_clip_rect; struct drm_device; +struct drm_file; +struct drm_framebuffer; +struct drm_gem_object; /** * struct drm_framebuffer_funcs - framebuffer hooks @@ -82,6 +88,9 @@ struct drm_framebuffer_funcs { * for more information as all the semantics and arguments have a one to * one mapping on this function. * + * Atomic drivers should use drm_atomic_helper_dirtyfb() to implement + * this hook. + * * RETURNS: * * 0 on success or a negative error code on failure. @@ -92,6 +101,8 @@ struct drm_framebuffer_funcs { unsigned num_clips); }; +#define DRM_FRAMEBUFFER_HAS_HANDLE_REF(_i) BIT(0u + (_i)) + /** * struct drm_framebuffer - frame buffer object * @@ -139,17 +150,17 @@ struct drm_framebuffer { * @pitches: Line stride per buffer. For userspace created object this * is copied from drm_mode_fb_cmd2. */ - unsigned int pitches[4]; + unsigned int pitches[DRM_FORMAT_MAX_PLANES]; /** * @offsets: Offset from buffer start to the actual pixel data in bytes, * per buffer. For userspace created object this is copied from * drm_mode_fb_cmd2. * * Note that this is a linear offset and does not take into account - * tiling or buffer laytou per @modifier. It meant to be used when the - * actual pixel data for this framebuffer plane starts at an offset, - * e.g. when multiple planes are allocated within the same backing - * storage buffer object. For tiled layouts this generally means it + * tiling or buffer layout per @modifier. It is meant to be used when + * the actual pixel data for this framebuffer plane starts at an offset, + * e.g. when multiple planes are allocated within the same backing + * storage buffer object. For tiled layouts this generally means its * @offsets must at least be tile-size aligned, but hardware often has * stricter requirements. * @@ -157,7 +168,7 @@ struct drm_framebuffer { * data (even for linear buffers). Specifying an x/y pixel offset is * instead done through the source rectangle in &struct drm_plane_state. */ - unsigned int offsets[4]; + unsigned int offsets[DRM_FORMAT_MAX_PLANES]; /** * @modifier: Data layout modifier. This is used to describe * tiling, or also special layouts (like compression) of auxiliary @@ -181,17 +192,9 @@ struct drm_framebuffer { */ int flags; /** - * @hot_x: X coordinate of the cursor hotspot. Used by the legacy cursor - * IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR - * universal plane. - */ - int hot_x; - /** - * @hot_y: Y coordinate of the cursor hotspot. Used by the legacy cursor - * IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR - * universal plane. + * @internal_flags: Framebuffer flags like DRM_FRAMEBUFFER_HAS_HANDLE_REF. */ - int hot_y; + unsigned int internal_flags; /** * @filp_head: Placed on &drm_file.fbs, protected by &drm_file.fbs_lock. */ @@ -202,7 +205,7 @@ struct drm_framebuffer { * This is used by the GEM framebuffer helpers, see e.g. * drm_gem_fb_create(). */ - struct drm_gem_object *obj[4]; + struct drm_gem_object *obj[DRM_FORMAT_MAX_PLANES]; }; #define obj_to_fb(x) container_of(x, struct drm_framebuffer, base) @@ -284,9 +287,42 @@ static inline void drm_framebuffer_assign(struct drm_framebuffer **p, &fb->head != (&(dev)->mode_config.fb_list); \ fb = list_next_entry(fb, head)) -int drm_framebuffer_plane_width(int width, - const struct drm_framebuffer *fb, int plane); -int drm_framebuffer_plane_height(int height, - const struct drm_framebuffer *fb, int plane); +/** + * struct drm_afbc_framebuffer - a special afbc frame buffer object + * + * A derived class of struct drm_framebuffer, dedicated for afbc use cases. + */ +struct drm_afbc_framebuffer { + /** + * @base: base framebuffer structure. + */ + struct drm_framebuffer base; + /** + * @block_width: width of a single afbc block + */ + u32 block_width; + /** + * @block_height: height of a single afbc block + */ + u32 block_height; + /** + * @aligned_width: aligned frame buffer width + */ + u32 aligned_width; + /** + * @aligned_height: aligned frame buffer height + */ + u32 aligned_height; + /** + * @offset: offset of the first afbc header + */ + u32 offset; + /** + * @afbc_size: minimum size of afbc buffer + */ + u32 afbc_size; +}; + +#define fb_to_afbc_fb(x) container_of(x, struct drm_afbc_framebuffer, base) #endif diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index c95727425284..8d48d2af2649 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -35,12 +35,38 @@ */ #include <linux/kref.h> +#include <linux/dma-buf.h> +#include <linux/dma-resv.h> +#include <linux/list.h> +#include <linux/mutex.h> #include <drm/drm_vma_manager.h> +struct iosys_map; struct drm_gem_object; /** + * enum drm_gem_object_status - bitmask of object state for fdinfo reporting + * @DRM_GEM_OBJECT_RESIDENT: object is resident in memory (ie. not unpinned) + * @DRM_GEM_OBJECT_PURGEABLE: object marked as purgeable by userspace + * @DRM_GEM_OBJECT_ACTIVE: object is currently used by an active submission + * + * Bitmask of status used for fdinfo memory stats, see &drm_gem_object_funcs.status + * and drm_show_fdinfo(). Note that an object can report DRM_GEM_OBJECT_PURGEABLE + * and be active or not resident, in which case drm_show_fdinfo() will not + * account for it as purgeable. So drivers do not need to check if the buffer + * is idle and resident to return this bit, i.e. userspace can mark a buffer as + * purgeable even while it is still busy on the GPU. It will not get reported in + * the puregeable stats until it becomes idle. The status gem object func does + * not need to consider this. + */ +enum drm_gem_object_status { + DRM_GEM_OBJECT_RESIDENT = BIT(0), + DRM_GEM_OBJECT_PURGEABLE = BIT(1), + DRM_GEM_OBJECT_ACTIVE = BIT(2), +}; + +/** * struct drm_gem_object_funcs - GEM object functions */ struct drm_gem_object_funcs { @@ -100,7 +126,8 @@ struct drm_gem_object_funcs { /** * @pin: * - * Pin backing buffer in memory. + * Pin backing buffer in memory, such that dma-buf importers can + * access it. Used by the drm_gem_map_attach() helper. * * This callback is optional. */ @@ -109,7 +136,7 @@ struct drm_gem_object_funcs { /** * @unpin: * - * Unpin backing buffer. + * Unpin backing buffer. Used by the drm_gem_map_detach() helper. * * This callback is optional. */ @@ -119,29 +146,83 @@ struct drm_gem_object_funcs { * @get_sg_table: * * Returns a Scatter-Gather table representation of the buffer. - * Used when exporting a buffer. + * Used when exporting a buffer by the drm_gem_map_dma_buf() helper. + * Releasing is done by calling dma_unmap_sg_attrs() and sg_free_table() + * in drm_gem_unmap_buf(), therefore these helpers and this callback + * here cannot be used for sg tables pointing at driver private memory + * ranges. * - * This callback is mandatory if buffer export is supported. + * See also drm_prime_pages_to_sg(). */ struct sg_table *(*get_sg_table)(struct drm_gem_object *obj); /** * @vmap: * - * Returns a virtual address for the buffer. + * Returns a virtual address for the buffer. Used by the + * drm_gem_dmabuf_vmap() helper. Called with a held GEM reservation + * lock. * * This callback is optional. */ - void *(*vmap)(struct drm_gem_object *obj); + int (*vmap)(struct drm_gem_object *obj, struct iosys_map *map); /** * @vunmap: * - * Releases the the address previously returned by @vmap. + * Releases the address previously returned by @vmap. Used by the + * drm_gem_dmabuf_vunmap() helper. Called with a held GEM reservation + * lock. * * This callback is optional. */ - void (*vunmap)(struct drm_gem_object *obj, void *vaddr); + void (*vunmap)(struct drm_gem_object *obj, struct iosys_map *map); + + /** + * @mmap: + * + * Handle mmap() of the gem object, setup vma accordingly. + * + * This callback is optional. + * + * The callback is used by both drm_gem_mmap_obj() and + * drm_gem_prime_mmap(). When @mmap is present @vm_ops is not + * used, the @mmap callback must set vma->vm_ops instead. + */ + int (*mmap)(struct drm_gem_object *obj, struct vm_area_struct *vma); + + /** + * @evict: + * + * Evicts gem object out from memory. Used by the drm_gem_object_evict() + * helper. Returns 0 on success, -errno otherwise. Called with a held + * GEM reservation lock. + * + * This callback is optional. + */ + int (*evict)(struct drm_gem_object *obj); + + /** + * @status: + * + * The optional status callback can return additional object state + * which determines which stats the object is counted against. The + * callback is called under table_lock. Racing against object status + * change is "harmless", and the callback can expect to not race + * against object destruction. + * + * Called by drm_show_memory_stats(). + */ + enum drm_gem_object_status (*status)(struct drm_gem_object *obj); + + /** + * @rss: + * + * Return resident size of the object in physical memory. + * + * Called by drm_show_memory_stats(). + */ + size_t (*rss)(struct drm_gem_object *obj); /** * @vm_ops: @@ -154,6 +235,41 @@ struct drm_gem_object_funcs { }; /** + * struct drm_gem_lru - A simple LRU helper + * + * A helper for tracking GEM objects in a given state, to aid in + * driver's shrinker implementation. Tracks the count of pages + * for lockless &shrinker.count_objects, and provides + * &drm_gem_lru_scan for driver's &shrinker.scan_objects + * implementation. + */ +struct drm_gem_lru { + /** + * @lock: + * + * Lock protecting movement of GEM objects between LRUs. All + * LRUs that the object can move between should be protected + * by the same lock. + */ + struct mutex *lock; + + /** + * @count: + * + * The total number of backing pages of the GEM objects in + * this LRU. + */ + long count; + + /** + * @list: + * + * The LRU list. + */ + struct list_head list; +}; + +/** * struct drm_gem_object - GEM buffer object * * This structure defines the generic parts for GEM buffer objects, which are @@ -167,8 +283,8 @@ struct drm_gem_object { * * Reference count of this object * - * Please use drm_gem_object_get() to acquire and drm_gem_object_put() - * or drm_gem_object_put_unlocked() to release a reference to a GEM + * Please use drm_gem_object_get() to acquire and drm_gem_object_put_locked() + * or drm_gem_object_put() to release a reference to a GEM * buffer object. */ struct kref refcount; @@ -196,7 +312,7 @@ struct drm_gem_object { * * SHMEM file node used as backing storage for swappable buffer objects. * GEM also supports driver private objects with driver-specific backing - * storage (contiguous CMA memory, special reserved blocks). In this + * storage (contiguous DMA memory, special reserved blocks). In this * case @filp is NULL. */ struct file *filp; @@ -252,8 +368,9 @@ struct drm_gem_object { * attachment point for the device. This is invariant over the lifetime * of a gem object. * - * The &drm_driver.gem_free_object callback is responsible for cleaning - * up the dma_buf attachment and references acquired at import time. + * The &drm_gem_object_funcs.free callback is responsible for + * cleaning up the dma_buf attachment and references acquired at import + * time. * * Note that the drm gem/prime core does not depend upon drivers setting * this field any more. So for drivers where this doesn't make sense @@ -263,6 +380,55 @@ struct drm_gem_object { struct dma_buf_attachment *import_attach; /** + * @resv: + * + * Pointer to reservation object associated with the this GEM object. + * + * Normally (@resv == &@_resv) except for imported GEM objects. + */ + struct dma_resv *resv; + + /** + * @_resv: + * + * A reservation object for this GEM object. + * + * This is unused for imported GEM objects. + */ + struct dma_resv _resv; + + /** + * @gpuva: Fields used by GPUVM to manage mappings pointing to this GEM object. + * + * When DRM_GPUVM_IMMEDIATE_MODE is set, this list is protected by the + * mutex. Otherwise, the list is protected by the GEMs &dma_resv lock. + * + * Note that all entries in this list must agree on whether + * DRM_GPUVM_IMMEDIATE_MODE is set. + */ + struct { + /** + * @gpuva.list: list of GPUVM mappings attached to this GEM object. + * + * Drivers should lock list accesses with either the GEMs + * &dma_resv lock (&drm_gem_object.resv) or the + * &drm_gem_object.gpuva.lock mutex. + */ + struct list_head list; + + /** + * @gpuva.lock: lock protecting access to &drm_gem_object.gpuva.list + * when DRM_GPUVM_IMMEDIATE_MODE is used. + * + * Only used when DRM_GPUVM_IMMEDIATE_MODE is set. It should be + * safe to take this mutex during the fence signalling path, so + * do not allocate memory while holding this lock. Otherwise, + * the &dma_resv lock should be used. + */ + struct mutex lock; + } gpuva; + + /** * @funcs: * * Optional GEM object functions. If this is set, it will be used instead of the @@ -272,9 +438,41 @@ struct drm_gem_object { * */ const struct drm_gem_object_funcs *funcs; + + /** + * @lru_node: + * + * List node in a &drm_gem_lru. + */ + struct list_head lru_node; + + /** + * @lru: + * + * The current LRU list that the GEM object is on. + */ + struct drm_gem_lru *lru; }; /** + * DRM_GEM_FOPS - Default drm GEM file operations + * + * This macro provides a shorthand for setting the GEM file ops in the + * &file_operations structure. If all you need are the default ops, use + * DEFINE_DRM_GEM_FOPS instead. + */ +#define DRM_GEM_FOPS \ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_gem_mmap, \ + .fop_flags = FOP_UNSIGNED_OFFSET + +/** * DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers * @name: name for the generated structure * @@ -290,22 +488,19 @@ struct drm_gem_object { #define DEFINE_DRM_GEM_FOPS(name) \ static const struct file_operations name = {\ .owner = THIS_MODULE,\ - .open = drm_open,\ - .release = drm_release,\ - .unlocked_ioctl = drm_ioctl,\ - .compat_ioctl = drm_compat_ioctl,\ - .poll = drm_poll,\ - .read = drm_read,\ - .llseek = noop_llseek,\ - .mmap = drm_gem_mmap,\ + DRM_GEM_FOPS,\ } void drm_gem_object_release(struct drm_gem_object *obj); void drm_gem_object_free(struct kref *kref); int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); +int drm_gem_object_init_with_mnt(struct drm_device *dev, + struct drm_gem_object *obj, size_t size, + struct vfsmount *gemfs); void drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); +void drm_gem_private_object_fini(struct drm_gem_object *obj); void drm_gem_vm_open(struct vm_area_struct *vma); void drm_gem_vm_close(struct vm_area_struct *vma); int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, @@ -324,30 +519,26 @@ static inline void drm_gem_object_get(struct drm_gem_object *obj) kref_get(&obj->refcount); } +__attribute__((nonnull)) +static inline void +__drm_gem_object_put(struct drm_gem_object *obj) +{ + kref_put(&obj->refcount, drm_gem_object_free); +} + /** - * __drm_gem_object_put - raw function to release a GEM buffer object reference + * drm_gem_object_put - drop a GEM buffer object reference * @obj: GEM buffer object * - * This function is meant to be used by drivers which are not encumbered with - * &drm_device.struct_mutex legacy locking and which are using the - * gem_free_object_unlocked callback. It avoids all the locking checks and - * locking overhead of drm_gem_object_put() and drm_gem_object_put_unlocked(). - * - * Drivers should never call this directly in their code. Instead they should - * wrap it up into a ``driver_gem_object_put(struct driver_gem_object *obj)`` - * wrapper function, and use that. Shared code should never call this, to - * avoid breaking drivers by accident which still depend upon - * &drm_device.struct_mutex locking. + * This releases a reference to @obj. */ static inline void -__drm_gem_object_put(struct drm_gem_object *obj) +drm_gem_object_put(struct drm_gem_object *obj) { - kref_put(&obj->refcount, drm_gem_object_free); + if (obj) + __drm_gem_object_put(obj); } -void drm_gem_object_put_unlocked(struct drm_gem_object *obj); -void drm_gem_object_put(struct drm_gem_object *obj); - int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep); @@ -362,16 +553,110 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj); void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, bool dirty, bool accessed); +void drm_gem_lock(struct drm_gem_object *obj); +void drm_gem_unlock(struct drm_gem_object *obj); + +int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map); +void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map); + +int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, + int count, struct drm_gem_object ***objs_out); struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, u32 handle); +long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle, + bool wait_all, unsigned long timeout); +int drm_gem_lock_reservations(struct drm_gem_object **objs, int count, + struct ww_acquire_ctx *acquire_ctx); +void drm_gem_unlock_reservations(struct drm_gem_object **objs, int count, + struct ww_acquire_ctx *acquire_ctx); int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, u32 handle, u64 *offset); -int drm_gem_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle); - -int drm_gem_pin(struct drm_gem_object *obj); -void drm_gem_unpin(struct drm_gem_object *obj); -void *drm_gem_vmap(struct drm_gem_object *obj); -void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr); + +void drm_gem_lru_init(struct drm_gem_lru *lru, struct mutex *lock); +void drm_gem_lru_remove(struct drm_gem_object *obj); +void drm_gem_lru_move_tail_locked(struct drm_gem_lru *lru, struct drm_gem_object *obj); +void drm_gem_lru_move_tail(struct drm_gem_lru *lru, struct drm_gem_object *obj); +unsigned long +drm_gem_lru_scan(struct drm_gem_lru *lru, + unsigned int nr_to_scan, + unsigned long *remaining, + bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket), + struct ww_acquire_ctx *ticket); + +int drm_gem_evict_locked(struct drm_gem_object *obj); + +/** + * drm_gem_object_is_shared_for_memory_stats - helper for shared memory stats + * + * This helper should only be used for fdinfo shared memory stats to determine + * if a GEM object is shared. + * + * @obj: obj in question + */ +static inline bool drm_gem_object_is_shared_for_memory_stats(struct drm_gem_object *obj) +{ + return (obj->handle_count > 1) || obj->dma_buf; +} + +/** + * drm_gem_is_imported() - Tests if GEM object's buffer has been imported + * @obj: the GEM object + * + * Returns: + * True if the GEM object's buffer has been imported, false otherwise + */ +static inline bool drm_gem_is_imported(const struct drm_gem_object *obj) +{ + return !!obj->import_attach; +} + +#ifdef CONFIG_LOCKDEP +#define drm_gem_gpuva_assert_lock_held(gpuvm, obj) \ + lockdep_assert(drm_gpuvm_immediate_mode(gpuvm) ? \ + lockdep_is_held(&(obj)->gpuva.lock) : \ + dma_resv_held((obj)->resv)) +#else +#define drm_gem_gpuva_assert_lock_held(gpuvm, obj) do {} while (0) +#endif + +/** + * drm_gem_gpuva_init() - initialize the gpuva list of a GEM object + * @obj: the &drm_gem_object + * + * This initializes the &drm_gem_object's &drm_gpuvm_bo list. + * + * Calling this function is only necessary for drivers intending to support the + * &drm_driver_feature DRIVER_GEM_GPUVA. + * + * See also drm_gem_gpuva_set_lock(). + */ +static inline void drm_gem_gpuva_init(struct drm_gem_object *obj) +{ + INIT_LIST_HEAD(&obj->gpuva.list); +} + +/** + * drm_gem_for_each_gpuvm_bo() - iterator to walk over a list of &drm_gpuvm_bo + * @entry__: &drm_gpuvm_bo structure to assign to in each iteration step + * @obj__: the &drm_gem_object the &drm_gpuvm_bo to walk are associated with + * + * This iterator walks over all &drm_gpuvm_bo structures associated with the + * &drm_gem_object. + */ +#define drm_gem_for_each_gpuvm_bo(entry__, obj__) \ + list_for_each_entry(entry__, &(obj__)->gpuva.list, list.entry.gem) + +/** + * drm_gem_for_each_gpuvm_bo_safe() - iterator to safely walk over a list of + * &drm_gpuvm_bo + * @entry__: &drm_gpuvm_bostructure to assign to in each iteration step + * @next__: &next &drm_gpuvm_bo to store the next step + * @obj__: the &drm_gem_object the &drm_gpuvm_bo to walk are associated with + * + * This iterator walks over all &drm_gpuvm_bo structures associated with the + * &drm_gem_object. It is implemented with list_for_each_entry_safe(), hence + * it is save against removal of elements. + */ +#define drm_gem_for_each_gpuvm_bo_safe(entry__, next__, obj__) \ + list_for_each_entry_safe(entry__, next__, &(obj__)->gpuva.list, list.entry.gem) #endif /* __DRM_GEM_H__ */ diff --git a/include/drm/drm_gem_atomic_helper.h b/include/drm/drm_gem_atomic_helper.h new file mode 100644 index 000000000000..3e01c619a25e --- /dev/null +++ b/include/drm/drm_gem_atomic_helper.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __DRM_GEM_ATOMIC_HELPER_H__ +#define __DRM_GEM_ATOMIC_HELPER_H__ + +#include <linux/iosys-map.h> + +#include <drm/drm_format_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_plane.h> + +struct drm_simple_display_pipe; + +/* + * Plane Helpers + */ + +int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state); + +/* + * Helpers for planes with shadow buffers + */ + +/** + * DRM_SHADOW_PLANE_MAX_WIDTH - Maximum width of a plane's shadow buffer in pixels + * + * For drivers with shadow planes, the maximum width of the framebuffer is + * usually independent from hardware limitations. Drivers can initialize struct + * drm_mode_config.max_width from DRM_SHADOW_PLANE_MAX_WIDTH. + */ +#define DRM_SHADOW_PLANE_MAX_WIDTH (4096u) + +/** + * DRM_SHADOW_PLANE_MAX_HEIGHT - Maximum height of a plane's shadow buffer in scanlines + * + * For drivers with shadow planes, the maximum height of the framebuffer is + * usually independent from hardware limitations. Drivers can initialize struct + * drm_mode_config.max_height from DRM_SHADOW_PLANE_MAX_HEIGHT. + */ +#define DRM_SHADOW_PLANE_MAX_HEIGHT (4096u) + +/** + * struct drm_shadow_plane_state - plane state for planes with shadow buffers + * + * For planes that use a shadow buffer, struct drm_shadow_plane_state + * provides the regular plane state plus mappings of the shadow buffer + * into kernel address space. + */ +struct drm_shadow_plane_state { + /** @base: plane state */ + struct drm_plane_state base; + + /** + * @fmtcnv_state: Format-conversion state + * + * Per-plane state for format conversion. + * Flags for copying shadow buffers into backend storage. Also holds + * temporary storage for format conversion. + */ + struct drm_format_conv_state fmtcnv_state; + + /* Transitional state - do not export or duplicate */ + + /** + * @map: Mappings of the plane's framebuffer BOs in to kernel address space + * + * The memory mappings stored in map should be established in the plane's + * prepare_fb callback and removed in the cleanup_fb callback. + */ + struct iosys_map map[DRM_FORMAT_MAX_PLANES]; + + /** + * @data: Address of each framebuffer BO's data + * + * The address of the data stored in each mapping. This is different + * for framebuffers with non-zero offset fields. + */ + struct iosys_map data[DRM_FORMAT_MAX_PLANES]; +}; + +/** + * to_drm_shadow_plane_state - upcasts from struct drm_plane_state + * @state: the plane state + */ +static inline struct drm_shadow_plane_state * +to_drm_shadow_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct drm_shadow_plane_state, base); +} + +void __drm_gem_duplicate_shadow_plane_state(struct drm_plane *plane, + struct drm_shadow_plane_state *new_shadow_plane_state); +void __drm_gem_destroy_shadow_plane_state(struct drm_shadow_plane_state *shadow_plane_state); +void __drm_gem_reset_shadow_plane(struct drm_plane *plane, + struct drm_shadow_plane_state *shadow_plane_state); + +void drm_gem_reset_shadow_plane(struct drm_plane *plane); +struct drm_plane_state *drm_gem_duplicate_shadow_plane_state(struct drm_plane *plane); +void drm_gem_destroy_shadow_plane_state(struct drm_plane *plane, + struct drm_plane_state *plane_state); + +/** + * DRM_GEM_SHADOW_PLANE_FUNCS - + * Initializes struct drm_plane_funcs for shadow-buffered planes + * + * Drivers may use GEM BOs as shadow buffers over the framebuffer memory. This + * macro initializes struct drm_plane_funcs to use the rsp helper functions. + */ +#define DRM_GEM_SHADOW_PLANE_FUNCS \ + .reset = drm_gem_reset_shadow_plane, \ + .atomic_duplicate_state = drm_gem_duplicate_shadow_plane_state, \ + .atomic_destroy_state = drm_gem_destroy_shadow_plane_state + +int drm_gem_begin_shadow_fb_access(struct drm_plane *plane, struct drm_plane_state *plane_state); +void drm_gem_end_shadow_fb_access(struct drm_plane *plane, struct drm_plane_state *plane_state); + +/** + * DRM_GEM_SHADOW_PLANE_HELPER_FUNCS - + * Initializes struct drm_plane_helper_funcs for shadow-buffered planes + * + * Drivers may use GEM BOs as shadow buffers over the framebuffer memory. This + * macro initializes struct drm_plane_helper_funcs to use the rsp helper + * functions. + */ +#define DRM_GEM_SHADOW_PLANE_HELPER_FUNCS \ + .begin_fb_access = drm_gem_begin_shadow_fb_access, \ + .end_fb_access = drm_gem_end_shadow_fb_access + +int drm_gem_simple_kms_begin_shadow_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +void drm_gem_simple_kms_end_shadow_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +void drm_gem_simple_kms_reset_shadow_plane(struct drm_simple_display_pipe *pipe); +struct drm_plane_state * +drm_gem_simple_kms_duplicate_shadow_plane_state(struct drm_simple_display_pipe *pipe); +void drm_gem_simple_kms_destroy_shadow_plane_state(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); + +/** + * DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS - + * Initializes struct drm_simple_display_pipe_funcs for shadow-buffered planes + * + * Drivers may use GEM BOs as shadow buffers over the framebuffer memory. This + * macro initializes struct drm_simple_display_pipe_funcs to use the rsp helper + * functions. + */ +#define DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS \ + .begin_fb_access = drm_gem_simple_kms_begin_shadow_fb_access, \ + .end_fb_access = drm_gem_simple_kms_end_shadow_fb_access, \ + .reset_plane = drm_gem_simple_kms_reset_shadow_plane, \ + .duplicate_plane_state = drm_gem_simple_kms_duplicate_shadow_plane_state, \ + .destroy_plane_state = drm_gem_simple_kms_destroy_shadow_plane_state + +#endif /* __DRM_GEM_ATOMIC_HELPER_H__ */ diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h deleted file mode 100644 index 07c504940ba1..000000000000 --- a/include/drm/drm_gem_cma_helper.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __DRM_GEM_CMA_HELPER_H__ -#define __DRM_GEM_CMA_HELPER_H__ - -#include <drm/drmP.h> -#include <drm/drm_gem.h> - -/** - * struct drm_gem_cma_object - GEM object backed by CMA memory allocations - * @base: base GEM object - * @paddr: physical address of the backing memory - * @sgt: scatter/gather table for imported PRIME buffers. The table can have - * more than one entry but they are guaranteed to have contiguous - * DMA addresses. - * @vaddr: kernel virtual address of the backing memory - */ -struct drm_gem_cma_object { - struct drm_gem_object base; - dma_addr_t paddr; - struct sg_table *sgt; - - /* For objects with DMA memory allocated by GEM CMA */ - void *vaddr; -}; - -#define to_drm_gem_cma_obj(gem_obj) \ - container_of(gem_obj, struct drm_gem_cma_object, base) - -#ifndef CONFIG_MMU -#define DRM_GEM_CMA_UNMAPPED_AREA_FOPS \ - .get_unmapped_area = drm_gem_cma_get_unmapped_area, -#else -#define DRM_GEM_CMA_UNMAPPED_AREA_FOPS -#endif - -/** - * DEFINE_DRM_GEM_CMA_FOPS() - macro to generate file operations for CMA drivers - * @name: name for the generated structure - * - * This macro autogenerates a suitable &struct file_operations for CMA based - * drivers, which can be assigned to &drm_driver.fops. Note that this structure - * cannot be shared between drivers, because it contains a reference to the - * current module using THIS_MODULE. - * - * Note that the declaration is already marked as static - if you need a - * non-static version of this you're probably doing it wrong and will break the - * THIS_MODULE reference by accident. - */ -#define DEFINE_DRM_GEM_CMA_FOPS(name) \ - static const struct file_operations name = {\ - .owner = THIS_MODULE,\ - .open = drm_open,\ - .release = drm_release,\ - .unlocked_ioctl = drm_ioctl,\ - .compat_ioctl = drm_compat_ioctl,\ - .poll = drm_poll,\ - .read = drm_read,\ - .llseek = noop_llseek,\ - .mmap = drm_gem_cma_mmap,\ - DRM_GEM_CMA_UNMAPPED_AREA_FOPS \ - } - -/* free GEM object */ -void drm_gem_cma_free_object(struct drm_gem_object *gem_obj); - -/* create memory region for DRM framebuffer */ -int drm_gem_cma_dumb_create_internal(struct drm_file *file_priv, - struct drm_device *drm, - struct drm_mode_create_dumb *args); - -/* create memory region for DRM framebuffer */ -int drm_gem_cma_dumb_create(struct drm_file *file_priv, - struct drm_device *drm, - struct drm_mode_create_dumb *args); - -/* set vm_flags and we can change the VM attribute to other one at here */ -int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma); - -/* allocate physical memory */ -struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, - size_t size); - -extern const struct vm_operations_struct drm_gem_cma_vm_ops; - -#ifndef CONFIG_MMU -unsigned long drm_gem_cma_get_unmapped_area(struct file *filp, - unsigned long addr, - unsigned long len, - unsigned long pgoff, - unsigned long flags); -#endif - -void drm_gem_cma_print_info(struct drm_printer *p, unsigned int indent, - const struct drm_gem_object *obj); - -struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj); -struct drm_gem_object * -drm_gem_cma_prime_import_sg_table(struct drm_device *dev, - struct dma_buf_attachment *attach, - struct sg_table *sgt); -int drm_gem_cma_prime_mmap(struct drm_gem_object *obj, - struct vm_area_struct *vma); -void *drm_gem_cma_prime_vmap(struct drm_gem_object *obj); -void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr); - -struct drm_gem_object * -drm_cma_gem_create_object_default_funcs(struct drm_device *dev, size_t size); - -/** - * DRM_GEM_CMA_VMAP_DRIVER_OPS - CMA GEM driver operations ensuring a virtual - * address on the buffer - * - * This macro provides a shortcut for setting the default GEM operations in the - * &drm_driver structure for drivers that need the virtual address also on - * imported buffers. - */ -#define DRM_GEM_CMA_VMAP_DRIVER_OPS \ - .gem_create_object = drm_cma_gem_create_object_default_funcs, \ - .dumb_create = drm_gem_cma_dumb_create, \ - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ - .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table_vmap, \ - .gem_prime_mmap = drm_gem_prime_mmap - -struct drm_gem_object * -drm_gem_cma_prime_import_sg_table_vmap(struct drm_device *drm, - struct dma_buf_attachment *attach, - struct sg_table *sgt); - -#endif /* __DRM_GEM_CMA_HELPER_H__ */ diff --git a/include/drm/drm_gem_dma_helper.h b/include/drm/drm_gem_dma_helper.h new file mode 100644 index 000000000000..f2678e7ecb98 --- /dev/null +++ b/include/drm/drm_gem_dma_helper.h @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DRM_GEM_DMA_HELPER_H__ +#define __DRM_GEM_DMA_HELPER_H__ + +#include <drm/drm_file.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_gem.h> + +struct drm_mode_create_dumb; + +/** + * struct drm_gem_dma_object - GEM object backed by DMA memory allocations + * @base: base GEM object + * @dma_addr: DMA address of the backing memory + * @sgt: scatter/gather table for imported PRIME buffers. The table can have + * more than one entry but they are guaranteed to have contiguous + * DMA addresses. + * @vaddr: kernel virtual address of the backing memory + * @map_noncoherent: if true, the GEM object is backed by non-coherent memory + */ +struct drm_gem_dma_object { + struct drm_gem_object base; + dma_addr_t dma_addr; + struct sg_table *sgt; + + /* For objects with DMA memory allocated by GEM DMA */ + void *vaddr; + + bool map_noncoherent; +}; + +#define to_drm_gem_dma_obj(gem_obj) \ + container_of(gem_obj, struct drm_gem_dma_object, base) + +struct drm_gem_dma_object *drm_gem_dma_create(struct drm_device *drm, + size_t size); +void drm_gem_dma_free(struct drm_gem_dma_object *dma_obj); +void drm_gem_dma_print_info(const struct drm_gem_dma_object *dma_obj, + struct drm_printer *p, unsigned int indent); +struct sg_table *drm_gem_dma_get_sg_table(struct drm_gem_dma_object *dma_obj); +int drm_gem_dma_vmap(struct drm_gem_dma_object *dma_obj, + struct iosys_map *map); +int drm_gem_dma_mmap(struct drm_gem_dma_object *dma_obj, struct vm_area_struct *vma); + +extern const struct vm_operations_struct drm_gem_dma_vm_ops; + +/* + * GEM object functions + */ + +/** + * drm_gem_dma_object_free - GEM object function for drm_gem_dma_free() + * @obj: GEM object to free + * + * This function wraps drm_gem_dma_free_object(). Drivers that employ the DMA helpers + * should use it as their &drm_gem_object_funcs.free handler. + */ +static inline void drm_gem_dma_object_free(struct drm_gem_object *obj) +{ + struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj); + + drm_gem_dma_free(dma_obj); +} + +/** + * drm_gem_dma_object_print_info() - Print &drm_gem_dma_object info for debugfs + * @p: DRM printer + * @indent: Tab indentation level + * @obj: GEM object + * + * This function wraps drm_gem_dma_print_info(). Drivers that employ the DMA helpers + * should use this function as their &drm_gem_object_funcs.print_info handler. + */ +static inline void drm_gem_dma_object_print_info(struct drm_printer *p, unsigned int indent, + const struct drm_gem_object *obj) +{ + const struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj); + + drm_gem_dma_print_info(dma_obj, p, indent); +} + +/** + * drm_gem_dma_object_get_sg_table - GEM object function for drm_gem_dma_get_sg_table() + * @obj: GEM object + * + * This function wraps drm_gem_dma_get_sg_table(). Drivers that employ the DMA helpers should + * use it as their &drm_gem_object_funcs.get_sg_table handler. + * + * Returns: + * A pointer to the scatter/gather table of pinned pages or NULL on failure. + */ +static inline struct sg_table *drm_gem_dma_object_get_sg_table(struct drm_gem_object *obj) +{ + struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj); + + return drm_gem_dma_get_sg_table(dma_obj); +} + +/* + * drm_gem_dma_object_vmap - GEM object function for drm_gem_dma_vmap() + * @obj: GEM object + * @map: Returns the kernel virtual address of the DMA GEM object's backing store. + * + * This function wraps drm_gem_dma_vmap(). Drivers that employ the DMA helpers should + * use it as their &drm_gem_object_funcs.vmap handler. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_gem_dma_object_vmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj); + + return drm_gem_dma_vmap(dma_obj, map); +} + +/** + * drm_gem_dma_object_mmap - GEM object function for drm_gem_dma_mmap() + * @obj: GEM object + * @vma: VMA for the area to be mapped + * + * This function wraps drm_gem_dma_mmap(). Drivers that employ the dma helpers should + * use it as their &drm_gem_object_funcs.mmap handler. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_gem_dma_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj); + + return drm_gem_dma_mmap(dma_obj, vma); +} + +/* + * Driver ops + */ + +/* create memory region for DRM framebuffer */ +int drm_gem_dma_dumb_create_internal(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args); + +/* create memory region for DRM framebuffer */ +int drm_gem_dma_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args); + +struct drm_gem_object * +drm_gem_dma_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt); + +/** + * DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE - DMA GEM driver operations + * @dumb_create_func: callback function for .dumb_create + * + * This macro provides a shortcut for setting the default GEM operations in the + * &drm_driver structure. + * + * This macro is a variant of DRM_GEM_DMA_DRIVER_OPS for drivers that + * override the default implementation of &struct rm_driver.dumb_create. Use + * DRM_GEM_DMA_DRIVER_OPS if possible. Drivers that require a virtual address + * on imported buffers should use + * DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE() instead. + */ +#define DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(dumb_create_func) \ + .dumb_create = (dumb_create_func), \ + .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table + +/** + * DRM_GEM_DMA_DRIVER_OPS - DMA GEM driver operations + * + * This macro provides a shortcut for setting the default GEM operations in the + * &drm_driver structure. + * + * Drivers that come with their own implementation of + * &struct drm_driver.dumb_create should use + * DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE() instead. Use + * DRM_GEM_DMA_DRIVER_OPS if possible. Drivers that require a virtual address + * on imported buffers should use DRM_GEM_DMA_DRIVER_OPS_VMAP instead. + */ +#define DRM_GEM_DMA_DRIVER_OPS \ + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_dma_dumb_create) + +/** + * DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE - DMA GEM driver operations + * ensuring a virtual address + * on the buffer + * @dumb_create_func: callback function for .dumb_create + * + * This macro provides a shortcut for setting the default GEM operations in the + * &drm_driver structure for drivers that need the virtual address also on + * imported buffers. + * + * This macro is a variant of DRM_GEM_DMA_DRIVER_OPS_VMAP for drivers that + * override the default implementation of &struct drm_driver.dumb_create. Use + * DRM_GEM_DMA_DRIVER_OPS_VMAP if possible. Drivers that do not require a + * virtual address on imported buffers should use + * DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE() instead. + */ +#define DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(dumb_create_func) \ + .dumb_create = (dumb_create_func), \ + .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table_vmap + +/** + * DRM_GEM_DMA_DRIVER_OPS_VMAP - DMA GEM driver operations ensuring a virtual + * address on the buffer + * + * This macro provides a shortcut for setting the default GEM operations in the + * &drm_driver structure for drivers that need the virtual address also on + * imported buffers. + * + * Drivers that come with their own implementation of + * &struct drm_driver.dumb_create should use + * DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE() instead. Use + * DRM_GEM_DMA_DRIVER_OPS_VMAP if possible. Drivers that do not require a + * virtual address on imported buffers should use DRM_GEM_DMA_DRIVER_OPS + * instead. + */ +#define DRM_GEM_DMA_DRIVER_OPS_VMAP \ + DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(drm_gem_dma_dumb_create) + +struct drm_gem_object * +drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *drm, + struct dma_buf_attachment *attach, + struct sg_table *sgt); + +/* + * File ops + */ + +#ifndef CONFIG_MMU +unsigned long drm_gem_dma_get_unmapped_area(struct file *filp, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags); +#define DRM_GEM_DMA_UNMAPPED_AREA_FOPS \ + .get_unmapped_area = drm_gem_dma_get_unmapped_area, +#else +#define DRM_GEM_DMA_UNMAPPED_AREA_FOPS +#endif + +/** + * DEFINE_DRM_GEM_DMA_FOPS() - macro to generate file operations for DMA drivers + * @name: name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations for DMA based + * drivers, which can be assigned to &drm_driver.fops. Note that this structure + * cannot be shared between drivers, because it contains a reference to the + * current module using THIS_MODULE. + * + * Note that the declaration is already marked as static - if you need a + * non-static version of this you're probably doing it wrong and will break the + * THIS_MODULE reference by accident. + */ +#define DEFINE_DRM_GEM_DMA_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_gem_mmap,\ + .fop_flags = FOP_UNSIGNED_OFFSET, \ + DRM_GEM_DMA_UNMAPPED_AREA_FOPS \ + } + +#endif /* __DRM_GEM_DMA_HELPER_H__ */ diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h index a38de7eb55b4..24f1fd40d553 100644 --- a/include/drm/drm_gem_framebuffer_helper.h +++ b/include/drm/drm_gem_framebuffer_helper.h @@ -1,16 +1,20 @@ #ifndef __DRM_GEM_FB_HELPER_H__ #define __DRM_GEM_FB_HELPER_H__ +#include <linux/dma-buf.h> +#include <linux/iosys-map.h> + +struct drm_afbc_framebuffer; struct drm_device; struct drm_fb_helper_surface_size; struct drm_file; +struct drm_format_info; struct drm_framebuffer; struct drm_framebuffer_funcs; struct drm_gem_object; struct drm_mode_fb_cmd2; -struct drm_plane; -struct drm_plane_state; -struct drm_simple_display_pipe; + +#define AFBC_VENDOR_AND_TYPE_MASK GENMASK_ULL(63, 52) struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb, unsigned int plane); @@ -18,23 +22,38 @@ void drm_gem_fb_destroy(struct drm_framebuffer *fb); int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file, unsigned int *handle); +int drm_gem_fb_init_with_funcs(struct drm_device *dev, + struct drm_framebuffer *fb, + struct drm_file *file, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd, + const struct drm_framebuffer_funcs *funcs); struct drm_framebuffer * drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file, + const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd, const struct drm_framebuffer_funcs *funcs); struct drm_framebuffer * drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, + const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd); +struct drm_framebuffer * +drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd); -int drm_gem_fb_prepare_fb(struct drm_plane *plane, - struct drm_plane_state *state); -int drm_gem_fb_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state); +int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map, + struct iosys_map *data); +void drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map); +int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir); +void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir); -struct drm_framebuffer * -drm_gem_fbdev_fb_create(struct drm_device *dev, - struct drm_fb_helper_surface_size *sizes, - unsigned int pitch_align, struct drm_gem_object *obj, - const struct drm_framebuffer_funcs *funcs); +#define drm_is_afbc(modifier) \ + (((modifier) & AFBC_VENDOR_AND_TYPE_MASK) == DRM_FORMAT_MOD_ARM_AFBC(0)) + +int drm_gem_fb_afbc_init(struct drm_device *dev, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_afbc_framebuffer *afbc_fb); #endif diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h new file mode 100644 index 000000000000..589f7bfe7506 --- /dev/null +++ b/include/drm/drm_gem_shmem_helper.h @@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __DRM_GEM_SHMEM_HELPER_H__ +#define __DRM_GEM_SHMEM_HELPER_H__ + +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_prime.h> + +struct dma_buf_attachment; +struct drm_mode_create_dumb; +struct drm_printer; +struct sg_table; + +/** + * struct drm_gem_shmem_object - GEM object backed by shmem + */ +struct drm_gem_shmem_object { + /** + * @base: Base GEM object + */ + struct drm_gem_object base; + + /** + * @pages: Page table + */ + struct page **pages; + + /** + * @pages_use_count: + * + * Reference count on the pages table. + * The pages are put when the count reaches zero. + */ + refcount_t pages_use_count; + + /** + * @pages_pin_count: + * + * Reference count on the pinned pages table. + * + * Pages are hard-pinned and reside in memory if count + * greater than zero. Otherwise, when count is zero, the pages are + * allowed to be evicted and purged by memory shrinker. + */ + refcount_t pages_pin_count; + + /** + * @madv: State for madvise + * + * 0 is active/inuse. + * A negative value is the object is purged. + * Positive values are driver specific and not used by the helpers. + */ + int madv; + + /** + * @madv_list: List entry for madvise tracking + * + * Typically used by drivers to track purgeable objects + */ + struct list_head madv_list; + + /** + * @sgt: Scatter/gather table for imported PRIME buffers + */ + struct sg_table *sgt; + + /** + * @vaddr: Kernel virtual address of the backing memory + */ + void *vaddr; + + /** + * @vmap_use_count: + * + * Reference count on the virtual address. + * The address are un-mapped when the count reaches zero. + */ + refcount_t vmap_use_count; + + /** + * @pages_mark_dirty_on_put: + * + * Mark pages as dirty when they are put. + */ + bool pages_mark_dirty_on_put : 1; + + /** + * @pages_mark_accessed_on_put: + * + * Mark pages as accessed when they are put. + */ + bool pages_mark_accessed_on_put : 1; + + /** + * @map_wc: map object write-combined (instead of using shmem defaults). + */ + bool map_wc : 1; +}; + +#define to_drm_gem_shmem_obj(obj) \ + container_of(obj, struct drm_gem_shmem_object, base) + +int drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, size_t size); +struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size); +struct drm_gem_shmem_object *drm_gem_shmem_create_with_mnt(struct drm_device *dev, + size_t size, + struct vfsmount *gemfs); +void drm_gem_shmem_release(struct drm_gem_shmem_object *shmem); +void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem); + +void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem); +int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem); +void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem); +int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, + struct iosys_map *map); +void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, + struct iosys_map *map); +int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma); + +int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem); +void drm_gem_shmem_unpin_locked(struct drm_gem_shmem_object *shmem); + +int drm_gem_shmem_madvise_locked(struct drm_gem_shmem_object *shmem, int madv); + +static inline bool drm_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem) +{ + return (shmem->madv > 0) && + !refcount_read(&shmem->pages_pin_count) && shmem->sgt && + !shmem->base.dma_buf && !drm_gem_is_imported(&shmem->base); +} + +void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem); + +struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem); +struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem); + +void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem, + struct drm_printer *p, unsigned int indent); + +extern const struct vm_operations_struct drm_gem_shmem_vm_ops; + +/* + * GEM object functions + */ + +/** + * drm_gem_shmem_object_free - GEM object function for drm_gem_shmem_free() + * @obj: GEM object to free + * + * This function wraps drm_gem_shmem_free(). Drivers that employ the shmem helpers + * should use it as their &drm_gem_object_funcs.free handler. + */ +static inline void drm_gem_shmem_object_free(struct drm_gem_object *obj) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + drm_gem_shmem_free(shmem); +} + +/** + * drm_gem_shmem_object_print_info() - Print &drm_gem_shmem_object info for debugfs + * @p: DRM printer + * @indent: Tab indentation level + * @obj: GEM object + * + * This function wraps drm_gem_shmem_print_info(). Drivers that employ the shmem helpers should + * use this function as their &drm_gem_object_funcs.print_info handler. + */ +static inline void drm_gem_shmem_object_print_info(struct drm_printer *p, unsigned int indent, + const struct drm_gem_object *obj) +{ + const struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + drm_gem_shmem_print_info(shmem, p, indent); +} + +/** + * drm_gem_shmem_object_pin - GEM object function for drm_gem_shmem_pin() + * @obj: GEM object + * + * This function wraps drm_gem_shmem_pin(). Drivers that employ the shmem helpers should + * use it as their &drm_gem_object_funcs.pin handler. + */ +static inline int drm_gem_shmem_object_pin(struct drm_gem_object *obj) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + return drm_gem_shmem_pin_locked(shmem); +} + +/** + * drm_gem_shmem_object_unpin - GEM object function for drm_gem_shmem_unpin() + * @obj: GEM object + * + * This function wraps drm_gem_shmem_unpin(). Drivers that employ the shmem helpers should + * use it as their &drm_gem_object_funcs.unpin handler. + */ +static inline void drm_gem_shmem_object_unpin(struct drm_gem_object *obj) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + drm_gem_shmem_unpin_locked(shmem); +} + +/** + * drm_gem_shmem_object_get_sg_table - GEM object function for drm_gem_shmem_get_sg_table() + * @obj: GEM object + * + * This function wraps drm_gem_shmem_get_sg_table(). Drivers that employ the shmem helpers should + * use it as their &drm_gem_object_funcs.get_sg_table handler. + * + * Returns: + * A pointer to the scatter/gather table of pinned pages or error pointer on failure. + */ +static inline struct sg_table *drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + return drm_gem_shmem_get_sg_table(shmem); +} + +/* + * drm_gem_shmem_object_vmap - GEM object function for drm_gem_shmem_vmap_locked() + * @obj: GEM object + * @map: Returns the kernel virtual address of the SHMEM GEM object's backing store. + * + * This function wraps drm_gem_shmem_vmap_locked(). Drivers that employ the shmem + * helpers should use it as their &drm_gem_object_funcs.vmap handler. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_gem_shmem_object_vmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + return drm_gem_shmem_vmap_locked(shmem, map); +} + +/* + * drm_gem_shmem_object_vunmap - GEM object function for drm_gem_shmem_vunmap() + * @obj: GEM object + * @map: Kernel virtual address where the SHMEM GEM object was mapped + * + * This function wraps drm_gem_shmem_vunmap_locked(). Drivers that employ the shmem + * helpers should use it as their &drm_gem_object_funcs.vunmap handler. + */ +static inline void drm_gem_shmem_object_vunmap(struct drm_gem_object *obj, + struct iosys_map *map) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + drm_gem_shmem_vunmap_locked(shmem, map); +} + +/** + * drm_gem_shmem_object_mmap - GEM object function for drm_gem_shmem_mmap() + * @obj: GEM object + * @vma: VMA for the area to be mapped + * + * This function wraps drm_gem_shmem_mmap(). Drivers that employ the shmem helpers should + * use it as their &drm_gem_object_funcs.mmap handler. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + + return drm_gem_shmem_mmap(shmem, vma); +} + +/* + * Driver ops + */ + +struct drm_gem_object * +drm_gem_shmem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt); +int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); +struct drm_gem_object *drm_gem_shmem_prime_import_no_map(struct drm_device *dev, + struct dma_buf *buf); + +/** + * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations + * + * This macro provides a shortcut for setting the shmem GEM operations + * in the &drm_driver structure. Drivers that do not require an s/g table + * for imported buffers should use this. + */ +#define DRM_GEM_SHMEM_DRIVER_OPS \ + .gem_prime_import = drm_gem_shmem_prime_import_no_map, \ + .dumb_create = drm_gem_shmem_dumb_create + +#endif /* __DRM_GEM_SHMEM_HELPER_H__ */ diff --git a/include/drm/drm_gem_ttm_helper.h b/include/drm/drm_gem_ttm_helper.h new file mode 100644 index 000000000000..7b53d673ae7e --- /dev/null +++ b/include/drm/drm_gem_ttm_helper.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef DRM_GEM_TTM_HELPER_H +#define DRM_GEM_TTM_HELPER_H + +#include <linux/container_of.h> + +#include <drm/drm_device.h> +#include <drm/drm_gem.h> +#include <drm/ttm/ttm_bo.h> + +struct iosys_map; + +#define drm_gem_ttm_of_gem(gem_obj) \ + container_of(gem_obj, struct ttm_buffer_object, base) + +void drm_gem_ttm_print_info(struct drm_printer *p, unsigned int indent, + const struct drm_gem_object *gem); +int drm_gem_ttm_vmap(struct drm_gem_object *gem, + struct iosys_map *map); +void drm_gem_ttm_vunmap(struct drm_gem_object *gem, + struct iosys_map *map); +int drm_gem_ttm_mmap(struct drm_gem_object *gem, + struct vm_area_struct *vma); + +int drm_gem_ttm_dumb_map_offset(struct drm_file *file, struct drm_device *dev, + uint32_t handle, uint64_t *offset); + +#endif diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h new file mode 100644 index 000000000000..2dd42bed679d --- /dev/null +++ b/include/drm/drm_gem_vram_helper.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef DRM_GEM_VRAM_HELPER_H +#define DRM_GEM_VRAM_HELPER_H + +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_ttm_helper.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_modes.h> +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> + +#include <linux/container_of.h> +#include <linux/iosys-map.h> + +struct drm_mode_create_dumb; +struct drm_plane; +struct drm_plane_state; +struct filp; +struct vm_area_struct; + +#define DRM_GEM_VRAM_PL_FLAG_SYSTEM (1 << 0) +#define DRM_GEM_VRAM_PL_FLAG_VRAM (1 << 1) +#define DRM_GEM_VRAM_PL_FLAG_TOPDOWN (1 << 2) + +/* + * Buffer-object helpers + */ + +/** + * struct drm_gem_vram_object - GEM object backed by VRAM + * @bo: TTM buffer object + * @map: Mapping information for @bo + * @placement: TTM placement information. Supported placements are %TTM_PL_VRAM + * and %TTM_PL_SYSTEM + * @placements: TTM placement information. + * + * The type struct drm_gem_vram_object represents a GEM object that is + * backed by VRAM. It can be used for simple framebuffer devices with + * dedicated memory. The buffer object can be evicted to system memory if + * video memory becomes scarce. + * + * GEM VRAM objects perform reference counting for pin and mapping + * operations. So a buffer object that has been pinned N times with + * drm_gem_vram_pin() must be unpinned N times with + * drm_gem_vram_unpin(). The same applies to pairs of + * drm_gem_vram_kmap() and drm_gem_vram_kunmap(), as well as pairs of + * drm_gem_vram_vmap() and drm_gem_vram_vunmap(). + */ +struct drm_gem_vram_object { + struct ttm_buffer_object bo; + struct iosys_map map; + + /** + * @vmap_use_count: + * + * Reference count on the virtual address. + * The address are un-mapped when the count reaches zero. + */ + unsigned int vmap_use_count; + + /* Supported placements are %TTM_PL_VRAM and %TTM_PL_SYSTEM */ + struct ttm_placement placement; + struct ttm_place placements[2]; +}; + +/** + * drm_gem_vram_of_bo - Returns the container of type + * &struct drm_gem_vram_object for field bo. + * @bo: the VRAM buffer object + * Returns: The containing GEM VRAM object + */ +static inline struct drm_gem_vram_object *drm_gem_vram_of_bo( + struct ttm_buffer_object *bo) +{ + return container_of(bo, struct drm_gem_vram_object, bo); +} + +/** + * drm_gem_vram_of_gem - Returns the container of type + * &struct drm_gem_vram_object for field gem. + * @gem: the GEM object + * Returns: The containing GEM VRAM object + */ +static inline struct drm_gem_vram_object *drm_gem_vram_of_gem( + struct drm_gem_object *gem) +{ + return container_of(gem, struct drm_gem_vram_object, bo.base); +} + +struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, + size_t size, + unsigned long pg_align); +void drm_gem_vram_put(struct drm_gem_vram_object *gbo); +s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); +int drm_gem_vram_vmap(struct drm_gem_vram_object *gbo, struct iosys_map *map); +void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo, + struct iosys_map *map); + +int drm_gem_vram_fill_create_dumb(struct drm_file *file, + struct drm_device *dev, + unsigned long pg_align, + unsigned long pitch_align, + struct drm_mode_create_dumb *args); + +/* + * Helpers for struct drm_driver + */ + +int drm_gem_vram_driver_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args); + +/* + * Helpers for struct drm_plane_helper_funcs + */ +int +drm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state); +void +drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); + +/** + * DRM_GEM_VRAM_PLANE_HELPER_FUNCS - Initializes struct drm_plane_helper_funcs + * for VRAM handling + * + * Drivers may use GEM BOs as VRAM helpers for the framebuffer memory. This + * macro initializes struct drm_plane_helper_funcs to use the respective helper + * functions. + */ +#define DRM_GEM_VRAM_PLANE_HELPER_FUNCS \ + .prepare_fb = drm_gem_vram_plane_helper_prepare_fb, \ + .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb + +/** + * define DRM_GEM_VRAM_DRIVER - default callback functions for + * &struct drm_driver + * + * Drivers that use VRAM MM and GEM VRAM can use this macro to initialize + * &struct drm_driver with default functions. + */ +#define DRM_GEM_VRAM_DRIVER \ + .debugfs_init = drm_vram_mm_debugfs_init, \ + .dumb_create = drm_gem_vram_driver_dumb_create, \ + .dumb_map_offset = drm_gem_ttm_dumb_map_offset + +/* + * VRAM memory manager + */ + +/** + * struct drm_vram_mm - An instance of VRAM MM + * @vram_base: Base address of the managed video memory + * @vram_size: Size of the managed video memory in bytes + * @bdev: The TTM BO device. + * + * The fields &struct drm_vram_mm.vram_base and + * &struct drm_vram_mm.vrm_size are managed by VRAM MM, but are + * available for public read access. Use the field + * &struct drm_vram_mm.bdev to access the TTM BO device. + */ +struct drm_vram_mm { + uint64_t vram_base; + size_t vram_size; + + struct ttm_device bdev; +}; + +/** + * drm_vram_mm_of_bdev() - Returns the container of type &struct ttm_device for + * field bdev. + * @bdev: the TTM BO device + * + * Returns: + * The containing instance of &struct drm_vram_mm + */ +static inline struct drm_vram_mm *drm_vram_mm_of_bdev( + struct ttm_device *bdev) +{ + return container_of(bdev, struct drm_vram_mm, bdev); +} + +void drm_vram_mm_debugfs_init(struct drm_minor *minor); + +/* + * Helpers for integration with struct drm_device + */ + +int drmm_vram_helper_init(struct drm_device *dev, uint64_t vram_base, + size_t vram_size); + +/* + * Mode-config helpers + */ + +enum drm_mode_status +drm_vram_helper_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode); + +#endif diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h new file mode 100644 index 000000000000..632e100e6efb --- /dev/null +++ b/include/drm/drm_gpusvm.h @@ -0,0 +1,542 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Copyright © 2024 Intel Corporation + */ + +#ifndef __DRM_GPUSVM_H__ +#define __DRM_GPUSVM_H__ + +#include <linux/kref.h> +#include <linux/interval_tree.h> +#include <linux/mmu_notifier.h> + +struct dev_pagemap_ops; +struct drm_device; +struct drm_gpusvm; +struct drm_gpusvm_notifier; +struct drm_gpusvm_ops; +struct drm_gpusvm_range; +struct drm_pagemap; +struct drm_pagemap_addr; + +/** + * struct drm_gpusvm_ops - Operations structure for GPU SVM + * + * This structure defines the operations for GPU Shared Virtual Memory (SVM). + * These operations are provided by the GPU driver to manage SVM ranges and + * notifiers. + */ +struct drm_gpusvm_ops { + /** + * @notifier_alloc: Allocate a GPU SVM notifier (optional) + * + * Allocate a GPU SVM notifier. + * + * Return: Pointer to the allocated GPU SVM notifier on success, NULL on failure. + */ + struct drm_gpusvm_notifier *(*notifier_alloc)(void); + + /** + * @notifier_free: Free a GPU SVM notifier (optional) + * @notifier: Pointer to the GPU SVM notifier to be freed + * + * Free a GPU SVM notifier. + */ + void (*notifier_free)(struct drm_gpusvm_notifier *notifier); + + /** + * @range_alloc: Allocate a GPU SVM range (optional) + * @gpusvm: Pointer to the GPU SVM + * + * Allocate a GPU SVM range. + * + * Return: Pointer to the allocated GPU SVM range on success, NULL on failure. + */ + struct drm_gpusvm_range *(*range_alloc)(struct drm_gpusvm *gpusvm); + + /** + * @range_free: Free a GPU SVM range (optional) + * @range: Pointer to the GPU SVM range to be freed + * + * Free a GPU SVM range. + */ + void (*range_free)(struct drm_gpusvm_range *range); + + /** + * @invalidate: Invalidate GPU SVM notifier (required) + * @gpusvm: Pointer to the GPU SVM + * @notifier: Pointer to the GPU SVM notifier + * @mmu_range: Pointer to the mmu_notifier_range structure + * + * Invalidate the GPU page tables. It can safely walk the notifier range + * RB tree/list in this function. Called while holding the notifier lock. + */ + void (*invalidate)(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_notifier *notifier, + const struct mmu_notifier_range *mmu_range); +}; + +/** + * struct drm_gpusvm_notifier - Structure representing a GPU SVM notifier + * + * @gpusvm: Pointer to the GPU SVM structure + * @notifier: MMU interval notifier + * @itree: Interval tree node for the notifier (inserted in GPU SVM) + * @entry: List entry to fast interval tree traversal + * @root: Cached root node of the RB tree containing ranges + * @range_list: List head containing of ranges in the same order they appear in + * interval tree. This is useful to keep iterating ranges while + * doing modifications to RB tree. + * @flags: Flags for notifier + * @flags.removed: Flag indicating whether the MMU interval notifier has been + * removed + * + * This structure represents a GPU SVM notifier. + */ +struct drm_gpusvm_notifier { + struct drm_gpusvm *gpusvm; + struct mmu_interval_notifier notifier; + struct interval_tree_node itree; + struct list_head entry; + struct rb_root_cached root; + struct list_head range_list; + struct { + u32 removed : 1; + } flags; +}; + +/** + * struct drm_gpusvm_pages_flags - Structure representing a GPU SVM pages flags + * + * @migrate_devmem: Flag indicating whether the pages can be migrated to device memory + * @unmapped: Flag indicating if the pages has been unmapped + * @partial_unmap: Flag indicating if the pages has been partially unmapped + * @has_devmem_pages: Flag indicating if the pages has devmem pages + * @has_dma_mapping: Flag indicating if the pages has a DMA mapping + * @__flags: Flags for pages in u16 form (used for READ_ONCE) + */ +struct drm_gpusvm_pages_flags { + union { + struct { + /* All flags below must be set upon creation */ + u16 migrate_devmem : 1; + /* All flags below must be set / cleared under notifier lock */ + u16 unmapped : 1; + u16 partial_unmap : 1; + u16 has_devmem_pages : 1; + u16 has_dma_mapping : 1; + }; + u16 __flags; + }; +}; + +/** + * struct drm_gpusvm_pages - Structure representing a GPU SVM mapped pages + * + * @dma_addr: Device address array + * @dpagemap: The struct drm_pagemap of the device pages we're dma-mapping. + * Note this is assuming only one drm_pagemap per range is allowed. + * @notifier_seq: Notifier sequence number of the range's pages + * @flags: Flags for range + * @flags.migrate_devmem: Flag indicating whether the range can be migrated to device memory + * @flags.unmapped: Flag indicating if the range has been unmapped + * @flags.partial_unmap: Flag indicating if the range has been partially unmapped + * @flags.has_devmem_pages: Flag indicating if the range has devmem pages + * @flags.has_dma_mapping: Flag indicating if the range has a DMA mapping + */ +struct drm_gpusvm_pages { + struct drm_pagemap_addr *dma_addr; + struct drm_pagemap *dpagemap; + unsigned long notifier_seq; + struct drm_gpusvm_pages_flags flags; +}; + +/** + * struct drm_gpusvm_range - Structure representing a GPU SVM range + * + * @gpusvm: Pointer to the GPU SVM structure + * @notifier: Pointer to the GPU SVM notifier + * @refcount: Reference count for the range + * @itree: Interval tree node for the range (inserted in GPU SVM notifier) + * @entry: List entry to fast interval tree traversal + * @pages: The pages for this range. + * + * This structure represents a GPU SVM range used for tracking memory ranges + * mapped in a DRM device. + */ +struct drm_gpusvm_range { + struct drm_gpusvm *gpusvm; + struct drm_gpusvm_notifier *notifier; + struct kref refcount; + struct interval_tree_node itree; + struct list_head entry; + struct drm_gpusvm_pages pages; +}; + +/** + * struct drm_gpusvm - GPU SVM structure + * + * @name: Name of the GPU SVM + * @drm: Pointer to the DRM device structure + * @mm: Pointer to the mm_struct for the address space + * @mm_start: Start address of GPU SVM + * @mm_range: Range of the GPU SVM + * @notifier_size: Size of individual notifiers + * @ops: Pointer to the operations structure for GPU SVM + * @chunk_sizes: Pointer to the array of chunk sizes used in range allocation. + * Entries should be powers of 2 in descending order. + * @num_chunks: Number of chunks + * @notifier_lock: Read-write semaphore for protecting notifier operations + * @root: Cached root node of the Red-Black tree containing GPU SVM notifiers + * @notifier_list: list head containing of notifiers in the same order they + * appear in interval tree. This is useful to keep iterating + * notifiers while doing modifications to RB tree. + * + * This structure represents a GPU SVM (Shared Virtual Memory) used for tracking + * memory ranges mapped in a DRM (Direct Rendering Manager) device. + * + * No reference counting is provided, as this is expected to be embedded in the + * driver VM structure along with the struct drm_gpuvm, which handles reference + * counting. + */ +struct drm_gpusvm { + const char *name; + struct drm_device *drm; + struct mm_struct *mm; + unsigned long mm_start; + unsigned long mm_range; + unsigned long notifier_size; + const struct drm_gpusvm_ops *ops; + const unsigned long *chunk_sizes; + int num_chunks; + struct rw_semaphore notifier_lock; + struct rb_root_cached root; + struct list_head notifier_list; +#ifdef CONFIG_LOCKDEP + /** + * @lock_dep_map: Annotates drm_gpusvm_range_find_or_insert and + * drm_gpusvm_range_remove with a driver provided lock. + */ + struct lockdep_map *lock_dep_map; +#endif +}; + +/** + * struct drm_gpusvm_ctx - DRM GPU SVM context + * + * @device_private_page_owner: The device-private page owner to use for + * this operation + * @check_pages_threshold: Check CPU pages for present if chunk is less than or + * equal to threshold. If not present, reduce chunk + * size. + * @timeslice_ms: The timeslice MS which in minimum time a piece of memory + * remains with either exclusive GPU or CPU access. + * @in_notifier: entering from a MMU notifier + * @read_only: operating on read-only memory + * @devmem_possible: possible to use device memory + * @devmem_only: use only device memory + * @allow_mixed: Allow mixed mappings in get pages. Mixing between system and + * single dpagemap is supported, mixing between multiple dpagemap + * is unsupported. + * + * Context that is DRM GPUSVM is operating in (i.e. user arguments). + */ +struct drm_gpusvm_ctx { + void *device_private_page_owner; + unsigned long check_pages_threshold; + unsigned long timeslice_ms; + unsigned int in_notifier :1; + unsigned int read_only :1; + unsigned int devmem_possible :1; + unsigned int devmem_only :1; + unsigned int allow_mixed :1; +}; + +int drm_gpusvm_init(struct drm_gpusvm *gpusvm, + const char *name, struct drm_device *drm, + struct mm_struct *mm, + unsigned long mm_start, unsigned long mm_range, + unsigned long notifier_size, + const struct drm_gpusvm_ops *ops, + const unsigned long *chunk_sizes, int num_chunks); + +void drm_gpusvm_fini(struct drm_gpusvm *gpusvm); + +void drm_gpusvm_free(struct drm_gpusvm *gpusvm); + +unsigned long +drm_gpusvm_find_vma_start(struct drm_gpusvm *gpusvm, + unsigned long start, + unsigned long end); + +struct drm_gpusvm_range * +drm_gpusvm_range_find_or_insert(struct drm_gpusvm *gpusvm, + unsigned long fault_addr, + unsigned long gpuva_start, + unsigned long gpuva_end, + const struct drm_gpusvm_ctx *ctx); + +void drm_gpusvm_range_remove(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_range *range); + +int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_range *range); + +struct drm_gpusvm_range * +drm_gpusvm_range_get(struct drm_gpusvm_range *range); + +void drm_gpusvm_range_put(struct drm_gpusvm_range *range); + +bool drm_gpusvm_range_pages_valid(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_range *range); + +int drm_gpusvm_range_get_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_range *range, + const struct drm_gpusvm_ctx *ctx); + +void drm_gpusvm_range_unmap_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_range *range, + const struct drm_gpusvm_ctx *ctx); + +bool drm_gpusvm_has_mapping(struct drm_gpusvm *gpusvm, unsigned long start, + unsigned long end); + +struct drm_gpusvm_notifier * +drm_gpusvm_notifier_find(struct drm_gpusvm *gpusvm, unsigned long start, + unsigned long end); + +struct drm_gpusvm_range * +drm_gpusvm_range_find(struct drm_gpusvm_notifier *notifier, unsigned long start, + unsigned long end); + +void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range, + const struct mmu_notifier_range *mmu_range); + +int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + struct mm_struct *mm, + struct mmu_interval_notifier *notifier, + unsigned long pages_start, unsigned long pages_end, + const struct drm_gpusvm_ctx *ctx); + +void drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + unsigned long npages, + const struct drm_gpusvm_ctx *ctx); + +void drm_gpusvm_free_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + unsigned long npages); + +#ifdef CONFIG_LOCKDEP +/** + * drm_gpusvm_driver_set_lock() - Set the lock protecting accesses to GPU SVM + * @gpusvm: Pointer to the GPU SVM structure. + * @lock: the lock used to protect the gpuva list. The locking primitive + * must contain a dep_map field. + * + * Call this to annotate drm_gpusvm_range_find_or_insert and + * drm_gpusvm_range_remove. + */ +#define drm_gpusvm_driver_set_lock(gpusvm, lock) \ + do { \ + if (!WARN((gpusvm)->lock_dep_map, \ + "GPUSVM range lock should be set only once."))\ + (gpusvm)->lock_dep_map = &(lock)->dep_map; \ + } while (0) +#else +#define drm_gpusvm_driver_set_lock(gpusvm, lock) do {} while (0) +#endif + +/** + * drm_gpusvm_notifier_lock() - Lock GPU SVM notifier + * @gpusvm__: Pointer to the GPU SVM structure. + * + * Abstract client usage GPU SVM notifier lock, take lock + */ +#define drm_gpusvm_notifier_lock(gpusvm__) \ + down_read(&(gpusvm__)->notifier_lock) + +/** + * drm_gpusvm_notifier_unlock() - Unlock GPU SVM notifier + * @gpusvm__: Pointer to the GPU SVM structure. + * + * Abstract client usage GPU SVM notifier lock, drop lock + */ +#define drm_gpusvm_notifier_unlock(gpusvm__) \ + up_read(&(gpusvm__)->notifier_lock) + +/** + * drm_gpusvm_range_start() - GPU SVM range start address + * @range: Pointer to the GPU SVM range + * + * Return: GPU SVM range start address + */ +static inline unsigned long +drm_gpusvm_range_start(struct drm_gpusvm_range *range) +{ + return range->itree.start; +} + +/** + * drm_gpusvm_range_end() - GPU SVM range end address + * @range: Pointer to the GPU SVM range + * + * Return: GPU SVM range end address + */ +static inline unsigned long +drm_gpusvm_range_end(struct drm_gpusvm_range *range) +{ + return range->itree.last + 1; +} + +/** + * drm_gpusvm_range_size() - GPU SVM range size + * @range: Pointer to the GPU SVM range + * + * Return: GPU SVM range size + */ +static inline unsigned long +drm_gpusvm_range_size(struct drm_gpusvm_range *range) +{ + return drm_gpusvm_range_end(range) - drm_gpusvm_range_start(range); +} + +/** + * drm_gpusvm_notifier_start() - GPU SVM notifier start address + * @notifier: Pointer to the GPU SVM notifier + * + * Return: GPU SVM notifier start address + */ +static inline unsigned long +drm_gpusvm_notifier_start(struct drm_gpusvm_notifier *notifier) +{ + return notifier->itree.start; +} + +/** + * drm_gpusvm_notifier_end() - GPU SVM notifier end address + * @notifier: Pointer to the GPU SVM notifier + * + * Return: GPU SVM notifier end address + */ +static inline unsigned long +drm_gpusvm_notifier_end(struct drm_gpusvm_notifier *notifier) +{ + return notifier->itree.last + 1; +} + +/** + * drm_gpusvm_notifier_size() - GPU SVM notifier size + * @notifier: Pointer to the GPU SVM notifier + * + * Return: GPU SVM notifier size + */ +static inline unsigned long +drm_gpusvm_notifier_size(struct drm_gpusvm_notifier *notifier) +{ + return drm_gpusvm_notifier_end(notifier) - + drm_gpusvm_notifier_start(notifier); +} + +/** + * __drm_gpusvm_range_next() - Get the next GPU SVM range in the list + * @range: a pointer to the current GPU SVM range + * + * Return: A pointer to the next drm_gpusvm_range if available, or NULL if the + * current range is the last one or if the input range is NULL. + */ +static inline struct drm_gpusvm_range * +__drm_gpusvm_range_next(struct drm_gpusvm_range *range) +{ + if (range && !list_is_last(&range->entry, + &range->notifier->range_list)) + return list_next_entry(range, entry); + + return NULL; +} + +/** + * drm_gpusvm_for_each_range() - Iterate over GPU SVM ranges in a notifier + * @range__: Iterator variable for the ranges. If set, it indicates the start of + * the iterator. If NULL, call drm_gpusvm_range_find() to get the range. + * @notifier__: Pointer to the GPU SVM notifier + * @start__: Start address of the range + * @end__: End address of the range + * + * This macro is used to iterate over GPU SVM ranges in a notifier. It is safe + * to use while holding the driver SVM lock or the notifier lock. + */ +#define drm_gpusvm_for_each_range(range__, notifier__, start__, end__) \ + for ((range__) = (range__) ?: \ + drm_gpusvm_range_find((notifier__), (start__), (end__)); \ + (range__) && (drm_gpusvm_range_start(range__) < (end__)); \ + (range__) = __drm_gpusvm_range_next(range__)) + +/** + * drm_gpusvm_for_each_range_safe() - Safely iterate over GPU SVM ranges in a notifier + * @range__: Iterator variable for the ranges + * @next__: Iterator variable for the ranges temporay storage + * @notifier__: Pointer to the GPU SVM notifier + * @start__: Start address of the range + * @end__: End address of the range + * + * This macro is used to iterate over GPU SVM ranges in a notifier while + * removing ranges from it. + */ +#define drm_gpusvm_for_each_range_safe(range__, next__, notifier__, start__, end__) \ + for ((range__) = drm_gpusvm_range_find((notifier__), (start__), (end__)), \ + (next__) = __drm_gpusvm_range_next(range__); \ + (range__) && (drm_gpusvm_range_start(range__) < (end__)); \ + (range__) = (next__), (next__) = __drm_gpusvm_range_next(range__)) + +/** + * __drm_gpusvm_notifier_next() - get the next drm_gpusvm_notifier in the list + * @notifier: a pointer to the current drm_gpusvm_notifier + * + * Return: A pointer to the next drm_gpusvm_notifier if available, or NULL if + * the current notifier is the last one or if the input notifier is + * NULL. + */ +static inline struct drm_gpusvm_notifier * +__drm_gpusvm_notifier_next(struct drm_gpusvm_notifier *notifier) +{ + if (notifier && !list_is_last(¬ifier->entry, + ¬ifier->gpusvm->notifier_list)) + return list_next_entry(notifier, entry); + + return NULL; +} + +/** + * drm_gpusvm_for_each_notifier() - Iterate over GPU SVM notifiers in a gpusvm + * @notifier__: Iterator variable for the notifiers + * @gpusvm__: Pointer to the GPU SVM notifier + * @start__: Start address of the notifier + * @end__: End address of the notifier + * + * This macro is used to iterate over GPU SVM notifiers in a gpusvm. + */ +#define drm_gpusvm_for_each_notifier(notifier__, gpusvm__, start__, end__) \ + for ((notifier__) = drm_gpusvm_notifier_find((gpusvm__), (start__), (end__)); \ + (notifier__) && (drm_gpusvm_notifier_start(notifier__) < (end__)); \ + (notifier__) = __drm_gpusvm_notifier_next(notifier__)) + +/** + * drm_gpusvm_for_each_notifier_safe() - Safely iterate over GPU SVM notifiers in a gpusvm + * @notifier__: Iterator variable for the notifiers + * @next__: Iterator variable for the notifiers temporay storage + * @gpusvm__: Pointer to the GPU SVM notifier + * @start__: Start address of the notifier + * @end__: End address of the notifier + * + * This macro is used to iterate over GPU SVM notifiers in a gpusvm while + * removing notifiers from it. + */ +#define drm_gpusvm_for_each_notifier_safe(notifier__, next__, gpusvm__, start__, end__) \ + for ((notifier__) = drm_gpusvm_notifier_find((gpusvm__), (start__), (end__)), \ + (next__) = __drm_gpusvm_notifier_next(notifier__); \ + (notifier__) && (drm_gpusvm_notifier_start(notifier__) < (end__)); \ + (notifier__) = (next__), (next__) = __drm_gpusvm_notifier_next(notifier__)) + +#endif /* __DRM_GPUSVM_H__ */ diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h new file mode 100644 index 000000000000..fdfc575b2603 --- /dev/null +++ b/include/drm/drm_gpuvm.h @@ -0,0 +1,1304 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ + +#ifndef __DRM_GPUVM_H__ +#define __DRM_GPUVM_H__ + +/* + * Copyright (c) 2022 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <linux/dma-resv.h> +#include <linux/list.h> +#include <linux/llist.h> +#include <linux/rbtree.h> +#include <linux/types.h> + +#include <drm/drm_device.h> +#include <drm/drm_gem.h> +#include <drm/drm_exec.h> + +struct drm_gpuvm; +struct drm_gpuvm_bo; +struct drm_gpuvm_ops; + +/** + * enum drm_gpuva_flags - flags for struct drm_gpuva + */ +enum drm_gpuva_flags { + /** + * @DRM_GPUVA_INVALIDATED: + * + * Flag indicating that the &drm_gpuva's backing GEM is invalidated. + */ + DRM_GPUVA_INVALIDATED = (1 << 0), + + /** + * @DRM_GPUVA_SPARSE: + * + * Flag indicating that the &drm_gpuva is a sparse mapping. + */ + DRM_GPUVA_SPARSE = (1 << 1), + + /** + * @DRM_GPUVA_USERBITS: user defined bits + */ + DRM_GPUVA_USERBITS = (1 << 2), +}; + +/** + * struct drm_gpuva - structure to track a GPU VA mapping + * + * This structure represents a GPU VA mapping and is associated with a + * &drm_gpuvm. + * + * Typically, this structure is embedded in bigger driver structures. + */ +struct drm_gpuva { + /** + * @vm: the &drm_gpuvm this object is associated with + */ + struct drm_gpuvm *vm; + + /** + * @vm_bo: the &drm_gpuvm_bo abstraction for the mapped + * &drm_gem_object + */ + struct drm_gpuvm_bo *vm_bo; + + /** + * @flags: the &drm_gpuva_flags for this mapping + */ + enum drm_gpuva_flags flags; + + /** + * @va: structure containing the address and range of the &drm_gpuva + */ + struct { + /** + * @va.addr: the start address + */ + u64 addr; + + /* + * @range: the range + */ + u64 range; + } va; + + /** + * @gem: structure containing the &drm_gem_object and its offset + */ + struct { + /** + * @gem.offset: the offset within the &drm_gem_object + */ + u64 offset; + + /** + * @gem.obj: the mapped &drm_gem_object + */ + struct drm_gem_object *obj; + + /** + * @gem.entry: the &list_head to attach this object to a &drm_gpuvm_bo + */ + struct list_head entry; + } gem; + + /** + * @rb: structure containing data to store &drm_gpuvas in a rb-tree + */ + struct { + /** + * @rb.node: the rb-tree node + */ + struct rb_node node; + + /** + * @rb.entry: The &list_head to additionally connect &drm_gpuvas + * in the same order they appear in the interval tree. This is + * useful to keep iterating &drm_gpuvas from a start node found + * through the rb-tree while doing modifications on the rb-tree + * itself. + */ + struct list_head entry; + + /** + * @rb.__subtree_last: needed by the interval tree, holding last-in-subtree + */ + u64 __subtree_last; + } rb; +}; + +int drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va); +void drm_gpuva_remove(struct drm_gpuva *va); + +void drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo); +void drm_gpuva_unlink(struct drm_gpuva *va); +void drm_gpuva_unlink_defer(struct drm_gpuva *va); + +struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); +struct drm_gpuva *drm_gpuva_find_first(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); +struct drm_gpuva *drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start); +struct drm_gpuva *drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end); + +/** + * drm_gpuva_invalidate() - sets whether the backing GEM of this &drm_gpuva is + * invalidated + * @va: the &drm_gpuva to set the invalidate flag for + * @invalidate: indicates whether the &drm_gpuva is invalidated + */ +static inline void drm_gpuva_invalidate(struct drm_gpuva *va, bool invalidate) +{ + if (invalidate) + va->flags |= DRM_GPUVA_INVALIDATED; + else + va->flags &= ~DRM_GPUVA_INVALIDATED; +} + +/** + * drm_gpuva_invalidated() - indicates whether the backing BO of this &drm_gpuva + * is invalidated + * @va: the &drm_gpuva to check + * + * Returns: %true if the GPU VA is invalidated, %false otherwise + */ +static inline bool drm_gpuva_invalidated(struct drm_gpuva *va) +{ + return va->flags & DRM_GPUVA_INVALIDATED; +} + +/** + * enum drm_gpuvm_flags - flags for struct drm_gpuvm + */ +enum drm_gpuvm_flags { + /** + * @DRM_GPUVM_RESV_PROTECTED: GPUVM is protected externally by the + * GPUVM's &dma_resv lock + */ + DRM_GPUVM_RESV_PROTECTED = BIT(0), + + /** + * @DRM_GPUVM_IMMEDIATE_MODE: use the locking scheme for GEMs designed + * for modifying the GPUVM during the fence signalling path + * + * When set, gpuva.lock is used to protect gpuva.list in all GEM + * objects associated with this GPUVM. Otherwise, the GEMs dma-resv is + * used. + */ + DRM_GPUVM_IMMEDIATE_MODE = BIT(1), + + /** + * @DRM_GPUVM_USERBITS: user defined bits + */ + DRM_GPUVM_USERBITS = BIT(2), +}; + +/** + * struct drm_gpuvm - DRM GPU VA Manager + * + * The DRM GPU VA Manager keeps track of a GPU's virtual address space by using + * &maple_tree structures. Typically, this structure is embedded in bigger + * driver structures. + * + * Drivers can pass addresses and ranges in an arbitrary unit, e.g. bytes or + * pages. + * + * There should be one manager instance per GPU virtual address space. + */ +struct drm_gpuvm { + /** + * @name: the name of the DRM GPU VA space + */ + const char *name; + + /** + * @flags: the &drm_gpuvm_flags of this GPUVM + */ + enum drm_gpuvm_flags flags; + + /** + * @drm: the &drm_device this VM lives in + */ + struct drm_device *drm; + + /** + * @mm_start: start of the VA space + */ + u64 mm_start; + + /** + * @mm_range: length of the VA space + */ + u64 mm_range; + + /** + * @rb: structures to track &drm_gpuva entries + */ + struct { + /** + * @rb.tree: the rb-tree to track GPU VA mappings + */ + struct rb_root_cached tree; + + /** + * @rb.list: the &list_head to track GPU VA mappings + */ + struct list_head list; + } rb; + + /** + * @kref: reference count of this object + */ + struct kref kref; + + /** + * @kernel_alloc_node: + * + * &drm_gpuva representing the address space cutout reserved for + * the kernel + */ + struct drm_gpuva kernel_alloc_node; + + /** + * @ops: &drm_gpuvm_ops providing the split/merge steps to drivers + */ + const struct drm_gpuvm_ops *ops; + + /** + * @r_obj: Resv GEM object; representing the GPUVM's common &dma_resv. + */ + struct drm_gem_object *r_obj; + + /** + * @extobj: structure holding the extobj list + */ + struct { + /** + * @extobj.list: &list_head storing &drm_gpuvm_bos serving as + * external object + */ + struct list_head list; + + /** + * @extobj.local_list: pointer to the local list temporarily + * storing entries from the external object list + */ + struct list_head *local_list; + + /** + * @extobj.lock: spinlock to protect the extobj list + */ + spinlock_t lock; + } extobj; + + /** + * @evict: structure holding the evict list and evict list lock + */ + struct { + /** + * @evict.list: &list_head storing &drm_gpuvm_bos currently + * being evicted + */ + struct list_head list; + + /** + * @evict.local_list: pointer to the local list temporarily + * storing entries from the evicted object list + */ + struct list_head *local_list; + + /** + * @evict.lock: spinlock to protect the evict list + */ + spinlock_t lock; + } evict; + + /** + * @bo_defer: structure holding vm_bos that need to be destroyed + */ + struct llist_head bo_defer; +}; + +void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, + enum drm_gpuvm_flags flags, + struct drm_device *drm, + struct drm_gem_object *r_obj, + u64 start_offset, u64 range, + u64 reserve_offset, u64 reserve_range, + const struct drm_gpuvm_ops *ops); + +/** + * drm_gpuvm_get() - acquire a struct drm_gpuvm reference + * @gpuvm: the &drm_gpuvm to acquire the reference of + * + * This function acquires an additional reference to @gpuvm. It is illegal to + * call this without already holding a reference. No locks required. + * + * Returns: the &struct drm_gpuvm pointer + */ +static inline struct drm_gpuvm * +drm_gpuvm_get(struct drm_gpuvm *gpuvm) +{ + kref_get(&gpuvm->kref); + + return gpuvm; +} + +void drm_gpuvm_put(struct drm_gpuvm *gpuvm); + +bool drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range); +bool drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range); + +struct drm_gem_object * +drm_gpuvm_resv_object_alloc(struct drm_device *drm); + +/** + * drm_gpuvm_resv_protected() - indicates whether &DRM_GPUVM_RESV_PROTECTED is + * set + * @gpuvm: the &drm_gpuvm + * + * Returns: true if &DRM_GPUVM_RESV_PROTECTED is set, false otherwise. + */ +static inline bool +drm_gpuvm_resv_protected(struct drm_gpuvm *gpuvm) +{ + return gpuvm->flags & DRM_GPUVM_RESV_PROTECTED; +} + +/** + * drm_gpuvm_immediate_mode() - indicates whether &DRM_GPUVM_IMMEDIATE_MODE is + * set + * @gpuvm: the &drm_gpuvm + * + * Returns: true if &DRM_GPUVM_IMMEDIATE_MODE is set, false otherwise. + */ +static inline bool +drm_gpuvm_immediate_mode(struct drm_gpuvm *gpuvm) +{ + return gpuvm->flags & DRM_GPUVM_IMMEDIATE_MODE; +} + +/** + * drm_gpuvm_resv() - returns the &drm_gpuvm's &dma_resv + * @gpuvm__: the &drm_gpuvm + * + * Returns: a pointer to the &drm_gpuvm's shared &dma_resv + */ +#define drm_gpuvm_resv(gpuvm__) ((gpuvm__)->r_obj->resv) + +/** + * drm_gpuvm_resv_obj() - returns the &drm_gem_object holding the &drm_gpuvm's + * &dma_resv + * @gpuvm__: the &drm_gpuvm + * + * Returns: a pointer to the &drm_gem_object holding the &drm_gpuvm's shared + * &dma_resv + */ +#define drm_gpuvm_resv_obj(gpuvm__) ((gpuvm__)->r_obj) + +#define drm_gpuvm_resv_held(gpuvm__) \ + dma_resv_held(drm_gpuvm_resv(gpuvm__)) + +#define drm_gpuvm_resv_assert_held(gpuvm__) \ + dma_resv_assert_held(drm_gpuvm_resv(gpuvm__)) + +#define drm_gpuvm_resv_held(gpuvm__) \ + dma_resv_held(drm_gpuvm_resv(gpuvm__)) + +#define drm_gpuvm_resv_assert_held(gpuvm__) \ + dma_resv_assert_held(drm_gpuvm_resv(gpuvm__)) + +/** + * drm_gpuvm_is_extobj() - indicates whether the given &drm_gem_object is an + * external object + * @gpuvm: the &drm_gpuvm to check + * @obj: the &drm_gem_object to check + * + * Returns: true if the &drm_gem_object &dma_resv differs from the + * &drm_gpuvms &dma_resv, false otherwise + */ +static inline bool +drm_gpuvm_is_extobj(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + return obj && obj->resv != drm_gpuvm_resv(gpuvm); +} + +static inline struct drm_gpuva * +__drm_gpuva_next(struct drm_gpuva *va) +{ + if (va && !list_is_last(&va->rb.entry, &va->vm->rb.list)) + return list_next_entry(va, rb.entry); + + return NULL; +} + +/** + * drm_gpuvm_for_each_va_range() - iterate over a range of &drm_gpuvas + * @va__: &drm_gpuva structure to assign to in each iteration step + * @gpuvm__: &drm_gpuvm to walk over + * @start__: starting offset, the first gpuva will overlap this + * @end__: ending offset, the last gpuva will start before this (but may + * overlap) + * + * This iterator walks over all &drm_gpuvas in the &drm_gpuvm that lie + * between @start__ and @end__. It is implemented similarly to list_for_each(), + * but is using the &drm_gpuvm's internal interval tree to accelerate + * the search for the starting &drm_gpuva, and hence isn't safe against removal + * of elements. It assumes that @end__ is within (or is the upper limit of) the + * &drm_gpuvm. This iterator does not skip over the &drm_gpuvm's + * @kernel_alloc_node. + */ +#define drm_gpuvm_for_each_va_range(va__, gpuvm__, start__, end__) \ + for (va__ = drm_gpuva_find_first((gpuvm__), (start__), (end__) - (start__)); \ + va__ && (va__->va.addr < (end__)); \ + va__ = __drm_gpuva_next(va__)) + +/** + * drm_gpuvm_for_each_va_range_safe() - safely iterate over a range of + * &drm_gpuvas + * @va__: &drm_gpuva to assign to in each iteration step + * @next__: another &drm_gpuva to use as temporary storage + * @gpuvm__: &drm_gpuvm to walk over + * @start__: starting offset, the first gpuva will overlap this + * @end__: ending offset, the last gpuva will start before this (but may + * overlap) + * + * This iterator walks over all &drm_gpuvas in the &drm_gpuvm that lie + * between @start__ and @end__. It is implemented similarly to + * list_for_each_safe(), but is using the &drm_gpuvm's internal interval + * tree to accelerate the search for the starting &drm_gpuva, and hence is safe + * against removal of elements. It assumes that @end__ is within (or is the + * upper limit of) the &drm_gpuvm. This iterator does not skip over the + * &drm_gpuvm's @kernel_alloc_node. + */ +#define drm_gpuvm_for_each_va_range_safe(va__, next__, gpuvm__, start__, end__) \ + for (va__ = drm_gpuva_find_first((gpuvm__), (start__), (end__) - (start__)), \ + next__ = __drm_gpuva_next(va__); \ + va__ && (va__->va.addr < (end__)); \ + va__ = next__, next__ = __drm_gpuva_next(va__)) + +/** + * drm_gpuvm_for_each_va() - iterate over all &drm_gpuvas + * @va__: &drm_gpuva to assign to in each iteration step + * @gpuvm__: &drm_gpuvm to walk over + * + * This iterator walks over all &drm_gpuva structures associated with the given + * &drm_gpuvm. + */ +#define drm_gpuvm_for_each_va(va__, gpuvm__) \ + list_for_each_entry(va__, &(gpuvm__)->rb.list, rb.entry) + +/** + * drm_gpuvm_for_each_va_safe() - safely iterate over all &drm_gpuvas + * @va__: &drm_gpuva to assign to in each iteration step + * @next__: another &drm_gpuva to use as temporary storage + * @gpuvm__: &drm_gpuvm to walk over + * + * This iterator walks over all &drm_gpuva structures associated with the given + * &drm_gpuvm. It is implemented with list_for_each_entry_safe(), and + * hence safe against the removal of elements. + */ +#define drm_gpuvm_for_each_va_safe(va__, next__, gpuvm__) \ + list_for_each_entry_safe(va__, next__, &(gpuvm__)->rb.list, rb.entry) + +/** + * struct drm_gpuvm_exec - &drm_gpuvm abstraction of &drm_exec + * + * This structure should be created on the stack as &drm_exec should be. + * + * Optionally, @extra can be set in order to lock additional &drm_gem_objects. + */ +struct drm_gpuvm_exec { + /** + * @exec: the &drm_exec structure + */ + struct drm_exec exec; + + /** + * @flags: the flags for the struct drm_exec + */ + u32 flags; + + /** + * @vm: the &drm_gpuvm to lock its DMA reservations + */ + struct drm_gpuvm *vm; + + /** + * @num_fences: the number of fences to reserve for the &dma_resv of the + * locked &drm_gem_objects + */ + unsigned int num_fences; + + /** + * @extra: Callback and corresponding private data for the driver to + * lock arbitrary additional &drm_gem_objects. + */ + struct { + /** + * @extra.fn: The driver callback to lock additional + * &drm_gem_objects. + */ + int (*fn)(struct drm_gpuvm_exec *vm_exec); + + /** + * @extra.priv: driver private data for the @fn callback + */ + void *priv; + } extra; +}; + +int drm_gpuvm_prepare_vm(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences); + +int drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences); + +int drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + u64 addr, u64 range, + unsigned int num_fences); + +int drm_gpuvm_exec_lock(struct drm_gpuvm_exec *vm_exec); + +int drm_gpuvm_exec_lock_array(struct drm_gpuvm_exec *vm_exec, + struct drm_gem_object **objs, + unsigned int num_objs); + +int drm_gpuvm_exec_lock_range(struct drm_gpuvm_exec *vm_exec, + u64 addr, u64 range); + +/** + * drm_gpuvm_exec_unlock() - lock all dma-resv of all assoiciated BOs + * @vm_exec: the &drm_gpuvm_exec wrapper + * + * Releases all dma-resv locks of all &drm_gem_objects previously acquired + * through drm_gpuvm_exec_lock() or its variants. + * + * Returns: 0 on success, negative error code on failure. + */ +static inline void +drm_gpuvm_exec_unlock(struct drm_gpuvm_exec *vm_exec) +{ + drm_exec_fini(&vm_exec->exec); +} + +int drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec); +void drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + struct dma_fence *fence, + enum dma_resv_usage private_usage, + enum dma_resv_usage extobj_usage); + +/** + * drm_gpuvm_exec_resv_add_fence() - add fence to private and all extobj + * @vm_exec: the &drm_gpuvm_exec wrapper + * @fence: fence to add + * @private_usage: private dma-resv usage + * @extobj_usage: extobj dma-resv usage + * + * See drm_gpuvm_resv_add_fence(). + */ +static inline void +drm_gpuvm_exec_resv_add_fence(struct drm_gpuvm_exec *vm_exec, + struct dma_fence *fence, + enum dma_resv_usage private_usage, + enum dma_resv_usage extobj_usage) +{ + drm_gpuvm_resv_add_fence(vm_exec->vm, &vm_exec->exec, fence, + private_usage, extobj_usage); +} + +/** + * drm_gpuvm_exec_validate() - validate all BOs marked as evicted + * @vm_exec: the &drm_gpuvm_exec wrapper + * + * See drm_gpuvm_validate(). + * + * Returns: 0 on success, negative error code on failure. + */ +static inline int +drm_gpuvm_exec_validate(struct drm_gpuvm_exec *vm_exec) +{ + return drm_gpuvm_validate(vm_exec->vm, &vm_exec->exec); +} + +/** + * struct drm_gpuvm_bo - structure representing a &drm_gpuvm and + * &drm_gem_object combination + * + * This structure is an abstraction representing a &drm_gpuvm and + * &drm_gem_object combination. It serves as an indirection to accelerate + * iterating all &drm_gpuvas within a &drm_gpuvm backed by the same + * &drm_gem_object. + * + * Furthermore it is used cache evicted GEM objects for a certain GPU-VM to + * accelerate validation. + * + * Typically, drivers want to create an instance of a struct drm_gpuvm_bo once + * a GEM object is mapped first in a GPU-VM and release the instance once the + * last mapping of the GEM object in this GPU-VM is unmapped. + */ +struct drm_gpuvm_bo { + /** + * @vm: The &drm_gpuvm the @obj is mapped in. This is a reference + * counted pointer. + */ + struct drm_gpuvm *vm; + + /** + * @obj: The &drm_gem_object being mapped in @vm. This is a reference + * counted pointer. + */ + struct drm_gem_object *obj; + + /** + * @evicted: Indicates whether the &drm_gem_object is evicted; field + * protected by the &drm_gem_object's dma-resv lock. + */ + bool evicted; + + /** + * @kref: The reference count for this &drm_gpuvm_bo. + */ + struct kref kref; + + /** + * @list: Structure containing all &list_heads. + */ + struct { + /** + * @list.gpuva: The list of linked &drm_gpuvas. + * + * It is safe to access entries from this list as long as the + * GEM's gpuva lock is held. See also struct drm_gem_object. + */ + struct list_head gpuva; + + /** + * @list.entry: Structure containing all &list_heads serving as + * entry. + */ + struct { + /** + * @list.entry.gem: List entry to attach to the + * &drm_gem_objects gpuva list. + */ + struct list_head gem; + + /** + * @list.entry.evict: List entry to attach to the + * &drm_gpuvms extobj list. + */ + struct list_head extobj; + + /** + * @list.entry.evict: List entry to attach to the + * &drm_gpuvms evict list. + */ + struct list_head evict; + + /** + * @list.entry.bo_defer: List entry to attach to + * the &drm_gpuvms bo_defer list. + */ + struct llist_node bo_defer; + } entry; + } list; +}; + +struct drm_gpuvm_bo * +drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); + +struct drm_gpuvm_bo * +drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); +struct drm_gpuvm_bo * +drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm_bo *vm_bo); + +/** + * drm_gpuvm_bo_get() - acquire a struct drm_gpuvm_bo reference + * @vm_bo: the &drm_gpuvm_bo to acquire the reference of + * + * This function acquires an additional reference to @vm_bo. It is illegal to + * call this without already holding a reference. No locks required. + * + * Returns: the &struct vm_bo pointer + */ +static inline struct drm_gpuvm_bo * +drm_gpuvm_bo_get(struct drm_gpuvm_bo *vm_bo) +{ + kref_get(&vm_bo->kref); + return vm_bo; +} + +bool drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo); + +bool drm_gpuvm_bo_put_deferred(struct drm_gpuvm_bo *vm_bo); +void drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm); + +struct drm_gpuvm_bo * +drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); + +void drm_gpuvm_bo_evict(struct drm_gpuvm_bo *vm_bo, bool evict); + +/** + * drm_gpuvm_bo_gem_evict() - add/remove all &drm_gpuvm_bo's in the list + * to/from the &drm_gpuvms evicted list + * @obj: the &drm_gem_object + * @evict: indicates whether @obj is evicted + * + * See drm_gpuvm_bo_evict(). + */ +static inline void +drm_gpuvm_bo_gem_evict(struct drm_gem_object *obj, bool evict) +{ + struct drm_gpuvm_bo *vm_bo; + + drm_gem_for_each_gpuvm_bo(vm_bo, obj) { + drm_gem_gpuva_assert_lock_held(vm_bo->vm, obj); + drm_gpuvm_bo_evict(vm_bo, evict); + } +} + +void drm_gpuvm_bo_extobj_add(struct drm_gpuvm_bo *vm_bo); + +/** + * drm_gpuvm_bo_for_each_va() - iterator to walk over a list of &drm_gpuva + * @va__: &drm_gpuva structure to assign to in each iteration step + * @vm_bo__: the &drm_gpuvm_bo the &drm_gpuva to walk are associated with + * + * This iterator walks over all &drm_gpuva structures associated with the + * &drm_gpuvm_bo. + * + * The caller must hold the GEM's gpuva lock. + */ +#define drm_gpuvm_bo_for_each_va(va__, vm_bo__) \ + list_for_each_entry(va__, &(vm_bo)->list.gpuva, gem.entry) + +/** + * drm_gpuvm_bo_for_each_va_safe() - iterator to safely walk over a list of + * &drm_gpuva + * @va__: &drm_gpuva structure to assign to in each iteration step + * @next__: &next &drm_gpuva to store the next step + * @vm_bo__: the &drm_gpuvm_bo the &drm_gpuva to walk are associated with + * + * This iterator walks over all &drm_gpuva structures associated with the + * &drm_gpuvm_bo. It is implemented with list_for_each_entry_safe(), hence + * it is save against removal of elements. + * + * The caller must hold the GEM's gpuva lock. + */ +#define drm_gpuvm_bo_for_each_va_safe(va__, next__, vm_bo__) \ + list_for_each_entry_safe(va__, next__, &(vm_bo)->list.gpuva, gem.entry) + +/** + * enum drm_gpuva_op_type - GPU VA operation type + * + * Operations to alter the GPU VA mappings tracked by the &drm_gpuvm. + */ +enum drm_gpuva_op_type { + /** + * @DRM_GPUVA_OP_MAP: the map op type + */ + DRM_GPUVA_OP_MAP, + + /** + * @DRM_GPUVA_OP_REMAP: the remap op type + */ + DRM_GPUVA_OP_REMAP, + + /** + * @DRM_GPUVA_OP_UNMAP: the unmap op type + */ + DRM_GPUVA_OP_UNMAP, + + /** + * @DRM_GPUVA_OP_PREFETCH: the prefetch op type + */ + DRM_GPUVA_OP_PREFETCH, + + /** + * @DRM_GPUVA_OP_DRIVER: the driver defined op type + */ + DRM_GPUVA_OP_DRIVER, +}; + +/** + * struct drm_gpuva_op_map - GPU VA map operation + * + * This structure represents a single map operation generated by the + * DRM GPU VA manager. + */ +struct drm_gpuva_op_map { + /** + * @va: structure containing address and range of a map + * operation + */ + struct { + /** + * @va.addr: the base address of the new mapping + */ + u64 addr; + + /** + * @va.range: the range of the new mapping + */ + u64 range; + } va; + + /** + * @gem: structure containing the &drm_gem_object and its offset + */ + struct { + /** + * @gem.offset: the offset within the &drm_gem_object + */ + u64 offset; + + /** + * @gem.obj: the &drm_gem_object to map + */ + struct drm_gem_object *obj; + } gem; +}; + +/** + * struct drm_gpuva_op_unmap - GPU VA unmap operation + * + * This structure represents a single unmap operation generated by the + * DRM GPU VA manager. + */ +struct drm_gpuva_op_unmap { + /** + * @va: the &drm_gpuva to unmap + */ + struct drm_gpuva *va; + + /** + * @keep: + * + * Indicates whether this &drm_gpuva is physically contiguous with the + * original mapping request. + * + * Optionally, if &keep is set, drivers may keep the actual page table + * mappings for this &drm_gpuva, adding the missing page table entries + * only and update the &drm_gpuvm accordingly. + */ + bool keep; +}; + +/** + * struct drm_gpuva_op_remap - GPU VA remap operation + * + * This represents a single remap operation generated by the DRM GPU VA manager. + * + * A remap operation is generated when an existing GPU VA mmapping is split up + * by inserting a new GPU VA mapping or by partially unmapping existent + * mapping(s), hence it consists of a maximum of two map and one unmap + * operation. + * + * The @unmap operation takes care of removing the original existing mapping. + * @prev is used to remap the preceding part, @next the subsequent part. + * + * If either a new mapping's start address is aligned with the start address + * of the old mapping or the new mapping's end address is aligned with the + * end address of the old mapping, either @prev or @next is NULL. + * + * Note, the reason for a dedicated remap operation, rather than arbitrary + * unmap and map operations, is to give drivers the chance of extracting driver + * specific data for creating the new mappings from the unmap operations's + * &drm_gpuva structure which typically is embedded in larger driver specific + * structures. + */ +struct drm_gpuva_op_remap { + /** + * @prev: the preceding part of a split mapping + */ + struct drm_gpuva_op_map *prev; + + /** + * @next: the subsequent part of a split mapping + */ + struct drm_gpuva_op_map *next; + + /** + * @unmap: the unmap operation for the original existing mapping + */ + struct drm_gpuva_op_unmap *unmap; +}; + +/** + * struct drm_gpuva_op_prefetch - GPU VA prefetch operation + * + * This structure represents a single prefetch operation generated by the + * DRM GPU VA manager. + */ +struct drm_gpuva_op_prefetch { + /** + * @va: the &drm_gpuva to prefetch + */ + struct drm_gpuva *va; +}; + +/** + * struct drm_gpuva_op - GPU VA operation + * + * This structure represents a single generic operation. + * + * The particular type of the operation is defined by @op. + */ +struct drm_gpuva_op { + /** + * @entry: + * + * The &list_head used to distribute instances of this struct within + * &drm_gpuva_ops. + */ + struct list_head entry; + + /** + * @op: the type of the operation + */ + enum drm_gpuva_op_type op; + + union { + /** + * @map: the map operation + */ + struct drm_gpuva_op_map map; + + /** + * @remap: the remap operation + */ + struct drm_gpuva_op_remap remap; + + /** + * @unmap: the unmap operation + */ + struct drm_gpuva_op_unmap unmap; + + /** + * @prefetch: the prefetch operation + */ + struct drm_gpuva_op_prefetch prefetch; + }; +}; + +/** + * struct drm_gpuva_ops - wraps a list of &drm_gpuva_op + */ +struct drm_gpuva_ops { + /** + * @list: the &list_head + */ + struct list_head list; +}; + +/** + * drm_gpuva_for_each_op() - iterator to walk over &drm_gpuva_ops + * @op: &drm_gpuva_op to assign in each iteration step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations. + */ +#define drm_gpuva_for_each_op(op, ops) list_for_each_entry(op, &(ops)->list, entry) + +/** + * drm_gpuva_for_each_op_safe() - iterator to safely walk over &drm_gpuva_ops + * @op: &drm_gpuva_op to assign in each iteration step + * @next: &next &drm_gpuva_op to store the next step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations. It is + * implemented with list_for_each_safe(), so save against removal of elements. + */ +#define drm_gpuva_for_each_op_safe(op, next, ops) \ + list_for_each_entry_safe(op, next, &(ops)->list, entry) + +/** + * drm_gpuva_for_each_op_from_reverse() - iterate backwards from the given point + * @op: &drm_gpuva_op to assign in each iteration step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations beginning + * from the given operation in reverse order. + */ +#define drm_gpuva_for_each_op_from_reverse(op, ops) \ + list_for_each_entry_from_reverse(op, &(ops)->list, entry) + +/** + * drm_gpuva_for_each_op_reverse - iterator to walk over &drm_gpuva_ops in reverse + * @op: &drm_gpuva_op to assign in each iteration step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations in reverse + */ +#define drm_gpuva_for_each_op_reverse(op, ops) \ + list_for_each_entry_reverse(op, &(ops)->list, entry) + +/** + * drm_gpuva_first_op() - returns the first &drm_gpuva_op from &drm_gpuva_ops + * @ops: the &drm_gpuva_ops to get the fist &drm_gpuva_op from + */ +#define drm_gpuva_first_op(ops) \ + list_first_entry(&(ops)->list, struct drm_gpuva_op, entry) + +/** + * drm_gpuva_last_op() - returns the last &drm_gpuva_op from &drm_gpuva_ops + * @ops: the &drm_gpuva_ops to get the last &drm_gpuva_op from + */ +#define drm_gpuva_last_op(ops) \ + list_last_entry(&(ops)->list, struct drm_gpuva_op, entry) + +/** + * drm_gpuva_prev_op() - previous &drm_gpuva_op in the list + * @op: the current &drm_gpuva_op + */ +#define drm_gpuva_prev_op(op) list_prev_entry(op, entry) + +/** + * drm_gpuva_next_op() - next &drm_gpuva_op in the list + * @op: the current &drm_gpuva_op + */ +#define drm_gpuva_next_op(op) list_next_entry(op, entry) + +/** + * struct drm_gpuvm_map_req - arguments passed to drm_gpuvm_sm_map[_ops_create]() + */ +struct drm_gpuvm_map_req { + /** + * @map: struct drm_gpuva_op_map + */ + struct drm_gpuva_op_map map; +}; + +struct drm_gpuva_ops * +drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm, + const struct drm_gpuvm_map_req *req); +struct drm_gpuva_ops * +drm_gpuvm_madvise_ops_create(struct drm_gpuvm *gpuvm, + const struct drm_gpuvm_map_req *req); + +struct drm_gpuva_ops * +drm_gpuvm_sm_unmap_ops_create(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); + +struct drm_gpuva_ops * +drm_gpuvm_prefetch_ops_create(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); + +struct drm_gpuva_ops * +drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm_bo *vm_bo); + +void drm_gpuva_ops_free(struct drm_gpuvm *gpuvm, + struct drm_gpuva_ops *ops); + +static inline void drm_gpuva_init_from_op(struct drm_gpuva *va, + struct drm_gpuva_op_map *op) +{ + va->va.addr = op->va.addr; + va->va.range = op->va.range; + va->gem.obj = op->gem.obj; + va->gem.offset = op->gem.offset; +} + +/** + * struct drm_gpuvm_ops - callbacks for split/merge steps + * + * This structure defines the callbacks used by &drm_gpuvm_sm_map and + * &drm_gpuvm_sm_unmap to provide the split/merge steps for map and unmap + * operations to drivers. + */ +struct drm_gpuvm_ops { + /** + * @vm_free: called when the last reference of a struct drm_gpuvm is + * dropped + * + * This callback is mandatory. + */ + void (*vm_free)(struct drm_gpuvm *gpuvm); + + /** + * @op_alloc: called when the &drm_gpuvm allocates + * a struct drm_gpuva_op + * + * Some drivers may want to embed struct drm_gpuva_op into driver + * specific structures. By implementing this callback drivers can + * allocate memory accordingly. + * + * This callback is optional. + */ + struct drm_gpuva_op *(*op_alloc)(void); + + /** + * @op_free: called when the &drm_gpuvm frees a + * struct drm_gpuva_op + * + * Some drivers may want to embed struct drm_gpuva_op into driver + * specific structures. By implementing this callback drivers can + * free the previously allocated memory accordingly. + * + * This callback is optional. + */ + void (*op_free)(struct drm_gpuva_op *op); + + /** + * @vm_bo_alloc: called when the &drm_gpuvm allocates + * a struct drm_gpuvm_bo + * + * Some drivers may want to embed struct drm_gpuvm_bo into driver + * specific structures. By implementing this callback drivers can + * allocate memory accordingly. + * + * This callback is optional. + */ + struct drm_gpuvm_bo *(*vm_bo_alloc)(void); + + /** + * @vm_bo_free: called when the &drm_gpuvm frees a + * struct drm_gpuvm_bo + * + * Some drivers may want to embed struct drm_gpuvm_bo into driver + * specific structures. By implementing this callback drivers can + * free the previously allocated memory accordingly. + * + * This callback is optional. + */ + void (*vm_bo_free)(struct drm_gpuvm_bo *vm_bo); + + /** + * @vm_bo_validate: called from drm_gpuvm_validate() + * + * Drivers receive this callback for every evicted &drm_gem_object being + * mapped in the corresponding &drm_gpuvm. + * + * Typically, drivers would call their driver specific variant of + * ttm_bo_validate() from within this callback. + */ + int (*vm_bo_validate)(struct drm_gpuvm_bo *vm_bo, + struct drm_exec *exec); + + /** + * @sm_step_map: called from &drm_gpuvm_sm_map to finally insert the + * mapping once all previous steps were completed + * + * The &priv pointer matches the one the driver passed to + * &drm_gpuvm_sm_map or &drm_gpuvm_sm_unmap, respectively. + * + * Can be NULL if &drm_gpuvm_sm_map is used. + */ + int (*sm_step_map)(struct drm_gpuva_op *op, void *priv); + + /** + * @sm_step_remap: called from &drm_gpuvm_sm_map and + * &drm_gpuvm_sm_unmap to split up an existent mapping + * + * This callback is called when existent mapping needs to be split up. + * This is the case when either a newly requested mapping overlaps or + * is enclosed by an existent mapping or a partial unmap of an existent + * mapping is requested. + * + * The &priv pointer matches the one the driver passed to + * &drm_gpuvm_sm_map or &drm_gpuvm_sm_unmap, respectively. + * + * Can be NULL if neither &drm_gpuvm_sm_map nor &drm_gpuvm_sm_unmap is + * used. + */ + int (*sm_step_remap)(struct drm_gpuva_op *op, void *priv); + + /** + * @sm_step_unmap: called from &drm_gpuvm_sm_map and + * &drm_gpuvm_sm_unmap to unmap an existing mapping + * + * This callback is called when existing mapping needs to be unmapped. + * This is the case when either a newly requested mapping encloses an + * existing mapping or an unmap of an existing mapping is requested. + * + * The &priv pointer matches the one the driver passed to + * &drm_gpuvm_sm_map or &drm_gpuvm_sm_unmap, respectively. + * + * Can be NULL if neither &drm_gpuvm_sm_map nor &drm_gpuvm_sm_unmap is + * used. + */ + int (*sm_step_unmap)(struct drm_gpuva_op *op, void *priv); +}; + +int drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv, + const struct drm_gpuvm_map_req *req); + +int drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv, + u64 addr, u64 range); + +int drm_gpuvm_sm_map_exec_lock(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, unsigned int num_fences, + struct drm_gpuvm_map_req *req); + +int drm_gpuvm_sm_unmap_exec_lock(struct drm_gpuvm *gpuvm, struct drm_exec *exec, + u64 req_addr, u64 req_range); + +void drm_gpuva_map(struct drm_gpuvm *gpuvm, + struct drm_gpuva *va, + struct drm_gpuva_op_map *op); + +void drm_gpuva_remap(struct drm_gpuva *prev, + struct drm_gpuva *next, + struct drm_gpuva_op_remap *op); + +void drm_gpuva_unmap(struct drm_gpuva_op_unmap *op); + +/** + * drm_gpuva_op_remap_to_unmap_range() - Helper to get the start and range of + * the unmap stage of a remap op. + * @op: Remap op. + * @start_addr: Output pointer for the start of the required unmap. + * @range: Output pointer for the length of the required unmap. + * + * The given start address and range will be set such that they represent the + * range of the address space that was previously covered by the mapping being + * re-mapped, but is now empty. + */ +static inline void +drm_gpuva_op_remap_to_unmap_range(const struct drm_gpuva_op_remap *op, + u64 *start_addr, u64 *range) +{ + const u64 va_start = op->prev ? + op->prev->va.addr + op->prev->va.range : + op->unmap->va->va.addr; + const u64 va_end = op->next ? + op->next->va.addr : + op->unmap->va->va.addr + op->unmap->va->va.range; + + if (start_addr) + *start_addr = va_start; + if (range) + *range = va_end - va_start; +} + +#endif /* __DRM_GPUVM_H__ */ diff --git a/include/drm/drm_hashtab.h b/include/drm/drm_hashtab.h deleted file mode 100644 index bb95ff011baf..000000000000 --- a/include/drm/drm_hashtab.h +++ /dev/null @@ -1,79 +0,0 @@ -/************************************************************************** - * - * Copyright 2006 Tungsten Graphics, Inc., Bismack, ND. USA. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * - **************************************************************************/ -/* - * Simple open hash tab implementation. - * - * Authors: - * Thomas Hellström <thomas-at-tungstengraphics-dot-com> - */ - -#ifndef DRM_HASHTAB_H -#define DRM_HASHTAB_H - -#include <linux/list.h> - -#define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member) - -struct drm_hash_item { - struct hlist_node head; - unsigned long key; -}; - -struct drm_open_hash { - struct hlist_head *table; - u8 order; -}; - -int drm_ht_create(struct drm_open_hash *ht, unsigned int order); -int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item); -int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item, - unsigned long seed, int bits, int shift, - unsigned long add); -int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key, struct drm_hash_item **item); - -void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key); -int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key); -int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item); -void drm_ht_remove(struct drm_open_hash *ht); - -/* - * RCU-safe interface - * - * The user of this API needs to make sure that two or more instances of the - * hash table manipulation functions are never run simultaneously. - * The lookup function drm_ht_find_item_rcu may, however, run simultaneously - * with any of the manipulation functions as long as it's called from within - * an RCU read-locked section. - */ -#define drm_ht_insert_item_rcu drm_ht_insert_item -#define drm_ht_just_insert_please_rcu drm_ht_just_insert_please -#define drm_ht_remove_key_rcu drm_ht_remove_key -#define drm_ht_remove_item_rcu drm_ht_remove_item -#define drm_ht_find_item_rcu drm_ht_find_item - -#endif diff --git a/include/drm/drm_ioctl.h b/include/drm/drm_ioctl.h index fafb6f592c4b..171760b6c4a1 100644 --- a/include/drm/drm_ioctl.h +++ b/include/drm/drm_ioctl.h @@ -68,6 +68,7 @@ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd, unsigned long arg); #define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IOCTL_TYPE(n) _IOC_TYPE(n) #define DRM_MAJOR 226 /** @@ -109,14 +110,6 @@ enum drm_ioctl_flags { */ DRM_ROOT_ONLY = BIT(2), /** - * @DRM_UNLOCKED: - * - * Whether &drm_ioctl_desc.func should be called with the DRM BKL held - * or not. Enforced as the default for all modern drivers, hence there - * should never be a need to set this flag. - */ - DRM_UNLOCKED = BIT(4), - /** * @DRM_RENDER_ALLOW: * * This is used for all ioctl needed for rendering only, for drivers @@ -163,7 +156,6 @@ struct drm_ioctl_desc { .name = #ioctl \ } -int drm_ioctl_permit(u32 flags, struct drm_file *file_priv); long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long drm_ioctl_kernel(struct file *, drm_ioctl_t, void *, u32); #ifdef CONFIG_COMPAT diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h deleted file mode 100644 index d77f6e65b1c6..000000000000 --- a/include/drm/drm_irq.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2016 Intel Corp. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _DRM_IRQ_H_ -#define _DRM_IRQ_H_ - -struct drm_device; - -int drm_irq_install(struct drm_device *dev, int irq); -int drm_irq_uninstall(struct drm_device *dev); - -#endif diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h new file mode 100644 index 000000000000..4948379237e9 --- /dev/null +++ b/include/drm/drm_kunit_helpers.h @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef DRM_KUNIT_HELPERS_H_ +#define DRM_KUNIT_HELPERS_H_ + +#include <drm/drm_drv.h> + +#include <linux/device.h> + +#include <kunit/test.h> + +struct drm_connector; +struct drm_crtc_funcs; +struct drm_crtc_helper_funcs; +struct drm_device; +struct drm_plane_funcs; +struct drm_plane_helper_funcs; +struct kunit; + +struct device *drm_kunit_helper_alloc_device(struct kunit *test); +void drm_kunit_helper_free_device(struct kunit *test, struct device *dev); + +struct drm_device * +__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + const struct drm_driver *driver); + +/** + * drm_kunit_helper_alloc_drm_device_with_driver - Allocates a mock DRM device for KUnit tests + * @_test: The test context object + * @_dev: The parent device object + * @_type: the type of the struct which contains struct &drm_device + * @_member: the name of the &drm_device within @_type. + * @_drv: Mocked DRM device driver features + * + * This function creates a struct &drm_device from @_dev and @_drv. + * + * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @_test context and will get cleaned at the + * end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ +#define drm_kunit_helper_alloc_drm_device_with_driver(_test, _dev, _type, _member, _drv) \ + ((_type *)__drm_kunit_helper_alloc_drm_device_with_driver(_test, _dev, \ + sizeof(_type), \ + offsetof(_type, _member), \ + _drv)) + +static inline struct drm_device * +__drm_kunit_helper_alloc_drm_device(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + u32 features) +{ + struct drm_driver *driver; + + driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, driver); + + driver->driver_features = features; + + return __drm_kunit_helper_alloc_drm_device_with_driver(test, dev, + size, offset, + driver); +} + +/** + * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests + * @_test: The test context object + * @_dev: The parent device object + * @_type: the type of the struct which contains struct &drm_device + * @_member: the name of the &drm_device within @_type. + * @_feat: Mocked DRM device driver features + * + * This function creates a struct &drm_driver and will create a struct + * &drm_device from @_dev and that driver. + * + * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @_test context and will get cleaned at the + * end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ +#define drm_kunit_helper_alloc_drm_device(_test, _dev, _type, _member, _feat) \ + ((_type *)__drm_kunit_helper_alloc_drm_device(_test, _dev, \ + sizeof(_type), \ + offsetof(_type, _member), \ + _feat)) + +struct drm_atomic_state * +drm_kunit_helper_atomic_state_alloc(struct kunit *test, + struct drm_device *drm, + struct drm_modeset_acquire_ctx *ctx); + +struct drm_plane * +drm_kunit_helper_create_primary_plane(struct kunit *test, + struct drm_device *drm, + const struct drm_plane_funcs *funcs, + const struct drm_plane_helper_funcs *helper_funcs, + const uint32_t *formats, + unsigned int num_formats, + const uint64_t *modifiers); + +struct drm_crtc * +drm_kunit_helper_create_crtc(struct kunit *test, + struct drm_device *drm, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const struct drm_crtc_helper_funcs *helper_funcs); + +int drm_kunit_helper_enable_crtc_connector(struct kunit *test, + struct drm_device *drm, + struct drm_crtc *crtc, + struct drm_connector *connector, + const struct drm_display_mode *mode, + struct drm_modeset_acquire_ctx *ctx); + +int drm_kunit_add_mode_destroy_action(struct kunit *test, + struct drm_display_mode *mode); + +struct drm_display_mode * +drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev, + u8 video_code); + +#endif // DRM_KUNIT_HELPERS_H_ diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h index fbc0ab54855b..53545b4ca9ef 100644 --- a/include/drm/drm_lease.h +++ b/include/drm/drm_lease.h @@ -1,20 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright © 2017 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * 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_LEASE_H_ #define _DRM_LEASE_H_ +#include <linux/types.h> + struct drm_file; struct drm_device; struct drm_master; diff --git a/include/drm/drm_legacy.h b/include/drm/drm_legacy.h deleted file mode 100644 index 8fad66f88e4f..000000000000 --- a/include/drm/drm_legacy.h +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef __DRM_DRM_LEGACY_H__ -#define __DRM_DRM_LEGACY_H__ - -#include <drm/drm_auth.h> - -/* - * Legacy driver interfaces for the Direct Rendering Manager - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * Copyright (c) 2009-2010, Code Aurora Forum. - * All rights reserved. - * Copyright © 2014 Intel Corporation - * Daniel Vetter <daniel.vetter@ffwll.ch> - * - * Author: Rickard E. (Rik) Faith <faith@valinux.com> - * Author: Gareth Hughes <gareth@valinux.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - - -/* - * Legacy Support for palateontologic DRM drivers - * - * If you add a new driver and it uses any of these functions or structures, - * you're doing it terribly wrong. - */ - -/** - * DMA buffer. - */ -struct drm_buf { - int idx; /**< Index into master buflist */ - int total; /**< Buffer size */ - int order; /**< log-base-2(total) */ - int used; /**< Amount of buffer in use (for DMA) */ - unsigned long offset; /**< Byte offset (used internally) */ - void *address; /**< Address of buffer */ - unsigned long bus_address; /**< Bus address of buffer */ - struct drm_buf *next; /**< Kernel-only: used for free list */ - __volatile__ int waiting; /**< On kernel DMA queue */ - __volatile__ int pending; /**< On hardware DMA queue */ - struct drm_file *file_priv; /**< Private of holding file descr */ - int context; /**< Kernel queue for this buffer */ - int while_locked; /**< Dispatch this buffer while locked */ - enum { - DRM_LIST_NONE = 0, - DRM_LIST_FREE = 1, - DRM_LIST_WAIT = 2, - DRM_LIST_PEND = 3, - DRM_LIST_PRIO = 4, - DRM_LIST_RECLAIM = 5 - } list; /**< Which list we're on */ - - int dev_priv_size; /**< Size of buffer private storage */ - void *dev_private; /**< Per-buffer private storage */ -}; - -typedef struct drm_dma_handle { - dma_addr_t busaddr; - void *vaddr; - size_t size; -} drm_dma_handle_t; - -/** - * Buffer entry. There is one of this for each buffer size order. - */ -struct drm_buf_entry { - int buf_size; /**< size */ - int buf_count; /**< number of buffers */ - struct drm_buf *buflist; /**< buffer list */ - int seg_count; - int page_order; - struct drm_dma_handle **seglist; - - int low_mark; /**< Low water mark */ - int high_mark; /**< High water mark */ -}; - -/** - * DMA data. - */ -struct drm_device_dma { - - struct drm_buf_entry bufs[DRM_MAX_ORDER + 1]; /**< buffers, grouped by their size order */ - int buf_count; /**< total number of buffers */ - struct drm_buf **buflist; /**< Vector of pointers into drm_device_dma::bufs */ - int seg_count; - int page_count; /**< number of pages */ - unsigned long *pagelist; /**< page list */ - unsigned long byte_count; - enum { - _DRM_DMA_USE_AGP = 0x01, - _DRM_DMA_USE_SG = 0x02, - _DRM_DMA_USE_FB = 0x04, - _DRM_DMA_USE_PCI_RO = 0x08 - } flags; - -}; - -/** - * Scatter-gather memory. - */ -struct drm_sg_mem { - unsigned long handle; - void *virtual; - int pages; - struct page **pagelist; - dma_addr_t *busaddr; -}; - -/** - * Kernel side of a mapping - */ -struct drm_local_map { - resource_size_t offset; /**< Requested physical address (0 for SAREA)*/ - unsigned long size; /**< Requested physical size (bytes) */ - enum drm_map_type type; /**< Type of memory to map */ - enum drm_map_flags flags; /**< Flags */ - void *handle; /**< User-space: "Handle" to pass to mmap() */ - /**< Kernel-space: kernel-virtual address */ - int mtrr; /**< MTRR slot used */ -}; - -typedef struct drm_local_map drm_local_map_t; - -/** - * Mappings list - */ -struct drm_map_list { - struct list_head head; /**< list head */ - struct drm_hash_item hash; - struct drm_local_map *map; /**< mapping */ - uint64_t user_token; - struct drm_master *master; -}; - -int drm_legacy_addmap(struct drm_device *d, resource_size_t offset, - unsigned int size, enum drm_map_type type, - enum drm_map_flags flags, struct drm_local_map **map_p); -void drm_legacy_rmmap(struct drm_device *d, struct drm_local_map *map); -int drm_legacy_rmmap_locked(struct drm_device *d, struct drm_local_map *map); -void drm_legacy_master_rmmaps(struct drm_device *dev, - struct drm_master *master); -struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev); -int drm_legacy_mmap(struct file *filp, struct vm_area_struct *vma); - -int drm_legacy_addbufs_agp(struct drm_device *d, struct drm_buf_desc *req); -int drm_legacy_addbufs_pci(struct drm_device *d, struct drm_buf_desc *req); - -/** - * Test that the hardware lock is held by the caller, returning otherwise. - * - * \param dev DRM device. - * \param filp file pointer of the caller. - */ -#define LOCK_TEST_WITH_RETURN( dev, _file_priv ) \ -do { \ - if (!_DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock) || \ - _file_priv->master->lock.file_priv != _file_priv) { \ - DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\ - __func__, _DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock),\ - _file_priv->master->lock.file_priv, _file_priv); \ - return -EINVAL; \ - } \ -} while (0) - -void drm_legacy_idlelock_take(struct drm_lock_data *lock); -void drm_legacy_idlelock_release(struct drm_lock_data *lock); - -/* drm_pci.c dma alloc wrappers */ -void __drm_legacy_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); - -/* drm_memory.c */ -void drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev); -void drm_legacy_ioremap_wc(struct drm_local_map *map, struct drm_device *dev); -void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev); - -static inline struct drm_local_map *drm_legacy_findmap(struct drm_device *dev, - unsigned int token) -{ - struct drm_map_list *_entry; - list_for_each_entry(_entry, &dev->maplist, head) - if (_entry->user_token == token) - return _entry->map; - return NULL; -} - -#endif /* __DRM_DRM_LEGACY_H__ */ diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h new file mode 100644 index 000000000000..72bfac002c06 --- /dev/null +++ b/include/drm/drm_managed.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef _DRM_MANAGED_H_ +#define _DRM_MANAGED_H_ + +#include <linux/gfp.h> +#include <linux/overflow.h> +#include <linux/types.h> + +struct drm_device; +struct mutex; + +typedef void (*drmres_release_t)(struct drm_device *dev, void *res); + +/** + * drmm_add_action - add a managed release action to a &drm_device + * @dev: DRM device + * @action: function which should be called when @dev is released + * @data: opaque pointer, passed to @action + * + * This function adds the @release action with optional parameter @data to the + * list of cleanup actions for @dev. The cleanup actions will be run in reverse + * order in the final drm_dev_put() call for @dev. + */ +#define drmm_add_action(dev, action, data) \ + __drmm_add_action(dev, action, data, #action) + +int __must_check __drmm_add_action(struct drm_device *dev, + drmres_release_t action, + void *data, const char *name); + +/** + * drmm_add_action_or_reset - add a managed release action to a &drm_device + * @dev: DRM device + * @action: function which should be called when @dev is released + * @data: opaque pointer, passed to @action + * + * Similar to drmm_add_action(), with the only difference that upon failure + * @action is directly called for any cleanup work necessary on failures. + */ +#define drmm_add_action_or_reset(dev, action, data) \ + __drmm_add_action_or_reset(dev, action, data, #action) + +int __must_check __drmm_add_action_or_reset(struct drm_device *dev, + drmres_release_t action, + void *data, const char *name); + +void drmm_release_action(struct drm_device *dev, + drmres_release_t action, + void *data); + +void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc; + +/** + * drmm_kzalloc - &drm_device managed kzalloc() + * @dev: DRM device + * @size: size of the memory allocation + * @gfp: GFP allocation flags + * + * This is a &drm_device managed version of kzalloc(). The allocated memory is + * automatically freed on the final drm_dev_put(). Memory can also be freed + * before the final drm_dev_put() by calling drmm_kfree(). + */ +static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{ + return drmm_kmalloc(dev, size, gfp | __GFP_ZERO); +} + +/** + * drmm_kmalloc_array - &drm_device managed kmalloc_array() + * @dev: DRM device + * @n: number of array elements to allocate + * @size: size of array member + * @flags: GFP allocation flags + * + * This is a &drm_device managed version of kmalloc_array(). The allocated + * memory is automatically freed on the final drm_dev_put() and works exactly + * like a memory allocation obtained by drmm_kmalloc(). + */ +static inline void *drmm_kmalloc_array(struct drm_device *dev, + size_t n, size_t size, gfp_t flags) +{ + size_t bytes; + + if (unlikely(check_mul_overflow(n, size, &bytes))) + return NULL; + + return drmm_kmalloc(dev, bytes, flags); +} + +/** + * drmm_kcalloc - &drm_device managed kcalloc() + * @dev: DRM device + * @n: number of array elements to allocate + * @size: size of array member + * @flags: GFP allocation flags + * + * This is a &drm_device managed version of kcalloc(). The allocated memory is + * automatically freed on the final drm_dev_put() and works exactly like a + * memory allocation obtained by drmm_kmalloc(). + */ +static inline void *drmm_kcalloc(struct drm_device *dev, + size_t n, size_t size, gfp_t flags) +{ + return drmm_kmalloc_array(dev, n, size, flags | __GFP_ZERO); +} + +char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp); + +void drmm_kfree(struct drm_device *dev, void *data); + +void __drmm_mutex_release(struct drm_device *dev, void *res); + +/** + * drmm_mutex_init - &drm_device-managed mutex_init() + * @dev: DRM device + * @lock: lock to be initialized + * + * Returns: + * 0 on success, or a negative errno code otherwise. + * + * This is a &drm_device-managed version of mutex_init(). The initialized + * lock is automatically destroyed on the final drm_dev_put(). + */ +#define drmm_mutex_init(dev, lock) ({ \ + mutex_init(lock); \ + drmm_add_action_or_reset(dev, __drmm_mutex_release, lock); \ +}) \ + +void __drmm_workqueue_release(struct drm_device *device, void *wq); + +/** + * drmm_alloc_ordered_workqueue - &drm_device managed alloc_ordered_workqueue() + * @dev: DRM device + * @fmt: printf format for the name of the workqueue + * @flags: WQ_* flags (only WQ_FREEZABLE and WQ_MEM_RECLAIM are meaningful) + * @args: args for @fmt + * + * This is a &drm_device-managed version of alloc_ordered_workqueue(). The + * allocated workqueue is automatically destroyed on the final drm_dev_put(). + * + * Returns: workqueue on success, negative ERR_PTR otherwise. + */ +#define drmm_alloc_ordered_workqueue(dev, fmt, flags, args...) \ + ({ \ + struct workqueue_struct *wq = alloc_ordered_workqueue(fmt, flags, ##args); \ + wq ? ({ \ + int ret = drmm_add_action_or_reset(dev, __drmm_workqueue_release, wq); \ + ret ? ERR_PTR(ret) : wq; \ + }) : ERR_PTR(-ENOMEM); \ + }) + +#endif diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h new file mode 100644 index 000000000000..f45f9612c0bc --- /dev/null +++ b/include/drm/drm_mipi_dbi.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * MIPI Display Bus Interface (DBI) LCD controller support + * + * Copyright 2016 Noralf Trønnes + */ + +#ifndef __LINUX_MIPI_DBI_H +#define __LINUX_MIPI_DBI_H + +#include <linux/mutex.h> +#include <drm/drm_device.h> +#include <drm/drm_simple_kms_helper.h> + +struct drm_format_conv_state; +struct drm_rect; +struct gpio_desc; +struct iosys_map; +struct regulator; +struct spi_device; + +/** + * struct mipi_dbi - MIPI DBI interface + */ +struct mipi_dbi { + /** + * @cmdlock: Command lock + */ + struct mutex cmdlock; + + /** + * @command: Bus specific callback executing commands. + */ + int (*command)(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num); + + /** + * @read_commands: Array of read commands terminated by a zero entry. + * Reading is disabled if this is NULL. + */ + const u8 *read_commands; + + /** + * @swap_bytes: Swap bytes in buffer before transfer + */ + bool swap_bytes; + + /** + * @reset: Optional reset gpio + */ + struct gpio_desc *reset; + + /* Type C specific */ + + /** + * @spi: SPI device + */ + struct spi_device *spi; + + /** + * @write_memory_bpw: Bits per word used on a MIPI_DCS_WRITE_MEMORY_START transfer + */ + unsigned int write_memory_bpw; + + /** + * @dc: Optional D/C gpio. + */ + struct gpio_desc *dc; + + /** + * @tx_buf9: Buffer used for Option 1 9-bit conversion + */ + void *tx_buf9; + + /** + * @tx_buf9_len: Size of tx_buf9. + */ + size_t tx_buf9_len; +}; + +/** + * struct mipi_dbi_dev - MIPI DBI device + */ +struct mipi_dbi_dev { + /** + * @drm: DRM device + */ + struct drm_device drm; + + /** + * @pipe: Display pipe structure + */ + struct drm_simple_display_pipe pipe; + + /** + * @connector: Connector + */ + struct drm_connector connector; + + /** + * @mode: Fixed display mode + */ + struct drm_display_mode mode; + + /** + * @pixel_format: Native pixel format (DRM_FORMAT\_\*) + */ + u32 pixel_format; + + /** + * @tx_buf: Buffer used for transfer (copy clip rect area) + */ + u16 *tx_buf; + + /** + * @rotation: initial rotation in degrees Counter Clock Wise + */ + unsigned int rotation; + + /** + * @left_offset: Horizontal offset of the display relative to the + * controller's driver array + */ + unsigned int left_offset; + + /** + * @top_offset: Vertical offset of the display relative to the + * controller's driver array + */ + unsigned int top_offset; + + /** + * @backlight: backlight device (optional) + */ + struct backlight_device *backlight; + + /** + * @regulator: power regulator (Vdd) (optional) + */ + struct regulator *regulator; + + /** + * @io_regulator: I/O power regulator (Vddi) (optional) + */ + struct regulator *io_regulator; + + /** + * @dbi: MIPI DBI interface + */ + struct mipi_dbi dbi; + + /** + * @driver_private: Driver private data. + * Necessary for drivers with private data since devm_drm_dev_alloc() + * can't allocate structures that embed a structure which then again + * embeds drm_device. + */ + void *driver_private; +}; + +static inline struct mipi_dbi_dev *drm_to_mipi_dbi_dev(struct drm_device *drm) +{ + return container_of(drm, struct mipi_dbi_dev, drm); +} + +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi, + struct gpio_desc *dc); +int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, + const struct drm_simple_display_pipe_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const struct drm_display_mode *mode, + unsigned int rotation, size_t tx_buf_size); +int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, + const struct drm_simple_display_pipe_funcs *funcs, + const struct drm_display_mode *mode, unsigned int rotation); +enum drm_mode_status mipi_dbi_pipe_mode_valid(struct drm_simple_display_pipe *pipe, + const struct drm_display_mode *mode); +void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_state); +void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plan_state); +void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe); +int mipi_dbi_pipe_begin_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +void mipi_dbi_pipe_end_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +void mipi_dbi_pipe_reset_plane(struct drm_simple_display_pipe *pipe); +struct drm_plane_state *mipi_dbi_pipe_duplicate_plane_state(struct drm_simple_display_pipe *pipe); +void mipi_dbi_pipe_destroy_plane_state(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); + +void mipi_dbi_hw_reset(struct mipi_dbi *dbi); +bool mipi_dbi_display_is_on(struct mipi_dbi *dbi); +int mipi_dbi_poweron_reset(struct mipi_dbi_dev *dbidev); +int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev); + +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len); +int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz, + u8 bpw, const void *buf, size_t len); + +int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val); +int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len); +int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, + size_t len); +int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, + struct drm_rect *clip, bool swap, + struct drm_format_conv_state *fmtcnv_state); + +/** + * mipi_dbi_command - MIPI DCS command with optional parameter(s) + * @dbi: MIPI DBI structure + * @cmd: Command + * @seq: Optional parameter(s) + * + * Send MIPI DCS command to the controller. Use mipi_dbi_command_read() for + * get/read. + * + * Returns: + * Zero on success, negative error code on failure. + */ +#define mipi_dbi_command(dbi, cmd, seq...) \ +({ \ + const u8 d[] = { seq }; \ + struct device *dev = &(dbi)->spi->dev; \ + int ret; \ + ret = mipi_dbi_command_stackbuf(dbi, cmd, d, ARRAY_SIZE(d)); \ + if (ret) \ + dev_err_ratelimited(dev, "error %d when sending command %#02x\n", ret, cmd); \ + ret; \ +}) + +#ifdef CONFIG_DEBUG_FS +void mipi_dbi_debugfs_init(struct drm_minor *minor); +#else +static inline void mipi_dbi_debugfs_init(struct drm_minor *minor) {} +#endif + +/** + * DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS - Initializes struct drm_simple_display_pipe_funcs + * for MIPI-DBI devices + * @enable_: Enable-callback implementation + * + * This macro initializes struct drm_simple_display_pipe_funcs with default + * values for MIPI-DBI-based devices. The only callback that depends on the + * hardware is @enable, for which the driver has to provide an implementation. + * MIPI-based drivers are encouraged to use this macro for initialization. + */ +#define DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(enable_) \ + .mode_valid = mipi_dbi_pipe_mode_valid, \ + .enable = (enable_), \ + .disable = mipi_dbi_pipe_disable, \ + .update = mipi_dbi_pipe_update, \ + .begin_fb_access = mipi_dbi_pipe_begin_fb_access, \ + .end_fb_access = mipi_dbi_pipe_end_fb_access, \ + .reset_plane = mipi_dbi_pipe_reset_plane, \ + .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, \ + .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state + +#endif /* __LINUX_MIPI_DBI_H */ diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 491528f48cfb..3aba7b380c8d 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -1,21 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MIPI DSI Bus * * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. * Andrzej Hajda <a.hajda@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __DRM_MIPI_DSI_H__ #define __DRM_MIPI_DSI_H__ #include <linux/device.h> +#include <linux/delay.h> struct mipi_dsi_host; struct mipi_dsi_device; +struct drm_dsc_picture_parameter_set; /* request ACK from peripheral */ #define MIPI_DSI_MSG_REQ_ACK BIT(0) @@ -82,6 +81,11 @@ int mipi_dsi_create_packet(struct mipi_dsi_packet *packet, * Note that typically DSI packet transmission is atomic, so the .transfer() * function will seldomly return anything other than the number of bytes * contained in the transmit buffer on success. + * + * Also note that those callbacks can be called no matter the state the + * host is in. Drivers that need the underlying device to be powered to + * perform these operations will first need to make sure it's been + * properly enabled. */ struct mipi_dsi_host_ops { int (*attach)(struct mipi_dsi_host *host, @@ -121,19 +125,19 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node); /* enable hsync-end packets in vsync-pulse and v-porch area */ #define MIPI_DSI_MODE_VIDEO_HSE BIT(4) /* disable hfront-porch area */ -#define MIPI_DSI_MODE_VIDEO_HFP BIT(5) +#define MIPI_DSI_MODE_VIDEO_NO_HFP BIT(5) /* disable hback-porch area */ -#define MIPI_DSI_MODE_VIDEO_HBP BIT(6) +#define MIPI_DSI_MODE_VIDEO_NO_HBP BIT(6) /* disable hsync-active area */ -#define MIPI_DSI_MODE_VIDEO_HSA BIT(7) -/* flush display FIFO on vsync pulse */ -#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) +#define MIPI_DSI_MODE_VIDEO_NO_HSA BIT(7) /* disable EoT packets in HS mode */ -#define MIPI_DSI_MODE_EOT_PACKET BIT(9) +#define MIPI_DSI_MODE_NO_EOT_PACKET BIT(9) /* device supports non-continuous clock behavior (DSI spec 5.6.1) */ #define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) /* transmit data in low power */ #define MIPI_DSI_MODE_LPM BIT(11) +/* transmit data ending at the same time for all lanes within one hsync */ +#define MIPI_DSI_HS_PKT_END_ALIGNED BIT(12) enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888, @@ -163,6 +167,7 @@ struct mipi_dsi_device_info { * struct mipi_dsi_device - DSI peripheral device * @host: DSI host for this peripheral * @dev: driver model device node for this peripheral + * @attached: the DSI device has been successfully attached * @name: DSI peripheral chip type * @channel: virtual channel assigned to the peripheral * @format: pixel format for video mode @@ -174,10 +179,12 @@ struct mipi_dsi_device_info { * @lp_rate: maximum lane frequency for low power mode in hertz, this should * be set to the real limits of the hardware, zero is only accepted for * legacy drivers + * @dsc: panel/bridge DSC pps payload to be sent */ struct mipi_dsi_device { struct mipi_dsi_host *host; struct device dev; + bool attached; char name[DSI_DEV_NAME_SIZE]; unsigned int channel; @@ -186,14 +193,36 @@ struct mipi_dsi_device { unsigned long mode_flags; unsigned long hs_rate; unsigned long lp_rate; + struct drm_dsc_config *dsc; +}; + +/** + * struct mipi_dsi_multi_context - Context to call multiple MIPI DSI funcs in a row + */ +struct mipi_dsi_multi_context { + /** + * @dsi: Pointer to the MIPI DSI device + */ + struct mipi_dsi_device *dsi; + + /** + * @accum_err: Storage for the accumulated error over the multiple calls + * + * Init to 0. If a function encounters an error then the error code + * will be stored here. If you call a function and this points to a + * non-zero value then the function will be a noop. This allows calling + * a function many times in a row and just checking the error at the + * end to see if any of them failed. + */ + int accum_err; }; #define MIPI_DSI_MODULE_PREFIX "mipi-dsi:" -static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev) -{ - return container_of(dev, struct mipi_dsi_device, dev); -} +#define to_mipi_dsi_device(__dev) container_of_const(__dev, struct mipi_dsi_device, dev) + +extern const struct bus_type mipi_dsi_bus_type; +#define dev_is_mipi_dsi(dev) ((dev)->bus == &mipi_dsi_bus_type) /** * mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any @@ -220,22 +249,66 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt) return -EINVAL; } +enum mipi_dsi_compression_algo { + MIPI_DSI_COMPRESSION_DSC = 0, + MIPI_DSI_COMPRESSION_VENDOR = 3, + /* other two values are reserved, DSI 1.3 */ +}; + struct mipi_dsi_device * mipi_dsi_device_register_full(struct mipi_dsi_host *host, const struct mipi_dsi_device_info *info); void mipi_dsi_device_unregister(struct mipi_dsi_device *dsi); +struct mipi_dsi_device * +devm_mipi_dsi_device_register_full(struct device *dev, struct mipi_dsi_host *host, + const struct mipi_dsi_device_info *info); struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np); int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); +int devm_mipi_dsi_attach(struct device *dev, struct mipi_dsi_device *dsi); int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi); int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi); int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi, u16 value); +int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable); +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable, + enum mipi_dsi_compression_algo algo, + unsigned int pps_selector); +int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi, + const struct drm_dsc_picture_parameter_set *pps); + +void mipi_dsi_compression_mode_ext_multi(struct mipi_dsi_multi_context *ctx, + bool enable, + enum mipi_dsi_compression_algo algo, + unsigned int pps_selector); +void mipi_dsi_compression_mode_multi(struct mipi_dsi_multi_context *ctx, + bool enable); +void mipi_dsi_picture_parameter_set_multi(struct mipi_dsi_multi_context *ctx, + const struct drm_dsc_picture_parameter_set *pps); ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload, size_t size); +void mipi_dsi_generic_write_multi(struct mipi_dsi_multi_context *ctx, + const void *payload, size_t size); +void mipi_dsi_dual_generic_write_multi(struct mipi_dsi_multi_context *ctx, + struct mipi_dsi_device *dsi1, + struct mipi_dsi_device *dsi2, + const void *payload, size_t size); ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params, size_t num_params, void *data, size_t size); +u32 drm_mipi_dsi_get_input_bus_fmt(enum mipi_dsi_pixel_format dsi_format); + +#define mipi_dsi_msleep(ctx, delay) \ + do { \ + if (!(ctx)->accum_err) \ + msleep(delay); \ + } while (0) + +#define mipi_dsi_usleep_range(ctx, min, max) \ + do { \ + if (!(ctx)->accum_err) \ + usleep_range(min, max); \ + } while (0) /** * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode @@ -257,10 +330,20 @@ enum mipi_dsi_dcs_tear_mode { ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, const void *data, size_t len); +int mipi_dsi_dcs_write_buffer_chatty(struct mipi_dsi_device *dsi, + const void *data, size_t len); +void mipi_dsi_dcs_write_buffer_multi(struct mipi_dsi_multi_context *ctx, + const void *data, size_t len); +void mipi_dsi_dual_dcs_write_buffer_multi(struct mipi_dsi_multi_context *ctx, + struct mipi_dsi_device *dsi1, + struct mipi_dsi_device *dsi2, + const void *data, size_t len); ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, const void *data, size_t len); ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, size_t len); +void mipi_dsi_dcs_read_multi(struct mipi_dsi_multi_context *ctx, u8 cmd, + void *data, size_t len); int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi); int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi); int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode); @@ -273,7 +356,6 @@ int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, u16 end); int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, u16 end); -int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi); int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, enum mipi_dsi_dcs_tear_mode mode); int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format); @@ -282,6 +364,182 @@ int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, u16 brightness); int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, u16 *brightness); +int mipi_dsi_dcs_set_display_brightness_large(struct mipi_dsi_device *dsi, + u16 brightness); +int mipi_dsi_dcs_get_display_brightness_large(struct mipi_dsi_device *dsi, + u16 *brightness); + +void mipi_dsi_dcs_nop_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_enter_sleep_mode_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_exit_sleep_mode_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_set_display_off_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_set_display_on_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_set_tear_on_multi(struct mipi_dsi_multi_context *ctx, + enum mipi_dsi_dcs_tear_mode mode); +void mipi_dsi_turn_on_peripheral_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_soft_reset_multi(struct mipi_dsi_multi_context *ctx); +void mipi_dsi_dcs_set_display_brightness_multi(struct mipi_dsi_multi_context *ctx, + u16 brightness); +void mipi_dsi_dcs_set_pixel_format_multi(struct mipi_dsi_multi_context *ctx, + u8 format); +void mipi_dsi_dcs_set_column_address_multi(struct mipi_dsi_multi_context *ctx, + u16 start, u16 end); +void mipi_dsi_dcs_set_page_address_multi(struct mipi_dsi_multi_context *ctx, + u16 start, u16 end); +void mipi_dsi_dcs_set_tear_scanline_multi(struct mipi_dsi_multi_context *ctx, + u16 scanline); +void mipi_dsi_dcs_set_tear_off_multi(struct mipi_dsi_multi_context *ctx); + +/** + * mipi_dsi_generic_write_seq_multi - transmit data using a generic write packet + * + * This macro will print errors for you and error handling is optimized for + * callers that call this multiple times in a row. + * + * @ctx: Context for multiple DSI transactions + * @seq: buffer containing the payload + */ +#define mipi_dsi_generic_write_seq_multi(ctx, seq...) \ + do { \ + static const u8 d[] = { seq }; \ + mipi_dsi_generic_write_multi(ctx, d, ARRAY_SIZE(d)); \ + } while (0) + +/** + * mipi_dsi_generic_write_var_seq_multi - transmit non-constant data using a + * generic write packet + * + * This macro will print errors for you and error handling is optimized for + * callers that call this multiple times in a row. + * + * @ctx: Context for multiple DSI transactions + * @seq: buffer containing the payload + */ +#define mipi_dsi_generic_write_var_seq_multi(ctx, seq...) \ + do { \ + const u8 d[] = { seq }; \ + mipi_dsi_generic_write_multi(ctx, d, ARRAY_SIZE(d)); \ + } while (0) + +/** + * mipi_dsi_dcs_write_seq_multi - transmit a DCS command with payload + * + * This macro will print errors for you and error handling is optimized for + * callers that call this multiple times in a row. + * + * @ctx: Context for multiple DSI transactions + * @cmd: Command + * @seq: buffer containing data to be transmitted + */ +#define mipi_dsi_dcs_write_seq_multi(ctx, cmd, seq...) \ + do { \ + static const u8 d[] = { cmd, seq }; \ + mipi_dsi_dcs_write_buffer_multi(ctx, d, ARRAY_SIZE(d)); \ + } while (0) + +/** + * mipi_dsi_dcs_write_var_seq_multi - transmit a DCS command with non-constant + * payload + * + * This macro will print errors for you and error handling is optimized for + * callers that call this multiple times in a row. + * + * @ctx: Context for multiple DSI transactions + * @cmd: Command + * @seq: buffer containing data to be transmitted + */ +#define mipi_dsi_dcs_write_var_seq_multi(ctx, cmd, seq...) \ + do { \ + const u8 d[] = { cmd, seq }; \ + mipi_dsi_dcs_write_buffer_multi(ctx, d, ARRAY_SIZE(d)); \ + } while (0) + +/** + * mipi_dsi_dual - send the same MIPI DSI command to two interfaces + * + * This macro will send the specified MIPI DSI command twice, once per each of + * the two interfaces supplied. This is useful for reducing duplication of code + * in panel drivers which use two parallel serial interfaces. + * + * Note that the _func parameter cannot accept a macro such as + * mipi_dsi_generic_write_multi() or mipi_dsi_dcs_write_buffer_multi(). See + * mipi_dsi_dual_generic_write_multi() and + * mipi_dsi_dual_dcs_write_buffer_multi() instead. + * + * WARNING: This macro reuses the _func argument and the optional trailing + * arguments twice each, which may cause unintended side effects. For example, + * adding the postfix increment ++ operator to one of the arguments to be + * passed to _func will cause the variable to be incremented twice instead of + * once and the variable will be its original value + 1 when sent to _dsi2. + * + * @_func: MIPI DSI function to pass context and arguments into + * @_ctx: Context for multiple DSI transactions + * @_dsi1: First DSI interface to act as recipient of the MIPI DSI command + * @_dsi2: Second DSI interface to act as recipient of the MIPI DSI command + * @...: Arguments to pass to MIPI DSI function or macro + */ + +#define mipi_dsi_dual(_func, _ctx, _dsi1, _dsi2, ...) \ + do { \ + struct mipi_dsi_multi_context *_ctxcpy = (_ctx); \ + _ctxcpy->dsi = (_dsi1); \ + (_func)(_ctxcpy, ##__VA_ARGS__); \ + _ctxcpy->dsi = (_dsi2); \ + (_func)(_ctxcpy, ##__VA_ARGS__); \ + } while (0) + +/** + * mipi_dsi_dual_generic_write_seq_multi - transmit data using a generic write + * packet to two dsi interfaces, one after the other + * + * This macro will send the specified generic packet twice, once per each of + * the two interfaces supplied. This is useful for reducing duplication of code + * in panel drivers which use two parallel serial interfaces. + * + * Note that if an error occurs while transmitting the packet to the first DSI + * interface, the packet will not be sent to the second DSI interface. + * + * This macro will print errors for you and error handling is optimized for + * callers that call this multiple times in a row. + * + * @_ctx: Context for multiple DSI transactions + * @_dsi1: First DSI interface to act as recipient of packet + * @_dsi2: Second DSI interface to act as recipient of packet + * @_seq: buffer containing the payload + */ +#define mipi_dsi_dual_generic_write_seq_multi(_ctx, _dsi1, _dsi2, _seq...) \ + do { \ + static const u8 d[] = { _seq }; \ + mipi_dsi_dual_generic_write_multi(_ctx, _dsi1, _dsi2, d, \ + ARRAY_SIZE(d)); \ + } while (0) + +/** + * mipi_dsi_dual_dcs_write_seq_multi - transmit a DCS command with payload to + * two dsi interfaces, one after the other + * + * This macro will send the specified DCS command with payload twice, once per + * each of the two interfaces supplied. This is useful for reducing duplication + * of code in panel drivers which use two parallel serial interfaces. + * + * Note that if an error occurs while transmitting the payload to the first DSI + * interface, the payload will not be sent to the second DSI interface. + * + * This macro will print errors for you and error handling is optimized for + * callers that call this multiple times in a row. + * + * @_ctx: Context for multiple DSI transactions + * @_dsi1: First DSI interface to act as recipient of packet + * @_dsi2: Second DSI interface to act as recipient of packet + * @_cmd: Command + * @_seq: buffer containing the payload + */ +#define mipi_dsi_dual_dcs_write_seq_multi(_ctx, _dsi1, _dsi2, _cmd, _seq...) \ + do { \ + static const u8 d[] = { _cmd, _seq }; \ + mipi_dsi_dual_dcs_write_buffer_multi(_ctx, _dsi1, _dsi2, d, \ + ARRAY_SIZE(d)); \ + } while (0) /** * struct mipi_dsi_driver - DSI driver @@ -293,7 +551,7 @@ int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, struct mipi_dsi_driver { struct device_driver driver; int(*probe)(struct mipi_dsi_device *dsi); - int(*remove)(struct mipi_dsi_device *dsi); + void (*remove)(struct mipi_dsi_device *dsi); void (*shutdown)(struct mipi_dsi_device *dsi); }; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 2c3bbb43c7d1..16ce0e8f36a6 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -39,14 +39,16 @@ */ #include <linux/bug.h> #include <linux/rbtree.h> -#include <linux/kernel.h> +#include <linux/limits.h> #include <linux/mm_types.h> #include <linux/list.h> #include <linux/spinlock.h> #ifdef CONFIG_DRM_DEBUG_MM #include <linux/stackdepot.h> #endif -#include <drm/drm_print.h> +#include <linux/types.h> + +struct drm_printer; #ifdef CONFIG_DRM_DEBUG_MM #define DRM_MM_BUG_ON(expr) BUG_ON(expr) @@ -168,8 +170,10 @@ struct drm_mm_node { struct rb_node rb_hole_addr; u64 __subtree_last; u64 hole_size; - bool allocated : 1; - bool scanned_block : 1; + u64 subtree_max_hole; + unsigned long flags; +#define DRM_MM_NODE_ALLOCATED_BIT 0 +#define DRM_MM_NODE_SCANNED_BIT 1 #ifdef CONFIG_DRM_DEBUG_MM depot_stack_handle_t stack; #endif @@ -253,7 +257,7 @@ struct drm_mm_scan { */ static inline bool drm_mm_node_allocated(const struct drm_mm_node *node) { - return node->allocated; + return test_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); } /** @@ -271,7 +275,7 @@ static inline bool drm_mm_node_allocated(const struct drm_mm_node *node) */ static inline bool drm_mm_initialized(const struct drm_mm *mm) { - return mm->hole_stack.next; + return READ_ONCE(mm->hole_stack.next); } /** @@ -336,7 +340,7 @@ static inline u64 drm_mm_hole_node_end(const struct drm_mm_node *hole_node) /** * drm_mm_nodes - list of nodes under the drm_mm range manager - * @mm: the struct drm_mm range manger + * @mm: the struct drm_mm range manager * * As the drm_mm range manager hides its node_list deep with its * structure, extracting it looks painful and repetitive. This is @@ -459,7 +463,6 @@ static inline int drm_mm_insert_node(struct drm_mm *mm, } void drm_mm_remove_node(struct drm_mm_node *node); -void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new); void drm_mm_init(struct drm_mm *mm, u64 start, u64 size); void drm_mm_takedown(struct drm_mm *mm); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 572274ccbec7..895fb820dba0 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -58,6 +58,12 @@ struct drm_mode_config_funcs { * actual modifier used if the request doesn't have it specified, * ie. when (@mode_cmd->flags & DRM_MODE_FB_MODIFIERS) == 0. * + * IMPORTANT: These implied modifiers for legacy userspace must be + * stored in struct &drm_framebuffer, including all relevant metadata + * like &drm_framebuffer.pitches and &drm_framebuffer.offsets if the + * modifier enables additional planes beyond the fourcc pixel format + * code. This is required by the GETFB2 ioctl. + * * If the parameters are deemed valid and the backing storage objects in * the underlying memory manager all exist, then the driver allocates * a new &drm_framebuffer structure, subclassed to contain @@ -76,6 +82,7 @@ struct drm_mode_config_funcs { */ struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, + const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd); /** @@ -89,24 +96,7 @@ struct drm_mode_config_funcs { * The format information specific to the given fb metadata, or * NULL if none is found. */ - const struct drm_format_info *(*get_format_info)(const struct drm_mode_fb_cmd2 *mode_cmd); - - /** - * @output_poll_changed: - * - * Callback used by helpers to inform the driver of output configuration - * changes. - * - * Drivers implementing fbdev emulation with the helpers can call - * drm_fb_helper_hotplug_changed from this hook to inform the fbdev - * helper of output changes. - * - * FIXME: - * - * Except that there's no vtable for device-level helper callbacks - * there's no reason this is a core function. - */ - void (*output_poll_changed)(struct drm_device *dev); + const struct drm_format_info *(*get_format_info)(u32 pixel_format, u64 modifier); /** * @mode_valid: @@ -340,7 +330,6 @@ struct drm_mode_config_funcs { * @max_width: maximum fb pixel width on this device * @max_height: maximum fb pixel height on this device * @funcs: core driver provided mode setting functions - * @fb_base: base address of the framebuffer * @poll_enabled: track polling support for this device * @poll_running: track polling status for this device * @delayed_event: track delayed poll uevent deliver for this device @@ -354,6 +343,19 @@ struct drm_mode_config_funcs { * Core mode resource tracking structure. All CRTC, encoders, and connectors * enumerated by the driver are added here, as are global properties. Some * global restrictions are also here, e.g. dimension restrictions. + * + * Framebuffer sizes refer to the virtual screen that can be displayed by + * the CRTC. This can be different from the physical resolution programmed. + * The minimum width and height, stored in @min_width and @min_height, + * describe the smallest size of the framebuffer. It correlates to the + * minimum programmable resolution. + * The maximum width, stored in @max_width, is typically limited by the + * maximum pitch between two adjacent scanlines. The maximum height, stored + * in @max_height, is usually only limited by the amount of addressable video + * memory. For hardware that has no real maximum, drivers should pick a + * reasonable default. + * + * See also @DRM_SHADOW_PLANE_MAX_WIDTH and @DRM_SHADOW_PLANE_MAX_HEIGHT. */ struct drm_mode_config { /** @@ -361,7 +363,7 @@ struct drm_mode_config { * * This is the big scary modeset BKL which protects everything that * isn't protect otherwise. Scope is unclear and fuzzy, try to remove - * anything from under it's protection and move it into more well-scoped + * anything from under its protection and move it into more well-scoped * locks. * * The one important thing this protects is the use of @acquire_ctx. @@ -391,18 +393,18 @@ struct drm_mode_config { /** * @idr_mutex: * - * Mutex for KMS ID allocation and management. Protects both @crtc_idr + * Mutex for KMS ID allocation and management. Protects both @object_idr * and @tile_idr. */ struct mutex idr_mutex; /** - * @crtc_idr: + * @object_idr: * * Main KMS ID tracking object. Use this idr for all IDs, fb, crtc, * connector, modes - just makes life easier to have only one. */ - struct idr crtc_idr; + struct idr object_idr; /** * @tile_idr: @@ -489,6 +491,34 @@ struct drm_mode_config { struct list_head plane_list; /** + * @panic_lock: + * + * Raw spinlock used to protect critical sections of code that access + * the display hardware or modeset software state, which the panic + * printing code must be protected against. See drm_panic_trylock(), + * drm_panic_lock() and drm_panic_unlock(). + */ + struct raw_spinlock panic_lock; + + /** + * @num_colorop: + * + * Number of colorop objects on this device. + * This is invariant over the lifetime of a device and hence doesn't + * need any locks. + */ + int num_colorop; + + /** + * @colorop_list: + * + * List of colorop objects linked with &drm_colorop.head. This is + * invariant over the lifetime of a device and hence doesn't need any + * locks. + */ + struct list_head colorop_list; + + /** * @num_crtc: * * Number of CRTCs on this device linked with &drm_crtc.head. This is invariant over the lifetime @@ -512,10 +542,18 @@ struct drm_mode_config { */ struct list_head property_list; - int min_width, min_height; - int max_width, max_height; + /** + * @privobj_list: + * + * List of private objects linked with &drm_private_obj.head. This is + * invariant over the lifetime of a device and hence doesn't need any + * locks. + */ + struct list_head privobj_list; + + unsigned int min_width, min_height; + unsigned int max_width, max_height; const struct drm_mode_config_funcs *funcs; - resource_size_t fb_base; /* output poll support */ bool poll_enabled; @@ -594,22 +632,22 @@ struct drm_mode_config { struct drm_property *prop_src_h; /** * @prop_crtc_x: Default atomic plane property for the plane destination - * position in the &drm_crtc is is being shown on. + * position in the &drm_crtc is being shown on. */ struct drm_property *prop_crtc_x; /** * @prop_crtc_y: Default atomic plane property for the plane destination - * position in the &drm_crtc is is being shown on. + * position in the &drm_crtc is being shown on. */ struct drm_property *prop_crtc_y; /** * @prop_crtc_w: Default atomic plane property for the plane destination - * position in the &drm_crtc is is being shown on. + * position in the &drm_crtc is being shown on. */ struct drm_property *prop_crtc_w; /** * @prop_crtc_h: Default atomic plane property for the plane destination - * position in the &drm_crtc is is being shown on. + * position in the &drm_crtc is being shown on. */ struct drm_property *prop_crtc_h; /** @@ -672,6 +710,12 @@ struct drm_mode_config { struct drm_property *dvi_i_select_subconnector_property; /** + * @dp_subconnector_property: Optional DP property to differentiate + * between different DP downstream port types. + */ + struct drm_property *dp_subconnector_property; + + /** * @tv_subconnector_property: Optional TV property to differentiate * between different TV connector types. */ @@ -681,29 +725,39 @@ struct drm_mode_config { * between different TV connector types. */ struct drm_property *tv_select_subconnector_property; + /** - * @tv_mode_property: Optional TV property to select + * @legacy_tv_mode_property: Optional TV property to select * the output TV mode. + * + * Superseded by @tv_mode_property + */ + struct drm_property *legacy_tv_mode_property; + + /** + * @tv_mode_property: Optional TV property to select the TV + * standard output on the connector. */ struct drm_property *tv_mode_property; + /** * @tv_left_margin_property: Optional TV property to set the left - * margin. + * margin (expressed in pixels). */ struct drm_property *tv_left_margin_property; /** * @tv_right_margin_property: Optional TV property to set the right - * margin. + * margin (expressed in pixels). */ struct drm_property *tv_right_margin_property; /** * @tv_top_margin_property: Optional TV property to set the right - * margin. + * margin (expressed in pixels). */ struct drm_property *tv_top_margin_property; /** * @tv_bottom_margin_property: Optional TV property to set the right - * margin. + * margin (expressed in pixels). */ struct drm_property *tv_bottom_margin_property; /** @@ -827,6 +881,25 @@ struct drm_mode_config { */ struct drm_property *writeback_out_fence_ptr_property; + /** + * @hdr_output_metadata_property: Connector property containing hdr + * metatada. This will be provided by userspace compositors based + * on HDR content + */ + struct drm_property *hdr_output_metadata_property; + + /** + * @content_protection_property: DRM ENUM property for content + * protection. See drm_connector_attach_content_protection_property(). + */ + struct drm_property *content_protection_property; + + /** + * @hdcp_content_type_property: DRM ENUM property for type of + * Protected Content. + */ + struct drm_property *hdcp_content_type_property; + /* dumb ioctl parameters */ uint32_t preferred_depth, prefer_shadow; @@ -859,11 +932,14 @@ struct drm_mode_config { bool async_page_flip; /** - * @allow_fb_modifiers: + * @fb_modifiers_not_supported: * - * Whether the driver supports fb modifiers in the ADDFB2.1 ioctl call. + * When this flag is set, the DRM device will not expose modifier + * support to userspace. This is only used by legacy drivers that infer + * the buffer layout through heuristics without using modifiers. New + * drivers shall not set fhis flag. */ - bool allow_fb_modifiers; + bool fb_modifiers_not_supported; /** * @normalize_zpos: @@ -879,6 +955,17 @@ struct drm_mode_config { */ struct drm_property *modifiers_property; + /** + * @async_modifiers_property: Plane property to list support modifier/format + * combination for asynchronous flips. + */ + struct drm_property *async_modifiers_property; + + /** + * @size_hints_property: Plane SIZE_HINTS property. + */ + struct drm_property *size_hints_property; + /* cursor size */ uint32_t cursor_width, cursor_height; @@ -894,7 +981,23 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; }; -void drm_mode_config_init(struct drm_device *dev); +int __must_check drmm_mode_config_init(struct drm_device *dev); + +/** + * drm_mode_config_init - DRM mode_configuration structure initialization + * @dev: DRM device + * + * This is the unmanaged version of drmm_mode_config_init() for drivers which + * still explicitly call drm_mode_config_cleanup(). + * + * FIXME: This function is deprecated and drivers should be converted over to + * drmm_mode_config_init(). + */ +static inline int drm_mode_config_init(struct drm_device *dev) +{ + return drmm_mode_config_init(dev); +} + void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev); diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h index c34a3e8030e1..c68edbd126d0 100644 --- a/include/drm/drm_mode_object.h +++ b/include/drm/drm_mode_object.h @@ -35,7 +35,7 @@ struct drm_file; * @id: userspace visible identifier * @type: type of the object, one of DRM_MODE_OBJECT\_\* * @properties: properties attached to this object, including values - * @refcount: reference count for objects which with dynamic lifetime + * @refcount: reference count for objects with dynamic lifetime * @free_cb: free function callback, only set for objects with dynamic lifetime * * Base structure for modeset objects visible to userspace. Objects can be @@ -60,7 +60,7 @@ struct drm_mode_object { void (*free_cb)(struct kref *kref); }; -#define DRM_OBJECT_MAX_PROPERTY 24 +#define DRM_OBJECT_MAX_PROPERTY 64 /** * struct drm_object_properties - property tracking for &drm_mode_object */ @@ -98,6 +98,10 @@ struct drm_object_properties { * Hence atomic drivers should not use drm_object_property_set_value() * and drm_object_property_get_value() on mutable objects, i.e. those * without the DRM_MODE_PROP_IMMUTABLE flag set. + * + * For atomic drivers the default value of properties is stored in this + * array, so drm_object_property_get_default_value can be used to + * retrieve it. */ uint64_t values[DRM_OBJECT_MAX_PROPERTY]; }; @@ -126,6 +130,9 @@ int drm_object_property_set_value(struct drm_mode_object *obj, int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value); +int drm_object_property_get_default_value(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t *val); void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index baded6514456..b9bb92e4b029 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -48,7 +48,7 @@ struct videomode; * @MODE_HSYNC: hsync out of range * @MODE_VSYNC: vsync out of range * @MODE_H_ILLEGAL: mode has illegal horizontal timings - * @MODE_V_ILLEGAL: mode has illegal horizontal timings + * @MODE_V_ILLEGAL: mode has illegal vertical timings * @MODE_BAD_WIDTH: requires an unsupported linepitch * @MODE_NOMODE: no mode with a matching name * @MODE_NO_INTERLACE: interlaced mode not supported @@ -136,8 +136,49 @@ enum drm_mode_status { .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ - .vscan = (vs), .flags = (f), \ - .base.type = DRM_MODE_OBJECT_MODE + .vscan = (vs), .flags = (f) + +/** + * DRM_MODE_RES_MM - Calculates the display size from resolution and DPI + * @res: The resolution in pixel + * @dpi: The number of dots per inch + */ +#define DRM_MODE_RES_MM(res, dpi) \ + (((res) * 254ul) / ((dpi) * 10ul)) + +#define __DRM_MODE_INIT(pix, hd, vd, hd_mm, vd_mm) \ + .type = DRM_MODE_TYPE_DRIVER, .clock = (pix), \ + .hdisplay = (hd), .hsync_start = (hd), .hsync_end = (hd), \ + .htotal = (hd), .vdisplay = (vd), .vsync_start = (vd), \ + .vsync_end = (vd), .vtotal = (vd), .width_mm = (hd_mm), \ + .height_mm = (vd_mm) + +/** + * DRM_MODE_INIT - Initialize display mode + * @hz: Vertical refresh rate in Hertz + * @hd: Horizontal resolution, width + * @vd: Vertical resolution, height + * @hd_mm: Display width in millimeters + * @vd_mm: Display height in millimeters + * + * This macro initializes a &drm_display_mode that contains information about + * refresh rate, resolution and physical size. + */ +#define DRM_MODE_INIT(hz, hd, vd, hd_mm, vd_mm) \ + __DRM_MODE_INIT((hd) * (vd) * (hz) / 1000 /* kHz */, hd, vd, hd_mm, vd_mm) + +/** + * DRM_SIMPLE_MODE - Simple display mode + * @hd: Horizontal resolution, width + * @vd: Vertical resolution, height + * @hd_mm: Display width in millimeters + * @vd_mm: Display height in millimeters + * + * This macro initializes a &drm_display_mode that only contains info about + * resolution and physical size. + */ +#define DRM_SIMPLE_MODE(hd, vd, hd_mm, vd_mm) \ + __DRM_MODE_INIT(1 /* pass validation */, hd, vd, hd_mm, vd_mm) #define CRTC_INTERLACE_HALVE_V (1 << 0) /* halve V values for interlacing */ #define CRTC_STEREO_DOUBLE (1 << 1) /* adjust timings for stereo modes */ @@ -179,6 +220,9 @@ enum drm_mode_status { * @crtc_vsync_end: hardware mode vertical sync end * @crtc_vtotal: hardware mode vertical total size * + * This is the kernel API display mode information structure. For the + * user-space version see struct drm_mode_modeinfo. + * * The horizontal and vertical timings are defined per the following diagram. * * :: @@ -207,85 +251,21 @@ enum drm_mode_status { */ struct drm_display_mode { /** - * @head: - * - * struct list_head for mode lists. - */ - struct list_head head; - - /** - * @base: - * - * A display mode is a normal modeset object, possibly including public - * userspace id. - * - * FIXME: - * - * This can probably be removed since the entire concept of userspace - * managing modes explicitly has never landed in upstream kernel mode - * setting support. - */ - struct drm_mode_object base; - - /** - * @name: - * - * Human-readable name of the mode, filled out with drm_mode_set_name(). - */ - char name[DRM_DISPLAY_MODE_LEN]; - - /** - * @status: - * - * Status of the mode, used to filter out modes not supported by the - * hardware. See enum &drm_mode_status. - */ - enum drm_mode_status status; - - /** - * @type: - * - * A bitmask of flags, mostly about the source of a mode. Possible flags - * are: - * - * - DRM_MODE_TYPE_PREFERRED: Preferred mode, usually the native - * resolution of an LCD panel. There should only be one preferred - * mode per connector at any given time. - * - DRM_MODE_TYPE_DRIVER: Mode created by the driver, which is all of - * them really. Drivers must set this bit for all modes they create - * and expose to userspace. - * - DRM_MODE_TYPE_USERDEF: Mode defined via kernel command line - * - * Plus a big list of flags which shouldn't be used at all, but are - * still around since these flags are also used in the userspace ABI. - * We no longer accept modes with these types though: - * - * - DRM_MODE_TYPE_BUILTIN: Meant for hard-coded modes, unused. - * Use DRM_MODE_TYPE_DRIVER instead. - * - DRM_MODE_TYPE_DEFAULT: Again a leftover, use - * DRM_MODE_TYPE_PREFERRED instead. - * - DRM_MODE_TYPE_CLOCK_C and DRM_MODE_TYPE_CRTC_C: Define leftovers - * which are stuck around for hysterical raisins only. No one has an - * idea what they were meant for. Don't use. - */ - unsigned int type; - - /** * @clock: * * Pixel clock in kHz. */ int clock; /* in kHz */ - int hdisplay; - int hsync_start; - int hsync_end; - int htotal; - int hskew; - int vdisplay; - int vsync_start; - int vsync_end; - int vtotal; - int vscan; + u16 hdisplay; + u16 hsync_start; + u16 hsync_end; + u16 htotal; + u16 hskew; + u16 vdisplay; + u16 vsync_start; + u16 vsync_end; + u16 vtotal; + u16 vscan; /** * @flags: * @@ -320,7 +300,37 @@ struct drm_display_mode { * - DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: frame split into left and * right parts. */ - unsigned int flags; + u32 flags; + + /** + * @crtc_clock: + * + * Actual pixel or dot clock in the hardware. This differs from the + * logical @clock when e.g. using interlacing, double-clocking, stereo + * modes or other fancy stuff that changes the timings and signals + * actually sent over the wire. + * + * This is again in kHz. + * + * Note that with digital outputs like HDMI or DP there's usually a + * massive confusion between the dot clock and the signal clock at the + * bit encoding level. Especially when a 8b/10b encoding is used and the + * difference is exactly a factor of 10. + */ + int crtc_clock; + u16 crtc_hdisplay; + u16 crtc_hblank_start; + u16 crtc_hblank_end; + u16 crtc_hsync_start; + u16 crtc_hsync_end; + u16 crtc_htotal; + u16 crtc_hskew; + u16 crtc_vdisplay; + u16 crtc_vblank_start; + u16 crtc_vblank_end; + u16 crtc_vsync_start; + u16 crtc_vsync_end; + u16 crtc_vtotal; /** * @width_mm: @@ -328,7 +338,7 @@ struct drm_display_mode { * Addressable size of the output in mm, projectors should set this to * 0. */ - int width_mm; + u16 width_mm; /** * @height_mm: @@ -336,74 +346,69 @@ struct drm_display_mode { * Addressable size of the output in mm, projectors should set this to * 0. */ - int height_mm; + u16 height_mm; /** - * @crtc_clock: + * @type: * - * Actual pixel or dot clock in the hardware. This differs from the - * logical @clock when e.g. using interlacing, double-clocking, stereo - * modes or other fancy stuff that changes the timings and signals - * actually sent over the wire. + * A bitmask of flags, mostly about the source of a mode. Possible flags + * are: * - * This is again in kHz. + * - DRM_MODE_TYPE_PREFERRED: Preferred mode, usually the native + * resolution of an LCD panel. There should only be one preferred + * mode per connector at any given time. + * - DRM_MODE_TYPE_DRIVER: Mode created by the driver, which is all of + * them really. Drivers must set this bit for all modes they create + * and expose to userspace. + * - DRM_MODE_TYPE_USERDEF: Mode defined or selected via the kernel + * command line. * - * Note that with digital outputs like HDMI or DP there's usually a - * massive confusion between the dot clock and the signal clock at the - * bit encoding level. Especially when a 8b/10b encoding is used and the - * difference is exactly a factor of 10. + * Plus a big list of flags which shouldn't be used at all, but are + * still around since these flags are also used in the userspace ABI. + * We no longer accept modes with these types though: + * + * - DRM_MODE_TYPE_BUILTIN: Meant for hard-coded modes, unused. + * Use DRM_MODE_TYPE_DRIVER instead. + * - DRM_MODE_TYPE_DEFAULT: Again a leftover, use + * DRM_MODE_TYPE_PREFERRED instead. + * - DRM_MODE_TYPE_CLOCK_C and DRM_MODE_TYPE_CRTC_C: Define leftovers + * which are stuck around for hysterical raisins only. No one has an + * idea what they were meant for. Don't use. */ - int crtc_clock; - int crtc_hdisplay; - int crtc_hblank_start; - int crtc_hblank_end; - int crtc_hsync_start; - int crtc_hsync_end; - int crtc_htotal; - int crtc_hskew; - int crtc_vdisplay; - int crtc_vblank_start; - int crtc_vblank_end; - int crtc_vsync_start; - int crtc_vsync_end; - int crtc_vtotal; + u8 type; /** - * @private: + * @expose_to_userspace: * - * Pointer for driver private data. This can only be used for mode - * objects passed to drivers in modeset operations. It shouldn't be used - * by atomic drivers since they can store any additional data by - * subclassing state structures. + * Indicates whether the mode is to be exposed to the userspace. + * This is to maintain a set of exposed modes while preparing + * user-mode's list in drm_mode_getconnector ioctl. The purpose of + * this only lies in the ioctl function, and is not to be used + * outside the function. */ - int *private; + bool expose_to_userspace; /** - * @private_flags: + * @head: * - * Similar to @private, but just an integer. + * struct list_head for mode lists. */ - int private_flags; + struct list_head head; /** - * @vrefresh: - * - * Vertical refresh rate, for debug output in human readable form. Not - * used in a functional way. + * @name: * - * This value is in Hz. + * Human-readable name of the mode, filled out with drm_mode_set_name(). */ - int vrefresh; + char name[DRM_DISPLAY_MODE_LEN]; /** - * @hsync: - * - * Horizontal refresh rate, for debug output in human readable form. Not - * used in a functional way. + * @status: * - * This value is in kHz. + * Status of the mode, used to filter out modes not supported by the + * hardware. See enum &drm_mode_status. */ - int hsync; + enum drm_mode_status status; /** * @picture_aspect_ratio: @@ -412,31 +417,19 @@ struct drm_display_mode { */ enum hdmi_picture_aspect picture_aspect_ratio; - /** - * @export_head: - * - * struct list_head for modes to be exposed to the userspace. - * This is to maintain a list of exposed modes while preparing - * user-mode's list in drm_mode_getconnector ioctl. The purpose of this - * list_head only lies in the ioctl function, and is not expected to be - * used outside the function. - * Once used, the stale pointers are not reset, but left as it is, to - * avoid overhead of protecting it by mode_config.mutex. - */ - struct list_head export_head; }; /** * DRM_MODE_FMT - printf string for &struct drm_display_mode */ -#define DRM_MODE_FMT "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x" +#define DRM_MODE_FMT "\"%s\": %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x" /** * DRM_MODE_ARG - printf arguments for &struct drm_display_mode * @m: display mode */ #define DRM_MODE_ARG(m) \ - (m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \ + (m)->name, drm_mode_vrefresh(m), (m)->clock, \ (m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \ (m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \ (m)->type, (m)->flags @@ -474,6 +467,25 @@ bool drm_mode_is_420_also(const struct drm_display_info *display, const struct drm_display_mode *mode); bool drm_mode_is_420(const struct drm_display_info *display, const struct drm_display_mode *mode); +void drm_set_preferred_mode(struct drm_connector *connector, + int hpref, int vpref); + +struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev, + enum drm_connector_tv_mode mode, + unsigned long pixel_clock_hz, + unsigned int hdisplay, + unsigned int vdisplay, + bool interlace); + +static inline struct drm_display_mode *drm_mode_analog_ntsc_480i(struct drm_device *dev) +{ + return drm_analog_tv_mode(dev, DRM_MODE_TV_MODE_NTSC, 13500000, 720, 480, true); +} + +static inline struct drm_display_mode *drm_mode_analog_pal_576i(struct drm_device *dev) +{ + return drm_analog_tv_mode(dev, DRM_MODE_TV_MODE_PAL, 13500000, 720, 576, true); +} struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, @@ -493,12 +505,29 @@ void drm_display_mode_from_videomode(const struct videomode *vm, void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, struct videomode *vm); void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags); + +#if defined(CONFIG_OF) int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode, u32 *bus_flags, int index); +int of_get_drm_panel_display_mode(struct device_node *np, + struct drm_display_mode *dmode, u32 *bus_flags); +#else +static inline int of_get_drm_display_mode(struct device_node *np, + struct drm_display_mode *dmode, + u32 *bus_flags, int index) +{ + return -EINVAL; +} + +static inline int of_get_drm_panel_display_mode(struct device_node *np, + struct drm_display_mode *dmode, u32 *bus_flags) +{ + return -EINVAL; +} +#endif void drm_mode_set_name(struct drm_display_mode *mode); -int drm_mode_hsync(const struct drm_display_mode *mode); int drm_mode_vrefresh(const struct drm_display_mode *mode); void drm_mode_get_hv_timing(const struct drm_display_mode *mode, int *hdisplay, int *vdisplay); @@ -507,6 +536,8 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src); +void drm_mode_init(struct drm_display_mode *dst, + const struct drm_display_mode *src); struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, const struct drm_display_mode *mode); bool drm_mode_match(const struct drm_display_mode *mode1, @@ -535,7 +566,7 @@ void drm_connector_list_update(struct drm_connector *connector); /* parsing cmdline modes */ bool drm_mode_parse_command_line_for_connector(const char *mode_option, - struct drm_connector *connector, + const struct drm_connector *connector, struct drm_cmdline_mode *mode); struct drm_display_mode * drm_mode_create_from_cmdline_mode(struct drm_device *dev, diff --git a/include/drm/drm_modeset_helper.h b/include/drm/drm_modeset_helper.h index efa337f03129..7e3d4c5a7f66 100644 --- a/include/drm/drm_modeset_helper.h +++ b/include/drm/drm_modeset_helper.h @@ -23,12 +23,18 @@ #ifndef __DRM_KMS_HELPER_H__ #define __DRM_KMS_HELPER_H__ -#include <drm/drmP.h> +struct drm_crtc; +struct drm_crtc_funcs; +struct drm_device; +struct drm_format_info; +struct drm_framebuffer; +struct drm_mode_fb_cmd2; void drm_helper_move_panel_connectors_to_head(struct drm_device *); void drm_helper_mode_fill_fb_struct(struct drm_device *dev, struct drm_framebuffer *fb, + const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd); int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 61142aa0ab23..fe32854b7ffe 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -48,13 +48,20 @@ * To make this clear all the helper vtables are pulled together in this location here. */ -enum mode_set_atomic; +struct drm_scanout_buffer; +struct drm_writeback_connector; +struct drm_writeback_job; + +enum mode_set_atomic { + LEAVE_ATOMIC_MODE_SET, + ENTER_ATOMIC_MODE_SET, +}; /** * struct drm_crtc_helper_funcs - helper operations for CRTCs * - * These hooks are used by the legacy CRTC helpers, the transitional plane - * helpers and the new atomic modesetting helpers. + * These hooks are used by the legacy CRTC helpers and the new atomic + * modesetting helpers. */ struct drm_crtc_helper_funcs { /** @@ -128,7 +135,7 @@ struct drm_crtc_helper_funcs { * Since this function is both called from the check phase of an atomic * commit, and the mode validation in the probe paths it is not allowed * to look at anything else but the passed-in mode, and validate it - * against configuration-invariant hardward constraints. Any further + * against configuration-invariant hardware constraints. Any further * limits which depend upon the configuration can only be checked in * @mode_fixup or @atomic_check. * @@ -210,9 +217,7 @@ struct drm_crtc_helper_funcs { * * This callback is used to update the display mode of a CRTC without * changing anything of the primary plane configuration. This fits the - * requirement of atomic and hence is used by the atomic helpers. It is - * also used by the transitional plane helpers to implement a - * @mode_set hook in drm_helper_crtc_mode_set(). + * requirement of atomic and hence is used by the atomic helpers. * * Note that the display pipe is completely off when this function is * called. Atomic drivers which need hardware to be running before they @@ -327,15 +332,14 @@ struct drm_crtc_helper_funcs { * all updated. Again the recommendation is to just call check helpers * until a maximal configuration is reached. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. * * NOTE: * * This function is called in the check phase of an atomic update. The * driver is not allowed to change anything outside of the free-standing - * state objects passed-in or assembled in the overall &drm_atomic_state - * update tracking structure. + * state object passed-in. * * Also beware that userspace can request its own custom modes, neither * core nor helpers filter modes to the list of probe modes reported by @@ -351,7 +355,7 @@ struct drm_crtc_helper_funcs { * deadlock. */ int (*atomic_check)(struct drm_crtc *crtc, - struct drm_crtc_state *state); + struct drm_atomic_state *state); /** * @atomic_begin: @@ -368,11 +372,11 @@ struct drm_crtc_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. */ void (*atomic_begin)(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state); + struct drm_atomic_state *state); /** * @atomic_flush: * @@ -392,11 +396,11 @@ struct drm_crtc_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. */ void (*atomic_flush)(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state); + struct drm_atomic_state *state); /** * @atomic_enable: @@ -415,12 +419,10 @@ struct drm_crtc_helper_funcs { * @atomic_enable must be the inverse of @atomic_disable for atomic * drivers. * - * Drivers can use the @old_crtc_state input parameter if the operations - * needed to enable the CRTC don't depend solely on the new state but - * also on the transition between the old state and the new state. + * This function is optional. */ void (*atomic_enable)(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state); + struct drm_atomic_state *state); /** * @atomic_disable: @@ -437,13 +439,69 @@ struct drm_crtc_helper_funcs { * need to implement it if there's no need to disable anything at the * CRTC level. * - * Comparing to @disable, this one provides the additional input - * parameter @old_crtc_state which could be used to access the old - * state. Atomic drivers should consider to use this one instead - * of @disable. + * This function is optional. */ void (*atomic_disable)(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state); + struct drm_atomic_state *state); + + /** + * @get_scanout_position: + * + * Called by vblank timestamping code. + * + * Returns the current display scanout position from a CRTC and an + * optional accurate ktime_get() timestamp of when the position was + * measured. Note that this is a helper callback which is only used + * if a driver uses drm_crtc_vblank_helper_get_vblank_timestamp() + * for the @drm_crtc_funcs.get_vblank_timestamp callback. + * + * Parameters: + * + * crtc: + * The CRTC. + * in_vblank_irq: + * True when called from drm_crtc_handle_vblank(). Some drivers + * need to apply some workarounds for gpu-specific vblank irq + * quirks if the flag is set. + * vpos: + * Target location for current vertical scanout position. + * hpos: + * Target location for current horizontal scanout position. + * stime: + * Target location for timestamp taken immediately before + * scanout position query. Can be NULL to skip timestamp. + * etime: + * Target location for timestamp taken immediately after + * scanout position query. Can be NULL to skip timestamp. + * mode: + * Current display timings. + * + * Returns vpos as a positive number while in active scanout area. + * Returns vpos as a negative number inside vblank, counting the number + * of scanlines to go until end of vblank, e.g., -1 means "one scanline + * until start of active scanout / end of vblank." + * + * Returns: + * + * True on success, false if a reliable scanout position counter could + * not be read out. + */ + bool (*get_scanout_position)(struct drm_crtc *crtc, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); + + /** + * @handle_vblank_timeout: Handles timeouts of the vblank timer. + * + * Called by CRTC's the vblank timer on each timeout. Semantics is + * equivalient to drm_crtc_handle_vblank(). Implementations should + * invoke drm_crtc_handle_vblank() as part of processing the timeout. + * + * This callback is optional. If unset, the vblank timer invokes + * drm_crtc_handle_vblank() directly. + */ + bool (*handle_vblank_timeout)(struct drm_crtc *crtc); }; /** @@ -460,8 +518,8 @@ static inline void drm_crtc_helper_add(struct drm_crtc *crtc, /** * struct drm_encoder_helper_funcs - helper operations for encoders * - * These hooks are used by the legacy CRTC helpers, the transitional plane - * helpers and the new atomic modesetting helpers. + * These hooks are used by the legacy CRTC helpers and the new atomic + * modesetting helpers. */ struct drm_encoder_helper_funcs { /** @@ -505,7 +563,7 @@ struct drm_encoder_helper_funcs { * Since this function is both called from the check phase of an atomic * commit, and the mode validation in the probe paths it is not allowed * to look at anything else but the passed-in mode, and validate it - * against configuration-invariant hardward constraints. Any further + * against configuration-invariant hardware constraints. Any further * limits which depend upon the configuration can only be checked in * @mode_fixup or @atomic_check. * @@ -640,22 +698,6 @@ struct drm_encoder_helper_funcs { struct drm_connector_state *conn_state); /** - * @get_crtc: - * - * This callback is used by the legacy CRTC helpers to work around - * deficiencies in its own book-keeping. - * - * Do not use, use atomic helpers instead, which get the book keeping - * right. - * - * FIXME: - * - * Currently only nouveau is using this, and as soon as nouveau is - * atomic we can ditch this hook. - */ - struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder); - - /** * @detect: * * This callback can be used by drivers who want to do detection on the @@ -674,6 +716,52 @@ struct drm_encoder_helper_funcs { struct drm_connector *connector); /** + * @atomic_disable: + * + * This callback should be used to disable the encoder. With the atomic + * drivers it is called before this encoder's CRTC has been shut off + * using their own &drm_crtc_helper_funcs.atomic_disable hook. If that + * sequence is too simple drivers can just add their own driver private + * encoder hooks and call them from CRTC's callback by looping over all + * encoders connected to it using for_each_encoder_on_crtc(). + * + * This callback is a variant of @disable that provides the atomic state + * to the driver. If @atomic_disable is implemented, @disable is not + * called by the helpers. + * + * This hook is only used by atomic helpers. Atomic drivers don't need + * to implement it if there's no need to disable anything at the encoder + * level. To ensure that runtime PM handling (using either DPMS or the + * new "ACTIVE" property) works @atomic_disable must be the inverse of + * @atomic_enable. + */ + void (*atomic_disable)(struct drm_encoder *encoder, + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * This callback should be used to enable the encoder. It is called + * after this encoder's CRTC has been enabled using their own + * &drm_crtc_helper_funcs.atomic_enable hook. If that sequence is + * too simple drivers can just add their own driver private encoder + * hooks and call them from CRTC's callback by looping over all encoders + * connected to it using for_each_encoder_on_crtc(). + * + * This callback is a variant of @enable that provides the atomic state + * to the driver. If @atomic_enable is implemented, @enable is not + * called by the helpers. + * + * This hook is only used by atomic helpers, it is the opposite of + * @atomic_disable. Atomic drivers don't need to implement it if there's + * no need to enable anything at the encoder level. To ensure that + * runtime PM handling works @atomic_enable must be the inverse of + * @atomic_disable. + */ + void (*atomic_enable)(struct drm_encoder *encoder, + struct drm_atomic_state *state); + + /** * @disable: * * This callback should be used to disable the encoder. With the atomic @@ -689,6 +777,9 @@ struct drm_encoder_helper_funcs { * handling (using either DPMS or the new "ACTIVE" property) works * @disable must be the inverse of @enable for atomic drivers. * + * For atomic drivers also consider @atomic_disable and save yourself + * from having to read the NOTE below! + * * NOTE: * * With legacy CRTC helpers there's a big semantic difference between @@ -713,11 +804,11 @@ struct drm_encoder_helper_funcs { * hooks and call them from CRTC's callback by looping over all encoders * connected to it using for_each_encoder_on_crtc(). * - * This hook is used only by atomic helpers, for symmetry with @disable. - * Atomic drivers don't need to implement it if there's no need to - * enable anything at the encoder level. To ensure that runtime PM handling - * (using either DPMS or the new "ACTIVE" property) works - * @enable must be the inverse of @disable for atomic drivers. + * This hook is only used by atomic helpers, it is the opposite of + * @disable. Atomic drivers don't need to implement it if there's no + * need to enable anything at the encoder level. To ensure that + * runtime PM handling (using either DPMS or the new "ACTIVE" property) + * works @enable must be the inverse of @disable for atomic drivers. */ void (*enable)(struct drm_encoder *encoder); @@ -790,13 +881,19 @@ struct drm_connector_helper_funcs { * The usual way to implement this is to cache the EDID retrieved in the * probe callback somewhere in the driver-private connector structure. * In this function drivers then parse the modes in the EDID and add - * them by calling drm_add_edid_modes(). But connectors that driver a + * them by calling drm_add_edid_modes(). But connectors that drive a * fixed panel can also manually add specific modes using * drm_mode_probed_add(). Drivers which manually add modes should also * make sure that the &drm_connector.display_info, * &drm_connector.width_mm and &drm_connector.height_mm fields are * filled in. * + * Note that the caller function will automatically add standard VESA + * DMT modes up to 1024x768 if the .get_modes() helper operation returns + * no mode and if the connector status is connector_status_connected or + * connector_status_unknown. There is no need to call + * drm_add_modes_noedid() manually in that case. + * * Virtual drivers that just want some standard VESA mode with a given * resolution can call drm_add_modes_noedid(), and mark the preferred * one using drm_set_preferred_mode(). @@ -814,7 +911,8 @@ struct drm_connector_helper_funcs { * * RETURNS: * - * The number of modes added by calling drm_mode_probed_add(). + * The number of modes added by calling drm_mode_probed_add(). Return 0 + * on failures (no modes) instead of negative error codes. */ int (*get_modes)(struct drm_connector *connector); @@ -881,7 +979,49 @@ struct drm_connector_helper_funcs { * drm_mode_status. */ enum drm_mode_status (*mode_valid)(struct drm_connector *connector, - struct drm_display_mode *mode); + const struct drm_display_mode *mode); + + /** + * @mode_valid_ctx: + * + * Callback to validate a mode for a connector, irrespective of the + * specific display configuration. + * + * This callback is used by the probe helpers to filter the mode list + * (which is usually derived from the EDID data block from the sink). + * See e.g. drm_helper_probe_single_connector_modes(). + * + * This function is optional, and is the atomic version of + * &drm_connector_helper_funcs.mode_valid. + * + * To allow for accessing the atomic state of modesetting objects, the + * helper libraries always call this with ctx set to a valid context, + * and &drm_mode_config.connection_mutex will always be locked with + * the ctx parameter set to @ctx. This allows for taking additional + * locks as required. + * + * Even though additional locks may be acquired, this callback is + * still expected not to take any constraints into account which would + * be influenced by the currently set display state - such constraints + * should be handled in the driver's atomic check. For example, if a + * connector shares display bandwidth with other connectors then it + * would be ok to validate the minimum bandwidth requirement of a mode + * against the maximum possible bandwidth of the connector. But it + * wouldn't be ok to take the current bandwidth usage of other + * connectors into account, as this would change depending on the + * display state. + * + * Returns: + * 0 if &drm_connector_helper_funcs.mode_valid_ctx succeeded and wrote + * the &enum drm_mode_status value to @status, or a negative error + * code otherwise. + * + */ + int (*mode_valid_ctx)(struct drm_connector *connector, + const struct drm_display_mode *mode, + struct drm_modeset_acquire_ctx *ctx, + enum drm_mode_status *status); + /** * @best_encoder: * @@ -900,9 +1040,8 @@ struct drm_connector_helper_funcs { * @atomic_best_encoder. * * You can leave this function to NULL if the connector is only - * attached to a single encoder and you are using the atomic helpers. - * In this case, the core will call drm_atomic_helper_best_encoder() - * for you. + * attached to a single encoder. In this case, the core will call + * drm_connector_get_single_encoder() for you. * * RETURNS: * @@ -922,14 +1061,13 @@ struct drm_connector_helper_funcs { * * This function is used by drm_atomic_helper_check_modeset(). * If it is not implemented, the core will fallback to @best_encoder - * (or drm_atomic_helper_best_encoder() if @best_encoder is NULL). + * (or drm_connector_get_single_encoder() if @best_encoder is NULL). * * NOTE: * * This function is called in the check phase of an atomic update. The - * driver is not allowed to change anything outside of the free-standing - * state objects passed-in or assembled in the overall &drm_atomic_state - * update tracking structure. + * driver is not allowed to change anything outside of the + * &drm_atomic_state update tracking structure passed in. * * RETURNS: * @@ -939,7 +1077,7 @@ struct drm_connector_helper_funcs { * for this. */ struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector, - struct drm_connector_state *connector_state); + struct drm_atomic_state *state); /** * @atomic_check: @@ -973,22 +1111,86 @@ struct drm_connector_helper_funcs { * deadlock. */ int (*atomic_check)(struct drm_connector *connector, - struct drm_connector_state *state); + struct drm_atomic_state *state); /** * @atomic_commit: * * This hook is to be used by drivers implementing writeback connectors * that need a point when to commit the writeback job to the hardware. - * The writeback_job to commit is available in - * &drm_connector_state.writeback_job. + * The writeback_job to commit is available in the new connector state, + * in &drm_connector_state.writeback_job. * * This hook is optional. * * This callback is used by the atomic modeset helpers. */ void (*atomic_commit)(struct drm_connector *connector, - struct drm_connector_state *state); + struct drm_atomic_state *state); + + /** + * @prepare_writeback_job: + * + * As writeback jobs contain a framebuffer, drivers may need to + * prepare and clean them up the same way they can prepare and + * clean up framebuffers for planes. This optional connector operation + * is used to support the preparation of writeback jobs. The job + * prepare operation is called from drm_atomic_helper_prepare_planes() + * for struct &drm_writeback_connector connectors only. + * + * This operation is optional. + * + * This callback is used by the atomic modeset helpers. + */ + int (*prepare_writeback_job)(struct drm_writeback_connector *connector, + struct drm_writeback_job *job); + /** + * @cleanup_writeback_job: + * + * This optional connector operation is used to support the + * cleanup of writeback jobs. The job cleanup operation is called + * from the existing drm_writeback_cleanup_job() function, invoked + * both when destroying the job as part of an aborted commit, or when + * the job completes. + * + * This operation is optional. + * + * This callback is used by the atomic modeset helpers. + */ + void (*cleanup_writeback_job)(struct drm_writeback_connector *connector, + struct drm_writeback_job *job); + + /** + * @enable_hpd: + * + * Enable hot-plug detection for the connector. + * + * This operation is optional. + * + * This callback is used by the drm_kms_helper_poll_enable() helpers. + * + * This operation does not need to perform any hpd state tracking as + * the DRM core handles that maintenance and ensures the calls to enable + * and disable hpd are balanced. + * + */ + void (*enable_hpd)(struct drm_connector *connector); + + /** + * @disable_hpd: + * + * Disable hot-plug detection for the connector. + * + * This operation is optional. + * + * This callback is used by the drm_kms_helper_poll_disable() helpers. + * + * This operation does not need to perform any hpd state tracking as + * the DRM core handles that maintenance and ensures the calls to enable + * and disable hpd are balanced. + * + */ + void (*disable_hpd)(struct drm_connector *connector); }; /** @@ -1005,15 +1207,14 @@ static inline void drm_connector_helper_add(struct drm_connector *connector, /** * struct drm_plane_helper_funcs - helper operations for planes * - * These functions are used by the atomic helpers and by the transitional plane - * helpers. + * These functions are used by the atomic helpers. */ struct drm_plane_helper_funcs { /** * @prepare_fb: * * This hook is to prepare a framebuffer for scanout by e.g. pinning - * it's backing storage or relocating it into a contiguous block of + * its backing storage or relocating it into a contiguous block of * VRAM. Other possible preparatory work includes flushing caches. * * This function must not block for outstanding rendering, since it is @@ -1024,14 +1225,25 @@ struct drm_plane_helper_funcs { * equivalent functionality should be implemented through private * members in the plane structure. * - * Drivers which always have their buffers pinned should use - * drm_gem_fb_prepare_fb() for this hook. + * For GEM drivers who neither have a @prepare_fb nor @cleanup_fb hook + * set drm_gem_plane_helper_prepare_fb() is called automatically to + * implement this. Other drivers which need additional plane processing + * can call drm_gem_plane_helper_prepare_fb() from their @prepare_fb + * hook. + * + * The resources acquired in @prepare_fb persist after the end of + * the atomic commit. Resources that can be release at the commit's end + * should be acquired in @begin_fb_access and released in @end_fb_access. + * For example, a GEM buffer's pin operation belongs into @prepare_fb to + * keep the buffer pinned after the commit. But a vmap operation for + * shadow-plane helpers belongs into @begin_fb_access, so that atomic + * helpers remove the mapping at the end of the commit. * * The helpers will call @cleanup_fb with matching arguments for every * successful call to this hook. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. See @begin_fb_access for preparing per-commit resources. * * RETURNS: * @@ -1048,13 +1260,43 @@ struct drm_plane_helper_funcs { * This hook is called to clean up any resources allocated for the given * framebuffer and plane configuration in @prepare_fb. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. */ void (*cleanup_fb)(struct drm_plane *plane, struct drm_plane_state *old_state); /** + * @begin_fb_access: + * + * This hook prepares the plane for access during an atomic commit. + * In contrast to @prepare_fb, resources acquired in @begin_fb_access, + * are released at the end of the atomic commit in @end_fb_access. + * + * For example, with shadow-plane helpers, the GEM buffer's vmap + * operation belongs into @begin_fb_access, so that the buffer's + * memory will be unmapped at the end of the commit in @end_fb_access. + * But a GEM buffer's pin operation belongs into @prepare_fb + * to keep the buffer pinned after the commit. + * + * The callback is used by the atomic modeset helpers, but it is optional. + * See @end_fb_cleanup for undoing the effects of @begin_fb_access and + * @prepare_fb for acquiring resources until the next pageflip. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ + int (*begin_fb_access)(struct drm_plane *plane, struct drm_plane_state *new_plane_state); + + /** + * @end_fb_access: + * + * This hook cleans up resources allocated by @begin_fb_access. It it called + * at the end of a commit for the new plane state. + */ + void (*end_fb_access)(struct drm_plane *plane, struct drm_plane_state *new_plane_state); + + /** * @atomic_check: * * Drivers should check plane specific constraints in this hook. @@ -1073,15 +1315,14 @@ struct drm_plane_helper_funcs { * all updated. Again the recommendation is to just call check helpers * until a maximal configuration is reached. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. * * NOTE: * * This function is called in the check phase of an atomic update. The - * driver is not allowed to change anything outside of the free-standing - * state objects passed-in or assembled in the overall &drm_atomic_state - * update tracking structure. + * driver is not allowed to change anything outside of the + * &drm_atomic_state update tracking structure. * * RETURNS: * @@ -1091,7 +1332,7 @@ struct drm_plane_helper_funcs { * deadlock. */ int (*atomic_check)(struct drm_plane *plane, - struct drm_plane_state *state); + struct drm_atomic_state *state); /** * @atomic_update: @@ -1105,11 +1346,36 @@ struct drm_plane_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is optional. */ void (*atomic_update)(struct drm_plane *plane, - struct drm_plane_state *old_state); + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * Drivers should use this function to unconditionally enable a plane. + * This hook is called in-between the &drm_crtc_helper_funcs.atomic_begin + * and drm_crtc_helper_funcs.atomic_flush callbacks. It is called after + * @atomic_update, which will be called for all enabled planes. Drivers + * that use @atomic_enable should set up a plane in @atomic_update and + * afterwards enable the plane in @atomic_enable. If a plane needs to be + * enabled before installing the scanout buffer, drivers can still do + * so in @atomic_update. + * + * Note that the power state of the display pipe when this function is + * called depends upon the exact helpers and calling sequence the driver + * has picked. See drm_atomic_helper_commit_planes() for a discussion of + * the tradeoffs and variants of plane commit helpers. + * + * This callback is used by the atomic modeset helpers, but it is + * optional. If implemented, @atomic_enable should be the inverse of + * @atomic_disable. Drivers that don't want to use either can still + * implement the complete plane update in @atomic_update. + */ + void (*atomic_enable)(struct drm_plane *plane, + struct drm_atomic_state *state); + /** * @atomic_disable: * @@ -1129,30 +1395,35 @@ struct drm_plane_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. It's intended to reverse the effects of @atomic_enable. */ void (*atomic_disable)(struct drm_plane *plane, - struct drm_plane_state *old_state); + struct drm_atomic_state *state); /** * @atomic_async_check: * - * Drivers should set this function pointer to check if the plane state - * can be updated in a async fashion. Here async means "not vblank - * synchronized". + * Drivers should set this function pointer to check if the plane's + * atomic state can be updated in a async fashion. Here async means + * "not vblank synchronized". * * This hook is called by drm_atomic_async_check() to establish if a * given update can be committed asynchronously, that is, if it can * jump ahead of the state currently queued for update. * + * This function is also used by drm_atomic_set_property() to determine + * if the plane can be flipped in async. The flip flag is used to + * distinguish if the function is used for just the plane state or for a + * flip. + * * RETURNS: * * Return 0 on success and any error returned indicates that the update * can not be applied in asynchronous manner. */ int (*atomic_async_check)(struct drm_plane *plane, - struct drm_plane_state *state); + struct drm_atomic_state *state, bool flip); /** * @atomic_async_update: @@ -1168,11 +1439,17 @@ struct drm_plane_helper_funcs { * update won't happen if there is an outstanding commit modifying * the same plane. * - * Note that unlike &drm_plane_helper_funcs.atomic_update this hook - * takes the new &drm_plane_state as parameter. When doing async_update - * drivers shouldn't replace the &drm_plane_state but update the - * current one with the new plane configurations in the new - * plane_state. + * When doing async_update drivers shouldn't replace the + * &drm_plane_state but update the current one with the new plane + * configurations in the new plane_state. + * + * Drivers should also swap the framebuffers between current plane + * state (&drm_plane.state) and new_state. + * This is required since cleanup for async commits is performed on + * the new state, rather than old state like for traditional commits. + * Since we want to give up the reference on the current (old) fb + * instead of our brand new one, swap them in the driver during the + * async commit. * * FIXME: * - It only works for single plane updates @@ -1183,7 +1460,45 @@ struct drm_plane_helper_funcs { * for deferring if needed, until a common solution is created. */ void (*atomic_async_update)(struct drm_plane *plane, - struct drm_plane_state *new_state); + struct drm_atomic_state *state); + + /** + * @get_scanout_buffer: + * + * Get the current scanout buffer, to display a message with drm_panic. + * The driver should do the minimum changes to provide a buffer, + * that can be used to display the panic screen. Currently only linear + * buffers are supported. Non-linear buffer support is on the TODO list. + * The device &dev.mode_config.panic_lock is taken before calling this + * function, so you can safely access the &plane.state + * It is called from a panic callback, and must follow its restrictions. + * Please look the documentation at drm_panic_trylock() for an in-depth + * discussions of what's safe and what is not allowed. + * It's a best effort mode, so it's expected that in some complex cases + * the panic screen won't be displayed. + * The returned &drm_scanout_buffer.map must be valid if no error code is + * returned. + * + * Return: + * %0 on success, negative errno on failure. + */ + int (*get_scanout_buffer)(struct drm_plane *plane, + struct drm_scanout_buffer *sb); + + /** + * @panic_flush: + * + * It is used by drm_panic, and is called after the panic screen is + * drawn to the scanout buffer. In this function, the driver + * can send additional commands to the hardware, to make the scanout + * buffer visible. + * It is only called if get_scanout_buffer() returned successfully, and + * the &dev.mode_config.panic_lock is held during the entire sequence. + * It is called from a panic callback, and must follow its restrictions. + * Please look the documentation at drm_panic_trylock() for an in-depth + * discussions of what's safe and what is not allowed. + */ + void (*panic_flush)(struct drm_plane *plane); }; /** @@ -1216,13 +1531,13 @@ struct drm_mode_config_helper_funcs { * swapped into the various state pointers. The passed in state * therefore contains copies of the old/previous state. This hook should * commit the new state into hardware. Note that the helpers have - * already waited for preceeding atomic commits and fences, but drivers + * already waited for preceding atomic commits and fences, but drivers * can add more waiting calls at the start of their implementation, e.g. * to wait for driver-internal request for implicit syncing, before * starting to commit the update to the hardware. * * After the atomic update is committed to the hardware this hook needs - * to call drm_atomic_helper_commit_hw_done(). Then wait for the upate + * to call drm_atomic_helper_commit_hw_done(). Then wait for the update * to be executed by the hardware, for example using * drm_atomic_helper_wait_for_vblanks() or * drm_atomic_helper_wait_for_flip_done(), and then clean up the old @@ -1239,6 +1554,27 @@ struct drm_mode_config_helper_funcs { * drm_atomic_helper_commit_tail(). */ void (*atomic_commit_tail)(struct drm_atomic_state *state); + + /** + * @atomic_commit_setup: + * + * This hook is used by the default atomic_commit() hook implemented in + * drm_atomic_helper_commit() together with the nonblocking helpers (see + * drm_atomic_helper_setup_commit()) to extend the DRM commit setup. It + * is not used by the atomic helpers. + * + * This function is called at the end of + * drm_atomic_helper_setup_commit(), so once the commit has been + * properly setup across the generic DRM object states. It allows + * drivers to do some additional commit tracking that isn't related to a + * CRTC, plane or connector, tracked in a &drm_private_obj structure. + * + * Note that the documentation of &drm_private_obj has more details on + * how one should implement this. + * + * This hook is optional. + */ + int (*atomic_commit_setup)(struct drm_atomic_state *state); }; #endif diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index a308f2d6496f..ec4f543c3d95 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -24,6 +24,8 @@ #ifndef DRM_MODESET_LOCK_H_ #define DRM_MODESET_LOCK_H_ +#include <linux/types.h> /* stackdepot.h is not self-contained */ +#include <linux/stackdepot.h> #include <linux/ww_mutex.h> struct drm_modeset_lock; @@ -32,6 +34,7 @@ struct drm_modeset_lock; * struct drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx) * @ww_ctx: base acquire ctx * @contended: used internally for -EDEADLK handling + * @stack_depot: used internally for contention debugging * @locked: list of held locks * @trylock_only: trylock mode used in atomic contexts/panic notifiers * @interruptible: whether interruptible locking should be used. @@ -52,6 +55,12 @@ struct drm_modeset_acquire_ctx { struct drm_modeset_lock *contended; /* + * Stack depot for debugging when a contended lock was not backed off + * from. + */ + depot_stack_handle_t stack_depot; + + /* * list of held locks (drm_modeset_lock) */ struct list_head locked; @@ -68,7 +77,7 @@ struct drm_modeset_acquire_ctx { /** * struct drm_modeset_lock - used for locking modeset resources. * @mutex: resource locking - * @head: used to hold it's place on &drm_atomi_state.locked list when + * @head: used to hold its place on &drm_atomi_state.locked list when * part of an atomic update * * Used for locking CRTCs and other modeset resources. @@ -114,6 +123,15 @@ static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock) return ww_mutex_is_locked(&lock->mutex); } +/** + * drm_modeset_lock_assert_held - equivalent to lockdep_assert_held() + * @lock: lock to check + */ +static inline void drm_modeset_lock_assert_held(struct drm_modeset_lock *lock) +{ + lockdep_assert_held(&lock->mutex.base); +} + int drm_modeset_lock(struct drm_modeset_lock *lock, struct drm_modeset_acquire_ctx *ctx); int __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock); @@ -155,6 +173,8 @@ int drm_modeset_lock_all_ctx(struct drm_device *dev, * is 0, so no error checking is necessary */ #define DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, flags, ret) \ + if (!drm_drv_uses_atomic_modeset(dev)) \ + mutex_lock(&dev->mode_config.mutex); \ drm_modeset_acquire_init(&ctx, flags); \ modeset_lock_retry: \ ret = drm_modeset_lock_all_ctx(dev, &ctx); \ @@ -163,6 +183,7 @@ modeset_lock_retry: \ /** * DRM_MODESET_LOCK_ALL_END - Helper to release and cleanup modeset locks + * @dev: drm device * @ctx: local modeset acquire context, will be dereferenced * @ret: local ret/err/etc variable to track error status * @@ -179,7 +200,7 @@ modeset_lock_retry: \ * to that failure. In both of these cases the code between BEGIN/END will not * be run, so the failure will reflect the inability to grab the locks. */ -#define DRM_MODESET_LOCK_ALL_END(ctx, ret) \ +#define DRM_MODESET_LOCK_ALL_END(dev, ctx, ret) \ modeset_lock_fail: \ if (ret == -EDEADLK) { \ ret = drm_modeset_backoff(&ctx); \ @@ -187,6 +208,8 @@ modeset_lock_fail: \ goto modeset_lock_retry; \ } \ drm_modeset_drop_locks(&ctx); \ - drm_modeset_acquire_fini(&ctx); + drm_modeset_acquire_fini(&ctx); \ + if (!drm_drv_uses_atomic_modeset(dev)) \ + mutex_unlock(&dev->mode_config.mutex); #endif /* DRM_MODESET_LOCK_H_ */ diff --git a/include/drm/drm_module.h b/include/drm/drm_module.h new file mode 100644 index 000000000000..4db1ae03d9a5 --- /dev/null +++ b/include/drm/drm_module.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_MODULE_H +#define DRM_MODULE_H + +#include <linux/pci.h> +#include <linux/platform_device.h> + +#include <drm/drm_drv.h> + +/** + * DOC: overview + * + * This library provides helpers registering DRM drivers during module + * initialization and shutdown. The provided helpers act like bus-specific + * module helpers, such as module_pci_driver(), but respect additional + * parameters that control DRM driver registration. + * + * Below is an example of initializing a DRM driver for a device on the + * PCI bus. + * + * .. code-block:: c + * + * struct pci_driver my_pci_drv = { + * }; + * + * drm_module_pci_driver(my_pci_drv); + * + * The generated code will test if DRM drivers are enabled and register + * the PCI driver my_pci_drv. For more complex module initialization, you + * can still use module_init() and module_exit() in your driver. + */ + +/* + * PCI drivers + */ + +static inline int __init drm_pci_register_driver(struct pci_driver *pci_drv) +{ + if (drm_firmware_drivers_only()) + return -ENODEV; + + return pci_register_driver(pci_drv); +} + +/** + * drm_module_pci_driver - Register a DRM driver for PCI-based devices + * @__pci_drv: the PCI driver structure + * + * Registers a DRM driver for devices on the PCI bus. The helper + * macro behaves like module_pci_driver() but tests the state of + * drm_firmware_drivers_only(). For more complex module initialization, + * use module_init() and module_exit() directly. + * + * Each module may only use this macro once. Calling it replaces + * module_init() and module_exit(). + */ +#define drm_module_pci_driver(__pci_drv) \ + module_driver(__pci_drv, drm_pci_register_driver, pci_unregister_driver) + +static inline int __init +drm_pci_register_driver_if_modeset(struct pci_driver *pci_drv, int modeset) +{ + if (drm_firmware_drivers_only() && modeset == -1) + return -ENODEV; + if (modeset == 0) + return -ENODEV; + + return pci_register_driver(pci_drv); +} + +static inline void __exit +drm_pci_unregister_driver_if_modeset(struct pci_driver *pci_drv, int modeset) +{ + pci_unregister_driver(pci_drv); +} + +/** + * drm_module_pci_driver_if_modeset - Register a DRM driver for PCI-based devices + * @__pci_drv: the PCI driver structure + * @__modeset: an additional parameter that disables the driver + * + * This macro is deprecated and only provided for existing drivers. For + * new drivers, use drm_module_pci_driver(). + * + * Registers a DRM driver for devices on the PCI bus. The helper macro + * behaves like drm_module_pci_driver() with an additional driver-specific + * flag. If __modeset is 0, the driver has been disabled, if __modeset is + * -1 the driver state depends on the global DRM state. For all other + * values, the PCI driver has been enabled. The default should be -1. + */ +#define drm_module_pci_driver_if_modeset(__pci_drv, __modeset) \ + module_driver(__pci_drv, drm_pci_register_driver_if_modeset, \ + drm_pci_unregister_driver_if_modeset, __modeset) + +/* + * Platform drivers + */ + +static inline int __init +drm_platform_driver_register(struct platform_driver *platform_drv) +{ + if (drm_firmware_drivers_only()) + return -ENODEV; + + return platform_driver_register(platform_drv); +} + +/** + * drm_module_platform_driver - Register a DRM driver for platform devices + * @__platform_drv: the platform driver structure + * + * Registers a DRM driver for devices on the platform bus. The helper + * macro behaves like module_platform_driver() but tests the state of + * drm_firmware_drivers_only(). For more complex module initialization, + * use module_init() and module_exit() directly. + * + * Each module may only use this macro once. Calling it replaces + * module_init() and module_exit(). + */ +#define drm_module_platform_driver(__platform_drv) \ + module_driver(__platform_drv, drm_platform_driver_register, \ + platform_driver_unregister) + +#endif diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index ead34ab5ca4e..7f0256dae3f1 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -2,6 +2,7 @@ #ifndef __DRM_OF_H__ #define __DRM_OF_H__ +#include <linux/err.h> #include <linux/of_graph.h> #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) #include <drm/drm_bridge.h> @@ -15,6 +16,20 @@ struct drm_encoder; struct drm_panel; struct drm_bridge; struct device_node; +struct mipi_dsi_device_info; +struct mipi_dsi_host; + +/** + * enum drm_lvds_dual_link_pixels - Pixel order of an LVDS dual-link connection + * @DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated + * from the first port, odd pixels from the second port + * @DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated + * from the first port, even pixels from the second port + */ +enum drm_lvds_dual_link_pixels { + DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 0, + DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 1, +}; #ifdef CONFIG_OF uint32_t drm_of_crtc_port_mask(struct drm_device *dev, @@ -35,6 +50,17 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, int port, int endpoint, struct drm_panel **panel, struct drm_bridge **bridge); +int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, + const struct device_node *port2); +int drm_of_lvds_get_dual_link_pixel_order_sink(struct device_node *port1, + struct device_node *port2); +int drm_of_lvds_get_data_mapping(const struct device_node *port); +int drm_of_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, const unsigned int max); +int drm_of_get_data_lanes_count_ep(const struct device_node *port, + int port_reg, int reg, + const unsigned int min, + const unsigned int max); #else static inline uint32_t drm_of_crtc_port_mask(struct drm_device *dev, struct device_node *port) @@ -77,8 +103,54 @@ static inline int drm_of_find_panel_or_bridge(const struct device_node *np, { return -EINVAL; } + +static inline int +drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, + const struct device_node *port2) +{ + return -EINVAL; +} + +static inline int +drm_of_lvds_get_dual_link_pixel_order_sink(struct device_node *port1, + struct device_node *port2) +{ + return -EINVAL; +} + +static inline int +drm_of_lvds_get_data_mapping(const struct device_node *port) +{ + return -EINVAL; +} + +static inline int +drm_of_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, const unsigned int max) +{ + return -EINVAL; +} + +static inline int +drm_of_get_data_lanes_count_ep(const struct device_node *port, + int port_reg, int reg, + const unsigned int min, + const unsigned int max) +{ + return -EINVAL; +} #endif +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_MIPI_DSI) +struct mipi_dsi_host *drm_of_get_dsi_bus(struct device *dev); +#else +static inline struct +mipi_dsi_host *drm_of_get_dsi_bus(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif /* CONFIG_OF && CONFIG_DRM_MIPI_DSI */ + /* * drm_of_panel_bridge_remove - remove panel bridge * @np: device tree node containing panel bridge output ports diff --git a/include/drm/drm_os_linux.h b/include/drm/drm_os_linux.h deleted file mode 100644 index ee8d61b64f29..000000000000 --- a/include/drm/drm_os_linux.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/** - * \file drm_os_linux.h - * OS abstraction macros. - */ - -#include <linux/interrupt.h> /* For task queue support */ -#include <linux/sched/signal.h> -#include <linux/delay.h> -#include <linux/io-64-nonatomic-lo-hi.h> - -/** Current process ID */ -#define DRM_CURRENTPID task_pid_nr(current) -#define DRM_UDELAY(d) udelay(d) -/** Read a byte from a MMIO region */ -#define DRM_READ8(map, offset) readb(((void __iomem *)(map)->handle) + (offset)) -/** Read a word from a MMIO region */ -#define DRM_READ16(map, offset) readw(((void __iomem *)(map)->handle) + (offset)) -/** Read a dword from a MMIO region */ -#define DRM_READ32(map, offset) readl(((void __iomem *)(map)->handle) + (offset)) -/** Write a byte into a MMIO region */ -#define DRM_WRITE8(map, offset, val) writeb(val, ((void __iomem *)(map)->handle) + (offset)) -/** Write a word into a MMIO region */ -#define DRM_WRITE16(map, offset, val) writew(val, ((void __iomem *)(map)->handle) + (offset)) -/** Write a dword into a MMIO region */ -#define DRM_WRITE32(map, offset, val) writel(val, ((void __iomem *)(map)->handle) + (offset)) - -/** Read a qword from a MMIO region - be careful using these unless you really understand them */ -#define DRM_READ64(map, offset) readq(((void __iomem *)(map)->handle) + (offset)) -/** Write a qword into a MMIO region */ -#define DRM_WRITE64(map, offset, val) writeq(val, ((void __iomem *)(map)->handle) + (offset)) - -#define DRM_WAIT_ON( ret, queue, timeout, condition ) \ -do { \ - DECLARE_WAITQUEUE(entry, current); \ - unsigned long end = jiffies + (timeout); \ - add_wait_queue(&(queue), &entry); \ - \ - for (;;) { \ - __set_current_state(TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (time_after_eq(jiffies, end)) { \ - ret = -EBUSY; \ - break; \ - } \ - schedule_timeout((HZ/100 > 1) ? HZ/100 : 1); \ - if (signal_pending(current)) { \ - ret = -EINTR; \ - break; \ - } \ - } \ - __set_current_state(TASK_RUNNING); \ - remove_wait_queue(&(queue), &entry); \ -} while (0) diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h new file mode 100644 index 000000000000..f6e7e234c089 --- /dev/null +++ b/include/drm/drm_pagemap.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef _DRM_PAGEMAP_H_ +#define _DRM_PAGEMAP_H_ + +#include <linux/dma-direction.h> +#include <linux/hmm.h> +#include <linux/types.h> + +#define NR_PAGES(order) (1U << (order)) + +struct drm_pagemap; +struct drm_pagemap_zdd; +struct device; + +/** + * enum drm_interconnect_protocol - Used to identify an interconnect protocol. + * + * @DRM_INTERCONNECT_SYSTEM: DMA map is system pages + * @DRM_INTERCONNECT_DRIVER: DMA map is driver defined + */ +enum drm_interconnect_protocol { + DRM_INTERCONNECT_SYSTEM, + DRM_INTERCONNECT_DRIVER, + /* A driver can add private values beyond DRM_INTERCONNECT_DRIVER */ +}; + +/** + * struct drm_pagemap_addr - Address representation. + * @addr: The dma address or driver-defined address for driver private interconnects. + * @proto: The interconnect protocol. + * @order: The page order of the device mapping. (Size is PAGE_SIZE << order). + * @dir: The DMA direction. + * + * Note: There is room for improvement here. We should be able to pack into + * 64 bits. + */ +struct drm_pagemap_addr { + dma_addr_t addr; + u64 proto : 54; + u64 order : 8; + u64 dir : 2; +}; + +/** + * drm_pagemap_addr_encode() - Encode a dma address with metadata + * @addr: The dma address or driver-defined address for driver private interconnects. + * @proto: The interconnect protocol. + * @order: The page order of the dma mapping. (Size is PAGE_SIZE << order). + * @dir: The DMA direction. + * + * Return: A struct drm_pagemap_addr encoding the above information. + */ +static inline struct drm_pagemap_addr +drm_pagemap_addr_encode(dma_addr_t addr, + enum drm_interconnect_protocol proto, + unsigned int order, + enum dma_data_direction dir) +{ + return (struct drm_pagemap_addr) { + .addr = addr, + .proto = proto, + .order = order, + .dir = dir, + }; +} + +/** + * struct drm_pagemap_ops: Ops for a drm-pagemap. + */ +struct drm_pagemap_ops { + /** + * @device_map: Map for device access or provide a virtual address suitable for + * + * @dpagemap: The struct drm_pagemap for the page. + * @dev: The device mapper. + * @page: The page to map. + * @order: The page order of the device mapping. (Size is PAGE_SIZE << order). + * @dir: The transfer direction. + */ + struct drm_pagemap_addr (*device_map)(struct drm_pagemap *dpagemap, + struct device *dev, + struct page *page, + unsigned int order, + enum dma_data_direction dir); + + /** + * @device_unmap: Unmap a device address previously obtained using @device_map. + * + * @dpagemap: The struct drm_pagemap for the mapping. + * @dev: The device unmapper. + * @addr: The device address obtained when mapping. + */ + void (*device_unmap)(struct drm_pagemap *dpagemap, + struct device *dev, + struct drm_pagemap_addr addr); + + /** + * @populate_mm: Populate part of the mm with @dpagemap memory, + * migrating existing data. + * @dpagemap: The struct drm_pagemap managing the memory. + * @start: The virtual start address in @mm + * @end: The virtual end address in @mm + * @mm: Pointer to a live mm. The caller must have an mmget() + * reference. + * + * The caller will have the mm lock at least in read mode. + * Note that there is no guarantee that the memory is resident + * after the function returns, it's best effort only. + * When the mm is not using the memory anymore, + * it will be released. The struct drm_pagemap might have a + * mechanism in place to reclaim the memory and the data will + * then be migrated. Typically to system memory. + * The implementation should hold sufficient runtime power- + * references while pages are used in an address space and + * should ideally guard against hardware device unbind in + * a way such that device pages are migrated back to system + * followed by device page removal. The implementation should + * return -ENODEV after device removal. + * + * Return: 0 if successful. Negative error code on error. + */ + int (*populate_mm)(struct drm_pagemap *dpagemap, + unsigned long start, unsigned long end, + struct mm_struct *mm, + unsigned long timeslice_ms); +}; + +/** + * struct drm_pagemap: Additional information for a struct dev_pagemap + * used for device p2p handshaking. + * @ops: The struct drm_pagemap_ops. + * @dev: The struct drevice owning the device-private memory. + */ +struct drm_pagemap { + const struct drm_pagemap_ops *ops; + struct device *dev; +}; + +struct drm_pagemap_devmem; + +/** + * struct drm_pagemap_devmem_ops - Operations structure for GPU SVM device memory + * + * This structure defines the operations for GPU Shared Virtual Memory (SVM) + * device memory. These operations are provided by the GPU driver to manage device memory + * allocations and perform operations such as migration between device memory and system + * RAM. + */ +struct drm_pagemap_devmem_ops { + /** + * @devmem_release: Release device memory allocation (optional) + * @devmem_allocation: device memory allocation + * + * Release device memory allocation and drop a reference to device + * memory allocation. + */ + void (*devmem_release)(struct drm_pagemap_devmem *devmem_allocation); + + /** + * @populate_devmem_pfn: Populate device memory PFN (required for migration) + * @devmem_allocation: device memory allocation + * @npages: Number of pages to populate + * @pfn: Array of page frame numbers to populate + * + * Populate device memory page frame numbers (PFN). + * + * Return: 0 on success, a negative error code on failure. + */ + int (*populate_devmem_pfn)(struct drm_pagemap_devmem *devmem_allocation, + unsigned long npages, unsigned long *pfn); + + /** + * @copy_to_devmem: Copy to device memory (required for migration) + * @pages: Pointer to array of device memory pages (destination) + * @pagemap_addr: Pointer to array of DMA information (source) + * @npages: Number of pages to copy + * + * Copy pages to device memory. If the order of a @pagemap_addr entry + * is greater than 0, the entry is populated but subsequent entries + * within the range of that order are not populated. + * + * Return: 0 on success, a negative error code on failure. + */ + int (*copy_to_devmem)(struct page **pages, + struct drm_pagemap_addr *pagemap_addr, + unsigned long npages); + + /** + * @copy_to_ram: Copy to system RAM (required for migration) + * @pages: Pointer to array of device memory pages (source) + * @pagemap_addr: Pointer to array of DMA information (destination) + * @npages: Number of pages to copy + * + * Copy pages to system RAM. If the order of a @pagemap_addr entry + * is greater than 0, the entry is populated but subsequent entries + * within the range of that order are not populated. + * + * Return: 0 on success, a negative error code on failure. + */ + int (*copy_to_ram)(struct page **pages, + struct drm_pagemap_addr *pagemap_addr, + unsigned long npages); +}; + +/** + * struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation + * + * @dev: Pointer to the device structure which device memory allocation belongs to + * @mm: Pointer to the mm_struct for the address space + * @detached: device memory allocations is detached from device pages + * @ops: Pointer to the operations structure for GPU SVM device memory + * @dpagemap: The struct drm_pagemap of the pages this allocation belongs to. + * @size: Size of device memory allocation + * @timeslice_expiration: Timeslice expiration in jiffies + */ +struct drm_pagemap_devmem { + struct device *dev; + struct mm_struct *mm; + struct completion detached; + const struct drm_pagemap_devmem_ops *ops; + struct drm_pagemap *dpagemap; + size_t size; + u64 timeslice_expiration; +}; + +int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, + struct mm_struct *mm, + unsigned long start, unsigned long end, + unsigned long timeslice_ms, + void *pgmap_owner); + +int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation); + +const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void); + +struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page); + +void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation, + struct device *dev, struct mm_struct *mm, + const struct drm_pagemap_devmem_ops *ops, + struct drm_pagemap *dpagemap, size_t size); + +int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, + unsigned long start, unsigned long end, + struct mm_struct *mm, + unsigned long timeslice_ms); + +#endif diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 8c738c0e6e9f..2407bfa60236 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -24,25 +24,24 @@ #ifndef __DRM_PANEL_H__ #define __DRM_PANEL_H__ +#include <linux/err.h> #include <linux/errno.h> #include <linux/list.h> +#include <linux/mutex.h> +#include <linux/kref.h> +struct backlight_device; +struct dentry; struct device_node; struct drm_connector; -struct drm_device; +struct drm_panel_follower; struct drm_panel; struct display_timing; +enum drm_panel_orientation; + /** * struct drm_panel_funcs - perform operations on a given panel - * @disable: disable panel (turn off back light, etc.) - * @unprepare: turn off panel - * @prepare: turn on panel and perform set up - * @enable: enable panel (turn on back light, etc.) - * @get_modes: add modes to the connector that the panel is attached to and - * return the number of modes added - * @get_timings: copy display timings into the provided array and return - * the number of display timings available * * The .prepare() function is typically called before the display controller * starts to transmit video data. Panel drivers can use this to turn the panel @@ -66,142 +65,331 @@ struct display_timing; * * To save power when no video data is transmitted, a driver can power down * the panel. This is the job of the .unprepare() function. + * + * Backlight can be handled automatically if configured using + * drm_panel_of_backlight() or drm_panel_dp_aux_backlight(). Then the driver + * does not need to implement the functionality to enable/disable backlight. */ struct drm_panel_funcs { - int (*disable)(struct drm_panel *panel); - int (*unprepare)(struct drm_panel *panel); + /** + * @prepare: + * + * Turn on panel and perform set up. + * + * This function is optional. + */ int (*prepare)(struct drm_panel *panel); + + /** + * @enable: + * + * Enable panel (turn on back light, etc.). + * + * This function is optional. + */ int (*enable)(struct drm_panel *panel); - int (*get_modes)(struct drm_panel *panel); + + /** + * @disable: + * + * Disable panel (turn off back light, etc.). + * + * This function is optional. + */ + int (*disable)(struct drm_panel *panel); + + /** + * @unprepare: + * + * Turn off panel. + * + * This function is optional. + */ + int (*unprepare)(struct drm_panel *panel); + + /** + * @get_modes: + * + * Add modes to the connector that the panel is attached to + * and returns the number of modes added. + * + * This function is mandatory. + */ + int (*get_modes)(struct drm_panel *panel, + struct drm_connector *connector); + + /** + * @get_orientation: + * + * Return the panel orientation set by device tree or EDID. + * + * This function is optional. + */ + enum drm_panel_orientation (*get_orientation)(struct drm_panel *panel); + + /** + * @get_timings: + * + * Copy display timings into the provided array and return + * the number of display timings available. + * + * This function is optional. + */ int (*get_timings)(struct drm_panel *panel, unsigned int num_timings, struct display_timing *timings); + + /** + * @debugfs_init: + * + * Allows panels to create panels-specific debugfs files. + */ + void (*debugfs_init)(struct drm_panel *panel, struct dentry *root); +}; + +struct drm_panel_follower_funcs { + /** + * @panel_prepared: + * + * Called after the panel has been powered on. + */ + int (*panel_prepared)(struct drm_panel_follower *follower); + + /** + * @panel_unpreparing: + * + * Called before the panel is powered off. + */ + int (*panel_unpreparing)(struct drm_panel_follower *follower); + + /** + * @panel_enabled: + * + * Called after the panel and the backlight have been enabled. + */ + int (*panel_enabled)(struct drm_panel_follower *follower); + + /** + * @panel_disabling: + * + * Called before the panel and the backlight are disabled. + */ + int (*panel_disabling)(struct drm_panel_follower *follower); +}; + +struct drm_panel_follower { + /** + * @funcs: + * + * Dependent device callbacks; should be initted by the caller. + */ + const struct drm_panel_follower_funcs *funcs; + + /** + * @list + * + * Used for linking into panel's list; set by drm_panel_add_follower(). + */ + struct list_head list; + + /** + * @panel + * + * The panel we're dependent on; set by drm_panel_add_follower(). + */ + struct drm_panel *panel; }; /** * struct drm_panel - DRM panel object - * @drm: DRM device owning the panel - * @connector: DRM connector that the panel is attached to - * @dev: parent device of the panel - * @link: link from panel device (supplier) to DRM device (consumer) - * @funcs: operations that can be performed on the panel - * @list: panel entry in registry */ struct drm_panel { - struct drm_device *drm; - struct drm_connector *connector; + /** + * @dev: + * + * Parent device of the panel. + */ struct device *dev; + /** + * @backlight: + * + * Backlight device, used to turn on backlight after the call + * to enable(), and to turn off backlight before the call to + * disable(). + * backlight is set by drm_panel_of_backlight() or + * drm_panel_dp_aux_backlight() and drivers shall not assign it. + */ + struct backlight_device *backlight; + + /** + * @funcs: + * + * Operations that can be performed on the panel. + */ const struct drm_panel_funcs *funcs; + /** + * @connector_type: + * + * Type of the panel as a DRM_MODE_CONNECTOR_* value. This is used to + * initialise the drm_connector corresponding to the panel with the + * correct connector type. + */ + int connector_type; + + /** + * @list: + * + * Panel entry in registry. + */ struct list_head list; -}; -/** - * drm_disable_unprepare - power off a panel - * @panel: DRM panel - * - * Calling this function will completely power off a panel (assert the panel's - * reset, turn off power supplies, ...). After this function has completed, it - * is usually no longer possible to communicate with the panel until another - * call to drm_panel_prepare(). - * - * Return: 0 on success or a negative error code on failure. - */ -static inline int drm_panel_unprepare(struct drm_panel *panel) -{ - if (panel && panel->funcs && panel->funcs->unprepare) - return panel->funcs->unprepare(panel); + /** + * @followers: + * + * A list of struct drm_panel_follower dependent on this panel. + */ + struct list_head followers; - return panel ? -ENOSYS : -EINVAL; -} + /** + * @follower_lock: + * + * Lock for followers list. + */ + struct mutex follower_lock; -/** - * drm_panel_disable - disable a panel - * @panel: DRM panel - * - * This will typically turn off the panel's backlight or disable the display - * drivers. For smart panels it should still be possible to communicate with - * the integrated circuitry via any command bus after this call. - * - * Return: 0 on success or a negative error code on failure. - */ -static inline int drm_panel_disable(struct drm_panel *panel) -{ - if (panel && panel->funcs && panel->funcs->disable) - return panel->funcs->disable(panel); + /** + * @prepare_prev_first: + * + * The previous controller should be prepared first, before the prepare + * for the panel is called. This is largely required for DSI panels + * where the DSI host controller should be initialised to LP-11 before + * the panel is powered up. + */ + bool prepare_prev_first; - return panel ? -ENOSYS : -EINVAL; -} + /** + * @prepared: + * + * If true then the panel has been prepared. + */ + bool prepared; -/** - * drm_panel_prepare - power on a panel - * @panel: DRM panel - * - * Calling this function will enable power and deassert any reset signals to - * the panel. After this has completed it is possible to communicate with any - * integrated circuitry via a command bus. - * - * Return: 0 on success or a negative error code on failure. - */ -static inline int drm_panel_prepare(struct drm_panel *panel) -{ - if (panel && panel->funcs && panel->funcs->prepare) - return panel->funcs->prepare(panel); + /** + * @enabled: + * + * If true then the panel has been enabled. + */ + bool enabled; - return panel ? -ENOSYS : -EINVAL; -} + /** + * @container: Pointer to the private driver struct embedding this + * @struct drm_panel. + */ + void *container; -/** - * drm_panel_enable - enable a panel - * @panel: DRM panel - * - * Calling this function will cause the panel display drivers to be turned on - * and the backlight to be enabled. Content will be visible on screen after - * this call completes. - * - * Return: 0 on success or a negative error code on failure. - */ -static inline int drm_panel_enable(struct drm_panel *panel) -{ - if (panel && panel->funcs && panel->funcs->enable) - return panel->funcs->enable(panel); + /** + * @refcount: reference count of users referencing this panel. + */ + struct kref refcount; +}; - return panel ? -ENOSYS : -EINVAL; -} +void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, + const struct drm_panel_funcs *funcs, + int connector_type); /** - * drm_panel_get_modes - probe the available display modes of a panel - * @panel: DRM panel + * devm_drm_panel_alloc - Allocate and initialize a refcounted panel. * - * The modes probed from the panel are automatically added to the connector - * that the panel is attached to. + * @dev: struct device of the panel device + * @type: the type of the struct which contains struct &drm_panel + * @member: the name of the &drm_panel within @type + * @funcs: callbacks for this panel + * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to + * the panel interface * - * Return: The number of modes available from the panel on success or a - * negative error code on failure. + * The reference count of the returned panel is initialized to 1. This + * reference will be automatically dropped via devm (by calling + * drm_panel_put()) when @dev is removed. + * + * Returns: + * Pointer to container structure embedding the panel, ERR_PTR on failure. */ -static inline int drm_panel_get_modes(struct drm_panel *panel) -{ - if (panel && panel->funcs && panel->funcs->get_modes) - return panel->funcs->get_modes(panel); +#define devm_drm_panel_alloc(dev, type, member, funcs, connector_type) \ + ((type *)__devm_drm_panel_alloc(dev, sizeof(type), \ + offsetof(type, member), funcs, \ + connector_type)) - return panel ? -ENOSYS : -EINVAL; -} +void drm_panel_init(struct drm_panel *panel, struct device *dev, + const struct drm_panel_funcs *funcs, + int connector_type); -void drm_panel_init(struct drm_panel *panel); +struct drm_panel *drm_panel_get(struct drm_panel *panel); +void drm_panel_put(struct drm_panel *panel); -int drm_panel_add(struct drm_panel *panel); +void drm_panel_add(struct drm_panel *panel); void drm_panel_remove(struct drm_panel *panel); -int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector); -int drm_panel_detach(struct drm_panel *panel); +void drm_panel_prepare(struct drm_panel *panel); +void drm_panel_unprepare(struct drm_panel *panel); + +void drm_panel_enable(struct drm_panel *panel); +void drm_panel_disable(struct drm_panel *panel); + +int drm_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector); #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL) struct drm_panel *of_drm_find_panel(const struct device_node *np); +int of_drm_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation); #else static inline struct drm_panel *of_drm_find_panel(const struct device_node *np) { return ERR_PTR(-ENODEV); } + +static inline int of_drm_get_panel_orientation(const struct device_node *np, + enum drm_panel_orientation *orientation) +{ + return -ENODEV; +} +#endif + +#if defined(CONFIG_DRM_PANEL) +bool drm_is_panel_follower(struct device *dev); +int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower); +void drm_panel_remove_follower(struct drm_panel_follower *follower); +int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower); +#else +static inline bool drm_is_panel_follower(struct device *dev) +{ + return false; +} + +static inline int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + return -ENODEV; +} + +static inline void drm_panel_remove_follower(struct drm_panel_follower *follower) { } +static inline int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + return -ENODEV; +} +#endif + +#if IS_ENABLED(CONFIG_DRM_PANEL) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ + (IS_MODULE(CONFIG_DRM) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE))) +int drm_panel_of_backlight(struct drm_panel *panel); +#else +static inline int drm_panel_of_backlight(struct drm_panel *panel) +{ + return 0; +} #endif #endif diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h new file mode 100644 index 000000000000..ac0e46b73436 --- /dev/null +++ b/include/drm/drm_panic.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ + +/* + * Copyright (c) 2024 Intel + * Copyright (c) 2024 Red Hat + */ + +#ifndef __DRM_PANIC_H__ +#define __DRM_PANIC_H__ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/iosys-map.h> + +#include <drm/drm_device.h> +#include <drm/drm_fourcc.h> + +/** + * struct drm_scanout_buffer - DRM scanout buffer + * + * This structure holds the information necessary for drm_panic to draw the + * panic screen, and display it. + */ +struct drm_scanout_buffer { + /** + * @format: + * + * drm format of the scanout buffer. + */ + const struct drm_format_info *format; + + /** + * @map: + * + * Virtual address of the scanout buffer, either in memory or iomem. + * The scanout buffer should be in linear format, and can be directly + * sent to the display hardware. Tearing is not an issue for the panic + * screen. + */ + struct iosys_map map[DRM_FORMAT_MAX_PLANES]; + + /** + * @pages: Optional, if the scanout buffer is not mapped, set this field + * to the array of pages of the scanout buffer. The panic code will use + * kmap_local_page_try_from_panic() to map one page at a time to write + * all the pixels. This array shouldn't be allocated from the + * get_scanoutbuffer() callback. + * The scanout buffer should be in linear format. + */ + struct page **pages; + + /** + * @width: Width of the scanout buffer, in pixels. + */ + unsigned int width; + + /** + * @height: Height of the scanout buffer, in pixels. + */ + unsigned int height; + + /** + * @pitch: Length in bytes between the start of two consecutive lines. + */ + unsigned int pitch[DRM_FORMAT_MAX_PLANES]; + + /** + * @set_pixel: Optional function, to set a pixel color on the + * framebuffer. It allows to handle special tiling format inside the + * driver. It takes precedence over the @map and @pages fields. + */ + void (*set_pixel)(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color); + + /** + * @private: private pointer that you can use in the callbacks + * set_pixel() + */ + void *private; + +}; + +#ifdef CONFIG_DRM_PANIC + +/** + * drm_panic_trylock - try to enter the panic printing critical section + * @dev: struct drm_device + * @flags: unsigned long irq flags you need to pass to the unlock() counterpart + * + * This function must be called by any panic printing code. The panic printing + * attempt must be aborted if the trylock fails. + * + * Panic printing code can make the following assumptions while holding the + * panic lock: + * + * - Anything protected by drm_panic_lock() and drm_panic_unlock() pairs is safe + * to access. + * + * - Furthermore the panic printing code only registers in drm_dev_unregister() + * and gets removed in drm_dev_unregister(). This allows the panic code to + * safely access any state which is invariant in between these two function + * calls, like the list of planes &drm_mode_config.plane_list or most of the + * struct drm_plane structure. + * + * Specifically thanks to the protection around plane updates in + * drm_atomic_helper_swap_state() the following additional guarantees hold: + * + * - It is safe to deference the drm_plane.state pointer. + * + * - Anything in struct drm_plane_state or the driver's subclass thereof which + * stays invariant after the atomic check code has finished is safe to access. + * Specifically this includes the reference counted pointers to framebuffer + * and buffer objects. + * + * - Anything set up by &drm_plane_helper_funcs.fb_prepare and cleaned up + * &drm_plane_helper_funcs.fb_cleanup is safe to access, as long as it stays + * invariant between these two calls. This also means that for drivers using + * dynamic buffer management the framebuffer is pinned, and therefer all + * relevant datastructures can be accessed without taking any further locks + * (which would be impossible in panic context anyway). + * + * - Importantly, software and hardware state set up by + * &drm_plane_helper_funcs.begin_fb_access and + * &drm_plane_helper_funcs.end_fb_access is not safe to access. + * + * Drivers must not make any assumptions about the actual state of the hardware, + * unless they explicitly protected these hardware access with drm_panic_lock() + * and drm_panic_unlock(). + * + * Return: + * %0 when failing to acquire the raw spinlock, nonzero on success. + */ +#define drm_panic_trylock(dev, flags) \ + raw_spin_trylock_irqsave(&(dev)->mode_config.panic_lock, flags) + +/** + * drm_panic_lock - protect panic printing relevant state + * @dev: struct drm_device + * @flags: unsigned long irq flags you need to pass to the unlock() counterpart + * + * This function must be called to protect software and hardware state that the + * panic printing code must be able to rely on. The protected sections must be + * as small as possible. It uses the irqsave/irqrestore variant, and can be + * called from irq handler. Examples include: + * + * - Access to peek/poke or other similar registers, if that is the way the + * driver prints the pixels into the scanout buffer at panic time. + * + * - Updates to pointers like &drm_plane.state, allowing the panic handler to + * safely deference these. This is done in drm_atomic_helper_swap_state(). + * + * - An state that isn't invariant and that the driver must be able to access + * during panic printing. + */ + +#define drm_panic_lock(dev, flags) \ + raw_spin_lock_irqsave(&(dev)->mode_config.panic_lock, flags) + +/** + * drm_panic_unlock - end of the panic printing critical section + * @dev: struct drm_device + * @flags: irq flags that were returned when acquiring the lock + * + * Unlocks the raw spinlock acquired by either drm_panic_lock() or + * drm_panic_trylock(). + */ +#define drm_panic_unlock(dev, flags) \ + raw_spin_unlock_irqrestore(&(dev)->mode_config.panic_lock, flags) + +#else + +static inline bool drm_panic_trylock(struct drm_device *dev, unsigned long flags) +{ + return true; +} + +static inline void drm_panic_lock(struct drm_device *dev, unsigned long flags) {} +static inline void drm_panic_unlock(struct drm_device *dev, unsigned long flags) {} + +#endif + +#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) +size_t drm_panic_qr_max_data_size(u8 version, size_t url_len); + +u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size, + u8 *tmp, size_t tmp_size); +#endif + +#endif /* __DRM_PANIC_H__ */ diff --git a/include/drm/drm_pci.h b/include/drm/drm_pci.h deleted file mode 100644 index 8181e9e7cf1d..000000000000 --- a/include/drm/drm_pci.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Internal Header for the Direct Rendering Manager - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * Copyright (c) 2009-2010, Code Aurora Forum. - * All rights reserved. - * - * Author: Rickard E. (Rik) Faith <faith@valinux.com> - * Author: Gareth Hughes <gareth@valinux.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _DRM_PCI_H_ -#define _DRM_PCI_H_ - -#include <linux/pci.h> - -struct drm_dma_handle; -struct drm_device; -struct drm_driver; -struct drm_master; - -struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size, - size_t align); -void drm_pci_free(struct drm_device *dev, struct drm_dma_handle * dmah); - -int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); -void drm_legacy_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); -#ifdef CONFIG_PCI -int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver); -#else -static inline int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver) -{ - return -ENOSYS; -} -#endif - -#endif /* _DRM_PCI_H_ */ diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index b7e899ce44f0..90e8abc08653 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -700,115 +700,3 @@ {0x1002, 0x99A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x99A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0, 0, 0} - -#define r128_PCI_IDS \ - {0x1002, 0x4c45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4d46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5041, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5042, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5044, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5045, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5046, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5047, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5048, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5049, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x504A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x504B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x504C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x504D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x504E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x504F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5052, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5245, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5247, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x524b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x524c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x534d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x544C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x5452, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0, 0, 0} - -#define mga_PCI_IDS \ - {0x102b, 0x0520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MGA_CARD_TYPE_G200}, \ - {0x102b, 0x0521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MGA_CARD_TYPE_G200}, \ - {0x102b, 0x0525, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MGA_CARD_TYPE_G400}, \ - {0x102b, 0x2527, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MGA_CARD_TYPE_G550}, \ - {0, 0, 0} - -#define sisdrv_PCI_IDS \ - {0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1039, 0x6300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1039, 0x6330, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_CHIP_315}, \ - {0x1039, 0x6351, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1039, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x18CA, 0x0040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_CHIP_315}, \ - {0x18CA, 0x0042, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_CHIP_315}, \ - {0, 0, 0} - -#define tdfx_PCI_IDS \ - {0x121a, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x121a, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x121a, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x121a, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x121a, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x121a, 0x000b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0, 0, 0} - -#define viadrv_PCI_IDS \ - {0x1106, 0x3022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1106, 0x3118, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VIA_PRO_GROUP_A}, \ - {0x1106, 0x3122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1106, 0x7205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1106, 0x3108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1106, 0x3344, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1106, 0x3343, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1106, 0x3230, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VIA_DX9_0}, \ - {0x1106, 0x3157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VIA_PRO_GROUP_A}, \ - {0, 0, 0} - -#define i810_PCI_IDS \ - {0x8086, 0x7121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x8086, 0x7123, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x8086, 0x7125, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x8086, 0x1132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0, 0, 0} - -#define savage_PCI_IDS \ - {0x5333, 0x8a20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE3D}, \ - {0x5333, 0x8a21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE3D}, \ - {0x5333, 0x8a22, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE4}, \ - {0x5333, 0x8a23, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE4}, \ - {0x5333, 0x8c10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE_MX}, \ - {0x5333, 0x8c11, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE_MX}, \ - {0x5333, 0x8c12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE_MX}, \ - {0x5333, 0x8c13, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE_MX}, \ - {0x5333, 0x8c22, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c24, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c26, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c2a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c2b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c2c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c2d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c2e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8c2f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SUPERSAVAGE}, \ - {0x5333, 0x8a25, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_PROSAVAGE}, \ - {0x5333, 0x8a26, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_PROSAVAGE}, \ - {0x5333, 0x8d01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_TWISTER}, \ - {0x5333, 0x8d02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_TWISTER}, \ - {0x5333, 0x8d03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_PROSAVAGEDDR}, \ - {0x5333, 0x8d04, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_PROSAVAGEDDR}, \ - {0, 0, 0} diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 6078c700d9ba..703ef4d1bbbc 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -25,6 +25,7 @@ #include <linux/list.h> #include <linux/ctype.h> +#include <linux/kmsg_dump.h> #include <drm/drm_mode_object.h> #include <drm/drm_color_mgmt.h> #include <drm/drm_rect.h> @@ -32,13 +33,19 @@ #include <drm/drm_util.h> struct drm_crtc; +struct drm_plane_size_hint; struct drm_printer; struct drm_modeset_acquire_ctx; +enum drm_scaling_filter { + DRM_SCALING_FILTER_DEFAULT, + DRM_SCALING_FILTER_NEAREST_NEIGHBOR, +}; + /** * struct drm_plane_state - mutable plane state * - * Please not that the destination coordinates @crtc_x, @crtc_y, @crtc_h and + * Please note that the destination coordinates @crtc_x, @crtc_y, @crtc_h and * @crtc_w and the source coordinates @src_x, @src_y, @src_h and @src_w are the * raw coordinates provided by userspace. Drivers should use * drm_atomic_helper_check_plane_state() and only use the derived rectangles in @@ -51,7 +58,7 @@ struct drm_plane_state { /** * @crtc: * - * Currently bound CRTC, NULL if disabled. Do not this write directly, + * Currently bound CRTC, NULL if disabled. Do not write this directly, * use drm_atomic_set_crtc_for_plane() */ struct drm_crtc *crtc; @@ -69,13 +76,11 @@ struct drm_plane_state { * * Optional fence to wait for before scanning out @fb. The core atomic * code will set this when userspace is using explicit fencing. Do not - * write this directly for a driver's implicit fence, use - * drm_atomic_set_fence_for_plane() to ensure that an explicit fence is - * preserved. + * write this field directly for a driver's implicit fence. * * Drivers should store any implicit fence in this from their - * &drm_plane_helper_funcs.prepare_fb callback. See drm_gem_fb_prepare_fb() - * and drm_gem_fb_simple_display_pipe_prepare_fb() for suitable helpers. + * &drm_plane_helper_funcs.prepare_fb callback. See + * drm_gem_plane_helper_prepare_fb() for a suitable helper. */ struct dma_fence *fence; @@ -113,6 +118,10 @@ struct drm_plane_state { /** @src_h: height of visible portion of plane (in 16.16) */ uint32_t src_h, src_w; + /** @hotspot_x: x offset to mouse cursor hotspot */ + /** @hotspot_y: y offset to mouse cursor hotspot */ + int32_t hotspot_x, hotspot_y; + /** * @alpha: * Opacity of the plane with 0 as completely transparent and 0xffff as @@ -140,10 +149,11 @@ struct drm_plane_state { * @zpos: * Priority of the given plane on crtc (optional). * - * Note that multiple active planes on the same crtc can have an - * identical zpos value. The rule to solving the conflict is to compare - * the plane object IDs; the plane with a higher ID must be stacked on - * top of a plane with a lower ID. + * User-space may set mutable zpos properties so that multiple active + * planes on the same CRTC have identical zpos values. This is a + * user-space bug, but drivers can solve the conflict by comparing the + * plane object IDs; the plane with a higher ID is stacked on top of a + * plane with a lower ID. * * See drm_plane_create_zpos_property() and * drm_plane_create_zpos_immutable_property() for more details. @@ -180,11 +190,42 @@ struct drm_plane_state { * since last plane update) as an array of &drm_mode_rect in framebuffer * coodinates of the attached framebuffer. Note that unlike plane src, * damage clips are not in 16.16 fixed point. + * + * See drm_plane_get_damage_clips() and + * drm_plane_get_damage_clips_count() for accessing these. */ struct drm_property_blob *fb_damage_clips; - /** @src: clipped source coordinates of the plane (in 16.16) */ - /** @dst: clipped destination coordinates of the plane */ + /** + * @ignore_damage_clips: + * + * Set by drivers to indicate the drm_atomic_helper_damage_iter_init() + * helper that the @fb_damage_clips blob property should be ignored. + * + * See :ref:`damage_tracking_properties` for more information. + */ + bool ignore_damage_clips; + + /** + * @src: + * + * source coordinates of the plane (in 16.16). + * + * When using drm_atomic_helper_check_plane_state(), + * the coordinates are clipped, but the driver may choose + * to use unclipped coordinates instead when the hardware + * performs the clipping automatically. + */ + /** + * @dst: + * + * clipped destination coordinates of the plane. + * + * When using drm_atomic_helper_check_plane_state(), + * the coordinates are clipped, but the driver may choose + * to use unclipped coordinates instead when the hardware + * performs the clipping automatically. + */ struct drm_rect src, dst; /** @@ -196,6 +237,21 @@ struct drm_plane_state { bool visible; /** + * @scaling_filter: + * + * Scaling filter to be applied + */ + enum drm_scaling_filter scaling_filter; + + /** + * @color_pipeline: + * + * The first colorop of the active color pipeline, or NULL, if no + * color pipeline is active. + */ + struct drm_colorop *color_pipeline; + + /** * @commit: Tracks the pending commit to prevent use-after-free conditions, * and for async plane updates. * @@ -205,6 +261,13 @@ struct drm_plane_state { /** @state: backpointer to global drm_atomic_state */ struct drm_atomic_state *state; + + /** + * @color_mgmt_changed: Color management properties have changed. Used + * by the atomic helpers and drivers to steer the atomic commit control + * flow. + */ + bool color_mgmt_changed : 1; }; static inline struct drm_rect @@ -482,7 +545,7 @@ struct drm_plane_funcs { * This optional hook is used for the DRM to determine if the given * format/modifier combination is valid for the plane. This allows the * DRM to generate the correct format bitmask (which formats apply to - * which modifier), and to valdiate modifiers at atomic_check time. + * which modifier), and to validate modifiers at atomic_check time. * * If not present, then any modifier in the plane's modifier * list is allowed with any of the plane's formats. @@ -494,6 +557,23 @@ struct drm_plane_funcs { */ bool (*format_mod_supported)(struct drm_plane *plane, uint32_t format, uint64_t modifier); + /** + * @format_mod_supported_async: + * + * This optional hook is used for the DRM to determine if for + * asynchronous flip the given format/modifier combination is valid for + * the plane. This allows the DRM to generate the correct format + * bitmask (which formats apply to which modifier), and to validate + * modifiers at atomic_check time. + * + * Returns: + * + * True if the given modifier is valid for that format on the plane. + * False otherwise. + */ + bool (*format_mod_supported_async)(struct drm_plane *plane, + u32 format, u64 modifier); + }; /** @@ -507,10 +587,14 @@ struct drm_plane_funcs { * * For compatibility with legacy userspace, only overlay planes are made * available to userspace by default. Userspace clients may set the - * DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate that they + * &DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate that they * wish to receive a universal plane list containing all plane types. See also * drm_for_each_legacy_plane(). * + * In addition to setting each plane's type, drivers need to setup the + * &drm_crtc.primary and optionally &drm_crtc.cursor pointers for legacy + * IOCTLs. See drm_crtc_init_with_planes(). + * * WARNING: The values of this enum is UABI since they're exposed in the "type" * property. */ @@ -526,19 +610,20 @@ enum drm_plane_type { /** * @DRM_PLANE_TYPE_PRIMARY: * - * Primary planes represent a "main" plane for a CRTC. Primary planes - * are the planes operated upon by CRTC modesetting and flipping - * operations described in the &drm_crtc_funcs.page_flip and - * &drm_crtc_funcs.set_config hooks. + * A primary plane attached to a CRTC is the most likely to be able to + * light up the CRTC when no scaling/cropping is used and the plane + * covers the whole CRTC. */ DRM_PLANE_TYPE_PRIMARY, /** * @DRM_PLANE_TYPE_CURSOR: * - * Cursor planes represent a "cursor" plane for a CRTC. Cursor planes - * are the planes operated upon by the DRM_IOCTL_MODE_CURSOR and - * DRM_IOCTL_MODE_CURSOR2 IOCTLs. + * A cursor plane attached to a CRTC is more likely to be able to be + * enabled when no scaling/cropping is used and the framebuffer has the + * size indicated by &drm_mode_config.cursor_width and + * &drm_mode_config.cursor_height. Additionally, if the driver doesn't + * support modifiers, the framebuffer should have a linear layout. */ DRM_PLANE_TYPE_CURSOR, }; @@ -594,7 +679,7 @@ struct drm_plane { unsigned int format_count; /** * @format_default: driver hasn't supplied supported formats for the - * plane. Used by the drm_plane_init compatibility wrapper only. + * plane. Used by the non-atomic driver compatibility wrapper only. */ bool format_default; @@ -705,6 +790,35 @@ struct drm_plane { * See drm_plane_create_color_properties(). */ struct drm_property *color_range_property; + + /** + * @color_pipeline_property: + * + * Optional "COLOR_PIPELINE" enum property for specifying + * a color pipeline to use on the plane. + */ + struct drm_property *color_pipeline_property; + + /** + * @scaling_filter_property: property to apply a particular filter while + * scaling. + */ + struct drm_property *scaling_filter_property; + + /** + * @hotspot_x_property: property to set mouse hotspot x offset. + */ + struct drm_property *hotspot_x_property; + + /** + * @hotspot_y_property: property to set mouse hotspot y offset. + */ + struct drm_property *hotspot_y_property; + + /** + * @kmsg_panic: Used to register a panic notifier for this plane + */ + struct kmsg_dumper kmsg_panic; }; #define obj_to_plane(x) container_of(x, struct drm_plane, base) @@ -719,14 +833,97 @@ int drm_universal_plane_init(struct drm_device *dev, const uint64_t *format_modifiers, enum drm_plane_type type, const char *name, ...); -int drm_plane_init(struct drm_device *dev, - struct drm_plane *plane, - uint32_t possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - bool is_primary); void drm_plane_cleanup(struct drm_plane *plane); +__printf(10, 11) +void *__drmm_universal_plane_alloc(struct drm_device *dev, + size_t size, size_t offset, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type plane_type, + const char *name, ...); + +/** + * drmm_universal_plane_alloc - Allocate and initialize an universal plane object + * @dev: DRM device + * @type: the type of the struct which contains struct &drm_plane + * @member: the name of the &drm_plane within @type + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (DRM_FORMAT\_\*) + * @format_count: number of elements in @formats + * @format_modifiers: array of struct drm_format modifiers terminated by + * DRM_FORMAT_MOD_INVALID + * @plane_type: type of plane (overlay, primary, cursor) + * @name: printf style format string for the plane name, or NULL for default name + * + * Allocates and initializes a plane object of type @type. Cleanup is + * automatically handled through registering drm_plane_cleanup() with + * drmm_add_action(). + * + * The @drm_plane_funcs.destroy hook must be NULL. + * + * Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set + * @format_modifiers to NULL. The plane will advertise the linear modifier. + * + * Returns: + * Pointer to new plane, or ERR_PTR on failure. + */ +#define drmm_universal_plane_alloc(dev, type, member, possible_crtcs, funcs, formats, \ + format_count, format_modifiers, plane_type, name, ...) \ + ((type *)__drmm_universal_plane_alloc(dev, sizeof(type), \ + offsetof(type, member), \ + possible_crtcs, funcs, formats, \ + format_count, format_modifiers, \ + plane_type, name, ##__VA_ARGS__)) + +__printf(10, 11) +void *__drm_universal_plane_alloc(struct drm_device *dev, + size_t size, size_t offset, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type plane_type, + const char *name, ...); + +/** + * drm_universal_plane_alloc() - Allocate and initialize an universal plane object + * @dev: DRM device + * @type: the type of the struct which contains struct &drm_plane + * @member: the name of the &drm_plane within @type + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (DRM_FORMAT\_\*) + * @format_count: number of elements in @formats + * @format_modifiers: array of struct drm_format modifiers terminated by + * DRM_FORMAT_MOD_INVALID + * @plane_type: type of plane (overlay, primary, cursor) + * @name: printf style format string for the plane name, or NULL for default name + * + * Allocates and initializes a plane object of type @type. The caller + * is responsible for releasing the allocated memory with kfree(). + * + * Drivers are encouraged to use drmm_universal_plane_alloc() instead. + * + * Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set + * @format_modifiers to NULL. The plane will advertise the linear modifier. + * + * Returns: + * Pointer to new plane, or ERR_PTR on failure. + */ +#define drm_universal_plane_alloc(dev, type, member, possible_crtcs, funcs, formats, \ + format_count, format_modifiers, plane_type, name, ...) \ + ((type *)__drm_universal_plane_alloc(dev, sizeof(type), \ + offsetof(type, member), \ + possible_crtcs, funcs, formats, \ + format_count, format_modifiers, \ + plane_type, name, ##__VA_ARGS__)) + /** * drm_plane_index - find the index of a registered plane * @plane: plane to find index for @@ -808,39 +1005,24 @@ static inline struct drm_plane *drm_plane_find(struct drm_device *dev, #define drm_for_each_plane(plane, dev) \ list_for_each_entry(plane, &(dev)->mode_config.plane_list, head) +bool drm_plane_has_format(struct drm_plane *plane, + u32 format, u64 modifier); bool drm_any_plane_has_format(struct drm_device *dev, u32 format, u64 modifier); -/** - * drm_plane_get_damage_clips_count - Returns damage clips count. - * @state: Plane state. - * - * Simple helper to get the number of &drm_mode_rect clips set by user-space - * during plane update. - * - * Return: Number of clips in plane fb_damage_clips blob property. - */ -static inline unsigned int -drm_plane_get_damage_clips_count(const struct drm_plane_state *state) -{ - return (state && state->fb_damage_clips) ? - state->fb_damage_clips->length/sizeof(struct drm_mode_rect) : 0; -} - -/** - * drm_plane_get_damage_clips - Returns damage clips. - * @state: Plane state. - * - * Note that this function returns uapi type &drm_mode_rect. Drivers might - * instead be interested in internal &drm_rect which can be obtained by calling - * drm_helper_get_plane_damage_clips(). - * - * Return: Damage clips in plane fb_damage_clips blob property. - */ -static inline struct drm_mode_rect * -drm_plane_get_damage_clips(const struct drm_plane_state *state) -{ - return (struct drm_mode_rect *)((state && state->fb_damage_clips) ? - state->fb_damage_clips->data : NULL); -} +void drm_plane_enable_fb_damage_clips(struct drm_plane *plane); +unsigned int +drm_plane_get_damage_clips_count(const struct drm_plane_state *state); +struct drm_mode_rect * +drm_plane_get_damage_clips(const struct drm_plane_state *state); + +int drm_plane_create_scaling_filter_property(struct drm_plane *plane, + unsigned int supported_filters); +int drm_plane_add_size_hints_property(struct drm_plane *plane, + const struct drm_plane_size_hint *hints, + int num_hints); + +int drm_plane_create_color_pipeline_property(struct drm_plane *plane, + const struct drm_prop_enum_list *pipelines, + int num_pipelines); #endif diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index 331ebd60b3a3..75f9c4830564 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -24,21 +24,34 @@ #ifndef DRM_PLANE_HELPER_H #define DRM_PLANE_HELPER_H -#include <drm/drm_rect.h> -#include <drm/drm_crtc.h> -#include <drm/drm_modeset_helper_vtables.h> -#include <drm/drm_modeset_helper.h> +#include <linux/types.h> -/* - * Drivers that don't allow primary plane scaling may pass this macro in place - * of the min/max scale parameters of the update checker function. +struct drm_crtc; +struct drm_framebuffer; +struct drm_modeset_acquire_ctx; +struct drm_plane; + +int drm_plane_helper_update_primary(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx); +int drm_plane_helper_disable_primary(struct drm_plane *plane, + struct drm_modeset_acquire_ctx *ctx); +void drm_plane_helper_destroy(struct drm_plane *plane); + +/** + * DRM_PLANE_NON_ATOMIC_FUNCS - Default plane functions for non-atomic drivers * - * Due to src being in 16.16 fixed point and dest being in integer pixels, - * 1<<16 represents no scaling. + * This macro initializes plane functions for non-atomic drivers to default + * values. Non-atomic interfaces are deprecated and should not be used in new + * drivers. */ -#define DRM_PLANE_HELPER_NO_SCALING (1<<16) - -void drm_primary_helper_destroy(struct drm_plane *plane); -extern const struct drm_plane_funcs drm_primary_helper_funcs; +#define DRM_PLANE_NON_ATOMIC_FUNCS \ + .update_plane = drm_plane_helper_update_primary, \ + .disable_plane = drm_plane_helper_disable_primary, \ + .destroy = drm_plane_helper_destroy #endif diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h index b03731a3f079..f50f862f0d8b 100644 --- a/include/drm/drm_prime.h +++ b/include/drm/drm_prime.h @@ -42,7 +42,6 @@ * This just contains the internal &struct dma_buf and handle caches for each * &struct drm_file used by the PRIME core code. */ - struct drm_prime_file_private { /* private: */ struct mutex lock; @@ -55,6 +54,7 @@ struct device; struct dma_buf_export_info; struct dma_buf; struct dma_buf_attachment; +struct iosys_map; enum dma_data_direction; @@ -62,27 +62,21 @@ struct drm_device; struct drm_gem_object; struct drm_file; -struct device; +/* core prime functions */ +struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, + struct dma_buf_export_info *exp_info); +void drm_gem_dmabuf_release(struct dma_buf *dma_buf); -struct dma_buf *drm_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags); +int drm_gem_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, uint32_t *handle); +struct dma_buf *drm_gem_prime_handle_to_dmabuf(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, + uint32_t flags); int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); -int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); -struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); - -struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, - struct dma_buf *dma_buf, - struct device *attach_dev); -int drm_gem_prime_fd_to_handle(struct drm_device *dev, - struct drm_file *file_priv, int prime_fd, uint32_t *handle); -struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, - struct dma_buf_export_info *exp_info); -void drm_gem_dmabuf_release(struct dma_buf *dma_buf); +/* helper functions for exporting */ int drm_gem_map_attach(struct dma_buf *dma_buf, struct dma_buf_attachment *attach); void drm_gem_map_detach(struct dma_buf *dma_buf, @@ -92,14 +86,34 @@ struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, struct sg_table *sgt, enum dma_data_direction dir); -void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf); -void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr); +int drm_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct iosys_map *map); +void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map); + +int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma); -int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, - dma_addr_t *addrs, int max_pages); -struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_pages); +struct sg_table *drm_prime_pages_to_sg(struct drm_device *dev, + struct page **pages, unsigned int nr_pages); +struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj, + int flags); + +unsigned long drm_prime_get_contiguous_size(struct sg_table *sgt); + +/* helper functions for importing */ +bool drm_gem_is_prime_exported_dma_buf(struct drm_device *dev, + struct dma_buf *dma_buf); + +struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, + struct dma_buf *dma_buf, + struct device *attach_dev); +struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); + void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); +int drm_prime_sg_to_page_array(struct sg_table *sgt, struct page **pages, + int max_pages); +int drm_prime_sg_to_dma_addr_array(struct sg_table *sgt, dma_addr_t *addrs, + int max_pages); #endif /* __DRM_PRIME_H__ */ diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index afbc3beef089..ab017b05e175 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -28,8 +28,18 @@ #include <linux/compiler.h> #include <linux/printk.h> -#include <linux/seq_file.h> #include <linux/device.h> +#include <linux/dynamic_debug.h> + +#include <drm/drm.h> +#include <drm/drm_device.h> + +struct debugfs_regset32; +struct drm_device; +struct seq_file; + +/* Do *not* use outside of drm_print.[ch]! */ +extern unsigned long __drm_debug; /** * DOC: print @@ -61,6 +71,101 @@ */ /** + * enum drm_debug_category - The DRM debug categories + * + * Each of the DRM debug logging macros use a specific category, and the logging + * is filtered by the drm.debug module parameter. This enum specifies the values + * for the interface. + * + * Each DRM_DEBUG_<CATEGORY> macro logs to DRM_UT_<CATEGORY> category, except + * DRM_DEBUG() logs to DRM_UT_CORE. + * + * Enabling verbose debug messages is done through the drm.debug parameter, each + * category being enabled by a bit: + * + * - drm.debug=0x1 will enable CORE messages + * - drm.debug=0x2 will enable DRIVER messages + * - drm.debug=0x3 will enable CORE and DRIVER messages + * - ... + * - drm.debug=0x1ff will enable all messages + * + * An interesting feature is that it's possible to enable verbose logging at + * run-time by echoing the debug value in its sysfs node:: + * + * # echo 0xf > /sys/module/drm/parameters/debug + * + */ +enum drm_debug_category { + /* These names must match those in DYNAMIC_DEBUG_CLASSBITS */ + /** + * @DRM_UT_CORE: Used in the generic drm code: drm_ioctl.c, drm_mm.c, + * drm_memory.c, ... + */ + DRM_UT_CORE, + /** + * @DRM_UT_DRIVER: Used in the vendor specific part of the driver: i915, + * radeon, ... macro. + */ + DRM_UT_DRIVER, + /** + * @DRM_UT_KMS: Used in the modesetting code. + */ + DRM_UT_KMS, + /** + * @DRM_UT_PRIME: Used in the prime code. + */ + DRM_UT_PRIME, + /** + * @DRM_UT_ATOMIC: Used in the atomic code. + */ + DRM_UT_ATOMIC, + /** + * @DRM_UT_VBL: Used for verbose debug message in the vblank code. + */ + DRM_UT_VBL, + /** + * @DRM_UT_STATE: Used for verbose atomic state debugging. + */ + DRM_UT_STATE, + /** + * @DRM_UT_LEASE: Used in the lease code. + */ + DRM_UT_LEASE, + /** + * @DRM_UT_DP: Used in the DP code. + */ + DRM_UT_DP, + /** + * @DRM_UT_DRMRES: Used in the drm managed resources code. + */ + DRM_UT_DRMRES +}; + +static inline bool drm_debug_enabled_raw(enum drm_debug_category category) +{ + return unlikely(__drm_debug & BIT(category)); +} + +#define drm_debug_enabled_instrumented(category) \ + ({ \ + pr_debug("todo: is this frequent enough to optimize ?\n"); \ + drm_debug_enabled_raw(category); \ + }) + +#if defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) +/* + * the drm.debug API uses dyndbg, so each drm_*dbg macro/callsite gets + * a descriptor, and only enabled callsites are reachable. They use + * the private macro to avoid re-testing the enable-bit. + */ +#define __drm_debug_enabled(category) true +#define drm_debug_enabled(category) drm_debug_enabled_instrumented(category) +#else +#define __drm_debug_enabled(category) drm_debug_enabled_raw(category) +#define drm_debug_enabled(category) drm_debug_enabled_raw(category) +#endif + +/** * struct drm_printer - drm output "stream" * * Do not use struct members directly. Use drm_printer_seq_file(), @@ -71,7 +176,13 @@ struct drm_printer { void (*printfn)(struct drm_printer *p, struct va_format *vaf); void (*puts)(struct drm_printer *p, const char *str); void *arg; + const void *origin; const char *prefix; + struct { + unsigned int series; + unsigned int counter; + } line; + enum drm_debug_category category; }; void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf); @@ -79,11 +190,18 @@ void __drm_puts_coredump(struct drm_printer *p, const char *str); void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf); void __drm_puts_seq_file(struct drm_printer *p, const char *str); void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf); -void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf); +void __drm_printfn_dbg(struct drm_printer *p, struct va_format *vaf); +void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf); +void __drm_printfn_line(struct drm_printer *p, struct va_format *vaf); __printf(2, 3) void drm_printf(struct drm_printer *p, const char *f, ...); void drm_puts(struct drm_printer *p, const char *str); +void drm_print_regset32(struct drm_printer *p, struct debugfs_regset32 *regset); +void drm_print_bits(struct drm_printer *p, unsigned long value, + const char * const bits[], unsigned int nbits); +void drm_print_hex_dump(struct drm_printer *p, const char *prefix, + const u8 *buf, size_t len); __printf(2, 0) /** @@ -111,7 +229,8 @@ drm_vprintf(struct drm_printer *p, const char *fmt, va_list *va) /** * struct drm_print_iterator - local struct used with drm_printer_coredump - * @data: Pointer to the devcoredump output buffer + * @data: Pointer to the devcoredump output buffer, can be NULL if using + * drm_printer_coredump to determine size of devcoredump * @start: The offset within the buffer to start writing * @remain: The number of bytes to write for this iteration */ @@ -156,6 +275,57 @@ struct drm_print_iterator { * coredump_read, ...) * } * + * The above example has a time complexity of O(N^2), where N is the size of the + * devcoredump. This is acceptable for small devcoredumps but scales poorly for + * larger ones. + * + * Another use case for drm_coredump_printer is to capture the devcoredump into + * a saved buffer before the dev_coredump() callback. This involves two passes: + * one to determine the size of the devcoredump and another to print it to a + * buffer. Then, in dev_coredump(), copy from the saved buffer into the + * devcoredump read buffer. + * + * For example:: + * + * char *devcoredump_saved_buffer; + * + * ssize_t __coredump_print(char *buffer, ssize_t count, ...) + * { + * struct drm_print_iterator iter; + * struct drm_printer p; + * + * iter.data = buffer; + * iter.start = 0; + * iter.remain = count; + * + * p = drm_coredump_printer(&iter); + * + * drm_printf(p, "foo=%d\n", foo); + * ... + * return count - iter.remain; + * } + * + * void coredump_print(...) + * { + * ssize_t count; + * + * count = __coredump_print(NULL, INT_MAX, ...); + * devcoredump_saved_buffer = kvmalloc(count, GFP_KERNEL); + * __coredump_print(devcoredump_saved_buffer, count, ...); + * } + * + * void coredump_read(char *buffer, loff_t offset, size_t count, + * void *data, size_t datalen) + * { + * ... + * memcpy(buffer, devcoredump_saved_buffer + offset, count); + * ... + * } + * + * The above example has a time complexity of O(N*2), where N is the size of the + * devcoredump. This scales better than the previous example for larger + * devcoredumps. + * * RETURNS: * The &drm_printer object */ @@ -175,6 +345,26 @@ drm_coredump_printer(struct drm_print_iterator *iter) } /** + * drm_coredump_printer_is_full() - DRM coredump printer output is full + * @p: DRM coredump printer + * + * DRM printer output is full, useful to short circuit coredump printing once + * printer is full. + * + * RETURNS: + * True if DRM coredump printer output buffer is full, False otherwise + */ +static inline bool drm_coredump_printer_is_full(struct drm_printer *p) +{ + struct drm_print_iterator *iterator = p->arg; + + if (p->printfn != __drm_printfn_coredump) + return true; + + return !iterator->remain; +} + +/** * drm_seq_file_printer - construct a &drm_printer that outputs to &seq_file * @f: the &struct seq_file to output to * @@ -208,113 +398,141 @@ static inline struct drm_printer drm_info_printer(struct device *dev) } /** - * drm_debug_printer - construct a &drm_printer that outputs to pr_debug() - * @prefix: debug output prefix + * drm_dbg_printer - construct a &drm_printer for drm device specific output + * @drm: the &struct drm_device pointer, or NULL + * @category: the debug category to use + * @prefix: debug output prefix, or NULL for no prefix * * RETURNS: * The &drm_printer object */ -static inline struct drm_printer drm_debug_printer(const char *prefix) +static inline struct drm_printer drm_dbg_printer(struct drm_device *drm, + enum drm_debug_category category, + const char *prefix) { struct drm_printer p = { - .printfn = __drm_printfn_debug, + .printfn = __drm_printfn_dbg, + .arg = drm, + .origin = (const void *)_THIS_IP_, /* it's fine as we will be inlined */ + .prefix = prefix, + .category = category, + }; + return p; +} + +/** + * drm_err_printer - construct a &drm_printer that outputs to drm_err() + * @drm: the &struct drm_device pointer + * @prefix: debug output prefix, or NULL for no prefix + * + * RETURNS: + * The &drm_printer object + */ +static inline struct drm_printer drm_err_printer(struct drm_device *drm, + const char *prefix) +{ + struct drm_printer p = { + .printfn = __drm_printfn_err, + .arg = drm, .prefix = prefix }; return p; } -/* - * The following categories are defined: +/** + * drm_line_printer - construct a &drm_printer that prefixes outputs with line numbers + * @p: the &struct drm_printer which actually generates the output + * @prefix: optional output prefix, or NULL for no prefix + * @series: optional unique series identifier, or 0 to omit identifier in the output * - * CORE: Used in the generic drm code: drm_ioctl.c, drm_mm.c, drm_memory.c, ... - * This is the category used by the DRM_DEBUG() macro. + * This printer can be used to increase the robustness of the captured output + * to make sure we didn't lost any intermediate lines of the output. Helpful + * while capturing some crash data. * - * DRIVER: Used in the vendor specific part of the driver: i915, radeon, ... - * This is the category used by the DRM_DEBUG_DRIVER() macro. + * Example 1:: * - * KMS: used in the modesetting code. - * This is the category used by the DRM_DEBUG_KMS() macro. + * void crash_dump(struct drm_device *drm) + * { + * static unsigned int id; + * struct drm_printer p = drm_err_printer(drm, "crash"); + * struct drm_printer lp = drm_line_printer(&p, "dump", ++id); * - * PRIME: used in the prime code. - * This is the category used by the DRM_DEBUG_PRIME() macro. + * drm_printf(&lp, "foo"); + * drm_printf(&lp, "bar"); + * } * - * ATOMIC: used in the atomic code. - * This is the category used by the DRM_DEBUG_ATOMIC() macro. + * Above code will print into the dmesg something like:: * - * VBL: used for verbose debug message in the vblank code - * This is the category used by the DRM_DEBUG_VBL() macro. + * [ ] 0000:00:00.0: [drm] *ERROR* crash dump 1.1: foo + * [ ] 0000:00:00.0: [drm] *ERROR* crash dump 1.2: bar * - * Enabling verbose debug messages is done through the drm.debug parameter, - * each category being enabled by a bit. + * Example 2:: * - * drm.debug=0x1 will enable CORE messages - * drm.debug=0x2 will enable DRIVER messages - * drm.debug=0x3 will enable CORE and DRIVER messages - * ... - * drm.debug=0x3f will enable all messages + * void line_dump(struct device *dev) + * { + * struct drm_printer p = drm_info_printer(dev); + * struct drm_printer lp = drm_line_printer(&p, NULL, 0); * - * An interesting feature is that it's possible to enable verbose logging at - * run-time by echoing the debug value in its sysfs node: - * # echo 0xf > /sys/module/drm/parameters/debug + * drm_printf(&lp, "foo"); + * drm_printf(&lp, "bar"); + * } + * + * Above code will print:: + * + * [ ] 0000:00:00.0: [drm] 1: foo + * [ ] 0000:00:00.0: [drm] 2: bar + * + * RETURNS: + * The &drm_printer object + */ +static inline struct drm_printer drm_line_printer(struct drm_printer *p, + const char *prefix, + unsigned int series) +{ + struct drm_printer lp = { + .printfn = __drm_printfn_line, + .arg = p, + .prefix = prefix, + .line = { .series = series, }, + }; + return lp; +} + +/* + * struct device based logging + * + * Prefer drm_device based logging over device or printk based logging. */ -#define DRM_UT_NONE 0x00 -#define DRM_UT_CORE 0x01 -#define DRM_UT_DRIVER 0x02 -#define DRM_UT_KMS 0x04 -#define DRM_UT_PRIME 0x08 -#define DRM_UT_ATOMIC 0x10 -#define DRM_UT_VBL 0x20 -#define DRM_UT_STATE 0x40 -#define DRM_UT_LEASE 0x80 -#define DRM_UT_DP 0x100 __printf(3, 4) void drm_dev_printk(const struct device *dev, const char *level, const char *format, ...); -__printf(3, 4) -void drm_dev_dbg(const struct device *dev, unsigned int category, - const char *format, ...); - -__printf(2, 3) -void drm_dbg(unsigned int category, const char *format, ...); -__printf(1, 2) -void drm_err(const char *format, ...); - -/* Macros to make printk easier */ - -#define _DRM_PRINTK(once, level, fmt, ...) \ - printk##once(KERN_##level "[" DRM_NAME "] " fmt, ##__VA_ARGS__) - -#define DRM_INFO(fmt, ...) \ - _DRM_PRINTK(, INFO, fmt, ##__VA_ARGS__) -#define DRM_NOTE(fmt, ...) \ - _DRM_PRINTK(, NOTICE, fmt, ##__VA_ARGS__) -#define DRM_WARN(fmt, ...) \ - _DRM_PRINTK(, WARNING, fmt, ##__VA_ARGS__) - -#define DRM_INFO_ONCE(fmt, ...) \ - _DRM_PRINTK(_once, INFO, fmt, ##__VA_ARGS__) -#define DRM_NOTE_ONCE(fmt, ...) \ - _DRM_PRINTK(_once, NOTICE, fmt, ##__VA_ARGS__) -#define DRM_WARN_ONCE(fmt, ...) \ - _DRM_PRINTK(_once, WARNING, fmt, ##__VA_ARGS__) +struct _ddebug; +__printf(4, 5) +void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev, + enum drm_debug_category category, const char *format, ...); /** - * Error output. + * DRM_DEV_ERROR() - Error output. + * + * NOTE: this is deprecated in favor of drm_err() or dev_err(). * * @dev: device pointer * @fmt: printf() like format string. */ #define DRM_DEV_ERROR(dev, fmt, ...) \ drm_dev_printk(dev, KERN_ERR, "*ERROR* " fmt, ##__VA_ARGS__) -#define DRM_ERROR(fmt, ...) \ - drm_err(fmt, ##__VA_ARGS__) /** - * Rate limited error output. Like DRM_ERROR() but won't flood the log. + * DRM_DEV_ERROR_RATELIMITED() - Rate limited error output. + * + * NOTE: this is deprecated in favor of drm_err_ratelimited() or + * dev_err_ratelimited(). * * @dev: device pointer * @fmt: printf() like format string. + * + * Like DRM_ERROR() but won't flood the log. */ #define DRM_DEV_ERROR_RATELIMITED(dev, fmt, ...) \ ({ \ @@ -325,12 +543,12 @@ void drm_err(const char *format, ...); if (__ratelimit(&_rs)) \ DRM_DEV_ERROR(dev, fmt, ##__VA_ARGS__); \ }) -#define DRM_ERROR_RATELIMITED(fmt, ...) \ - DRM_DEV_ERROR_RATELIMITED(NULL, fmt, ##__VA_ARGS__) -#define DRM_DEV_INFO(dev, fmt, ...) \ +/* NOTE: this is deprecated in favor of drm_info() or dev_info(). */ +#define DRM_DEV_INFO(dev, fmt, ...) \ drm_dev_printk(dev, KERN_INFO, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of drm_info_once() or dev_info_once(). */ #define DRM_DEV_INFO_ONCE(dev, fmt, ...) \ ({ \ static bool __print_once __read_mostly; \ @@ -340,87 +558,239 @@ void drm_err(const char *format, ...); } \ }) +#if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) +#define drm_dev_dbg(dev, cat, fmt, ...) \ + __drm_dev_dbg(NULL, dev, cat, fmt, ##__VA_ARGS__) +#else +#define drm_dev_dbg(dev, cat, fmt, ...) \ + _dynamic_func_call_cls(cat, fmt, __drm_dev_dbg, \ + dev, cat, fmt, ##__VA_ARGS__) +#endif + /** - * Debug output. + * DRM_DEV_DEBUG() - Debug output for generic drm code + * + * NOTE: this is deprecated in favor of drm_dbg_core(). * * @dev: device pointer * @fmt: printf() like format string. */ #define DRM_DEV_DEBUG(dev, fmt, ...) \ drm_dev_dbg(dev, DRM_UT_CORE, fmt, ##__VA_ARGS__) -#define DRM_DEBUG(fmt, ...) \ - drm_dbg(DRM_UT_CORE, fmt, ##__VA_ARGS__) - +/** + * DRM_DEV_DEBUG_DRIVER() - Debug output for vendor specific part of the driver + * + * NOTE: this is deprecated in favor of drm_dbg() or dev_dbg(). + * + * @dev: device pointer + * @fmt: printf() like format string. + */ #define DRM_DEV_DEBUG_DRIVER(dev, fmt, ...) \ drm_dev_dbg(dev, DRM_UT_DRIVER, fmt, ##__VA_ARGS__) -#define DRM_DEBUG_DRIVER(fmt, ...) \ - drm_dbg(DRM_UT_DRIVER, fmt, ##__VA_ARGS__) - +/** + * DRM_DEV_DEBUG_KMS() - Debug output for modesetting code + * + * NOTE: this is deprecated in favor of drm_dbg_kms(). + * + * @dev: device pointer + * @fmt: printf() like format string. + */ #define DRM_DEV_DEBUG_KMS(dev, fmt, ...) \ drm_dev_dbg(dev, DRM_UT_KMS, fmt, ##__VA_ARGS__) + +/* + * struct drm_device based logging + * + * Prefer drm_device based logging over device or prink based logging. + */ + +/* Helper to enforce struct drm_device type */ +static inline struct device *__drm_to_dev(const struct drm_device *drm) +{ + return drm ? drm->dev : NULL; +} + +/* Helper for struct drm_device based logging. */ +#define __drm_printk(drm, level, type, fmt, ...) \ + dev_##level##type(__drm_to_dev(drm), "[drm] " fmt, ##__VA_ARGS__) + + +#define drm_info(drm, fmt, ...) \ + __drm_printk((drm), info,, fmt, ##__VA_ARGS__) + +#define drm_notice(drm, fmt, ...) \ + __drm_printk((drm), notice,, fmt, ##__VA_ARGS__) + +#define drm_warn(drm, fmt, ...) \ + __drm_printk((drm), warn,, fmt, ##__VA_ARGS__) + +#define drm_err(drm, fmt, ...) \ + __drm_printk((drm), err,, "*ERROR* " fmt, ##__VA_ARGS__) + + +#define drm_info_once(drm, fmt, ...) \ + __drm_printk((drm), info, _once, fmt, ##__VA_ARGS__) + +#define drm_notice_once(drm, fmt, ...) \ + __drm_printk((drm), notice, _once, fmt, ##__VA_ARGS__) + +#define drm_warn_once(drm, fmt, ...) \ + __drm_printk((drm), warn, _once, fmt, ##__VA_ARGS__) + +#define drm_err_once(drm, fmt, ...) \ + __drm_printk((drm), err, _once, "*ERROR* " fmt, ##__VA_ARGS__) + + +#define drm_err_ratelimited(drm, fmt, ...) \ + __drm_printk((drm), err, _ratelimited, "*ERROR* " fmt, ##__VA_ARGS__) + + +#define drm_dbg_core(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__) +#define drm_dbg_driver(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRIVER, fmt, ##__VA_ARGS__) +#define drm_dbg_kms(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_KMS, fmt, ##__VA_ARGS__) +#define drm_dbg_prime(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_PRIME, fmt, ##__VA_ARGS__) +#define drm_dbg_atomic(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_ATOMIC, fmt, ##__VA_ARGS__) +#define drm_dbg_vbl(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_VBL, fmt, ##__VA_ARGS__) +#define drm_dbg_state(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_STATE, fmt, ##__VA_ARGS__) +#define drm_dbg_lease(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_LEASE, fmt, ##__VA_ARGS__) +#define drm_dbg_dp(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DP, fmt, ##__VA_ARGS__) +#define drm_dbg_drmres(drm, fmt, ...) \ + drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRMRES, fmt, ##__VA_ARGS__) + +#define drm_dbg(drm, fmt, ...) drm_dbg_driver(drm, fmt, ##__VA_ARGS__) + +/* + * printk based logging + * + * Prefer drm_device based logging over device or prink based logging. + */ + +__printf(1, 2) +void __drm_err(const char *format, ...); + +#if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) +#define __drm_dbg(cat, fmt, ...) __drm_dev_dbg(NULL, NULL, cat, fmt, ##__VA_ARGS__) +#else +#define __drm_dbg(cat, fmt, ...) \ + _dynamic_func_call_cls(cat, fmt, __drm_dev_dbg, \ + NULL, cat, fmt, ##__VA_ARGS__) +#endif + +/* Macros to make printk easier */ + +#define _DRM_PRINTK(once, level, fmt, ...) \ + printk##once(KERN_##level "[" DRM_NAME "] " fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of pr_info(). */ +#define DRM_INFO(fmt, ...) \ + _DRM_PRINTK(, INFO, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of pr_notice(). */ +#define DRM_NOTE(fmt, ...) \ + _DRM_PRINTK(, NOTICE, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of pr_warn(). */ +#define DRM_WARN(fmt, ...) \ + _DRM_PRINTK(, WARNING, fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of pr_info_once(). */ +#define DRM_INFO_ONCE(fmt, ...) \ + _DRM_PRINTK(_once, INFO, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of pr_notice_once(). */ +#define DRM_NOTE_ONCE(fmt, ...) \ + _DRM_PRINTK(_once, NOTICE, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of pr_warn_once(). */ +#define DRM_WARN_ONCE(fmt, ...) \ + _DRM_PRINTK(_once, WARNING, fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of pr_err(). */ +#define DRM_ERROR(fmt, ...) \ + __drm_err(fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of pr_err_ratelimited(). */ +#define DRM_ERROR_RATELIMITED(fmt, ...) \ + DRM_DEV_ERROR_RATELIMITED(NULL, fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of drm_dbg_core(NULL, ...). */ +#define DRM_DEBUG(fmt, ...) \ + __drm_dbg(DRM_UT_CORE, fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of drm_dbg(NULL, ...). */ +#define DRM_DEBUG_DRIVER(fmt, ...) \ + __drm_dbg(DRM_UT_DRIVER, fmt, ##__VA_ARGS__) + +/* NOTE: this is deprecated in favor of drm_dbg_kms(NULL, ...). */ #define DRM_DEBUG_KMS(fmt, ...) \ - drm_dbg(DRM_UT_KMS, fmt, ##__VA_ARGS__) + __drm_dbg(DRM_UT_KMS, fmt, ##__VA_ARGS__) -#define DRM_DEV_DEBUG_PRIME(dev, fmt, ...) \ - drm_dev_dbg(dev, DRM_UT_PRIME, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of drm_dbg_prime(NULL, ...). */ #define DRM_DEBUG_PRIME(fmt, ...) \ - drm_dbg(DRM_UT_PRIME, fmt, ##__VA_ARGS__) + __drm_dbg(DRM_UT_PRIME, fmt, ##__VA_ARGS__) -#define DRM_DEV_DEBUG_ATOMIC(dev, fmt, ...) \ - drm_dev_dbg(dev, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of drm_dbg_atomic(NULL, ...). */ #define DRM_DEBUG_ATOMIC(fmt, ...) \ - drm_dbg(DRM_UT_ATOMIC, fmt, ##__VA_ARGS__) + __drm_dbg(DRM_UT_ATOMIC, fmt, ##__VA_ARGS__) -#define DRM_DEV_DEBUG_VBL(dev, fmt, ...) \ - drm_dev_dbg(dev, DRM_UT_VBL, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of drm_dbg_vbl(NULL, ...). */ #define DRM_DEBUG_VBL(fmt, ...) \ - drm_dbg(DRM_UT_VBL, fmt, ##__VA_ARGS__) + __drm_dbg(DRM_UT_VBL, fmt, ##__VA_ARGS__) +/* NOTE: this is deprecated in favor of drm_dbg_lease(NULL, ...). */ #define DRM_DEBUG_LEASE(fmt, ...) \ - drm_dbg(DRM_UT_LEASE, fmt, ##__VA_ARGS__) + __drm_dbg(DRM_UT_LEASE, fmt, ##__VA_ARGS__) -#define DRM_DEV_DEBUG_DP(dev, fmt, ...) \ - drm_dev_dbg(dev, DRM_UT_DP, fmt, ## __VA_ARGS__) +/* NOTE: this is deprecated in favor of drm_dbg_dp(NULL, ...). */ #define DRM_DEBUG_DP(fmt, ...) \ - drm_dbg(DRM_UT_DP, fmt, ## __VA_ARGS__) - -#define _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, category, fmt, ...) \ -({ \ - static DEFINE_RATELIMIT_STATE(_rs, \ - DEFAULT_RATELIMIT_INTERVAL, \ - DEFAULT_RATELIMIT_BURST); \ - if (__ratelimit(&_rs)) \ - drm_dev_dbg(dev, category, fmt, ##__VA_ARGS__); \ + __drm_dbg(DRM_UT_DP, fmt, ## __VA_ARGS__) + +#define __DRM_DEFINE_DBG_RATELIMITED(category, drm, fmt, ...) \ +({ \ + static DEFINE_RATELIMIT_STATE(rs_, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);\ + \ + if (drm_debug_enabled(DRM_UT_ ## category) && __ratelimit(&rs_)) \ + drm_dev_printk(__drm_to_dev(drm), KERN_DEBUG, fmt, ## __VA_ARGS__); \ }) -/** - * Rate limited debug output. Like DRM_DEBUG() but won't flood the log. +#define drm_dbg_ratelimited(drm, fmt, ...) \ + __DRM_DEFINE_DBG_RATELIMITED(DRIVER, drm, fmt, ## __VA_ARGS__) + +#define drm_dbg_kms_ratelimited(drm, fmt, ...) \ + __DRM_DEFINE_DBG_RATELIMITED(KMS, drm, fmt, ## __VA_ARGS__) + +/* + * struct drm_device based WARNs * - * @dev: device pointer - * @fmt: printf() like format string. + * drm_WARN*() acts like WARN*(), but with the key difference of + * using device specific information so that we know from which device + * warning is originating from. + * + * Prefer drm_device based drm_WARN* over regular WARN* */ -#define DRM_DEV_DEBUG_RATELIMITED(dev, fmt, ...) \ - _DEV_DRM_DEFINE_DEBUG_RATELIMITED(dev, DRM_UT_CORE, \ - fmt, ##__VA_ARGS__) -#define DRM_DEBUG_RATELIMITED(fmt, ...) \ - DRM_DEV_DEBUG_RATELIMITED(NULL, fmt, ##__VA_ARGS__) - -#define DRM_DEV_DEBUG_DRIVER_RATELIMITED(dev, fmt, ...) \ - _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, DRM_UT_DRIVER, \ - fmt, ##__VA_ARGS__) -#define DRM_DEBUG_DRIVER_RATELIMITED(fmt, ...) \ - DRM_DEV_DEBUG_DRIVER_RATELIMITED(NULL, fmt, ##__VA_ARGS__) - -#define DRM_DEV_DEBUG_KMS_RATELIMITED(dev, fmt, ...) \ - _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, DRM_UT_KMS, \ - fmt, ##__VA_ARGS__) -#define DRM_DEBUG_KMS_RATELIMITED(fmt, ...) \ - DRM_DEV_DEBUG_KMS_RATELIMITED(NULL, fmt, ##__VA_ARGS__) - -#define DRM_DEV_DEBUG_PRIME_RATELIMITED(dev, fmt, ...) \ - _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, DRM_UT_PRIME, \ - fmt, ##__VA_ARGS__) -#define DRM_DEBUG_PRIME_RATELIMITED(fmt, ...) \ - DRM_DEV_DEBUG_PRIME_RATELIMITED(NULL, fmt, ##__VA_ARGS__) + +/* Helper for struct drm_device based WARNs */ +#define drm_WARN(drm, condition, format, arg...) \ + WARN(condition, "%s %s: [drm] " format, \ + dev_driver_string(__drm_to_dev(drm)), \ + dev_name(__drm_to_dev(drm)), ## arg) + +#define drm_WARN_ONCE(drm, condition, format, arg...) \ + WARN_ONCE(condition, "%s %s: [drm] " format, \ + dev_driver_string(__drm_to_dev(drm)), \ + dev_name(__drm_to_dev(drm)), ## arg) + +#define drm_WARN_ON(drm, x) \ + drm_WARN((drm), (x), "%s", \ + "drm_WARN_ON(" __stringify(x) ")") + +#define drm_WARN_ON_ONCE(drm, x) \ + drm_WARN_ONCE((drm), (x), "%s", \ + "drm_WARN_ON_ONCE(" __stringify(x) ")") #endif /* DRM_PRINT_H_ */ diff --git a/include/drm/drm_privacy_screen_consumer.h b/include/drm/drm_privacy_screen_consumer.h new file mode 100644 index 000000000000..7f66a90d15b7 --- /dev/null +++ b/include/drm/drm_privacy_screen_consumer.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef __DRM_PRIVACY_SCREEN_CONSUMER_H__ +#define __DRM_PRIVACY_SCREEN_CONSUMER_H__ + +#include <linux/device.h> +#include <drm/drm_connector.h> + +struct drm_privacy_screen; + +#if IS_ENABLED(CONFIG_DRM_PRIVACY_SCREEN) +struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev, + const char *con_id); +void drm_privacy_screen_put(struct drm_privacy_screen *priv); + +int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state); +void drm_privacy_screen_get_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status *sw_state_ret, + enum drm_privacy_screen_status *hw_state_ret); + +int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb); +int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb); +#else +static inline struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev, + const char *con_id) +{ + return ERR_PTR(-ENODEV); +} +static inline void drm_privacy_screen_put(struct drm_privacy_screen *priv) +{ +} +static inline int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state) +{ + return -ENODEV; +} +static inline void drm_privacy_screen_get_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status *sw_state_ret, + enum drm_privacy_screen_status *hw_state_ret) +{ + *sw_state_ret = PRIVACY_SCREEN_DISABLED; + *hw_state_ret = PRIVACY_SCREEN_DISABLED; +} +static inline int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb) +{ + return -ENODEV; +} +static inline int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv, + struct notifier_block *nb) +{ + return -ENODEV; +} +#endif + +#endif diff --git a/include/drm/drm_privacy_screen_driver.h b/include/drm/drm_privacy_screen_driver.h new file mode 100644 index 000000000000..4ef246d5706f --- /dev/null +++ b/include/drm/drm_privacy_screen_driver.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef __DRM_PRIVACY_SCREEN_DRIVER_H__ +#define __DRM_PRIVACY_SCREEN_DRIVER_H__ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <drm/drm_connector.h> + +struct drm_privacy_screen; + +/** + * struct drm_privacy_screen_ops - drm_privacy_screen operations + * + * Defines the operations which the privacy-screen class code may call. + * These functions should be implemented by the privacy-screen driver. + */ +struct drm_privacy_screen_ops { + /** + * @set_sw_state: Called to request a change of the privacy-screen + * state. The privacy-screen class code contains a check to avoid this + * getting called when the hw_state reports the state is locked. + * It is the driver's responsibility to update sw_state and hw_state. + * This is always called with the drm_privacy_screen's lock held. + */ + int (*set_sw_state)(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status sw_state); + /** + * @get_hw_state: Called to request that the driver gets the current + * privacy-screen state from the hardware and then updates sw_state and + * hw_state accordingly. This will be called by the core just before + * the privacy-screen is registered in sysfs. + */ + void (*get_hw_state)(struct drm_privacy_screen *priv); +}; + +/** + * struct drm_privacy_screen - central privacy-screen structure + * + * Central privacy-screen structure, this contains the struct device used + * to register the screen in sysfs, the screen's state, ops, etc. + */ +struct drm_privacy_screen { + /** @dev: device used to register the privacy-screen in sysfs. */ + struct device dev; + /** @lock: mutex protection all fields in this struct. */ + struct mutex lock; + /** @list: privacy-screen devices list list-entry. */ + struct list_head list; + /** @notifier_head: privacy-screen notifier head. */ + struct blocking_notifier_head notifier_head; + /** + * @ops: &struct drm_privacy_screen_ops for this privacy-screen. + * This is NULL if the driver has unregistered the privacy-screen. + */ + const struct drm_privacy_screen_ops *ops; + /** + * @sw_state: The privacy-screen's software state, see + * :ref:`Standard Connector Properties<standard_connector_properties>` + * for more info. + */ + enum drm_privacy_screen_status sw_state; + /** + * @hw_state: The privacy-screen's hardware state, see + * :ref:`Standard Connector Properties<standard_connector_properties>` + * for more info. + */ + enum drm_privacy_screen_status hw_state; + /** + * @drvdata: Private data owned by the privacy screen provider + */ + void *drvdata; +}; + +static inline +void *drm_privacy_screen_get_drvdata(struct drm_privacy_screen *priv) +{ + return priv->drvdata; +} + +struct drm_privacy_screen *drm_privacy_screen_register( + struct device *parent, const struct drm_privacy_screen_ops *ops, + void *data); +void drm_privacy_screen_unregister(struct drm_privacy_screen *priv); + +void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv); + +#endif diff --git a/include/drm/drm_privacy_screen_machine.h b/include/drm/drm_privacy_screen_machine.h new file mode 100644 index 000000000000..02e5371904d3 --- /dev/null +++ b/include/drm/drm_privacy_screen_machine.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef __DRM_PRIVACY_SCREEN_MACHINE_H__ +#define __DRM_PRIVACY_SCREEN_MACHINE_H__ + +#include <linux/list.h> + +/** + * struct drm_privacy_screen_lookup - static privacy-screen lookup list entry + * + * Used for the static lookup-list for mapping privacy-screen consumer + * dev-connector pairs to a privacy-screen provider. + */ +struct drm_privacy_screen_lookup { + /** @list: Lookup list list-entry. */ + struct list_head list; + /** @dev_id: Consumer device name or NULL to match all devices. */ + const char *dev_id; + /** @con_id: Consumer connector name or NULL to match all connectors. */ + const char *con_id; + /** @provider: dev_name() of the privacy_screen provider. */ + const char *provider; +}; + +void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup); +void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup); + +#if IS_ENABLED(CONFIG_DRM_PRIVACY_SCREEN) && IS_ENABLED(CONFIG_X86) +void drm_privacy_screen_lookup_init(void); +void drm_privacy_screen_lookup_exit(void); +#else +static inline void drm_privacy_screen_lookup_init(void) +{ +} +static inline void drm_privacy_screen_lookup_exit(void) +{ +} +#endif + +#endif diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h new file mode 100644 index 000000000000..840ae5f798c2 --- /dev/null +++ b/include/drm/drm_probe_helper.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +#ifndef __DRM_PROBE_HELPER_H__ +#define __DRM_PROBE_HELPER_H__ + +#include <drm/drm_modes.h> + +struct drm_connector; +struct drm_crtc; +struct drm_device; +struct drm_modeset_acquire_ctx; + +int drm_helper_probe_single_connector_modes(struct drm_connector + *connector, uint32_t maxX, + uint32_t maxY); +int drm_helper_probe_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force); + +void drmm_kms_helper_poll_init(struct drm_device *dev); +void drm_kms_helper_poll_init(struct drm_device *dev); +void drm_kms_helper_poll_fini(struct drm_device *dev); +bool drm_helper_hpd_irq_event(struct drm_device *dev); +bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector); +void drm_kms_helper_hotplug_event(struct drm_device *dev); +void drm_kms_helper_connector_hotplug_event(struct drm_connector *connector); + +void drm_kms_helper_poll_disable(struct drm_device *dev); +void drm_kms_helper_poll_enable(struct drm_device *dev); +void drm_kms_helper_poll_reschedule(struct drm_device *dev); +bool drm_kms_helper_is_poll_worker(void); + +enum drm_mode_status drm_crtc_helper_mode_valid_fixed(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + const struct drm_display_mode *fixed_mode); + +int drm_connector_helper_get_modes_fixed(struct drm_connector *connector, + const struct drm_display_mode *fixed_mode); +int drm_connector_helper_get_modes(struct drm_connector *connector); +int drm_connector_helper_tv_get_modes(struct drm_connector *connector); + +int drm_connector_helper_detect_from_ddc(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force); + +#endif diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h index 4a0a80d658c7..082f29156b3e 100644 --- a/include/drm/drm_property.h +++ b/include/drm/drm_property.h @@ -31,7 +31,6 @@ /** * struct drm_property_enum - symbolic values for enumerations - * @value: numeric property value for this enum entry * @head: list of enum values, linked to &drm_property.enum_list * @name: symbolic name for the enum * @@ -39,6 +38,14 @@ * decoding for each value. This is used for example for the rotation property. */ struct drm_property_enum { + /** + * @value: numeric property value for this enum entry + * + * If the property has the type &DRM_MODE_PROP_BITMASK, @value stores a + * bitshift, not a bitmask. In other words, the enum entry is enabled + * if the bit number @value is set in the property's value. This enum + * entry has the bitmask ``1 << value``. + */ uint64_t value; struct list_head head; char name[DRM_PROP_NAME_LEN]; @@ -114,7 +121,7 @@ struct drm_property { * by the property. Bitmask properties are created using * drm_property_create_bitmask(). * - * DRM_MODE_PROB_OBJECT + * DRM_MODE_PROP_OBJECT * Object properties are used to link modeset objects. This is used * extensively in the atomic support to create the display pipeline, * by linking &drm_framebuffer to &drm_plane, &drm_plane to @@ -272,6 +279,12 @@ struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, const void *data); struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, uint32_t id); +int drm_property_replace_blob_from_id(struct drm_device *dev, + struct drm_property_blob **blob, + uint64_t blob_id, + ssize_t expected_size, + ssize_t expected_elem_size, + bool *replaced); int drm_property_replace_global_blob(struct drm_device *dev, struct drm_property_blob **replace, size_t length, diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index 6c54544a4be7..46f09cf68458 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -24,6 +24,8 @@ #ifndef DRM_RECT_H #define DRM_RECT_H +#include <linux/types.h> + /** * DOC: rect utils * @@ -37,12 +39,31 @@ * @x2: horizontal ending coordinate (exclusive) * @y1: vertical starting coordinate (inclusive) * @y2: vertical ending coordinate (exclusive) + * + * Note that this must match the layout of struct drm_mode_rect or the damage + * helpers like drm_atomic_helper_damage_iter_init() break. */ struct drm_rect { int x1, y1, x2, y2; }; /** + * DRM_RECT_INIT - initialize a rectangle from x/y/w/h + * @x: x coordinate + * @y: y coordinate + * @w: width + * @h: height + * + * RETURNS: + * A new rectangle of the specified size. + */ +#define DRM_RECT_INIT(x, y, w, h) ((struct drm_rect){ \ + .x1 = (x), \ + .y1 = (y), \ + .x2 = (x) + (w), \ + .y2 = (y) + (h) }) + +/** * DRM_RECT_FMT - printf string for &struct drm_rect */ #define DRM_RECT_FMT "%dx%d%+d%+d" @@ -70,6 +91,23 @@ struct drm_rect { (r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10 /** + * drm_rect_init - initialize the rectangle from x/y/w/h + * @r: rectangle + * @x: x coordinate + * @y: y coordinate + * @width: width + * @height: height + */ +static inline void drm_rect_init(struct drm_rect *r, int x, int y, + int width, int height) +{ + r->x1 = x; + r->y1 = y; + r->x2 = x + width; + r->y2 = y + height; +} + +/** * drm_rect_adjust_size - adjust the size of the rectangle * @r: rectangle to be adjusted * @dw: horizontal adjustment @@ -91,7 +129,7 @@ static inline void drm_rect_adjust_size(struct drm_rect *r, int dw, int dh) /** * drm_rect_translate - translate the rectangle - * @r: rectangle to be tranlated + * @r: rectangle to be translated * @dx: horizontal translation * @dy: vertical translation * @@ -107,6 +145,20 @@ static inline void drm_rect_translate(struct drm_rect *r, int dx, int dy) } /** + * drm_rect_translate_to - translate the rectangle to an absolute position + * @r: rectangle to be translated + * @x: horizontal position + * @y: vertical position + * + * Move rectangle @r to @x in the horizontal direction, + * and to @y in the vertical direction. + */ +static inline void drm_rect_translate_to(struct drm_rect *r, int x, int y) +{ + drm_rect_translate(r, x - r->x1, y - r->y1); +} + +/** * drm_rect_downscale - downscale a rectangle * @r: rectangle to be downscaled * @horz: horizontal downscale factor @@ -147,7 +199,7 @@ static inline int drm_rect_height(const struct drm_rect *r) } /** - * drm_rect_visible - determine if the the rectangle is visible + * drm_rect_visible - determine if the rectangle is visible * @r: rectangle whose visibility is returned * * RETURNS: @@ -173,6 +225,34 @@ static inline bool drm_rect_equals(const struct drm_rect *r1, r1->y1 == r2->y1 && r1->y2 == r2->y2; } +/** + * drm_rect_fp_to_int - Convert a rect in 16.16 fixed point form to int form. + * @dst: rect to be stored the converted value + * @src: rect in 16.16 fixed point form + */ +static inline void drm_rect_fp_to_int(struct drm_rect *dst, + const struct drm_rect *src) +{ + drm_rect_init(dst, src->x1 >> 16, src->y1 >> 16, + drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16); +} + +/** + * drm_rect_overlap - Check if two rectangles overlap + * @a: first rectangle + * @b: second rectangle + * + * RETURNS: + * %true if the rectangles overlap, %false otherwise. + */ +static inline bool drm_rect_overlap(const struct drm_rect *a, + const struct drm_rect *b) +{ + return (a->x2 > b->x1 && b->x2 > a->x1 && + a->y2 > b->y1 && b->y2 > a->y1); +} + bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, const struct drm_rect *clip); @@ -182,12 +262,6 @@ int drm_rect_calc_hscale(const struct drm_rect *src, int drm_rect_calc_vscale(const struct drm_rect *src, const struct drm_rect *dst, int min_vscale, int max_vscale); -int drm_rect_calc_hscale_relaxed(struct drm_rect *src, - struct drm_rect *dst, - int min_hscale, int max_hscale); -int drm_rect_calc_vscale_relaxed(struct drm_rect *src, - struct drm_rect *dst, - int min_vscale, int max_vscale); void drm_rect_debug_print(const char *prefix, const struct drm_rect *r, bool fixed_point); void drm_rect_rotate(struct drm_rect *r, diff --git a/include/drm/drm_self_refresh_helper.h b/include/drm/drm_self_refresh_helper.h new file mode 100644 index 000000000000..520235c20708 --- /dev/null +++ b/include/drm/drm_self_refresh_helper.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul <seanpaul@chromium.org> + */ +#ifndef DRM_SELF_REFRESH_HELPER_H_ +#define DRM_SELF_REFRESH_HELPER_H_ + +struct drm_atomic_state; +struct drm_crtc; + +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state); +void drm_self_refresh_helper_update_avg_times(struct drm_atomic_state *state, + unsigned int commit_time_ms, + unsigned int new_self_refresh_mask); + +int drm_self_refresh_helper_init(struct drm_crtc *crtc); +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc); +#endif diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 451960438a29..b2486d073763 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -1,10 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2016 Noralf Trønnes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __LINUX_DRM_SIMPLE_KMS_HELPER_H @@ -53,7 +49,7 @@ struct drm_simple_display_pipe_funcs { * * drm_mode_status Enum */ - enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc, + enum drm_mode_status (*mode_valid)(struct drm_simple_display_pipe *pipe, const struct drm_display_mode *mode); /** @@ -104,8 +100,11 @@ struct drm_simple_display_pipe_funcs { * This is the function drivers should submit the * &drm_pending_vblank_event from. Using either * drm_crtc_arm_vblank_event(), when the driver supports vblank - * interrupt handling, or drm_crtc_send_vblank_event() directly in case - * the hardware lacks vblank support entirely. + * interrupt handling, or drm_crtc_send_vblank_event() for more + * complex case. In case the hardware lacks vblank support entirely, + * drivers can set &struct drm_crtc_state.no_vblank in + * &struct drm_simple_display_pipe_funcs.check and let DRM's + * atomic helper fake a vblank event. */ void (*update)(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_plane_state); @@ -117,8 +116,11 @@ struct drm_simple_display_pipe_funcs { * the documentation for the &drm_plane_helper_funcs.prepare_fb hook for * more details. * - * Drivers which always have their buffers pinned should use - * drm_gem_fb_simple_display_pipe_prepare_fb() for this hook. + * For GEM drivers who neither have a @prepare_fb nor @cleanup_fb hook + * set, drm_gem_plane_helper_prepare_fb() is called automatically + * to implement this. Other drivers which need additional plane + * processing can call drm_gem_plane_helper_prepare_fb() from + * their @prepare_fb hook. */ int (*prepare_fb)(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state); @@ -134,6 +136,26 @@ struct drm_simple_display_pipe_funcs { struct drm_plane_state *plane_state); /** + * @begin_fb_access: + * + * Optional, called by &drm_plane_helper_funcs.begin_fb_access. Please read + * the documentation for the &drm_plane_helper_funcs.begin_fb_access hook for + * more details. + */ + int (*begin_fb_access)(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *new_plane_state); + + /** + * @end_fb_access: + * + * Optional, called by &drm_plane_helper_funcs.end_fb_access. Please read + * the documentation for the &drm_plane_helper_funcs.end_fb_access hook for + * more details. + */ + void (*end_fb_access)(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); + + /** * @enable_vblank: * * Optional, called by &drm_crtc_funcs.enable_vblank. Please read @@ -150,6 +172,60 @@ struct drm_simple_display_pipe_funcs { * more details. */ void (*disable_vblank)(struct drm_simple_display_pipe *pipe); + + /** + * @reset_crtc: + * + * Optional, called by &drm_crtc_funcs.reset. Please read the + * documentation for the &drm_crtc_funcs.reset hook for more details. + */ + void (*reset_crtc)(struct drm_simple_display_pipe *pipe); + + /** + * @duplicate_crtc_state: + * + * Optional, called by &drm_crtc_funcs.atomic_duplicate_state. Please + * read the documentation for the &drm_crtc_funcs.atomic_duplicate_state + * hook for more details. + */ + struct drm_crtc_state * (*duplicate_crtc_state)(struct drm_simple_display_pipe *pipe); + + /** + * @destroy_crtc_state: + * + * Optional, called by &drm_crtc_funcs.atomic_destroy_state. Please + * read the documentation for the &drm_crtc_funcs.atomic_destroy_state + * hook for more details. + */ + void (*destroy_crtc_state)(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state); + + /** + * @reset_plane: + * + * Optional, called by &drm_plane_funcs.reset. Please read the + * documentation for the &drm_plane_funcs.reset hook for more details. + */ + void (*reset_plane)(struct drm_simple_display_pipe *pipe); + + /** + * @duplicate_plane_state: + * + * Optional, called by &drm_plane_funcs.atomic_duplicate_state. Please + * read the documentation for the &drm_plane_funcs.atomic_duplicate_state + * hook for more details. + */ + struct drm_plane_state * (*duplicate_plane_state)(struct drm_simple_display_pipe *pipe); + + /** + * @destroy_plane_state: + * + * Optional, called by &drm_plane_funcs.atomic_destroy_state. Please + * read the documentation for the &drm_plane_funcs.atomic_destroy_state + * hook for more details. + */ + void (*destroy_plane_state)(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); }; /** @@ -182,4 +258,32 @@ int drm_simple_display_pipe_init(struct drm_device *dev, const uint64_t *format_modifiers, struct drm_connector *connector); +int drm_simple_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + int encoder_type); + +void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size, + size_t offset, int encoder_type); + +/** + * drmm_simple_encoder_alloc - Allocate and initialize an encoder with basic + * functionality. + * @dev: drm device + * @type: the type of the struct which contains struct &drm_encoder + * @member: the name of the &drm_encoder within @type. + * @encoder_type: user visible type of the encoder + * + * Allocates and initializes an encoder that has no further functionality. + * Settings for possible CRTC and clones are left to their initial values. + * Cleanup is automatically handled through registering drm_encoder_cleanup() + * with drmm_add_action(). + * + * Returns: + * Pointer to new encoder, or ERR_PTR on failure. + */ +#define drmm_simple_encoder_alloc(dev, type, member, encoder_type) \ + ((type *)__drmm_simple_encoder_alloc(dev, sizeof(type), \ + offsetof(type, member), \ + encoder_type)) + #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */ diff --git a/include/drm/drm_suballoc.h b/include/drm/drm_suballoc.h new file mode 100644 index 000000000000..7ba72a81a808 --- /dev/null +++ b/include/drm/drm_suballoc.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2011 Red Hat Inc. + * Copyright © 2022 Intel Corporation + */ +#ifndef _DRM_SUBALLOC_H_ +#define _DRM_SUBALLOC_H_ + +#include <drm/drm_mm.h> + +#include <linux/dma-fence.h> +#include <linux/types.h> + +#define DRM_SUBALLOC_MAX_QUEUES 32 +/** + * struct drm_suballoc_manager - fenced range allocations + * @wq: Wait queue for sleeping allocations on contention. + * @hole: Pointer to first hole node. + * @olist: List of allocated ranges. + * @flist: Array[fence context hash] of queues of fenced allocated ranges. + * @size: Size of the managed range. + * @align: Default alignment for the managed range. + */ +struct drm_suballoc_manager { + wait_queue_head_t wq; + struct list_head *hole; + struct list_head olist; + struct list_head flist[DRM_SUBALLOC_MAX_QUEUES]; + size_t size; + size_t align; +}; + +/** + * struct drm_suballoc - Sub-allocated range + * @olist: List link for list of allocated ranges. + * @flist: List linkk for the manager fenced allocated ranges queues. + * @manager: The drm_suballoc_manager. + * @soffset: Start offset. + * @eoffset: End offset + 1 so that @eoffset - @soffset = size. + * @fence: The fence protecting the allocation. + */ +struct drm_suballoc { + struct list_head olist; + struct list_head flist; + struct drm_suballoc_manager *manager; + size_t soffset; + size_t eoffset; + struct dma_fence *fence; +}; + +void drm_suballoc_manager_init(struct drm_suballoc_manager *sa_manager, + size_t size, size_t align); + +void drm_suballoc_manager_fini(struct drm_suballoc_manager *sa_manager); + +struct drm_suballoc * +drm_suballoc_new(struct drm_suballoc_manager *sa_manager, size_t size, + gfp_t gfp, bool intr, size_t align); + +void drm_suballoc_free(struct drm_suballoc *sa, struct dma_fence *fence); + +/** + * drm_suballoc_soffset - Range start. + * @sa: The struct drm_suballoc. + * + * Return: The start of the allocated range. + */ +static inline size_t drm_suballoc_soffset(struct drm_suballoc *sa) +{ + return sa->soffset; +} + +/** + * drm_suballoc_eoffset - Range end. + * @sa: The struct drm_suballoc. + * + * Return: The end of the allocated range + 1. + */ +static inline size_t drm_suballoc_eoffset(struct drm_suballoc *sa) +{ + return sa->eoffset; +} + +/** + * drm_suballoc_size - Range size. + * @sa: The struct drm_suballoc. + * + * Return: The size of the allocated range. + */ +static inline size_t drm_suballoc_size(struct drm_suballoc *sa) +{ + return sa->eoffset - sa->soffset; +} + +#ifdef CONFIG_DEBUG_FS +void drm_suballoc_dump_debug_info(struct drm_suballoc_manager *sa_manager, + struct drm_printer *p, + unsigned long long suballoc_base); +#else +static inline void +drm_suballoc_dump_debug_info(struct drm_suballoc_manager *sa_manager, + struct drm_printer *p, + unsigned long long suballoc_base) +{ } + +#endif + +#endif /* _DRM_SUBALLOC_H_ */ diff --git a/include/drm/drm_syncobj.h b/include/drm/drm_syncobj.h index b1fe921f8e8f..b40052132e52 100644 --- a/include/drm/drm_syncobj.h +++ b/include/drm/drm_syncobj.h @@ -26,9 +26,10 @@ #ifndef __DRM_SYNCOBJ_H__ #define __DRM_SYNCOBJ_H__ -#include "linux/dma-fence.h" +#include <linux/dma-fence.h> +#include <linux/dma-fence-chain.h> -struct drm_syncobj_cb; +struct drm_file; /** * struct drm_syncobj - sync object. @@ -53,7 +54,11 @@ struct drm_syncobj { */ struct list_head cb_list; /** - * @lock: Protects &cb_list and write-locks &fence. + * @ev_fd_list: List of registered eventfd. + */ + struct list_head ev_fd_list; + /** + * @lock: Protects &cb_list and &ev_fd_list, and write-locks &fence. */ spinlock_t lock; /** @@ -62,25 +67,6 @@ struct drm_syncobj { struct file *file; }; -typedef void (*drm_syncobj_func_t)(struct drm_syncobj *syncobj, - struct drm_syncobj_cb *cb); - -/** - * struct drm_syncobj_cb - callback for drm_syncobj_add_callback - * @node: used by drm_syncob_add_callback to append this struct to - * &drm_syncobj.cb_list - * @func: drm_syncobj_func_t to call - * - * This struct will be initialized by drm_syncobj_add_callback, additional - * data can be passed along by embedding drm_syncobj_cb in another struct. - * The callback will get called the next time drm_syncobj_replace_fence is - * called. - */ -struct drm_syncobj_cb { - struct list_head node; - drm_syncobj_func_t func; -}; - void drm_syncobj_free(struct kref *kref); /** @@ -131,6 +117,10 @@ drm_syncobj_fence_get(struct drm_syncobj *syncobj) struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, u32 handle); +void drm_syncobj_add_point(struct drm_syncobj *syncobj, + struct dma_fence_chain *chain, + struct dma_fence *fence, + uint64_t point); void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, struct dma_fence *fence); int drm_syncobj_find_fence(struct drm_file *file_private, diff --git a/include/drm/drm_sysfs.h b/include/drm/drm_sysfs.h index 4f311e836cdc..96a5d858404b 100644 --- a/include/drm/drm_sysfs.h +++ b/include/drm/drm_sysfs.h @@ -4,10 +4,14 @@ struct drm_device; struct device; +struct drm_connector; +struct drm_property; int drm_class_device_register(struct device *dev); void drm_class_device_unregister(struct device *dev); void drm_sysfs_hotplug_event(struct drm_device *dev); - +void drm_sysfs_connector_hotplug_event(struct drm_connector *connector); +void drm_sysfs_connector_property_event(struct drm_connector *connector, + struct drm_property *property); #endif diff --git a/include/drm/drm_util.h b/include/drm/drm_util.h index 88abdca89baa..440199618620 100644 --- a/include/drm/drm_util.h +++ b/include/drm/drm_util.h @@ -26,7 +26,44 @@ #ifndef _DRM_UTIL_H_ #define _DRM_UTIL_H_ -/* helper for handling conditionals in various for_each macros */ -#define for_each_if(condition) if (!(condition)) {} else +/** + * DOC: drm utils + * + * Macros and inline functions that does not naturally belong in other places + */ + +#include <linux/interrupt.h> +#include <linux/kgdb.h> +#include <linux/preempt.h> +#include <linux/smp.h> +#include <linux/util_macros.h> + +/* + * Use EXPORT_SYMBOL_FOR_TESTS_ONLY() for functions that shall + * only be visible for drmselftests. + */ +#if defined(CONFIG_DRM_EXPORT_FOR_TESTS) +#define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) EXPORT_SYMBOL(x) +#else +#define EXPORT_SYMBOL_FOR_TESTS_ONLY(x) +#endif + +/** + * drm_can_sleep - returns true if currently okay to sleep + * + * This function shall not be used in new code. + * The check for running in atomic context may not work - see linux/preempt.h. + * + * FIXME: All users of drm_can_sleep should be removed (see todo.rst) + * + * Returns: + * False if kgdb is active, we are in atomic context or irqs are disabled. + */ +static inline bool drm_can_sleep(void) +{ + if (in_atomic() || in_dbg_master() || irqs_disabled()) + return false; + return true; +} #endif diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h index a803988d8579..6a46f755daba 100644 --- a/include/drm/drm_utils.h +++ b/include/drm/drm_utils.h @@ -10,6 +10,20 @@ #ifndef __DRM_UTILS_H__ #define __DRM_UTILS_H__ +#include <linux/types.h> + +struct drm_edid; + int drm_get_panel_orientation_quirk(int width, int height); +struct drm_panel_backlight_quirk { + u16 min_brightness; + u32 brightness_mask; +}; + +const struct drm_panel_backlight_quirk * +drm_get_panel_backlight_quirk(const struct drm_edid *edid); + +signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec); + #endif diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6ad9630d4f48..ffa564d79638 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -25,15 +25,17 @@ #define _DRM_VBLANK_H_ #include <linux/seqlock.h> +#include <linux/hrtimer.h> #include <linux/idr.h> #include <linux/poll.h> +#include <linux/kthread.h> #include <drm/drm_file.h> #include <drm/drm_modes.h> -#include <uapi/drm/drm.h> struct drm_device; struct drm_crtc; +struct drm_vblank_work; /** * struct drm_pending_vblank_event - pending vblank event tracking @@ -78,6 +80,53 @@ struct drm_pending_vblank_event { }; /** + * struct drm_vblank_crtc_config - vblank configuration for a CRTC + */ +struct drm_vblank_crtc_config { + /** + * @offdelay_ms: Vblank off delay in ms, used to determine how long + * &drm_vblank_crtc.disable_timer waits before disabling. + * + * Defaults to the value of drm_vblank_offdelay in drm_crtc_vblank_on(). + */ + int offdelay_ms; + + /** + * @disable_immediate: See &drm_device.vblank_disable_immediate + * for the exact semantics of immediate vblank disabling. + * + * Additionally, this tracks the disable immediate value per crtc, just + * in case it needs to differ from the default value for a given device. + * + * Defaults to the value of &drm_device.vblank_disable_immediate in + * drm_crtc_vblank_on(). + */ + bool disable_immediate; +}; + +/** + * struct drm_vblank_crtc_timer - vblank timer for a CRTC + */ +struct drm_vblank_crtc_timer { + /** + * @timer: The vblank's high-resolution timer + */ + struct hrtimer timer; + /** + * @interval_lock: Protects @interval + */ + spinlock_t interval_lock; + /** + * @interval: Duration between two vblanks + */ + ktime_t interval; + /** + * @crtc: The timer's CRTC + */ + struct drm_crtc *crtc; +}; + +/** * struct drm_vblank_crtc - vblank tracking for a CRTC * * This structure tracks the vblank state for one CRTC. @@ -98,8 +147,8 @@ struct drm_vblank_crtc { wait_queue_head_t queue; /** * @disable_timer: Disable timer for the delayed vblank disabling - * hysteresis logic. Vblank disabling is controlled through the - * drm_vblank_offdelay module option and the setting of the + * hysteresis logic. Vblank disabling is controlled through + * &drm_vblank_crtc_config.offdelay_ms and the setting of the * &drm_device.max_vblank_count value. */ struct timer_list disable_timer; @@ -110,9 +159,20 @@ struct drm_vblank_crtc { seqlock_t seqlock; /** - * @count: Current software vblank counter. + * @count: + * + * Current software vblank counter. + * + * Note that for a given vblank counter value drm_crtc_handle_vblank() + * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time() + * provide a barrier: Any writes done before calling + * drm_crtc_handle_vblank() will be visible to callers of the later + * functions, iff the vblank count is the same or a later one. + * + * IMPORTANT: This guarantee requires barriers, therefor never access + * this field directly. Use drm_crtc_vblank_count() instead. */ - u64 count; + atomic64_t count; /** * @time: Vblank timestamp corresponding to @count. */ @@ -129,6 +189,26 @@ struct drm_vblank_crtc { */ u32 last; /** + * @max_vblank_count: + * + * Maximum value of the vblank registers for this crtc. This value +1 + * will result in a wrap-around of the vblank register. It is used + * by the vblank core to handle wrap-arounds. + * + * If set to zero the vblank core will try to guess the elapsed vblanks + * between times when the vblank interrupt is disabled through + * high-precision timestamps. That approach is suffering from small + * races and imprecision over longer time periods, hence exposing a + * hardware vblank counter is always recommended. + * + * This is the runtime configurable per-crtc maximum set through + * drm_crtc_set_max_vblank_count(). If this is used the driver + * must leave the device wide &drm_device.max_vblank_count at zero. + * + * If non-zero, &drm_crtc_funcs.get_vblank_counter must be set. + */ + u32 max_vblank_count; + /** * @inmodeset: Tracks whether the vblank is disabled due to a modeset. * For legacy driver bit 2 additionally tracks whether an additional * temporary vblank reference has been acquired to paper over the @@ -144,13 +224,13 @@ struct drm_vblank_crtc { unsigned int pipe; /** * @framedur_ns: Frame/Field duration in ns, used by - * drm_calc_vbltimestamp_from_scanoutpos() and computed by + * drm_crtc_vblank_helper_get_vblank_timestamp() and computed by * drm_calc_timestamping_constants(). */ int framedur_ns; /** * @linedur_ns: Line duration in ns, used by - * drm_calc_vbltimestamp_from_scanoutpos() and computed by + * drm_crtc_vblank_helper_get_vblank_timestamp() and computed by * drm_calc_timestamping_constants(). */ int linedur_ns; @@ -160,25 +240,57 @@ struct drm_vblank_crtc { * * Cache of the current hardware display mode. Only valid when @enabled * is set. This is used by helpers like - * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the - * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode, + * drm_crtc_vblank_helper_get_vblank_timestamp(). We can't just access + * the hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode, * because that one is really hard to get from interrupt context. */ struct drm_display_mode hwmode; /** + * @config: Stores vblank configuration values for a given CRTC. + * Also, see drm_crtc_vblank_on_config(). + */ + struct drm_vblank_crtc_config config; + + /** * @enabled: Tracks the enabling state of the corresponding &drm_crtc to * avoid double-disabling and hence corrupting saved state. Needed by * drivers not using atomic KMS, since those might go through their CRTC * disabling functions multiple times. */ bool enabled; + + /** + * @worker: The &kthread_worker used for executing vblank works. + */ + struct kthread_worker *worker; + + /** + * @pending_work: A list of scheduled &drm_vblank_work items that are + * waiting for a future vblank. + */ + struct list_head pending_work; + + /** + * @work_wait_queue: The wait queue used for signaling that a + * &drm_vblank_work item has either finished executing, or was + * cancelled. + */ + wait_queue_head_t work_wait_queue; + + /** + * @vblank_timer: Holds the state of the vblank timer + */ + struct drm_vblank_crtc_timer vblank_timer; }; +struct drm_vblank_crtc *drm_crtc_vblank_crtc(struct drm_crtc *crtc); int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); +bool drm_dev_has_vblank(const struct drm_device *dev); u64 drm_crtc_vblank_count(struct drm_crtc *crtc); u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, ktime_t *vblanktime); +int drm_crtc_next_vblank_start(struct drm_crtc *crtc, ktime_t *vblanktime); void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, @@ -194,16 +306,42 @@ void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe); void drm_crtc_wait_one_vblank(struct drm_crtc *crtc); void drm_crtc_vblank_off(struct drm_crtc *crtc); void drm_crtc_vblank_reset(struct drm_crtc *crtc); +void drm_crtc_vblank_on_config(struct drm_crtc *crtc, + const struct drm_vblank_crtc_config *config); void drm_crtc_vblank_on(struct drm_crtc *crtc); u64 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc); -void drm_vblank_restore(struct drm_device *dev, unsigned int pipe); void drm_crtc_vblank_restore(struct drm_crtc *crtc); -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, - unsigned int pipe, int *max_error, - ktime_t *vblank_time, - bool in_vblank_irq); void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); +void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, + u32 max_vblank_count); + +int drm_crtc_vblank_start_timer(struct drm_crtc *crtc); +void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc); +void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time); + +/* + * Helpers for struct drm_crtc_funcs + */ + +typedef bool (*drm_vblank_get_scanout_position_func)(struct drm_crtc *crtc, + bool in_vblank_irq, + int *vpos, int *hpos, + ktime_t *stime, + ktime_t *etime, + const struct drm_display_mode *mode); + +bool +drm_crtc_vblank_helper_get_vblank_timestamp_internal(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq, + drm_vblank_get_scanout_position_func get_scanout_position); +bool drm_crtc_vblank_helper_get_vblank_timestamp(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq); + #endif diff --git a/include/drm/drm_vblank_helper.h b/include/drm/drm_vblank_helper.h new file mode 100644 index 000000000000..fcd8a9b35846 --- /dev/null +++ b/include/drm/drm_vblank_helper.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _DRM_VBLANK_HELPER_H_ +#define _DRM_VBLANK_HELPER_H_ + +#include <linux/hrtimer_types.h> +#include <linux/types.h> + +struct drm_atomic_state; +struct drm_crtc; + +/* + * VBLANK helpers + */ + +void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *crtc_state); + +/** + * DRM_CRTC_HELPER_VBLANK_FUNCS - Default implementation for VBLANK helpers + * + * This macro initializes struct &drm_crtc_helper_funcs to default helpers + * for VBLANK handling. + */ +#define DRM_CRTC_HELPER_VBLANK_FUNCS \ + .atomic_flush = drm_crtc_vblank_atomic_flush, \ + .atomic_enable = drm_crtc_vblank_atomic_enable, \ + .atomic_disable = drm_crtc_vblank_atomic_disable + +/* + * VBLANK timer + */ + +int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc); +void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc); +bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq); + +/** + * DRM_CRTC_VBLANK_TIMER_FUNCS - Default implementation for VBLANK timers + * + * This macro initializes struct &drm_crtc_funcs to default helpers for + * VBLANK timers. + */ +#define DRM_CRTC_VBLANK_TIMER_FUNCS \ + .enable_vblank = drm_crtc_vblank_helper_enable_vblank_timer, \ + .disable_vblank = drm_crtc_vblank_helper_disable_vblank_timer, \ + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp_from_timer + +#endif diff --git a/include/drm/drm_vblank_work.h b/include/drm/drm_vblank_work.h new file mode 100644 index 000000000000..e04d436b7297 --- /dev/null +++ b/include/drm/drm_vblank_work.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef _DRM_VBLANK_WORK_H_ +#define _DRM_VBLANK_WORK_H_ + +#include <linux/kthread.h> + +struct drm_crtc; + +/** + * struct drm_vblank_work - A delayed work item which delays until a target + * vblank passes, and then executes at realtime priority outside of IRQ + * context. + * + * See also: + * drm_vblank_work_schedule() + * drm_vblank_work_init() + * drm_vblank_work_cancel_sync() + * drm_vblank_work_flush() + * drm_vblank_work_flush_all() + */ +struct drm_vblank_work { + /** + * @base: The base &kthread_work item which will be executed by + * &drm_vblank_crtc.worker. Drivers should not interact with this + * directly, and instead rely on drm_vblank_work_init() to initialize + * this. + */ + struct kthread_work base; + + /** + * @vblank: A pointer to &drm_vblank_crtc this work item belongs to. + */ + struct drm_vblank_crtc *vblank; + + /** + * @count: The target vblank this work will execute on. Drivers should + * not modify this value directly, and instead use + * drm_vblank_work_schedule() + */ + u64 count; + + /** + * @cancelling: The number of drm_vblank_work_cancel_sync() calls that + * are currently running. A work item cannot be rescheduled until all + * calls have finished. + */ + int cancelling; + + /** + * @node: The position of this work item in + * &drm_vblank_crtc.pending_work. + */ + struct list_head node; +}; + +/** + * to_drm_vblank_work - Retrieve the respective &drm_vblank_work item from a + * &kthread_work + * @_work: The &kthread_work embedded inside a &drm_vblank_work + */ +#define to_drm_vblank_work(_work) \ + container_of((_work), struct drm_vblank_work, base) + +int drm_vblank_work_schedule(struct drm_vblank_work *work, + u64 count, bool nextonmiss); +void drm_vblank_work_init(struct drm_vblank_work *work, struct drm_crtc *crtc, + void (*func)(struct kthread_work *work)); +bool drm_vblank_work_cancel_sync(struct drm_vblank_work *work); +void drm_vblank_work_flush(struct drm_vblank_work *work); +void drm_vblank_work_flush_all(struct drm_crtc *crtc); + +#endif /* !_DRM_VBLANK_WORK_H_ */ diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index c7987daeaed0..6c2a2f21dbf0 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -29,6 +29,18 @@ #include <linux/spinlock.h> #include <linux/types.h> +/* We make up offsets for buffer objects so we can recognize them at + * mmap time. pgoff in mmap is an unsigned long, so we need to make sure + * that the faked up offset will fit + */ +#if BITS_PER_LONG == 64 +#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) +#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 256) +#else +#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) +#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) +#endif + struct drm_file; struct drm_vma_offset_file { @@ -41,7 +53,7 @@ struct drm_vma_offset_node { rwlock_t vm_lock; struct drm_mm_node vm_node; struct rb_root vm_files; - bool readonly:1; + void *driver_private; }; struct drm_vma_offset_manager { @@ -62,6 +74,7 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, struct drm_vma_offset_node *node); int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag); +int drm_vma_node_allow_once(struct drm_vma_offset_node *node, struct drm_file *tag); void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct drm_file *tag); bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h index 23df9d463003..c380a7b8f55a 100644 --- a/include/drm/drm_writeback.h +++ b/include/drm/drm_writeback.h @@ -15,7 +15,13 @@ #include <drm/drm_encoder.h> #include <linux/workqueue.h> +/** + * struct drm_writeback_connector - DRM writeback connector + */ struct drm_writeback_connector { + /** + * @base: base drm_connector object + */ struct drm_connector base; /** @@ -24,6 +30,8 @@ struct drm_writeback_connector { * @drm_writeback_connector control the behaviour of the @encoder * by passing the @enc_funcs parameter to drm_writeback_connector_init() * function. + * For users of drm_writeback_connector_init_with_encoder(), this field + * is not valid as the encoder is managed within their drivers. */ struct drm_encoder encoder; @@ -78,8 +86,25 @@ struct drm_writeback_connector { char timeline_name[32]; }; +/** + * struct drm_writeback_job - DRM writeback job + */ struct drm_writeback_job { /** + * @connector: + * + * Back-pointer to the writeback connector associated with the job + */ + struct drm_writeback_connector *connector; + + /** + * @prepared: + * + * Set when the job has been prepared with drm_writeback_prepare_job() + */ + bool prepared; + + /** * @cleanup_work: * * Used to allow drm_writeback_signal_completion to defer dropping the @@ -98,7 +123,7 @@ struct drm_writeback_job { * @fb: * * Framebuffer to be written to by the writeback connector. Do not set - * directly, use drm_atomic_set_writeback_fb_for_connector() + * directly, use drm_writeback_set_fb() */ struct drm_framebuffer *fb; @@ -108,6 +133,13 @@ struct drm_writeback_job { * Fence which will signal once the writeback has completed */ struct dma_fence *out_fence; + + /** + * @priv: + * + * Driver-private data + */ + void *priv; }; static inline struct drm_writeback_connector * @@ -120,10 +152,28 @@ int drm_writeback_connector_init(struct drm_device *dev, struct drm_writeback_connector *wb_connector, const struct drm_connector_funcs *con_funcs, const struct drm_encoder_helper_funcs *enc_helper_funcs, - const u32 *formats, int n_formats); + const u32 *formats, int n_formats, + u32 possible_crtcs); + +int drm_writeback_connector_init_with_encoder(struct drm_device *dev, + struct drm_writeback_connector *wb_connector, + struct drm_encoder *enc, + const struct drm_connector_funcs *con_funcs, const u32 *formats, + int n_formats); + +int drmm_writeback_connector_init(struct drm_device *dev, + struct drm_writeback_connector *wb_connector, + const struct drm_connector_funcs *con_funcs, + struct drm_encoder *enc, + const u32 *formats, int n_formats); + +int drm_writeback_set_fb(struct drm_connector_state *conn_state, + struct drm_framebuffer *fb); + +int drm_writeback_prepare_job(struct drm_writeback_job *job); void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector, - struct drm_writeback_job *job); + struct drm_connector_state *conn_state); void drm_writeback_cleanup_job(struct drm_writeback_job *job); diff --git a/include/drm/gma_drm.h b/include/drm/gma_drm.h deleted file mode 100644 index 87ac5e6ca551..000000000000 --- a/include/drm/gma_drm.h +++ /dev/null @@ -1,25 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * Copyright (c) 2008, Tungsten Graphics Inc. Cedar Park, TX., USA. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#ifndef _GMA_DRM_H_ -#define _GMA_DRM_H_ - -#endif diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 47e19796c450..fb88301b3c45 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -26,93 +26,236 @@ #include <drm/spsc_queue.h> #include <linux/dma-fence.h> +#include <linux/completion.h> +#include <linux/xarray.h> +#include <linux/workqueue.h> #define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000) +/** + * DRM_SCHED_FENCE_DONT_PIPELINE - Prevent dependency pipelining + * + * Setting this flag on a scheduler fence prevents pipelining of jobs depending + * on this fence. In other words we always insert a full CPU round trip before + * dependent jobs are pushed to the hw queue. + */ +#define DRM_SCHED_FENCE_DONT_PIPELINE DMA_FENCE_FLAG_USER_BITS + +/** + * DRM_SCHED_FENCE_FLAG_HAS_DEADLINE_BIT - A fence deadline hint has been set + * + * Because we could have a deadline hint can be set before the backing hw + * fence is created, we need to keep track of whether a deadline has already + * been set. + */ +#define DRM_SCHED_FENCE_FLAG_HAS_DEADLINE_BIT (DMA_FENCE_FLAG_USER_BITS + 1) + +enum dma_resv_usage; +struct dma_resv; +struct drm_gem_object; + struct drm_gpu_scheduler; struct drm_sched_rq; +struct drm_file; + +/* These are often used as an (initial) index + * to an array, and as such should start at 0. + */ enum drm_sched_priority { - DRM_SCHED_PRIORITY_MIN, - DRM_SCHED_PRIORITY_LOW = DRM_SCHED_PRIORITY_MIN, - DRM_SCHED_PRIORITY_NORMAL, - DRM_SCHED_PRIORITY_HIGH_SW, - DRM_SCHED_PRIORITY_HIGH_HW, DRM_SCHED_PRIORITY_KERNEL, - DRM_SCHED_PRIORITY_MAX, - DRM_SCHED_PRIORITY_INVALID = -1, - DRM_SCHED_PRIORITY_UNSET = -2 + DRM_SCHED_PRIORITY_HIGH, + DRM_SCHED_PRIORITY_NORMAL, + DRM_SCHED_PRIORITY_LOW, + + DRM_SCHED_PRIORITY_COUNT }; /** * struct drm_sched_entity - A wrapper around a job queue (typically * attached to the DRM file_priv). * - * @list: used to append this struct to the list of entities in the - * runqueue. - * @rq: runqueue on which this entity is currently scheduled. - * @rq_list: a list of run queues on which jobs from this entity can - * be scheduled - * @num_rq_list: number of run queues in the rq_list - * @rq_lock: lock to modify the runqueue to which this entity belongs. - * @job_queue: the list of jobs of this entity. - * @fence_seq: a linearly increasing seqno incremented with each - * new &drm_sched_fence which is part of the entity. - * @fence_context: a unique context for all the fences which belong - * to this entity. - * The &drm_sched_fence.scheduled uses the - * fence_context but &drm_sched_fence.finished uses - * fence_context + 1. - * @dependency: the dependency fence of the job which is on the top - * of the job queue. - * @cb: callback for the dependency fence above. - * @guilty: points to ctx's guilty. - * @fini_status: contains the exit status in case the process was signalled. - * @last_scheduled: points to the finished fence of the last scheduled job. - * @last_user: last group leader pushing a job into the entity. - * @stopped: Marks the enity as removed from rq and destined for termination. - * * Entities will emit jobs in order to their corresponding hardware * ring, and the scheduler will alternate between entities based on * scheduling policy. */ struct drm_sched_entity { + /** + * @list: + * + * Used to append this struct to the list of entities in the runqueue + * @rq under &drm_sched_rq.entities. + * + * Protected by &drm_sched_rq.lock of @rq. + */ struct list_head list; + + /** + * @lock: + * + * Lock protecting the run-queue (@rq) to which this entity belongs, + * @priority and the list of schedulers (@sched_list, @num_sched_list). + */ + spinlock_t lock; + + /** + * @rq: + * + * Runqueue on which this entity is currently scheduled. + * + * FIXME: Locking is very unclear for this. Writers are protected by + * @lock, but readers are generally lockless and seem to just race with + * not even a READ_ONCE. + */ struct drm_sched_rq *rq; - struct drm_sched_rq **rq_list; - unsigned int num_rq_list; - spinlock_t rq_lock; + /** + * @sched_list: + * + * A list of schedulers (struct drm_gpu_scheduler). Jobs from this entity can + * be scheduled on any scheduler on this list. + * + * This can be modified by calling drm_sched_entity_modify_sched(). + * Locking is entirely up to the driver, see the above function for more + * details. + * + * This will be set to NULL if &num_sched_list equals 1 and @rq has been + * set already. + * + * FIXME: This means priority changes through + * drm_sched_entity_set_priority() will be lost henceforth in this case. + */ + struct drm_gpu_scheduler **sched_list; + + /** + * @num_sched_list: + * + * Number of drm_gpu_schedulers in the @sched_list. + */ + unsigned int num_sched_list; + + /** + * @priority: + * + * Priority of the entity. This can be modified by calling + * drm_sched_entity_set_priority(). Protected by @lock. + */ + enum drm_sched_priority priority; + + /** + * @job_queue: the list of jobs of this entity. + */ struct spsc_queue job_queue; + /** + * @fence_seq: + * + * A linearly increasing seqno incremented with each new + * &drm_sched_fence which is part of the entity. + * + * FIXME: Callers of drm_sched_job_arm() need to ensure correct locking, + * this doesn't need to be atomic. + */ atomic_t fence_seq; + + /** + * @fence_context: + * + * A unique context for all the fences which belong to this entity. The + * &drm_sched_fence.scheduled uses the fence_context but + * &drm_sched_fence.finished uses fence_context + 1. + */ uint64_t fence_context; + /** + * @dependency: + * + * The dependency fence of the job which is on the top of the job queue. + */ struct dma_fence *dependency; + + /** + * @cb: + * + * Callback for the dependency fence above. + */ struct dma_fence_cb cb; + + /** + * @guilty: + * + * Points to entities' guilty. + */ atomic_t *guilty; - struct dma_fence *last_scheduled; + + /** + * @last_scheduled: + * + * Points to the finished fence of the last scheduled job. Only written + * by drm_sched_entity_pop_job(). Can be accessed locklessly from + * drm_sched_job_arm() if the queue is empty. + */ + struct dma_fence __rcu *last_scheduled; + + /** + * @last_user: last group leader pushing a job into the entity. + */ struct task_struct *last_user; + + /** + * @stopped: + * + * Marks the enity as removed from rq and destined for + * termination. This is set by calling drm_sched_entity_flush() and by + * drm_sched_fini(). + */ bool stopped; + + /** + * @entity_idle: + * + * Signals when entity is not in use, used to sequence entity cleanup in + * drm_sched_entity_fini(). + */ + struct completion entity_idle; + + /** + * @oldest_job_waiting: + * + * Marks earliest job waiting in SW queue + */ + ktime_t oldest_job_waiting; + + /** + * @rb_tree_node: + * + * The node used to insert this entity into time based priority queue + */ + struct rb_node rb_tree_node; + }; /** * struct drm_sched_rq - queue of entities to be scheduled. * - * @lock: to modify the entities list. * @sched: the scheduler to which this rq belongs to. - * @entities: list of the entities to be scheduled. + * @lock: protects @entities, @rb_tree_root and @current_entity. * @current_entity: the entity which is to be scheduled. + * @entities: list of the entities to be scheduled. + * @rb_tree_root: root of time based priority queue of entities for FIFO scheduling * * Run queue is a set of entities scheduling command submissions for * one specific ring. It implements the scheduling policy that selects * the next entity to emit commands from. */ struct drm_sched_rq { - spinlock_t lock; struct drm_gpu_scheduler *sched; - struct list_head entities; + + spinlock_t lock; + /* Following members are protected by the @lock: */ struct drm_sched_entity *current_entity; + struct list_head entities; + struct rb_root_cached rb_tree_root; }; /** @@ -137,10 +280,12 @@ struct drm_sched_fence { */ struct dma_fence finished; - /** - * @cb: the callback for the parent fence below. - */ - struct dma_fence_cb cb; + /** + * @deadline: deadline set on &drm_sched_fence.finished which + * potentially needs to be propagated to &drm_sched_fence.parent + */ + ktime_t deadline; + /** * @parent: the fence returned by &drm_sched_backend_ops.run_job * when scheduling the job on hardware. We signal the @@ -160,6 +305,13 @@ struct drm_sched_fence { * @owner: job owner for debugging */ void *owner; + + /** + * @drm_client_id: + * + * The client_id of the drm_file which owns the job. + */ + uint64_t drm_client_id; }; struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); @@ -168,171 +320,382 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * struct drm_sched_job - A job to be run by an entity. * * @queue_node: used to append this struct to the queue of jobs in an entity. + * @list: a job participates in a "pending" and "done" lists. * @sched: the scheduler instance on which this job is scheduled. * @s_fence: contains the fences for the scheduling of job. * @finish_cb: the callback for the finished fence. - * @finish_work: schedules the function @drm_sched_job_finish once the job has - * finished to remove the job from the - * @drm_gpu_scheduler.ring_mirror_list. - * @node: used to append this struct to the @drm_gpu_scheduler.ring_mirror_list. - * @id: a unique id assigned to each job scheduled on the scheduler. + * @credits: the number of credits this job contributes to the scheduler + * @work: Helper to reschedule job kill to different context. * @karma: increment on every hang caused by this job. If this exceeds the hang * limit of the scheduler then the job is marked guilty and will not * be scheduled further. * @s_priority: the priority of the job. * @entity: the entity to which this job belongs. + * @cb: the callback for the parent fence in s_fence. * * A job is created by the driver using drm_sched_job_init(), and * should call drm_sched_entity_push_job() once it wants the scheduler * to schedule the job. */ struct drm_sched_job { - struct spsc_node queue_node; + /** + * @submit_ts: + * + * When the job was pushed into the entity queue. + */ + ktime_t submit_ts; + + /** + * @sched: + * + * The scheduler this job is or will be scheduled on. Gets set by + * drm_sched_job_arm(). Valid until drm_sched_backend_ops.free_job() + * has finished. + */ struct drm_gpu_scheduler *sched; + struct drm_sched_fence *s_fence; - struct dma_fence_cb finish_cb; - struct work_struct finish_work; - struct list_head node; - uint64_t id; - atomic_t karma; + struct drm_sched_entity *entity; + enum drm_sched_priority s_priority; - struct drm_sched_entity *entity; + u32 credits; + /** @last_dependency: tracks @dependencies as they signal */ + unsigned int last_dependency; + atomic_t karma; + + struct spsc_node queue_node; + struct list_head list; + + /* + * work is used only after finish_cb has been used and will not be + * accessed anymore. + */ + union { + struct dma_fence_cb finish_cb; + struct work_struct work; + }; + + struct dma_fence_cb cb; + + /** + * @dependencies: + * + * Contains the dependencies as struct dma_fence for this job, see + * drm_sched_job_add_dependency() and + * drm_sched_job_add_implicit_dependencies(). + */ + struct xarray dependencies; }; -static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, - int threshold) -{ - return (s_job && atomic_inc_return(&s_job->karma) > threshold); -} +/** + * enum drm_gpu_sched_stat - the scheduler's status + * + * @DRM_GPU_SCHED_STAT_NONE: Reserved. Do not use. + * @DRM_GPU_SCHED_STAT_RESET: The GPU hung and successfully reset. + * @DRM_GPU_SCHED_STAT_ENODEV: Error: Device is not available anymore. + * @DRM_GPU_SCHED_STAT_NO_HANG: Contrary to scheduler's assumption, the GPU + * did not hang and is still running. + */ +enum drm_gpu_sched_stat { + DRM_GPU_SCHED_STAT_NONE, + DRM_GPU_SCHED_STAT_RESET, + DRM_GPU_SCHED_STAT_ENODEV, + DRM_GPU_SCHED_STAT_NO_HANG, +}; /** - * struct drm_sched_backend_ops + * struct drm_sched_backend_ops - Define the backend operations + * called by the scheduler * - * Define the backend operations called by the scheduler, - * these functions should be implemented in driver side. + * These functions should be implemented in the driver side. */ struct drm_sched_backend_ops { /** - * @dependency: Called when the scheduler is considering scheduling - * this job next, to get another struct dma_fence for this job to - * block on. Once it returns NULL, run_job() may be called. + * @prepare_job: + * + * Called when the scheduler is considering scheduling this job next, to + * get another struct dma_fence for this job to block on. Once it + * returns NULL, run_job() may be called. + * + * Can be NULL if no additional preparation to the dependencies are + * necessary. Skipped when jobs are killed instead of run. */ - struct dma_fence *(*dependency)(struct drm_sched_job *sched_job, - struct drm_sched_entity *s_entity); + struct dma_fence *(*prepare_job)(struct drm_sched_job *sched_job, + struct drm_sched_entity *s_entity); /** - * @run_job: Called to execute the job once all of the dependencies - * have been resolved. This may be called multiple times, if - * timedout_job() has happened and drm_sched_job_recovery() - * decides to try it again. + * @run_job: Called to execute the job once all of the dependencies + * have been resolved. + * + * @sched_job: the job to run + * + * The deprecated drm_sched_resubmit_jobs() (called by &struct + * drm_sched_backend_ops.timedout_job) can invoke this again with the + * same parameters. Using this is discouraged because it violates + * dma_fence rules, notably dma_fence_init() has to be called on + * already initialized fences for a second time. Moreover, this is + * dangerous because attempts to allocate memory might deadlock with + * memory management code waiting for the reset to complete. + * + * TODO: Document what drivers should do / use instead. + * + * This method is called in a workqueue context - either from the + * submit_wq the driver passed through drm_sched_init(), or, if the + * driver passed NULL, a separate, ordered workqueue the scheduler + * allocated. + * + * Note that the scheduler expects to 'inherit' its own reference to + * this fence from the callback. It does not invoke an extra + * dma_fence_get() on it. Consequently, this callback must take a + * reference for the scheduler, and additional ones for the driver's + * respective needs. + * + * Return: + * * On success: dma_fence the driver must signal once the hardware has + * completed the job ("hardware fence"). + * * On failure: NULL or an ERR_PTR. */ struct dma_fence *(*run_job)(struct drm_sched_job *sched_job); /** - * @timedout_job: Called when a job has taken too long to execute, - * to trigger GPU recovery. + * @timedout_job: Called when a job has taken too long to execute, + * to trigger GPU recovery. + * + * @sched_job: The job that has timed out + * + * Drivers typically issue a reset to recover from GPU hangs. + * This procedure looks very different depending on whether a firmware + * or a hardware scheduler is being used. + * + * For a FIRMWARE SCHEDULER, each ring has one scheduler, and each + * scheduler has one entity. Hence, the steps taken typically look as + * follows: + * + * 1. Stop the scheduler using drm_sched_stop(). This will pause the + * scheduler workqueues and cancel the timeout work, guaranteeing + * that nothing is queued while the ring is being removed. + * 2. Remove the ring. The firmware will make sure that the + * corresponding parts of the hardware are resetted, and that other + * rings are not impacted. + * 3. Kill the entity and the associated scheduler. + * + * + * For a HARDWARE SCHEDULER, a scheduler instance schedules jobs from + * one or more entities to one ring. This implies that all entities + * associated with the affected scheduler cannot be torn down, because + * this would effectively also affect innocent userspace processes which + * did not submit faulty jobs (for example). + * + * Consequently, the procedure to recover with a hardware scheduler + * should look like this: + * + * 1. Stop all schedulers impacted by the reset using drm_sched_stop(). + * 2. Kill the entity the faulty job stems from. + * 3. Issue a GPU reset on all faulty rings (driver-specific). + * 4. Re-submit jobs on all schedulers impacted by re-submitting them to + * the entities which are still alive. + * 5. Restart all schedulers that were stopped in step #1 using + * drm_sched_start(). + * + * Note that some GPUs have distinct hardware queues but need to reset + * the GPU globally, which requires extra synchronization between the + * timeout handlers of different schedulers. One way to achieve this + * synchronization is to create an ordered workqueue (using + * alloc_ordered_workqueue()) at the driver level, and pass this queue + * as drm_sched_init()'s @timeout_wq parameter. This will guarantee + * that timeout handlers are executed sequentially. + * + * Return: The scheduler's status, defined by &enum drm_gpu_sched_stat + * */ - void (*timedout_job)(struct drm_sched_job *sched_job); + enum drm_gpu_sched_stat (*timedout_job)(struct drm_sched_job *sched_job); /** * @free_job: Called once the job's finished fence has been signaled * and it's time to clean it up. */ void (*free_job)(struct drm_sched_job *sched_job); + + /** + * @cancel_job: Used by the scheduler to guarantee remaining jobs' fences + * get signaled in drm_sched_fini(). + * + * Used by the scheduler to cancel all jobs that have not been executed + * with &struct drm_sched_backend_ops.run_job by the time + * drm_sched_fini() gets invoked. + * + * Drivers need to signal the passed job's hardware fence with an + * appropriate error code (e.g., -ECANCELED) in this callback. They + * must not free the job. + * + * The scheduler will only call this callback once it stopped calling + * all other callbacks forever, with the exception of &struct + * drm_sched_backend_ops.free_job. + */ + void (*cancel_job)(struct drm_sched_job *sched_job); }; /** - * struct drm_gpu_scheduler + * struct drm_gpu_scheduler - scheduler instance-specific data * * @ops: backend operations provided by the driver. - * @hw_submission_limit: the max size of the hardware queue. + * @credit_limit: the credit limit of this scheduler + * @credit_count: the current credit count of this scheduler * @timeout: the time after which a job is removed from the scheduler. * @name: name of the ring for which this scheduler is being used. - * @sched_rq: priority wise array of run queues. - * @wake_up_worker: the wait queue on which the scheduler sleeps until a job - * is ready to be scheduled. - * @job_scheduled: once @drm_sched_entity_do_release is called the scheduler + * @num_rqs: Number of run-queues. This is at most DRM_SCHED_PRIORITY_COUNT, + * as there's usually one run-queue per priority, but could be less. + * @sched_rq: An allocated array of run-queues of size @num_rqs; + * @job_scheduled: once drm_sched_entity_flush() is called the scheduler * waits on this wait queue until all the scheduled jobs are * finished. - * @hw_rq_count: the number of jobs currently in the hardware queue. * @job_id_count: used to assign unique id to the each job. + * @submit_wq: workqueue used to queue @work_run_job and @work_free_job + * @timeout_wq: workqueue used to queue @work_tdr + * @work_run_job: work which calls run_job op of each scheduler. + * @work_free_job: work which calls free_job op of each scheduler. * @work_tdr: schedules a delayed call to @drm_sched_job_timedout after the * timeout interval is over. - * @thread: the kthread on which the scheduler which run. - * @ring_mirror_list: the list of jobs which are currently in the job queue. - * @job_list_lock: lock to protect the ring_mirror_list. + * @pending_list: the list of jobs which are currently in the job queue. + * @job_list_lock: lock to protect the pending_list. * @hang_limit: once the hangs by a job crosses this limit then it is marked - * guilty and it will be considered for scheduling further. - * @num_jobs: the number of jobs in queue in the scheduler + * guilty and it will no longer be considered for scheduling. + * @score: score to help loadbalancer pick a idle sched + * @_score: score used when the driver doesn't provide one * @ready: marks if the underlying HW is ready to work + * @free_guilty: A hit to time out handler to free the guilty job. + * @pause_submit: pause queuing of @work_run_job on @submit_wq + * @own_submit_wq: scheduler owns allocation of @submit_wq + * @dev: system &struct device * * One scheduler is implemented for each hardware ring. */ struct drm_gpu_scheduler { const struct drm_sched_backend_ops *ops; - uint32_t hw_submission_limit; + u32 credit_limit; + atomic_t credit_count; long timeout; const char *name; - struct drm_sched_rq sched_rq[DRM_SCHED_PRIORITY_MAX]; - wait_queue_head_t wake_up_worker; + u32 num_rqs; + struct drm_sched_rq **sched_rq; wait_queue_head_t job_scheduled; - atomic_t hw_rq_count; atomic64_t job_id_count; + struct workqueue_struct *submit_wq; + struct workqueue_struct *timeout_wq; + struct work_struct work_run_job; + struct work_struct work_free_job; struct delayed_work work_tdr; - struct task_struct *thread; - struct list_head ring_mirror_list; + struct list_head pending_list; spinlock_t job_list_lock; int hang_limit; - atomic_t num_jobs; - bool ready; + atomic_t *score; + atomic_t _score; + bool ready; + bool free_guilty; + bool pause_submit; + bool own_submit_wq; + struct device *dev; +}; + +/** + * struct drm_sched_init_args - parameters for initializing a DRM GPU scheduler + * + * @ops: backend operations provided by the driver + * @submit_wq: workqueue to use for submission. If NULL, an ordered wq is + * allocated and used. + * @num_rqs: Number of run-queues. This may be at most DRM_SCHED_PRIORITY_COUNT, + * as there's usually one run-queue per priority, but may be less. + * @credit_limit: the number of credits this scheduler can hold from all jobs + * @hang_limit: number of times to allow a job to hang before dropping it. + * This mechanism is DEPRECATED. Set it to 0. + * @timeout: timeout value in jiffies for submitted jobs. + * @timeout_wq: workqueue to use for timeout work. If NULL, the system_wq is used. + * @score: score atomic shared with other schedulers. May be NULL. + * @name: name (typically the driver's name). Used for debugging + * @dev: associated device. Used for debugging + */ +struct drm_sched_init_args { + const struct drm_sched_backend_ops *ops; + struct workqueue_struct *submit_wq; + struct workqueue_struct *timeout_wq; + u32 num_rqs; + u32 credit_limit; + unsigned int hang_limit; + long timeout; + atomic_t *score; + const char *name; + struct device *dev; }; +/* Scheduler operations */ + int drm_sched_init(struct drm_gpu_scheduler *sched, - const struct drm_sched_backend_ops *ops, - uint32_t hw_submission, unsigned hang_limit, long timeout, - const char *name); + const struct drm_sched_init_args *args); void drm_sched_fini(struct drm_gpu_scheduler *sched); + +unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched); +void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, + unsigned long remaining); +void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched); +bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched); +void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched); +void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched); +void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad); +void drm_sched_start(struct drm_gpu_scheduler *sched, int errno); +void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); +void drm_sched_fault(struct drm_gpu_scheduler *sched); + +struct drm_gpu_scheduler * +drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, + unsigned int num_sched_list); + +/* Jobs */ + int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, - void *owner); + u32 credits, void *owner, + u64 drm_client_id); +void drm_sched_job_arm(struct drm_sched_job *job); +void drm_sched_entity_push_job(struct drm_sched_job *sched_job); +int drm_sched_job_add_dependency(struct drm_sched_job *job, + struct dma_fence *fence); +int drm_sched_job_add_syncobj_dependency(struct drm_sched_job *job, + struct drm_file *file, + u32 handle, + u32 point); +int drm_sched_job_add_resv_dependencies(struct drm_sched_job *job, + struct dma_resv *resv, + enum dma_resv_usage usage); +int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, + struct drm_gem_object *obj, + bool write); +bool drm_sched_job_has_dependency(struct drm_sched_job *job, + struct dma_fence *fence); void drm_sched_job_cleanup(struct drm_sched_job *job); -void drm_sched_wakeup(struct drm_gpu_scheduler *sched); -void drm_sched_hw_job_reset(struct drm_gpu_scheduler *sched, - struct drm_sched_job *job); -void drm_sched_job_recovery(struct drm_gpu_scheduler *sched); -bool drm_sched_dependency_optimized(struct dma_fence* fence, - struct drm_sched_entity *entity); -void drm_sched_fault(struct drm_gpu_scheduler *sched); -void drm_sched_job_kickout(struct drm_sched_job *s_job); +void drm_sched_increase_karma(struct drm_sched_job *bad); -void drm_sched_rq_add_entity(struct drm_sched_rq *rq, - struct drm_sched_entity *entity); -void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, - struct drm_sched_entity *entity); +static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, + int threshold) +{ + return s_job && atomic_inc_return(&s_job->karma) > threshold; +} + +/* Entities */ int drm_sched_entity_init(struct drm_sched_entity *entity, - struct drm_sched_rq **rq_list, - unsigned int num_rq_list, + enum drm_sched_priority priority, + struct drm_gpu_scheduler **sched_list, + unsigned int num_sched_list, atomic_t *guilty); long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout); void drm_sched_entity_fini(struct drm_sched_entity *entity); void drm_sched_entity_destroy(struct drm_sched_entity *entity); -void drm_sched_entity_select_rq(struct drm_sched_entity *entity); -struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); -void drm_sched_entity_push_job(struct drm_sched_job *sched_job, - struct drm_sched_entity *entity); void drm_sched_entity_set_priority(struct drm_sched_entity *entity, enum drm_sched_priority priority); -bool drm_sched_entity_is_ready(struct drm_sched_entity *entity); - -struct drm_sched_fence *drm_sched_fence_create( - struct drm_sched_entity *s_entity, void *owner); -void drm_sched_fence_scheduled(struct drm_sched_fence *fence); -void drm_sched_fence_finished(struct drm_sched_fence *fence); - -unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched); -void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, - unsigned long remaining); +int drm_sched_entity_error(struct drm_sched_entity *entity); +void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, + struct drm_gpu_scheduler **sched_list, + unsigned int num_sched_list); #endif diff --git a/include/drm/gud.h b/include/drm/gud.h new file mode 100644 index 000000000000..c52a8ba4ae4e --- /dev/null +++ b/include/drm/gud.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2020 Noralf Trønnes + */ + +#ifndef __LINUX_GUD_H +#define __LINUX_GUD_H + +#include <linux/types.h> + +/* + * struct gud_display_descriptor_req - Display descriptor + * @magic: Magic value GUD_DISPLAY_MAGIC + * @version: Protocol version + * @flags: Flags + * - STATUS_ON_SET: Always do a status request after a SET request. + * This is used by the Linux gadget driver since it has + * no way to control the status stage of a control OUT + * request that has a payload. + * - FULL_UPDATE: Always send the entire framebuffer when flushing changes. + * The GUD_REQ_SET_BUFFER request will not be sent + * before each bulk transfer, it will only be sent if the + * previous bulk transfer had failed. This gives the device + * a chance to reset its state machine if needed. + * This flag can not be used in combination with compression. + * @compression: Supported compression types + * - GUD_COMPRESSION_LZ4: LZ4 lossless compression. + * @max_buffer_size: Maximum buffer size the device can handle (optional). + * This is useful for devices that don't have a big enough + * buffer to decompress the entire framebuffer in one go. + * @min_width: Minimum pixel width the controller can handle + * @max_width: Maximum width + * @min_height: Minimum height + * @max_height: Maximum height + * + * Devices that have only one display mode will have min_width == max_width + * and min_height == max_height. + */ +struct gud_display_descriptor_req { + __le32 magic; +#define GUD_DISPLAY_MAGIC 0x1d50614d + __u8 version; + __le32 flags; +#define GUD_DISPLAY_FLAG_STATUS_ON_SET BIT(0) +#define GUD_DISPLAY_FLAG_FULL_UPDATE BIT(1) + __u8 compression; +#define GUD_COMPRESSION_LZ4 BIT(0) + __le32 max_buffer_size; + __le32 min_width; + __le32 max_width; + __le32 min_height; + __le32 max_height; +} __packed; + +/* + * struct gud_property_req - Property + * @prop: Property + * @val: Value + */ +struct gud_property_req { + __le16 prop; + __le64 val; +} __packed; + +/* + * struct gud_display_mode_req - Display mode + * @clock: Pixel clock in kHz + * @hdisplay: Horizontal display size + * @hsync_start: Horizontal sync start + * @hsync_end: Horizontal sync end + * @htotal: Horizontal total size + * @vdisplay: Vertical display size + * @vsync_start: Vertical sync start + * @vsync_end: Vertical sync end + * @vtotal: Vertical total size + * @flags: Bits 0-13 are the same as in the RandR protocol and also what DRM uses. + * The deprecated bits are reused for internal protocol flags leaving us + * free to follow DRM for the other bits in the future. + * - FLAG_PREFERRED: Set on the preferred display mode. + */ +struct gud_display_mode_req { + __le32 clock; + __le16 hdisplay; + __le16 hsync_start; + __le16 hsync_end; + __le16 htotal; + __le16 vdisplay; + __le16 vsync_start; + __le16 vsync_end; + __le16 vtotal; + __le32 flags; +#define GUD_DISPLAY_MODE_FLAG_PHSYNC BIT(0) +#define GUD_DISPLAY_MODE_FLAG_NHSYNC BIT(1) +#define GUD_DISPLAY_MODE_FLAG_PVSYNC BIT(2) +#define GUD_DISPLAY_MODE_FLAG_NVSYNC BIT(3) +#define GUD_DISPLAY_MODE_FLAG_INTERLACE BIT(4) +#define GUD_DISPLAY_MODE_FLAG_DBLSCAN BIT(5) +#define GUD_DISPLAY_MODE_FLAG_CSYNC BIT(6) +#define GUD_DISPLAY_MODE_FLAG_PCSYNC BIT(7) +#define GUD_DISPLAY_MODE_FLAG_NCSYNC BIT(8) +#define GUD_DISPLAY_MODE_FLAG_HSKEW BIT(9) +/* BCast and PixelMultiplex are deprecated */ +#define GUD_DISPLAY_MODE_FLAG_DBLCLK BIT(12) +#define GUD_DISPLAY_MODE_FLAG_CLKDIV2 BIT(13) +#define GUD_DISPLAY_MODE_FLAG_USER_MASK \ + (GUD_DISPLAY_MODE_FLAG_PHSYNC | GUD_DISPLAY_MODE_FLAG_NHSYNC | \ + GUD_DISPLAY_MODE_FLAG_PVSYNC | GUD_DISPLAY_MODE_FLAG_NVSYNC | \ + GUD_DISPLAY_MODE_FLAG_INTERLACE | GUD_DISPLAY_MODE_FLAG_DBLSCAN | \ + GUD_DISPLAY_MODE_FLAG_CSYNC | GUD_DISPLAY_MODE_FLAG_PCSYNC | \ + GUD_DISPLAY_MODE_FLAG_NCSYNC | GUD_DISPLAY_MODE_FLAG_HSKEW | \ + GUD_DISPLAY_MODE_FLAG_DBLCLK | GUD_DISPLAY_MODE_FLAG_CLKDIV2) +/* Internal protocol flags */ +#define GUD_DISPLAY_MODE_FLAG_PREFERRED BIT(10) +} __packed; + +/* + * struct gud_connector_descriptor_req - Connector descriptor + * @connector_type: Connector type (GUD_CONNECTOR_TYPE_*). + * If the host doesn't support the type it should fall back to PANEL. + * @flags: Flags + * - POLL_STATUS: Connector status can change (polled every 10 seconds) + * - INTERLACE: Interlaced modes are supported + * - DOUBLESCAN: Doublescan modes are supported + */ +struct gud_connector_descriptor_req { + __u8 connector_type; +#define GUD_CONNECTOR_TYPE_PANEL 0 +#define GUD_CONNECTOR_TYPE_VGA 1 +#define GUD_CONNECTOR_TYPE_COMPOSITE 2 +#define GUD_CONNECTOR_TYPE_SVIDEO 3 +#define GUD_CONNECTOR_TYPE_COMPONENT 4 +#define GUD_CONNECTOR_TYPE_DVI 5 +#define GUD_CONNECTOR_TYPE_DISPLAYPORT 6 +#define GUD_CONNECTOR_TYPE_HDMI 7 + __le32 flags; +#define GUD_CONNECTOR_FLAGS_POLL_STATUS BIT(0) +#define GUD_CONNECTOR_FLAGS_INTERLACE BIT(1) +#define GUD_CONNECTOR_FLAGS_DOUBLESCAN BIT(2) +} __packed; + +/* + * struct gud_set_buffer_req - Set buffer transfer info + * @x: X position of rectangle + * @y: Y position + * @width: Pixel width of rectangle + * @height: Pixel height + * @length: Buffer length in bytes + * @compression: Transfer compression + * @compressed_length: Compressed buffer length + * + * This request is issued right before the bulk transfer. + * @x, @y, @width and @height specifies the rectangle where the buffer should be + * placed inside the framebuffer. + */ +struct gud_set_buffer_req { + __le32 x; + __le32 y; + __le32 width; + __le32 height; + __le32 length; + __u8 compression; + __le32 compressed_length; +} __packed; + +/* + * struct gud_state_req - Display state + * @mode: Display mode + * @format: Pixel format GUD_PIXEL_FORMAT_* + * @connector: Connector index + * @properties: Array of properties + * + * The entire state is transferred each time there's a change. + */ +struct gud_state_req { + struct gud_display_mode_req mode; + __u8 format; + __u8 connector; + struct gud_property_req properties[]; +} __packed; + +/* List of supported connector properties: */ + +/* Margins in pixels to deal with overscan, range 0-100 */ +#define GUD_PROPERTY_TV_LEFT_MARGIN 1 +#define GUD_PROPERTY_TV_RIGHT_MARGIN 2 +#define GUD_PROPERTY_TV_TOP_MARGIN 3 +#define GUD_PROPERTY_TV_BOTTOM_MARGIN 4 +#define GUD_PROPERTY_TV_MODE 5 +/* Brightness in percent, range 0-100 */ +#define GUD_PROPERTY_TV_BRIGHTNESS 6 +/* Contrast in percent, range 0-100 */ +#define GUD_PROPERTY_TV_CONTRAST 7 +/* Flicker reduction in percent, range 0-100 */ +#define GUD_PROPERTY_TV_FLICKER_REDUCTION 8 +/* Overscan in percent, range 0-100 */ +#define GUD_PROPERTY_TV_OVERSCAN 9 +/* Saturation in percent, range 0-100 */ +#define GUD_PROPERTY_TV_SATURATION 10 +/* Hue in percent, range 0-100 */ +#define GUD_PROPERTY_TV_HUE 11 + +/* + * Backlight brightness is in the range 0-100 inclusive. The value represents the human perceptual + * brightness and not a linear PWM value. 0 is minimum brightness which should not turn the + * backlight completely off. The DPMS connector property should be used to control power which will + * trigger a GUD_REQ_SET_DISPLAY_ENABLE request. + * + * This does not map to a DRM property, it is used with the backlight device. + */ +#define GUD_PROPERTY_BACKLIGHT_BRIGHTNESS 12 + +/* List of supported properties that are not connector propeties: */ + +/* + * Plane rotation. Should return the supported bitmask on + * GUD_REQ_GET_PROPERTIES. GUD_ROTATION_0 is mandatory. + * + * Note: This is not display rotation so 90/270 will need scaling to make it fit (unless squared). + */ +#define GUD_PROPERTY_ROTATION 50 + #define GUD_ROTATION_0 BIT(0) + #define GUD_ROTATION_90 BIT(1) + #define GUD_ROTATION_180 BIT(2) + #define GUD_ROTATION_270 BIT(3) + #define GUD_ROTATION_REFLECT_X BIT(4) + #define GUD_ROTATION_REFLECT_Y BIT(5) + #define GUD_ROTATION_MASK (GUD_ROTATION_0 | GUD_ROTATION_90 | \ + GUD_ROTATION_180 | GUD_ROTATION_270 | \ + GUD_ROTATION_REFLECT_X | GUD_ROTATION_REFLECT_Y) + +/* USB Control requests: */ + +/* Get status from the last GET/SET control request. Value is u8. */ +#define GUD_REQ_GET_STATUS 0x00 + /* Status values: */ + #define GUD_STATUS_OK 0x00 + #define GUD_STATUS_BUSY 0x01 + #define GUD_STATUS_REQUEST_NOT_SUPPORTED 0x02 + #define GUD_STATUS_PROTOCOL_ERROR 0x03 + #define GUD_STATUS_INVALID_PARAMETER 0x04 + #define GUD_STATUS_ERROR 0x05 + +/* Get display descriptor as a &gud_display_descriptor_req */ +#define GUD_REQ_GET_DESCRIPTOR 0x01 + +/* Get supported pixel formats as a byte array of GUD_PIXEL_FORMAT_* */ +#define GUD_REQ_GET_FORMATS 0x40 + #define GUD_FORMATS_MAX_NUM 32 + #define GUD_PIXEL_FORMAT_R1 0x01 /* 1-bit monochrome */ + #define GUD_PIXEL_FORMAT_R8 0x08 /* 8-bit greyscale */ + #define GUD_PIXEL_FORMAT_XRGB1111 0x20 + #define GUD_PIXEL_FORMAT_RGB332 0x30 + #define GUD_PIXEL_FORMAT_RGB565 0x40 + #define GUD_PIXEL_FORMAT_RGB888 0x50 + #define GUD_PIXEL_FORMAT_XRGB8888 0x80 + #define GUD_PIXEL_FORMAT_ARGB8888 0x81 + +/* + * Get supported properties that are not connector propeties as a &gud_property_req array. + * gud_property_req.val often contains the initial value for the property. + */ +#define GUD_REQ_GET_PROPERTIES 0x41 + #define GUD_PROPERTIES_MAX_NUM 32 + +/* Connector requests have the connector index passed in the wValue field */ + +/* Get connector descriptors as an array of &gud_connector_descriptor_req */ +#define GUD_REQ_GET_CONNECTORS 0x50 + #define GUD_CONNECTORS_MAX_NUM 32 + +/* + * Get properties supported by the connector as a &gud_property_req array. + * gud_property_req.val often contains the initial value for the property. + */ +#define GUD_REQ_GET_CONNECTOR_PROPERTIES 0x51 + #define GUD_CONNECTOR_PROPERTIES_MAX_NUM 32 + +/* + * Issued when there's a TV_MODE property present. + * Gets an array of the supported TV_MODE names each entry of length + * GUD_CONNECTOR_TV_MODE_NAME_LEN. Names must be NUL-terminated. + */ +#define GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES 0x52 + #define GUD_CONNECTOR_TV_MODE_NAME_LEN 16 + #define GUD_CONNECTOR_TV_MODE_MAX_NUM 16 + +/* When userspace checks connector status, this is issued first, not used for poll requests. */ +#define GUD_REQ_SET_CONNECTOR_FORCE_DETECT 0x53 + +/* + * Get connector status. Value is u8. + * + * Userspace will get a HOTPLUG uevent if one of the following is true: + * - Connection status has changed since last + * - CHANGED is set + */ +#define GUD_REQ_GET_CONNECTOR_STATUS 0x54 + #define GUD_CONNECTOR_STATUS_DISCONNECTED 0x00 + #define GUD_CONNECTOR_STATUS_CONNECTED 0x01 + #define GUD_CONNECTOR_STATUS_UNKNOWN 0x02 + #define GUD_CONNECTOR_STATUS_CONNECTED_MASK 0x03 + #define GUD_CONNECTOR_STATUS_CHANGED BIT(7) + +/* + * Display modes can be fetched as either EDID data or an array of &gud_display_mode_req. + * + * If GUD_REQ_GET_CONNECTOR_MODES returns zero, EDID is used to create display modes. + * If both display modes and EDID are returned, EDID is just passed on to userspace + * in the EDID connector property. + */ + +/* Get &gud_display_mode_req array of supported display modes */ +#define GUD_REQ_GET_CONNECTOR_MODES 0x55 + #define GUD_CONNECTOR_MAX_NUM_MODES 128 + +/* Get Extended Display Identification Data */ +#define GUD_REQ_GET_CONNECTOR_EDID 0x56 + #define GUD_CONNECTOR_MAX_EDID_LEN 2048 + +/* Set buffer properties before bulk transfer as &gud_set_buffer_req */ +#define GUD_REQ_SET_BUFFER 0x60 + +/* Check display configuration as &gud_state_req */ +#define GUD_REQ_SET_STATE_CHECK 0x61 + +/* Apply the previous STATE_CHECK configuration */ +#define GUD_REQ_SET_STATE_COMMIT 0x62 + +/* Enable/disable the display controller, value is u8: 0/1 */ +#define GUD_REQ_SET_CONTROLLER_ENABLE 0x63 + +/* Enable/disable display/output (DPMS), value is u8: 0/1 */ +#define GUD_REQ_SET_DISPLAY_ENABLE 0x64 + +#endif diff --git a/include/drm/i2c/ch7006.h b/include/drm/i2c/ch7006.h deleted file mode 100644 index 8390b437a1f8..000000000000 --- a/include/drm/i2c/ch7006.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __DRM_I2C_CH7006_H__ -#define __DRM_I2C_CH7006_H__ - -/** - * struct ch7006_encoder_params - * - * Describes how the ch7006 is wired up with the GPU. It should be - * used as the @params parameter of its @set_config method. - * - * See "http://www.chrontel.com/pdf/7006.pdf" for their precise - * meaning. - */ -struct ch7006_encoder_params { - enum { - CH7006_FORMAT_RGB16 = 0, - CH7006_FORMAT_YCrCb24m16, - CH7006_FORMAT_RGB24m16, - CH7006_FORMAT_RGB15, - CH7006_FORMAT_RGB24m12C, - CH7006_FORMAT_RGB24m12I, - CH7006_FORMAT_RGB24m8, - CH7006_FORMAT_RGB16m8, - CH7006_FORMAT_RGB15m8, - CH7006_FORMAT_YCrCb24m8, - } input_format; - - enum { - CH7006_CLOCK_SLAVE = 0, - CH7006_CLOCK_MASTER, - } clock_mode; - - enum { - CH7006_CLOCK_EDGE_NEG = 0, - CH7006_CLOCK_EDGE_POS, - } clock_edge; - - int xcm, pcm; - - enum { - CH7006_SYNC_SLAVE = 0, - CH7006_SYNC_MASTER, - } sync_direction; - - enum { - CH7006_SYNC_SEPARATED = 0, - CH7006_SYNC_EMBEDDED, - } sync_encoding; - - enum { - CH7006_POUT_1_8V = 0, - CH7006_POUT_3_3V, - } pout_level; - - enum { - CH7006_ACTIVE_HSYNC = 0, - CH7006_ACTIVE_DSTART, - } active_detect; -}; - -#endif diff --git a/include/drm/i2c/sil164.h b/include/drm/i2c/sil164.h deleted file mode 100644 index 205e27384c83..000000000000 --- a/include/drm/i2c/sil164.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __DRM_I2C_SIL164_H__ -#define __DRM_I2C_SIL164_H__ - -/** - * struct sil164_encoder_params - * - * Describes how the sil164 is connected to the GPU. It should be used - * as the @params parameter of its @set_config method. - * - * See "http://www.siliconimage.com/docs/SiI-DS-0021-E-164.pdf". - */ -struct sil164_encoder_params { - enum { - SIL164_INPUT_EDGE_FALLING = 0, - SIL164_INPUT_EDGE_RISING - } input_edge; - - enum { - SIL164_INPUT_WIDTH_12BIT = 0, - SIL164_INPUT_WIDTH_24BIT - } input_width; - - enum { - SIL164_INPUT_SINGLE_EDGE = 0, - SIL164_INPUT_DUAL_EDGE - } input_dual; - - enum { - SIL164_PLL_FILTER_ON = 0, - SIL164_PLL_FILTER_OFF, - } pll_filter; - - int input_skew; /** < Allowed range [-4, 3], use 0 for no de-skew. */ - int duallink_skew; /** < Allowed range [-4, 3]. */ -}; - -#endif diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h deleted file mode 100644 index 3cb25ccbe5e6..000000000000 --- a/include/drm/i2c/tda998x.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __DRM_I2C_TDA998X_H__ -#define __DRM_I2C_TDA998X_H__ - -#include <linux/hdmi.h> -#include <dt-bindings/display/tda998x.h> - -enum { - AFMT_UNUSED = 0, - AFMT_SPDIF = TDA998x_SPDIF, - AFMT_I2S = TDA998x_I2S, -}; - -struct tda998x_audio_params { - u8 config; - u8 format; - unsigned sample_width; - unsigned sample_rate; - struct hdmi_audio_infoframe cea; - u8 status[5]; -}; - -struct tda998x_encoder_params { - u8 swap_b:3; - u8 mirr_b:1; - u8 swap_a:3; - u8 mirr_a:1; - u8 swap_d:3; - u8 mirr_d:1; - u8 swap_c:3; - u8 mirr_c:1; - u8 swap_f:3; - u8 mirr_f:1; - u8 swap_e:3; - u8 mirr_e:1; - - struct tda998x_audio_params audio_params; -}; - -#endif diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h deleted file mode 100644 index 192667144693..000000000000 --- a/include/drm/i915_pciids.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2013 Intel Corporation - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef _I915_PCIIDS_H -#define _I915_PCIIDS_H - -/* - * A pci_device_id struct { - * __u32 vendor, device; - * __u32 subvendor, subdevice; - * __u32 class, class_mask; - * kernel_ulong_t driver_data; - * }; - * Don't use C99 here because "class" is reserved and we want to - * give userspace flexibility. - */ -#define INTEL_VGA_DEVICE(id, info) { \ - 0x8086, id, \ - ~0, ~0, \ - 0x030000, 0xff0000, \ - (unsigned long) info } - -#define INTEL_QUANTA_VGA_DEVICE(info) { \ - 0x8086, 0x16a, \ - 0x152d, 0x8990, \ - 0x030000, 0xff0000, \ - (unsigned long) info } - -#define INTEL_I810_IDS(info) \ - INTEL_VGA_DEVICE(0x7121, info), /* I810 */ \ - INTEL_VGA_DEVICE(0x7123, info), /* I810_DC100 */ \ - INTEL_VGA_DEVICE(0x7125, info) /* I810_E */ - -#define INTEL_I815_IDS(info) \ - INTEL_VGA_DEVICE(0x1132, info) /* I815*/ - -#define INTEL_I830_IDS(info) \ - INTEL_VGA_DEVICE(0x3577, info) - -#define INTEL_I845G_IDS(info) \ - INTEL_VGA_DEVICE(0x2562, info) - -#define INTEL_I85X_IDS(info) \ - INTEL_VGA_DEVICE(0x3582, info), /* I855_GM */ \ - INTEL_VGA_DEVICE(0x358e, info) - -#define INTEL_I865G_IDS(info) \ - INTEL_VGA_DEVICE(0x2572, info) /* I865_G */ - -#define INTEL_I915G_IDS(info) \ - INTEL_VGA_DEVICE(0x2582, info), /* I915_G */ \ - INTEL_VGA_DEVICE(0x258a, info) /* E7221_G */ - -#define INTEL_I915GM_IDS(info) \ - INTEL_VGA_DEVICE(0x2592, info) /* I915_GM */ - -#define INTEL_I945G_IDS(info) \ - INTEL_VGA_DEVICE(0x2772, info) /* I945_G */ - -#define INTEL_I945GM_IDS(info) \ - INTEL_VGA_DEVICE(0x27a2, info), /* I945_GM */ \ - INTEL_VGA_DEVICE(0x27ae, info) /* I945_GME */ - -#define INTEL_I965G_IDS(info) \ - INTEL_VGA_DEVICE(0x2972, info), /* I946_GZ */ \ - INTEL_VGA_DEVICE(0x2982, info), /* G35_G */ \ - INTEL_VGA_DEVICE(0x2992, info), /* I965_Q */ \ - INTEL_VGA_DEVICE(0x29a2, info) /* I965_G */ - -#define INTEL_G33_IDS(info) \ - INTEL_VGA_DEVICE(0x29b2, info), /* Q35_G */ \ - INTEL_VGA_DEVICE(0x29c2, info), /* G33_G */ \ - INTEL_VGA_DEVICE(0x29d2, info) /* Q33_G */ - -#define INTEL_I965GM_IDS(info) \ - INTEL_VGA_DEVICE(0x2a02, info), /* I965_GM */ \ - INTEL_VGA_DEVICE(0x2a12, info) /* I965_GME */ - -#define INTEL_GM45_IDS(info) \ - INTEL_VGA_DEVICE(0x2a42, info) /* GM45_G */ - -#define INTEL_G45_IDS(info) \ - INTEL_VGA_DEVICE(0x2e02, info), /* IGD_E_G */ \ - INTEL_VGA_DEVICE(0x2e12, info), /* Q45_G */ \ - INTEL_VGA_DEVICE(0x2e22, info), /* G45_G */ \ - INTEL_VGA_DEVICE(0x2e32, info), /* G41_G */ \ - INTEL_VGA_DEVICE(0x2e42, info), /* B43_G */ \ - INTEL_VGA_DEVICE(0x2e92, info) /* B43_G.1 */ - -#define INTEL_PINEVIEW_IDS(info) \ - INTEL_VGA_DEVICE(0xa001, info), \ - INTEL_VGA_DEVICE(0xa011, info) - -#define INTEL_IRONLAKE_D_IDS(info) \ - INTEL_VGA_DEVICE(0x0042, info) - -#define INTEL_IRONLAKE_M_IDS(info) \ - INTEL_VGA_DEVICE(0x0046, info) - -#define INTEL_SNB_D_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x0102, info), \ - INTEL_VGA_DEVICE(0x010A, info) - -#define INTEL_SNB_D_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x0112, info), \ - INTEL_VGA_DEVICE(0x0122, info) - -#define INTEL_SNB_D_IDS(info) \ - INTEL_SNB_D_GT1_IDS(info), \ - INTEL_SNB_D_GT2_IDS(info) - -#define INTEL_SNB_M_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x0106, info) - -#define INTEL_SNB_M_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x0116, info), \ - INTEL_VGA_DEVICE(0x0126, info) - -#define INTEL_SNB_M_IDS(info) \ - INTEL_SNB_M_GT1_IDS(info), \ - INTEL_SNB_M_GT2_IDS(info) - -#define INTEL_IVB_M_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x0156, info) /* GT1 mobile */ - -#define INTEL_IVB_M_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x0166, info) /* GT2 mobile */ - -#define INTEL_IVB_M_IDS(info) \ - INTEL_IVB_M_GT1_IDS(info), \ - INTEL_IVB_M_GT2_IDS(info) - -#define INTEL_IVB_D_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x0152, info), /* GT1 desktop */ \ - INTEL_VGA_DEVICE(0x015a, info) /* GT1 server */ - -#define INTEL_IVB_D_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x0162, info), /* GT2 desktop */ \ - INTEL_VGA_DEVICE(0x016a, info) /* GT2 server */ - -#define INTEL_IVB_D_IDS(info) \ - INTEL_IVB_D_GT1_IDS(info), \ - INTEL_IVB_D_GT2_IDS(info) - -#define INTEL_IVB_Q_IDS(info) \ - INTEL_QUANTA_VGA_DEVICE(info) /* Quanta transcode */ - -#define INTEL_HSW_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x0402, info), /* GT1 desktop */ \ - INTEL_VGA_DEVICE(0x040a, info), /* GT1 server */ \ - INTEL_VGA_DEVICE(0x040B, info), /* GT1 reserved */ \ - INTEL_VGA_DEVICE(0x040E, info), /* GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0C02, info), /* SDV GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0C0A, info), /* SDV GT1 server */ \ - INTEL_VGA_DEVICE(0x0C0B, info), /* SDV GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0C0E, info), /* SDV GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0A02, info), /* ULT GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0A0A, info), /* ULT GT1 server */ \ - INTEL_VGA_DEVICE(0x0A0B, info), /* ULT GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0D02, info), /* CRW GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0D0A, info), /* CRW GT1 server */ \ - INTEL_VGA_DEVICE(0x0D0B, info), /* CRW GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0D0E, info), /* CRW GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0406, info), /* GT1 mobile */ \ - INTEL_VGA_DEVICE(0x0C06, info), /* SDV GT1 mobile */ \ - INTEL_VGA_DEVICE(0x0A06, info), /* ULT GT1 mobile */ \ - INTEL_VGA_DEVICE(0x0A0E, info), /* ULX GT1 mobile */ \ - INTEL_VGA_DEVICE(0x0D06, info) /* CRW GT1 mobile */ - -#define INTEL_HSW_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x0412, info), /* GT2 desktop */ \ - INTEL_VGA_DEVICE(0x041a, info), /* GT2 server */ \ - INTEL_VGA_DEVICE(0x041B, info), /* GT2 reserved */ \ - INTEL_VGA_DEVICE(0x041E, info), /* GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0C12, info), /* SDV GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0C1A, info), /* SDV GT2 server */ \ - INTEL_VGA_DEVICE(0x0C1B, info), /* SDV GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0C1E, info), /* SDV GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0A12, info), /* ULT GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0A1A, info), /* ULT GT2 server */ \ - INTEL_VGA_DEVICE(0x0A1B, info), /* ULT GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0D12, info), /* CRW GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0D1A, info), /* CRW GT2 server */ \ - INTEL_VGA_DEVICE(0x0D1B, info), /* CRW GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0D1E, info), /* CRW GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0416, info), /* GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0426, info), /* GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0C16, info), /* SDV GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0A16, info), /* ULT GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0A1E, info), /* ULX GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0D16, info) /* CRW GT2 mobile */ - -#define INTEL_HSW_GT3_IDS(info) \ - INTEL_VGA_DEVICE(0x0422, info), /* GT3 desktop */ \ - INTEL_VGA_DEVICE(0x042a, info), /* GT3 server */ \ - INTEL_VGA_DEVICE(0x042B, info), /* GT3 reserved */ \ - INTEL_VGA_DEVICE(0x042E, info), /* GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0C22, info), /* SDV GT3 desktop */ \ - INTEL_VGA_DEVICE(0x0C2A, info), /* SDV GT3 server */ \ - INTEL_VGA_DEVICE(0x0C2B, info), /* SDV GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0C2E, info), /* SDV GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0A22, info), /* ULT GT3 desktop */ \ - INTEL_VGA_DEVICE(0x0A2A, info), /* ULT GT3 server */ \ - INTEL_VGA_DEVICE(0x0A2B, info), /* ULT GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0D22, info), /* CRW GT3 desktop */ \ - INTEL_VGA_DEVICE(0x0D2A, info), /* CRW GT3 server */ \ - INTEL_VGA_DEVICE(0x0D2B, info), /* CRW GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0D2E, info), /* CRW GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0C26, info), /* SDV GT3 mobile */ \ - INTEL_VGA_DEVICE(0x0A26, info), /* ULT GT3 mobile */ \ - INTEL_VGA_DEVICE(0x0A2E, info), /* ULT GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0D26, info) /* CRW GT3 mobile */ - -#define INTEL_HSW_IDS(info) \ - INTEL_HSW_GT1_IDS(info), \ - INTEL_HSW_GT2_IDS(info), \ - INTEL_HSW_GT3_IDS(info) - -#define INTEL_VLV_IDS(info) \ - INTEL_VGA_DEVICE(0x0f30, info), \ - INTEL_VGA_DEVICE(0x0f31, info), \ - INTEL_VGA_DEVICE(0x0f32, info), \ - INTEL_VGA_DEVICE(0x0f33, info), \ - INTEL_VGA_DEVICE(0x0157, info), \ - INTEL_VGA_DEVICE(0x0155, info) - -#define INTEL_BDW_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x1602, info), /* GT1 ULT */ \ - INTEL_VGA_DEVICE(0x1606, info), /* GT1 ULT */ \ - INTEL_VGA_DEVICE(0x160B, info), /* GT1 Iris */ \ - INTEL_VGA_DEVICE(0x160E, info), /* GT1 ULX */ \ - INTEL_VGA_DEVICE(0x160A, info), /* GT1 Server */ \ - INTEL_VGA_DEVICE(0x160D, info) /* GT1 Workstation */ - -#define INTEL_BDW_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x1612, info), /* GT2 Halo */ \ - INTEL_VGA_DEVICE(0x1616, info), /* GT2 ULT */ \ - INTEL_VGA_DEVICE(0x161B, info), /* GT2 ULT */ \ - INTEL_VGA_DEVICE(0x161E, info), /* GT2 ULX */ \ - INTEL_VGA_DEVICE(0x161A, info), /* GT2 Server */ \ - INTEL_VGA_DEVICE(0x161D, info) /* GT2 Workstation */ - -#define INTEL_BDW_GT3_IDS(info) \ - INTEL_VGA_DEVICE(0x1622, info), /* ULT */ \ - INTEL_VGA_DEVICE(0x1626, info), /* ULT */ \ - INTEL_VGA_DEVICE(0x162B, info), /* Iris */ \ - INTEL_VGA_DEVICE(0x162E, info), /* ULX */\ - INTEL_VGA_DEVICE(0x162A, info), /* Server */ \ - INTEL_VGA_DEVICE(0x162D, info) /* Workstation */ - -#define INTEL_BDW_RSVD_IDS(info) \ - INTEL_VGA_DEVICE(0x1632, info), /* ULT */ \ - INTEL_VGA_DEVICE(0x1636, info), /* ULT */ \ - INTEL_VGA_DEVICE(0x163B, info), /* Iris */ \ - INTEL_VGA_DEVICE(0x163E, info), /* ULX */ \ - INTEL_VGA_DEVICE(0x163A, info), /* Server */ \ - INTEL_VGA_DEVICE(0x163D, info) /* Workstation */ - -#define INTEL_BDW_IDS(info) \ - INTEL_BDW_GT1_IDS(info), \ - INTEL_BDW_GT2_IDS(info), \ - INTEL_BDW_GT3_IDS(info), \ - INTEL_BDW_RSVD_IDS(info) - -#define INTEL_CHV_IDS(info) \ - INTEL_VGA_DEVICE(0x22b0, info), \ - INTEL_VGA_DEVICE(0x22b1, info), \ - INTEL_VGA_DEVICE(0x22b2, info), \ - INTEL_VGA_DEVICE(0x22b3, info) - -#define INTEL_SKL_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x1906, info), /* ULT GT1 */ \ - INTEL_VGA_DEVICE(0x190E, info), /* ULX GT1 */ \ - INTEL_VGA_DEVICE(0x1902, info), /* DT GT1 */ \ - INTEL_VGA_DEVICE(0x190B, info), /* Halo GT1 */ \ - INTEL_VGA_DEVICE(0x190A, info) /* SRV GT1 */ - -#define INTEL_SKL_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x1916, info), /* ULT GT2 */ \ - INTEL_VGA_DEVICE(0x1921, info), /* ULT GT2F */ \ - INTEL_VGA_DEVICE(0x191E, info), /* ULX GT2 */ \ - INTEL_VGA_DEVICE(0x1912, info), /* DT GT2 */ \ - INTEL_VGA_DEVICE(0x191B, info), /* Halo GT2 */ \ - INTEL_VGA_DEVICE(0x191A, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x191D, info) /* WKS GT2 */ - -#define INTEL_SKL_GT3_IDS(info) \ - INTEL_VGA_DEVICE(0x1923, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x1926, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x1927, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x192B, info), /* Halo GT3 */ \ - INTEL_VGA_DEVICE(0x192D, info) /* SRV GT3 */ - -#define INTEL_SKL_GT4_IDS(info) \ - INTEL_VGA_DEVICE(0x1932, info), /* DT GT4 */ \ - INTEL_VGA_DEVICE(0x193B, info), /* Halo GT4 */ \ - INTEL_VGA_DEVICE(0x193D, info), /* WKS GT4 */ \ - INTEL_VGA_DEVICE(0x192A, info), /* SRV GT4 */ \ - INTEL_VGA_DEVICE(0x193A, info) /* SRV GT4e */ - -#define INTEL_SKL_IDS(info) \ - INTEL_SKL_GT1_IDS(info), \ - INTEL_SKL_GT2_IDS(info), \ - INTEL_SKL_GT3_IDS(info), \ - INTEL_SKL_GT4_IDS(info) - -#define INTEL_BXT_IDS(info) \ - INTEL_VGA_DEVICE(0x0A84, info), \ - INTEL_VGA_DEVICE(0x1A84, info), \ - INTEL_VGA_DEVICE(0x1A85, info), \ - INTEL_VGA_DEVICE(0x5A84, info), /* APL HD Graphics 505 */ \ - INTEL_VGA_DEVICE(0x5A85, info) /* APL HD Graphics 500 */ - -#define INTEL_GLK_IDS(info) \ - INTEL_VGA_DEVICE(0x3184, info), \ - INTEL_VGA_DEVICE(0x3185, info) - -#define INTEL_KBL_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x5913, info), /* ULT GT1.5 */ \ - INTEL_VGA_DEVICE(0x5915, info), /* ULX GT1.5 */ \ - INTEL_VGA_DEVICE(0x5906, info), /* ULT GT1 */ \ - INTEL_VGA_DEVICE(0x590E, info), /* ULX GT1 */ \ - INTEL_VGA_DEVICE(0x5902, info), /* DT GT1 */ \ - INTEL_VGA_DEVICE(0x5908, info), /* Halo GT1 */ \ - INTEL_VGA_DEVICE(0x590B, info), /* Halo GT1 */ \ - INTEL_VGA_DEVICE(0x590A, info) /* SRV GT1 */ - -#define INTEL_KBL_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x5916, info), /* ULT GT2 */ \ - INTEL_VGA_DEVICE(0x5917, info), /* Mobile GT2 */ \ - INTEL_VGA_DEVICE(0x5921, info), /* ULT GT2F */ \ - INTEL_VGA_DEVICE(0x591E, info), /* ULX GT2 */ \ - INTEL_VGA_DEVICE(0x5912, info), /* DT GT2 */ \ - INTEL_VGA_DEVICE(0x591B, info), /* Halo GT2 */ \ - INTEL_VGA_DEVICE(0x591A, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x591D, info) /* WKS GT2 */ - -#define INTEL_KBL_GT3_IDS(info) \ - INTEL_VGA_DEVICE(0x5923, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x5926, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x5927, info) /* ULT GT3 */ - -#define INTEL_KBL_GT4_IDS(info) \ - INTEL_VGA_DEVICE(0x593B, info) /* Halo GT4 */ - -/* AML/KBL Y GT2 */ -#define INTEL_AML_KBL_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x591C, info), /* ULX GT2 */ \ - INTEL_VGA_DEVICE(0x87C0, info) /* ULX GT2 */ - -/* AML/CFL Y GT2 */ -#define INTEL_AML_CFL_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x87CA, info) - -#define INTEL_KBL_IDS(info) \ - INTEL_KBL_GT1_IDS(info), \ - INTEL_KBL_GT2_IDS(info), \ - INTEL_KBL_GT3_IDS(info), \ - INTEL_KBL_GT4_IDS(info), \ - INTEL_AML_KBL_GT2_IDS(info) - -/* CFL S */ -#define INTEL_CFL_S_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x3E90, info), /* SRV GT1 */ \ - INTEL_VGA_DEVICE(0x3E93, info), /* SRV GT1 */ \ - INTEL_VGA_DEVICE(0x3E99, info) /* SRV GT1 */ - -#define INTEL_CFL_S_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x3E91, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x3E92, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x3E96, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x3E98, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x3E9A, info) /* SRV GT2 */ - -/* CFL H */ -#define INTEL_CFL_H_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x3E9B, info), /* Halo GT2 */ \ - INTEL_VGA_DEVICE(0x3E94, info) /* Halo GT2 */ - -/* CFL U GT2 */ -#define INTEL_CFL_U_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x3EA9, info) - -/* CFL U GT3 */ -#define INTEL_CFL_U_GT3_IDS(info) \ - INTEL_VGA_DEVICE(0x3EA5, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x3EA6, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x3EA7, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x3EA8, info) /* ULT GT3 */ - -/* WHL/CFL U GT1 */ -#define INTEL_WHL_U_GT1_IDS(info) \ - INTEL_VGA_DEVICE(0x3EA1, info), \ - INTEL_VGA_DEVICE(0x3EA4, info) - -/* WHL/CFL U GT2 */ -#define INTEL_WHL_U_GT2_IDS(info) \ - INTEL_VGA_DEVICE(0x3EA0, info), \ - INTEL_VGA_DEVICE(0x3EA3, info) - -/* WHL/CFL U GT3 */ -#define INTEL_WHL_U_GT3_IDS(info) \ - INTEL_VGA_DEVICE(0x3EA2, info) - -#define INTEL_CFL_IDS(info) \ - INTEL_CFL_S_GT1_IDS(info), \ - INTEL_CFL_S_GT2_IDS(info), \ - INTEL_CFL_H_GT2_IDS(info), \ - INTEL_CFL_U_GT2_IDS(info), \ - INTEL_CFL_U_GT3_IDS(info), \ - INTEL_WHL_U_GT1_IDS(info), \ - INTEL_WHL_U_GT2_IDS(info), \ - INTEL_WHL_U_GT3_IDS(info), \ - INTEL_AML_CFL_GT2_IDS(info) - -/* CNL */ -#define INTEL_CNL_IDS(info) \ - INTEL_VGA_DEVICE(0x5A51, info), \ - INTEL_VGA_DEVICE(0x5A59, info), \ - INTEL_VGA_DEVICE(0x5A41, info), \ - INTEL_VGA_DEVICE(0x5A49, info), \ - INTEL_VGA_DEVICE(0x5A52, info), \ - INTEL_VGA_DEVICE(0x5A5A, info), \ - INTEL_VGA_DEVICE(0x5A42, info), \ - INTEL_VGA_DEVICE(0x5A4A, info), \ - INTEL_VGA_DEVICE(0x5A50, info), \ - INTEL_VGA_DEVICE(0x5A40, info), \ - INTEL_VGA_DEVICE(0x5A54, info), \ - INTEL_VGA_DEVICE(0x5A5C, info), \ - INTEL_VGA_DEVICE(0x5A44, info), \ - INTEL_VGA_DEVICE(0x5A4C, info) - -/* ICL */ -#define INTEL_ICL_11_IDS(info) \ - INTEL_VGA_DEVICE(0x8A50, info), \ - INTEL_VGA_DEVICE(0x8A51, info), \ - INTEL_VGA_DEVICE(0x8A5C, info), \ - INTEL_VGA_DEVICE(0x8A5D, info), \ - INTEL_VGA_DEVICE(0x8A52, info), \ - INTEL_VGA_DEVICE(0x8A5A, info), \ - INTEL_VGA_DEVICE(0x8A5B, info), \ - INTEL_VGA_DEVICE(0x8A71, info), \ - INTEL_VGA_DEVICE(0x8A70, info) - -#endif /* _I915_PCIIDS_H */ diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h deleted file mode 100644 index 2324c84a25c0..000000000000 --- a/include/drm/intel-gtt.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Common header for intel-gtt.ko and i915.ko */ - -#ifndef _DRM_INTEL_GTT_H -#define _DRM_INTEL_GTT_H - -void intel_gtt_get(u64 *gtt_total, - phys_addr_t *mappable_base, - resource_size_t *mappable_end); - -int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, - struct agp_bridge_data *bridge); -void intel_gmch_remove(void); - -bool intel_enable_gtt(void); - -void intel_gtt_chipset_flush(void); -void intel_gtt_insert_page(dma_addr_t addr, - unsigned int pg, - unsigned int flags); -void intel_gtt_insert_sg_entries(struct sg_table *st, - unsigned int pg_start, - unsigned int flags); -void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); - -/* Special gtt memory types */ -#define AGP_DCACHE_MEMORY 1 -#define AGP_PHYS_MEMORY 2 - -/* flag for GFDT type */ -#define AGP_USER_CACHED_MEMORY_GFDT (1 << 3) - -#ifdef CONFIG_INTEL_IOMMU -extern int intel_iommu_gfx_mapped; -#endif - -#endif diff --git a/include/drm/intel/display_member.h b/include/drm/intel/display_member.h new file mode 100644 index 000000000000..0319ea560b60 --- /dev/null +++ b/include/drm/intel/display_member.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2025 Intel Corporation */ + +#ifndef __DRM_INTEL_DISPLAY_H__ +#define __DRM_INTEL_DISPLAY_H__ + +#include <linux/build_bug.h> +#include <linux/stddef.h> +#include <linux/stringify.h> + +#include <drm/drm_device.h> + +struct intel_display; + +/* + * A dummy device struct to define the relative offsets of drm and display + * members. With the members identically placed in struct drm_i915_private and + * struct xe_device, this allows figuring out the struct intel_display pointer + * without the definition of either driver specific structure. + */ +struct __intel_generic_device { + struct drm_device drm; + struct intel_display *display; +}; + +/** + * INTEL_DISPLAY_MEMBER_STATIC_ASSERT() - ensure correct placing of drm and display members + * @type: The struct to check + * @drm_member: Name of the struct drm_device member + * @display_member: Name of the struct intel_display * member. + * + * Use this static assert macro to ensure the struct drm_i915_private and struct + * xe_device struct drm_device and struct intel_display * members are at the + * same relative offsets. + */ +#define INTEL_DISPLAY_MEMBER_STATIC_ASSERT(type, drm_member, display_member) \ + static_assert( \ + offsetof(struct __intel_generic_device, display) - offsetof(struct __intel_generic_device, drm) == \ + offsetof(type, display_member) - offsetof(type, drm_member), \ + __stringify(type) " " __stringify(drm_member) " and " __stringify(display_member) " members at invalid offsets") + +#endif diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h new file mode 100644 index 000000000000..26bedc360044 --- /dev/null +++ b/include/drm/intel/display_parent_interface.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2025 Intel Corporation x*/ + +#ifndef __DISPLAY_PARENT_INTERFACE_H__ +#define __DISPLAY_PARENT_INTERFACE_H__ + +#include <linux/types.h> + +struct drm_device; +struct ref_tracker; + +struct intel_display_rpm_interface { + struct ref_tracker *(*get)(const struct drm_device *drm); + struct ref_tracker *(*get_raw)(const struct drm_device *drm); + struct ref_tracker *(*get_if_in_use)(const struct drm_device *drm); + struct ref_tracker *(*get_noresume)(const struct drm_device *drm); + + void (*put)(const struct drm_device *drm, struct ref_tracker *wakeref); + void (*put_raw)(const struct drm_device *drm, struct ref_tracker *wakeref); + void (*put_unchecked)(const struct drm_device *drm); + + bool (*suspended)(const struct drm_device *drm); + void (*assert_held)(const struct drm_device *drm); + void (*assert_block)(const struct drm_device *drm); + void (*assert_unblock)(const struct drm_device *drm); +}; + +/** + * struct intel_display_parent_interface - services parent driver provides to display + * + * The parent, or core, driver provides a pointer to this structure to display + * driver when calling intel_display_device_probe(). The display driver uses it + * to access services provided by the parent driver. The structure may contain + * sub-struct pointers to group function pointers by functionality. + * + * All function and sub-struct pointers must be initialized and callable unless + * explicitly marked as "optional" below. The display driver will only NULL + * check the optional pointers. + */ +struct intel_display_parent_interface { + /** @rpm: Runtime PM functions */ + const struct intel_display_rpm_interface *rpm; +}; + +#endif diff --git a/include/drm/i915_component.h b/include/drm/intel/i915_component.h index fca22d463e1b..8082db222e00 100644 --- a/include/drm/i915_component.h +++ b/include/drm/intel/i915_component.h @@ -24,12 +24,20 @@ #ifndef _I915_COMPONENT_H_ #define _I915_COMPONENT_H_ -#include "drm_audio_component.h" +#include <drm/drm_audio_component.h> + +enum i915_component_type { + I915_COMPONENT_AUDIO = 1, + I915_COMPONENT_HDCP, + I915_COMPONENT_PXP, + I915_COMPONENT_GSC_PROXY, + INTEL_COMPONENT_LB, +}; /* MAX_PORT is the number of port * It must be sync with I915_MAX_PORTS defined i915_drv.h */ -#define MAX_PORTS 6 +#define MAX_PORTS 9 /** * struct i915_audio_component - Used for direct communication between i915 and hda drivers diff --git a/include/drm/i915_drm.h b/include/drm/intel/i915_drm.h index c44703f471b3..adff68538484 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/intel/i915_drm.h @@ -26,15 +26,14 @@ #ifndef _I915_DRM_H_ #define _I915_DRM_H_ -#include <drm/i915_pciids.h> -#include <uapi/drm/i915_drm.h> +#include <linux/types.h> /* For use by IPS driver */ -extern unsigned long i915_read_mch_val(void); -extern bool i915_gpu_raise(void); -extern bool i915_gpu_lower(void); -extern bool i915_gpu_busy(void); -extern bool i915_gpu_turbo_disable(void); +unsigned long i915_read_mch_val(void); +bool i915_gpu_raise(void); +bool i915_gpu_lower(void); +bool i915_gpu_busy(void); +bool i915_gpu_turbo_disable(void); /* Exported from arch/x86/kernel/early-quirks.c */ extern struct resource intel_graphics_stolen_res; @@ -43,7 +42,7 @@ extern struct resource intel_graphics_stolen_res; * The Bridge device's PCI config space has information about the * fb aperture size and the amount of pre-reserved memory. * This is all handled in the intel-gtt.ko module. i915.ko only - * cares about the vga bit for the vga rbiter. + * cares about the vga bit for the vga arbiter. */ #define INTEL_GMCH_CTRL 0x52 #define INTEL_GMCH_VGA_DISABLE (1 << 1) diff --git a/include/drm/intel/i915_gsc_proxy_mei_interface.h b/include/drm/intel/i915_gsc_proxy_mei_interface.h new file mode 100644 index 000000000000..850dfbf40607 --- /dev/null +++ b/include/drm/intel/i915_gsc_proxy_mei_interface.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022-2023 Intel Corporation + */ + +#ifndef _I915_GSC_PROXY_MEI_INTERFACE_H_ +#define _I915_GSC_PROXY_MEI_INTERFACE_H_ + +#include <linux/types.h> + +struct device; +struct module; + +/** + * struct i915_gsc_proxy_component_ops - ops for GSC Proxy services. + * @owner: Module providing the ops + * @send: sends a proxy message from GSC FW to ME FW + * @recv: receives a proxy message for GSC FW from ME FW + */ +struct i915_gsc_proxy_component_ops { + struct module *owner; + + /** + * @send: Sends a proxy message to ME FW. + * @dev: device struct corresponding to the mei device + * @buf: message buffer to send + * @size: size of the message + * Return: bytes sent on success, negative errno value on failure + */ + int (*send)(struct device *dev, const void *buf, size_t size); + + /** + * @recv: Receives a proxy message from ME FW. + * @dev: device struct corresponding to the mei device + * @buf: message buffer to contain the received message + * @size: size of the buffer + * Return: bytes received on success, negative errno value on failure + */ + int (*recv)(struct device *dev, void *buf, size_t size); +}; + +/** + * struct i915_gsc_proxy_component - Used for communication between i915 and + * MEI drivers for GSC proxy services + * @mei_dev: device that provide the GSC proxy service. + * @ops: Ops implemented by GSC proxy driver, used by i915 driver. + */ +struct i915_gsc_proxy_component { + struct device *mei_dev; + const struct i915_gsc_proxy_component_ops *ops; +}; + +#endif /* _I915_GSC_PROXY_MEI_INTERFACE_H_ */ diff --git a/include/drm/intel/i915_hdcp_interface.h b/include/drm/intel/i915_hdcp_interface.h new file mode 100644 index 000000000000..d776ed7dcd00 --- /dev/null +++ b/include/drm/intel/i915_hdcp_interface.h @@ -0,0 +1,547 @@ +/* SPDX-License-Identifier: (GPL-2.0+) */ +/* + * Copyright © 2017-2019 Intel Corporation + * + * Authors: + * Ramalingam C <ramalingam.c@intel.com> + */ + +#ifndef _I915_HDCP_INTERFACE_H_ +#define _I915_HDCP_INTERFACE_H_ + +#include <linux/mutex.h> +#include <linux/device.h> +#include <drm/display/drm_hdcp.h> + +/** + * enum hdcp_port_type - HDCP port implementation type defined by ME/GSC FW + * @HDCP_PORT_TYPE_INVALID: Invalid hdcp port type + * @HDCP_PORT_TYPE_INTEGRATED: In-Host HDCP2.x port + * @HDCP_PORT_TYPE_LSPCON: HDCP2.2 discrete wired Tx port with LSPCON + * (HDMI 2.0) solution + * @HDCP_PORT_TYPE_CPDP: HDCP2.2 discrete wired Tx port using the CPDP (DP 1.3) + * solution + */ +enum hdcp_port_type { + HDCP_PORT_TYPE_INVALID, + HDCP_PORT_TYPE_INTEGRATED, + HDCP_PORT_TYPE_LSPCON, + HDCP_PORT_TYPE_CPDP +}; + +/** + * enum hdcp_wired_protocol - HDCP adaptation used on the port + * @HDCP_PROTOCOL_INVALID: Invalid HDCP adaptation protocol + * @HDCP_PROTOCOL_HDMI: HDMI adaptation of HDCP used on the port + * @HDCP_PROTOCOL_DP: DP adaptation of HDCP used on the port + */ +enum hdcp_wired_protocol { + HDCP_PROTOCOL_INVALID, + HDCP_PROTOCOL_HDMI, + HDCP_PROTOCOL_DP +}; + +enum hdcp_ddi { + HDCP_DDI_INVALID_PORT = 0x0, + + HDCP_DDI_B = 1, + HDCP_DDI_C, + HDCP_DDI_D, + HDCP_DDI_E, + HDCP_DDI_F, + HDCP_DDI_A = 7, + HDCP_DDI_RANGE_END = HDCP_DDI_A, +}; + +/** + * enum hdcp_transcoder - ME/GSC Firmware defined index for transcoders + * @HDCP_INVALID_TRANSCODER: Index for Invalid transcoder + * @HDCP_TRANSCODER_EDP: Index for EDP Transcoder + * @HDCP_TRANSCODER_DSI0: Index for DSI0 Transcoder + * @HDCP_TRANSCODER_DSI1: Index for DSI1 Transcoder + * @HDCP_TRANSCODER_A: Index for Transcoder A + * @HDCP_TRANSCODER_B: Index for Transcoder B + * @HDCP_TRANSCODER_C: Index for Transcoder C + * @HDCP_TRANSCODER_D: Index for Transcoder D + */ +enum hdcp_transcoder { + HDCP_INVALID_TRANSCODER = 0x00, + HDCP_TRANSCODER_EDP, + HDCP_TRANSCODER_DSI0, + HDCP_TRANSCODER_DSI1, + HDCP_TRANSCODER_A = 0x10, + HDCP_TRANSCODER_B, + HDCP_TRANSCODER_C, + HDCP_TRANSCODER_D +}; + +/** + * struct hdcp_port_data - intel specific HDCP port data + * @hdcp_ddi: ddi index as per ME/GSC FW + * @hdcp_transcoder: transcoder index as per ME/GSC FW + * @port_type: HDCP port type as per ME/GSC FW classification + * @protocol: HDCP adaptation as per ME/GSC FW + * @k: No of streams transmitted on a port. Only on DP MST this is != 1 + * @seq_num_m: Count of RepeaterAuth_Stream_Manage msg propagated. + * Initialized to 0 on AKE_INIT. Incremented after every successful + * transmission of RepeaterAuth_Stream_Manage message. When it rolls + * over re-Auth has to be triggered. + * @streams: struct hdcp2_streamid_type[k]. Defines the type and id for the + * streams + */ +struct hdcp_port_data { + enum hdcp_ddi hdcp_ddi; + enum hdcp_transcoder hdcp_transcoder; + u8 port_type; + u8 protocol; + u16 k; + u32 seq_num_m; + struct hdcp2_streamid_type *streams; +}; + +/** + * struct i915_hdcp_ops- ops for HDCP2.2 services. + * @owner: Module providing the ops + * @initiate_hdcp2_session: Initiate a Wired HDCP2.2 Tx Session. + * And Prepare AKE_Init. + * @verify_receiver_cert_prepare_km: Verify the Receiver Certificate + * AKE_Send_Cert and prepare + * AKE_Stored_Km/AKE_No_Stored_Km + * @verify_hprime: Verify AKE_Send_H_prime + * @store_pairing_info: Store pairing info received + * @initiate_locality_check: Prepare LC_Init + * @verify_lprime: Verify lprime + * @get_session_key: Prepare SKE_Send_Eks + * @repeater_check_flow_prepare_ack: Validate the Downstream topology + * and prepare rep_ack + * @verify_mprime: Verify mprime + * @enable_hdcp_authentication: Mark a port as authenticated. + * @close_hdcp_session: Close the Wired HDCP Tx session per port. + * This also disables the authenticated state of the port. + */ +struct i915_hdcp_ops { + /** + * @owner: hdcp module + */ + struct module *owner; + + int (*initiate_hdcp2_session)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_ake_init *ake_data); + int (*verify_receiver_cert_prepare_km)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_ake_send_cert + *rx_cert, + bool *km_stored, + struct hdcp2_ake_no_stored_km + *ek_pub_km, + size_t *msg_sz); + int (*verify_hprime)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_ake_send_hprime *rx_hprime); + int (*store_pairing_info)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_ake_send_pairing_info + *pairing_info); + int (*initiate_locality_check)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_lc_init *lc_init_data); + int (*verify_lprime)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_lc_send_lprime *rx_lprime); + int (*get_session_key)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_ske_send_eks *ske_data); + int (*repeater_check_flow_prepare_ack)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_rep_send_receiverid_list + *rep_topology, + struct hdcp2_rep_send_ack + *rep_send_ack); + int (*verify_mprime)(struct device *dev, + struct hdcp_port_data *data, + struct hdcp2_rep_stream_ready *stream_ready); + int (*enable_hdcp_authentication)(struct device *dev, + struct hdcp_port_data *data); + int (*close_hdcp_session)(struct device *dev, + struct hdcp_port_data *data); +}; + +/** + * struct i915_hdcp_arbiter - Used for communication between i915 + * and hdcp drivers for the HDCP2.2 services + */ +struct i915_hdcp_arbiter { + /** + * @hdcp_dev: device that provides the HDCP2.2 service from MEI Bus. + */ + struct device *hdcp_dev; + + /** + * @ops: Ops implemented by hdcp driver or intel_hdcp_gsc, used by i915 + * driver. + */ + const struct i915_hdcp_ops *ops; + + /** + * @mutex: To protect the above members. + */ + struct mutex mutex; +}; + +/* fw_hdcp_status: Enumeration of all HDCP Status Codes */ +enum fw_hdcp_status { + FW_HDCP_STATUS_SUCCESS = 0x0000, + + /* WiDi Generic Status Codes */ + FW_HDCP_STATUS_INTERNAL_ERROR = 0x1000, + FW_HDCP_STATUS_UNKNOWN_ERROR = 0x1001, + FW_HDCP_STATUS_INCORRECT_API_VERSION = 0x1002, + FW_HDCP_STATUS_INVALID_FUNCTION = 0x1003, + FW_HDCP_STATUS_INVALID_BUFFER_LENGTH = 0x1004, + FW_HDCP_STATUS_INVALID_PARAMS = 0x1005, + FW_HDCP_STATUS_AUTHENTICATION_FAILED = 0x1006, + + /* WiDi Status Codes */ + FW_HDCP_INVALID_SESSION_STATE = 0x6000, + FW_HDCP_SRM_FRAGMENT_UNEXPECTED = 0x6001, + FW_HDCP_SRM_INVALID_LENGTH = 0x6002, + FW_HDCP_SRM_FRAGMENT_OFFSET_INVALID = 0x6003, + FW_HDCP_SRM_VERIFICATION_FAILED = 0x6004, + FW_HDCP_SRM_VERSION_TOO_OLD = 0x6005, + FW_HDCP_RX_CERT_VERIFICATION_FAILED = 0x6006, + FW_HDCP_RX_REVOKED = 0x6007, + FW_HDCP_H_VERIFICATION_FAILED = 0x6008, + FW_HDCP_REPEATER_CHECK_UNEXPECTED = 0x6009, + FW_HDCP_TOPOLOGY_MAX_EXCEEDED = 0x600A, + FW_HDCP_V_VERIFICATION_FAILED = 0x600B, + FW_HDCP_L_VERIFICATION_FAILED = 0x600C, + FW_HDCP_STREAM_KEY_ALLOC_FAILED = 0x600D, + FW_HDCP_BASE_KEY_RESET_FAILED = 0x600E, + FW_HDCP_NONCE_GENERATION_FAILED = 0x600F, + FW_HDCP_STATUS_INVALID_E_KEY_STATE = 0x6010, + FW_HDCP_STATUS_INVALID_CS_ICV = 0x6011, + FW_HDCP_STATUS_INVALID_KB_KEY_STATE = 0x6012, + FW_HDCP_STATUS_INVALID_PAVP_MODE_ICV = 0x6013, + FW_HDCP_STATUS_INVALID_PAVP_MODE = 0x6014, + FW_HDCP_STATUS_LC_MAX_ATTEMPTS = 0x6015, + + /* New status for HDCP 2.1 */ + FW_HDCP_STATUS_MISMATCH_IN_M = 0x6016, + + /* New status code for HDCP 2.2 Rx */ + FW_HDCP_STATUS_RX_PROV_NOT_ALLOWED = 0x6017, + FW_HDCP_STATUS_RX_PROV_WRONG_SUBJECT = 0x6018, + FW_HDCP_RX_NEEDS_PROVISIONING = 0x6019, + FW_HDCP_BKSV_ICV_AUTH_FAILED = 0x6020, + FW_HDCP_STATUS_INVALID_STREAM_ID = 0x6021, + FW_HDCP_STATUS_CHAIN_NOT_INITIALIZED = 0x6022, + FW_HDCP_FAIL_NOT_EXPECTED = 0x6023, + FW_HDCP_FAIL_HDCP_OFF = 0x6024, + FW_HDCP_FAIL_INVALID_PAVP_MEMORY_MODE = 0x6025, + FW_HDCP_FAIL_AES_ECB_FAILURE = 0x6026, + FW_HDCP_FEATURE_NOT_SUPPORTED = 0x6027, + FW_HDCP_DMA_READ_ERROR = 0x6028, + FW_HDCP_DMA_WRITE_ERROR = 0x6029, + FW_HDCP_FAIL_INVALID_PACKET_SIZE = 0x6030, + FW_HDCP_H264_PARSING_ERROR = 0x6031, + FW_HDCP_HDCP2_ERRATA_VIDEO_VIOLATION = 0x6032, + FW_HDCP_HDCP2_ERRATA_AUDIO_VIOLATION = 0x6033, + FW_HDCP_TX_ACTIVE_ERROR = 0x6034, + FW_HDCP_MODE_CHANGE_ERROR = 0x6035, + FW_HDCP_STREAM_TYPE_ERROR = 0x6036, + FW_HDCP_STREAM_MANAGE_NOT_POSSIBLE = 0x6037, + + FW_HDCP_STATUS_PORT_INVALID_COMMAND = 0x6038, + FW_HDCP_STATUS_UNSUPPORTED_PROTOCOL = 0x6039, + FW_HDCP_STATUS_INVALID_PORT_INDEX = 0x603a, + FW_HDCP_STATUS_TX_AUTH_NEEDED = 0x603b, + FW_HDCP_STATUS_NOT_INTEGRATED_PORT = 0x603c, + FW_HDCP_STATUS_SESSION_MAX_REACHED = 0x603d, + + /* hdcp capable bit is not set in rx_caps(error is unique to DP) */ + FW_HDCP_STATUS_NOT_HDCP_CAPABLE = 0x6041, + + FW_HDCP_STATUS_INVALID_STREAM_COUNT = 0x6042, +}; + +#define HDCP_API_VERSION 0x00010000 + +#define HDCP_M_LEN 16 +#define HDCP_KH_LEN 16 + +/* Payload Buffer size(Excluding Header) for CMDs and corresponding response */ +/* Wired_Tx_AKE */ +#define WIRED_CMD_BUF_LEN_INITIATE_HDCP2_SESSION_IN (4 + 1) +#define WIRED_CMD_BUF_LEN_INITIATE_HDCP2_SESSION_OUT (4 + 8 + 3) + +#define WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_IN (4 + 522 + 8 + 3) +#define WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_MIN_OUT (4 + 1 + 3 + 16 + 16) +#define WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_MAX_OUT (4 + 1 + 3 + 128) + +#define WIRED_CMD_BUF_LEN_AKE_SEND_HPRIME_IN (4 + 32) +#define WIRED_CMD_BUF_LEN_AKE_SEND_HPRIME_OUT (4) + +#define WIRED_CMD_BUF_LEN_SEND_PAIRING_INFO_IN (4 + 16) +#define WIRED_CMD_BUF_LEN_SEND_PAIRING_INFO_OUT (4) + +#define WIRED_CMD_BUF_LEN_CLOSE_SESSION_IN (4) +#define WIRED_CMD_BUF_LEN_CLOSE_SESSION_OUT (4) + +/* Wired_Tx_LC */ +#define WIRED_CMD_BUF_LEN_INIT_LOCALITY_CHECK_IN (4) +#define WIRED_CMD_BUF_LEN_INIT_LOCALITY_CHECK_OUT (4 + 8) + +#define WIRED_CMD_BUF_LEN_VALIDATE_LOCALITY_IN (4 + 32) +#define WIRED_CMD_BUF_LEN_VALIDATE_LOCALITY_OUT (4) + +/* Wired_Tx_SKE */ +#define WIRED_CMD_BUF_LEN_GET_SESSION_KEY_IN (4) +#define WIRED_CMD_BUF_LEN_GET_SESSION_KEY_OUT (4 + 16 + 8) + +/* Wired_Tx_SKE */ +#define WIRED_CMD_BUF_LEN_ENABLE_AUTH_IN (4 + 1) +#define WIRED_CMD_BUF_LEN_ENABLE_AUTH_OUT (4) + +/* Wired_Tx_Repeater */ +#define WIRED_CMD_BUF_LEN_VERIFY_REPEATER_IN (4 + 2 + 3 + 16 + 155) +#define WIRED_CMD_BUF_LEN_VERIFY_REPEATER_OUT (4 + 1 + 16) + +#define WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_MIN_IN (4 + 3 + \ + 32 + 2 + 2) + +#define WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_OUT (4) + +/* hdcp_command_id: Enumeration of all WIRED HDCP Command IDs */ +enum hdcp_command_id { + _WIDI_COMMAND_BASE = 0x00030000, + WIDI_INITIATE_HDCP2_SESSION = _WIDI_COMMAND_BASE, + HDCP_GET_SRM_STATUS, + HDCP_SEND_SRM_FRAGMENT, + + /* The wired HDCP Tx commands */ + _WIRED_COMMAND_BASE = 0x00031000, + WIRED_INITIATE_HDCP2_SESSION = _WIRED_COMMAND_BASE, + WIRED_VERIFY_RECEIVER_CERT, + WIRED_AKE_SEND_HPRIME, + WIRED_AKE_SEND_PAIRING_INFO, + WIRED_INIT_LOCALITY_CHECK, + WIRED_VALIDATE_LOCALITY, + WIRED_GET_SESSION_KEY, + WIRED_ENABLE_AUTH, + WIRED_VERIFY_REPEATER, + WIRED_REPEATER_AUTH_STREAM_REQ, + WIRED_CLOSE_SESSION, + + _WIRED_COMMANDS_COUNT, +}; + +union encrypted_buff { + u8 e_kpub_km[HDCP_2_2_E_KPUB_KM_LEN]; + u8 e_kh_km_m[HDCP_2_2_E_KH_KM_M_LEN]; + struct { + u8 e_kh_km[HDCP_KH_LEN]; + u8 m[HDCP_M_LEN]; + } __packed; +}; + +/* HDCP HECI message header. All header values are little endian. */ +struct hdcp_cmd_header { + u32 api_version; + u32 command_id; + enum fw_hdcp_status status; + /* Length of the HECI message (excluding the header) */ + u32 buffer_len; +} __packed; + +/* Empty command request or response. No data follows the header. */ +struct hdcp_cmd_no_data { + struct hdcp_cmd_header header; +} __packed; + +/* Uniquely identifies the hdcp port being addressed for a given command. */ +struct hdcp_port_id { + u8 integrated_port_type; + /* physical_port is used until Gen11.5. Must be zero for Gen11.5+ */ + u8 physical_port; + /* attached_transcoder is for Gen11.5+. Set to zero for <Gen11.5 */ + u8 attached_transcoder; + u8 reserved; +} __packed; + +/* + * Data structures for integrated wired HDCP2 Tx in + * support of the AKE protocol + */ +/* HECI struct for integrated wired HDCP Tx session initiation. */ +struct wired_cmd_initiate_hdcp2_session_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 protocol; /* for HDMI vs DP */ +} __packed; + +struct wired_cmd_initiate_hdcp2_session_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 r_tx[HDCP_2_2_RTX_LEN]; + struct hdcp2_tx_caps tx_caps; +} __packed; + +/* HECI struct for ending an integrated wired HDCP Tx session. */ +struct wired_cmd_close_session_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +struct wired_cmd_close_session_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +/* HECI struct for integrated wired HDCP Tx Rx Cert verification. */ +struct wired_cmd_verify_receiver_cert_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + struct hdcp2_cert_rx cert_rx; + u8 r_rx[HDCP_2_2_RRX_LEN]; + u8 rx_caps[HDCP_2_2_RXCAPS_LEN]; +} __packed; + +struct wired_cmd_verify_receiver_cert_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 km_stored; + u8 reserved[3]; + union encrypted_buff ekm_buff; +} __packed; + +/* HECI struct for verification of Rx's Hprime in a HDCP Tx session */ +struct wired_cmd_ake_send_hprime_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 h_prime[HDCP_2_2_H_PRIME_LEN]; +} __packed; + +struct wired_cmd_ake_send_hprime_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +/* + * HECI struct for sending in AKE pairing data generated by the Rx in an + * integrated wired HDCP Tx session. + */ +struct wired_cmd_ake_send_pairing_info_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 e_kh_km[HDCP_2_2_E_KH_KM_LEN]; +} __packed; + +struct wired_cmd_ake_send_pairing_info_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +/* Data structures for integrated wired HDCP2 Tx in support of the LC protocol*/ +/* + * HECI struct for initiating locality check with an + * integrated wired HDCP Tx session. + */ +struct wired_cmd_init_locality_check_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +struct wired_cmd_init_locality_check_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 r_n[HDCP_2_2_RN_LEN]; +} __packed; + +/* + * HECI struct for validating an Rx's LPrime value in an + * integrated wired HDCP Tx session. + */ +struct wired_cmd_validate_locality_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 l_prime[HDCP_2_2_L_PRIME_LEN]; +} __packed; + +struct wired_cmd_validate_locality_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +/* + * Data structures for integrated wired HDCP2 Tx in support of the + * SKE protocol + */ +/* HECI struct for creating session key */ +struct wired_cmd_get_session_key_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +struct wired_cmd_get_session_key_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 e_dkey_ks[HDCP_2_2_E_DKEY_KS_LEN]; + u8 r_iv[HDCP_2_2_RIV_LEN]; +} __packed; + +/* HECI struct for the Tx enable authentication command */ +struct wired_cmd_enable_auth_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 stream_type; +} __packed; + +struct wired_cmd_enable_auth_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +/* + * Data structures for integrated wired HDCP2 Tx in support of + * the repeater protocols + */ +/* + * HECI struct for verifying the downstream repeater's HDCP topology in an + * integrated wired HDCP Tx session. + */ +struct wired_cmd_verify_repeater_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 rx_info[HDCP_2_2_RXINFO_LEN]; + u8 seq_num_v[HDCP_2_2_SEQ_NUM_LEN]; + u8 v_prime[HDCP_2_2_V_PRIME_HALF_LEN]; + u8 receiver_ids[HDCP_2_2_RECEIVER_IDS_MAX_LEN]; +} __packed; + +struct wired_cmd_verify_repeater_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 content_type_supported; + u8 v[HDCP_2_2_V_PRIME_HALF_LEN]; +} __packed; + +/* + * HECI struct in support of stream management in an + * integrated wired HDCP Tx session. + */ +struct wired_cmd_repeater_auth_stream_req_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + u8 seq_num_m[HDCP_2_2_SEQ_NUM_LEN]; + u8 m_prime[HDCP_2_2_MPRIME_LEN]; + __be16 k; + struct hdcp2_streamid_type streams[]; +} __packed; + +struct wired_cmd_repeater_auth_stream_req_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; +} __packed; + +#endif /* _I915_HDCP_INTERFACE_H_ */ diff --git a/include/drm/intel/i915_pxp_tee_interface.h b/include/drm/intel/i915_pxp_tee_interface.h new file mode 100644 index 000000000000..a532d32f58f3 --- /dev/null +++ b/include/drm/intel/i915_pxp_tee_interface.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef _I915_PXP_TEE_INTERFACE_H_ +#define _I915_PXP_TEE_INTERFACE_H_ + +#include <linux/mutex.h> +#include <linux/device.h> +struct scatterlist; + +/** + * struct i915_pxp_component_ops - ops for PXP services. + */ +struct i915_pxp_component_ops { + /** + * @owner: Module providing the ops. + */ + struct module *owner; + + /** + * @send: Send a PXP message. + */ + int (*send)(struct device *dev, const void *message, size_t size, + unsigned long timeout_ms); + /** + * @recv: Receive a PXP message. + */ + int (*recv)(struct device *dev, void *buffer, size_t size, + unsigned long timeout_ms); + /** + * @gsc_command: Send a GSC command. + */ + ssize_t (*gsc_command)(struct device *dev, u8 client_id, u32 fence_id, + struct scatterlist *sg_in, size_t total_in_len, + struct scatterlist *sg_out); + +}; + +/** + * struct i915_pxp_component - Used for communication between i915 and TEE + * drivers for the PXP services + */ +struct i915_pxp_component { + /** + * @tee_dev: device that provide the PXP service from TEE Bus. + */ + struct device *tee_dev; + + /** + * @ops: Ops implemented by TEE driver, used by i915 driver. + */ + const struct i915_pxp_component_ops *ops; + + /** + * @mutex: To protect the above members. + */ + struct mutex mutex; +}; + +#endif /* _I915_TEE_PXP_INTERFACE_H_ */ diff --git a/include/drm/intel/intel-gtt.h b/include/drm/intel/intel-gtt.h new file mode 100644 index 000000000000..f53bcff01f22 --- /dev/null +++ b/include/drm/intel/intel-gtt.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Common header for intel-gtt.ko and i915.ko */ + +#ifndef _DRM_INTEL_GTT_H +#define _DRM_INTEL_GTT_H + +#include <linux/types.h> + +struct agp_bridge_data; +struct pci_dev; +struct sg_table; + +void intel_gmch_gtt_get(u64 *gtt_total, + phys_addr_t *mappable_base, + resource_size_t *mappable_end); + +int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, + struct agp_bridge_data *bridge); +void intel_gmch_remove(void); + +bool intel_gmch_enable_gtt(void); + +void intel_gmch_gtt_flush(void); +void intel_gmch_gtt_insert_page(dma_addr_t addr, + unsigned int pg, + unsigned int flags); +void intel_gmch_gtt_insert_sg_entries(struct sg_table *st, + unsigned int pg_start, + unsigned int flags); +void intel_gmch_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); +dma_addr_t intel_gmch_gtt_read_entry(unsigned int pg, + bool *is_present, bool *is_local); + +/* Special gtt memory types */ +#define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 + +/* flag for GFDT type */ +#define AGP_USER_CACHED_MEMORY_GFDT (1 << 3) + +#endif diff --git a/include/drm/intel/intel_lb_mei_interface.h b/include/drm/intel/intel_lb_mei_interface.h new file mode 100644 index 000000000000..d65be2cba2ab --- /dev/null +++ b/include/drm/intel/intel_lb_mei_interface.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2025 Intel Corporation + */ + +#ifndef _INTEL_LB_MEI_INTERFACE_H_ +#define _INTEL_LB_MEI_INTERFACE_H_ + +#include <linux/types.h> + +struct device; + +/** + * define INTEL_LB_FLAG_IS_PERSISTENT - Mark the payload as persistent + * + * This flag indicates that the late binding payload should be stored + * persistently in flash across warm resets. + */ +#define INTEL_LB_FLAG_IS_PERSISTENT BIT(0) + +/** + * enum intel_lb_type - enum to determine late binding payload type + * @INTEL_LB_TYPE_FAN_CONTROL: Fan controller configuration + */ +enum intel_lb_type { + INTEL_LB_TYPE_FAN_CONTROL = 1, +}; + +/** + * enum intel_lb_status - Status codes returned on late binding transmissions + * @INTEL_LB_STATUS_SUCCESS: Operation completed successfully + * @INTEL_LB_STATUS_4ID_MISMATCH: Mismatch in the expected 4ID (firmware identity/token) + * @INTEL_LB_STATUS_ARB_FAILURE: Arbitration failure (e.g. conflicting access or state) + * @INTEL_LB_STATUS_GENERAL_ERROR: General firmware error not covered by other codes + * @INTEL_LB_STATUS_INVALID_PARAMS: One or more input parameters are invalid + * @INTEL_LB_STATUS_INVALID_SIGNATURE: Payload has an invalid or untrusted signature + * @INTEL_LB_STATUS_INVALID_PAYLOAD: Payload contents are not accepted by firmware + * @INTEL_LB_STATUS_TIMEOUT: Operation timed out before completion + */ +enum intel_lb_status { + INTEL_LB_STATUS_SUCCESS = 0, + INTEL_LB_STATUS_4ID_MISMATCH = 1, + INTEL_LB_STATUS_ARB_FAILURE = 2, + INTEL_LB_STATUS_GENERAL_ERROR = 3, + INTEL_LB_STATUS_INVALID_PARAMS = 4, + INTEL_LB_STATUS_INVALID_SIGNATURE = 5, + INTEL_LB_STATUS_INVALID_PAYLOAD = 6, + INTEL_LB_STATUS_TIMEOUT = 7, +}; + +/** + * struct intel_lb_component_ops - Ops for late binding services + */ +struct intel_lb_component_ops { + /** + * push_payload - Sends a payload to the authentication firmware + * @dev: Device struct corresponding to the mei device + * @type: Payload type (see &enum intel_lb_type) + * @flags: Payload flags bitmap (e.g. %INTEL_LB_FLAGS_IS_PERSISTENT) + * @payload: Pointer to payload buffer + * @payload_size: Payload buffer size in bytes + * + * Return: 0 success, negative errno value on transport failure, + * positive status returned by firmware + */ + int (*push_payload)(struct device *dev, u32 type, u32 flags, + const void *payload, size_t payload_size); +}; + +#endif /* _INTEL_LB_MEI_INTERFACE_H_ */ diff --git a/include/drm/intel_lpe_audio.h b/include/drm/intel/intel_lpe_audio.h index b6121c8fe539..b6121c8fe539 100644 --- a/include/drm/intel_lpe_audio.h +++ b/include/drm/intel/intel_lpe_audio.h diff --git a/include/drm/intel/pciids.h b/include/drm/intel/pciids.h new file mode 100644 index 000000000000..52520e684ab1 --- /dev/null +++ b/include/drm/intel/pciids.h @@ -0,0 +1,903 @@ +/* + * Copyright 2013 Intel Corporation + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef __PCIIDS_H__ +#define __PCIIDS_H__ + +#ifdef __KERNEL__ +#define INTEL_PCI_DEVICE(_id, _info) { \ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, (_id)), \ + .driver_data = (kernel_ulong_t)(_info), \ +} + +#define INTEL_VGA_DEVICE(_id, _info) { \ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, (_id)), \ + .class = PCI_BASE_CLASS_DISPLAY << 16, .class_mask = 0xff << 16, \ + .driver_data = (kernel_ulong_t)(_info), \ +} + +#define INTEL_QUANTA_VGA_DEVICE(_info) { \ + .vendor = PCI_VENDOR_ID_INTEL, .device = 0x16a, \ + .subvendor = 0x152d, .subdevice = 0x8990, \ + .class = PCI_BASE_CLASS_DISPLAY << 16, .class_mask = 0xff << 16, \ + .driver_data = (kernel_ulong_t)(_info), \ +} +#endif + +#define INTEL_I810_IDS(MACRO__, ...) \ + MACRO__(0x7121, ## __VA_ARGS__), /* I810 */ \ + MACRO__(0x7123, ## __VA_ARGS__), /* I810_DC100 */ \ + MACRO__(0x7125, ## __VA_ARGS__) /* I810_E */ + +#define INTEL_I815_IDS(MACRO__, ...) \ + MACRO__(0x1132, ## __VA_ARGS__) /* I815*/ + +#define INTEL_I830_IDS(MACRO__, ...) \ + MACRO__(0x3577, ## __VA_ARGS__) + +#define INTEL_I845G_IDS(MACRO__, ...) \ + MACRO__(0x2562, ## __VA_ARGS__) + +#define INTEL_I85X_IDS(MACRO__, ...) \ + MACRO__(0x3582, ## __VA_ARGS__), /* I855_GM */ \ + MACRO__(0x358e, ## __VA_ARGS__) + +#define INTEL_I865G_IDS(MACRO__, ...) \ + MACRO__(0x2572, ## __VA_ARGS__) /* I865_G */ + +#define INTEL_I915G_IDS(MACRO__, ...) \ + MACRO__(0x2582, ## __VA_ARGS__), /* I915_G */ \ + MACRO__(0x258a, ## __VA_ARGS__) /* E7221_G */ + +#define INTEL_I915GM_IDS(MACRO__, ...) \ + MACRO__(0x2592, ## __VA_ARGS__) /* I915_GM */ + +#define INTEL_I945G_IDS(MACRO__, ...) \ + MACRO__(0x2772, ## __VA_ARGS__) /* I945_G */ + +#define INTEL_I945GM_IDS(MACRO__, ...) \ + MACRO__(0x27a2, ## __VA_ARGS__), /* I945_GM */ \ + MACRO__(0x27ae, ## __VA_ARGS__) /* I945_GME */ + +#define INTEL_I965G_IDS(MACRO__, ...) \ + MACRO__(0x2972, ## __VA_ARGS__), /* I946_GZ */ \ + MACRO__(0x2982, ## __VA_ARGS__), /* G35_G */ \ + MACRO__(0x2992, ## __VA_ARGS__), /* I965_Q */ \ + MACRO__(0x29a2, ## __VA_ARGS__) /* I965_G */ + +#define INTEL_G33_IDS(MACRO__, ...) \ + MACRO__(0x29b2, ## __VA_ARGS__), /* Q35_G */ \ + MACRO__(0x29c2, ## __VA_ARGS__), /* G33_G */ \ + MACRO__(0x29d2, ## __VA_ARGS__) /* Q33_G */ + +#define INTEL_I965GM_IDS(MACRO__, ...) \ + MACRO__(0x2a02, ## __VA_ARGS__), /* I965_GM */ \ + MACRO__(0x2a12, ## __VA_ARGS__) /* I965_GME */ + +#define INTEL_GM45_IDS(MACRO__, ...) \ + MACRO__(0x2a42, ## __VA_ARGS__) /* GM45_G */ + +#define INTEL_G45_IDS(MACRO__, ...) \ + MACRO__(0x2e02, ## __VA_ARGS__), /* IGD_E_G */ \ + MACRO__(0x2e12, ## __VA_ARGS__), /* Q45_G */ \ + MACRO__(0x2e22, ## __VA_ARGS__), /* G45_G */ \ + MACRO__(0x2e32, ## __VA_ARGS__), /* G41_G */ \ + MACRO__(0x2e42, ## __VA_ARGS__), /* B43_G */ \ + MACRO__(0x2e92, ## __VA_ARGS__) /* B43_G.1 */ + +#define INTEL_PNV_G_IDS(MACRO__, ...) \ + MACRO__(0xa001, ## __VA_ARGS__) + +#define INTEL_PNV_M_IDS(MACRO__, ...) \ + MACRO__(0xa011, ## __VA_ARGS__) + +#define INTEL_PNV_IDS(MACRO__, ...) \ + INTEL_PNV_G_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_PNV_M_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_ILK_D_IDS(MACRO__, ...) \ + MACRO__(0x0042, ## __VA_ARGS__) + +#define INTEL_ILK_M_IDS(MACRO__, ...) \ + MACRO__(0x0046, ## __VA_ARGS__) + +#define INTEL_ILK_IDS(MACRO__, ...) \ + INTEL_ILK_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_ILK_M_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_SNB_D_GT1_IDS(MACRO__, ...) \ + MACRO__(0x0102, ## __VA_ARGS__), \ + MACRO__(0x010A, ## __VA_ARGS__) + +#define INTEL_SNB_D_GT2_IDS(MACRO__, ...) \ + MACRO__(0x0112, ## __VA_ARGS__), \ + MACRO__(0x0122, ## __VA_ARGS__) + +#define INTEL_SNB_D_IDS(MACRO__, ...) \ + INTEL_SNB_D_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SNB_D_GT2_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_SNB_M_GT1_IDS(MACRO__, ...) \ + MACRO__(0x0106, ## __VA_ARGS__) + +#define INTEL_SNB_M_GT2_IDS(MACRO__, ...) \ + MACRO__(0x0116, ## __VA_ARGS__), \ + MACRO__(0x0126, ## __VA_ARGS__) + +#define INTEL_SNB_M_IDS(MACRO__, ...) \ + INTEL_SNB_M_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SNB_M_GT2_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_SNB_IDS(MACRO__, ...) \ + INTEL_SNB_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SNB_M_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_IVB_M_GT1_IDS(MACRO__, ...) \ + MACRO__(0x0156, ## __VA_ARGS__) /* GT1 mobile */ + +#define INTEL_IVB_M_GT2_IDS(MACRO__, ...) \ + MACRO__(0x0166, ## __VA_ARGS__) /* GT2 mobile */ + +#define INTEL_IVB_M_IDS(MACRO__, ...) \ + INTEL_IVB_M_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_IVB_M_GT2_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_IVB_D_GT1_IDS(MACRO__, ...) \ + MACRO__(0x0152, ## __VA_ARGS__), /* GT1 desktop */ \ + MACRO__(0x015a, ## __VA_ARGS__) /* GT1 server */ + +#define INTEL_IVB_D_GT2_IDS(MACRO__, ...) \ + MACRO__(0x0162, ## __VA_ARGS__), /* GT2 desktop */ \ + MACRO__(0x016a, ## __VA_ARGS__) /* GT2 server */ + +#define INTEL_IVB_D_IDS(MACRO__, ...) \ + INTEL_IVB_D_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_IVB_D_GT2_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_IVB_IDS(MACRO__, ...) \ + INTEL_IVB_M_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_IVB_D_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_IVB_Q_IDS(MACRO__, ...) \ + INTEL_QUANTA_VGA_DEVICE(__VA_ARGS__) /* Quanta transcode */ + +#define INTEL_HSW_ULT_GT1_IDS(MACRO__, ...) \ + MACRO__(0x0A02, ## __VA_ARGS__), /* ULT GT1 desktop */ \ + MACRO__(0x0A06, ## __VA_ARGS__), /* ULT GT1 mobile */ \ + MACRO__(0x0A0A, ## __VA_ARGS__), /* ULT GT1 server */ \ + MACRO__(0x0A0B, ## __VA_ARGS__) /* ULT GT1 reserved */ + +#define INTEL_HSW_ULX_GT1_IDS(MACRO__, ...) \ + MACRO__(0x0A0E, ## __VA_ARGS__) /* ULX GT1 mobile */ + +#define INTEL_HSW_GT1_IDS(MACRO__, ...) \ + INTEL_HSW_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_HSW_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x0402, ## __VA_ARGS__), /* GT1 desktop */ \ + MACRO__(0x0406, ## __VA_ARGS__), /* GT1 mobile */ \ + MACRO__(0x040A, ## __VA_ARGS__), /* GT1 server */ \ + MACRO__(0x040B, ## __VA_ARGS__), /* GT1 reserved */ \ + MACRO__(0x040E, ## __VA_ARGS__), /* GT1 reserved */ \ + MACRO__(0x0C02, ## __VA_ARGS__), /* SDV GT1 desktop */ \ + MACRO__(0x0C06, ## __VA_ARGS__), /* SDV GT1 mobile */ \ + MACRO__(0x0C0A, ## __VA_ARGS__), /* SDV GT1 server */ \ + MACRO__(0x0C0B, ## __VA_ARGS__), /* SDV GT1 reserved */ \ + MACRO__(0x0C0E, ## __VA_ARGS__), /* SDV GT1 reserved */ \ + MACRO__(0x0D02, ## __VA_ARGS__), /* CRW GT1 desktop */ \ + MACRO__(0x0D06, ## __VA_ARGS__), /* CRW GT1 mobile */ \ + MACRO__(0x0D0A, ## __VA_ARGS__), /* CRW GT1 server */ \ + MACRO__(0x0D0B, ## __VA_ARGS__), /* CRW GT1 reserved */ \ + MACRO__(0x0D0E, ## __VA_ARGS__) /* CRW GT1 reserved */ + +#define INTEL_HSW_ULT_GT2_IDS(MACRO__, ...) \ + MACRO__(0x0A12, ## __VA_ARGS__), /* ULT GT2 desktop */ \ + MACRO__(0x0A16, ## __VA_ARGS__), /* ULT GT2 mobile */ \ + MACRO__(0x0A1A, ## __VA_ARGS__), /* ULT GT2 server */ \ + MACRO__(0x0A1B, ## __VA_ARGS__) /* ULT GT2 reserved */ \ + +#define INTEL_HSW_ULX_GT2_IDS(MACRO__, ...) \ + MACRO__(0x0A1E, ## __VA_ARGS__) /* ULX GT2 mobile */ \ + +#define INTEL_HSW_GT2_IDS(MACRO__, ...) \ + INTEL_HSW_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_HSW_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x0412, ## __VA_ARGS__), /* GT2 desktop */ \ + MACRO__(0x0416, ## __VA_ARGS__), /* GT2 mobile */ \ + MACRO__(0x041A, ## __VA_ARGS__), /* GT2 server */ \ + MACRO__(0x041B, ## __VA_ARGS__), /* GT2 reserved */ \ + MACRO__(0x041E, ## __VA_ARGS__), /* GT2 reserved */ \ + MACRO__(0x0C12, ## __VA_ARGS__), /* SDV GT2 desktop */ \ + MACRO__(0x0C16, ## __VA_ARGS__), /* SDV GT2 mobile */ \ + MACRO__(0x0C1A, ## __VA_ARGS__), /* SDV GT2 server */ \ + MACRO__(0x0C1B, ## __VA_ARGS__), /* SDV GT2 reserved */ \ + MACRO__(0x0C1E, ## __VA_ARGS__), /* SDV GT2 reserved */ \ + MACRO__(0x0D12, ## __VA_ARGS__), /* CRW GT2 desktop */ \ + MACRO__(0x0D16, ## __VA_ARGS__), /* CRW GT2 mobile */ \ + MACRO__(0x0D1A, ## __VA_ARGS__), /* CRW GT2 server */ \ + MACRO__(0x0D1B, ## __VA_ARGS__), /* CRW GT2 reserved */ \ + MACRO__(0x0D1E, ## __VA_ARGS__) /* CRW GT2 reserved */ + +#define INTEL_HSW_ULT_GT3_IDS(MACRO__, ...) \ + MACRO__(0x0A22, ## __VA_ARGS__), /* ULT GT3 desktop */ \ + MACRO__(0x0A26, ## __VA_ARGS__), /* ULT GT3 mobile */ \ + MACRO__(0x0A2A, ## __VA_ARGS__), /* ULT GT3 server */ \ + MACRO__(0x0A2B, ## __VA_ARGS__), /* ULT GT3 reserved */ \ + MACRO__(0x0A2E, ## __VA_ARGS__) /* ULT GT3 reserved */ + +#define INTEL_HSW_GT3_IDS(MACRO__, ...) \ + INTEL_HSW_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x0422, ## __VA_ARGS__), /* GT3 desktop */ \ + MACRO__(0x0426, ## __VA_ARGS__), /* GT3 mobile */ \ + MACRO__(0x042A, ## __VA_ARGS__), /* GT3 server */ \ + MACRO__(0x042B, ## __VA_ARGS__), /* GT3 reserved */ \ + MACRO__(0x042E, ## __VA_ARGS__), /* GT3 reserved */ \ + MACRO__(0x0C22, ## __VA_ARGS__), /* SDV GT3 desktop */ \ + MACRO__(0x0C26, ## __VA_ARGS__), /* SDV GT3 mobile */ \ + MACRO__(0x0C2A, ## __VA_ARGS__), /* SDV GT3 server */ \ + MACRO__(0x0C2B, ## __VA_ARGS__), /* SDV GT3 reserved */ \ + MACRO__(0x0C2E, ## __VA_ARGS__), /* SDV GT3 reserved */ \ + MACRO__(0x0D22, ## __VA_ARGS__), /* CRW GT3 desktop */ \ + MACRO__(0x0D26, ## __VA_ARGS__), /* CRW GT3 mobile */ \ + MACRO__(0x0D2A, ## __VA_ARGS__), /* CRW GT3 server */ \ + MACRO__(0x0D2B, ## __VA_ARGS__), /* CRW GT3 reserved */ \ + MACRO__(0x0D2E, ## __VA_ARGS__) /* CRW GT3 reserved */ + +#define INTEL_HSW_IDS(MACRO__, ...) \ + INTEL_HSW_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_HSW_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_HSW_GT3_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_VLV_IDS(MACRO__, ...) \ + MACRO__(0x0f30, ## __VA_ARGS__), \ + MACRO__(0x0f31, ## __VA_ARGS__), \ + MACRO__(0x0f32, ## __VA_ARGS__), \ + MACRO__(0x0f33, ## __VA_ARGS__) + +#define INTEL_BDW_ULT_GT1_IDS(MACRO__, ...) \ + MACRO__(0x1606, ## __VA_ARGS__), /* GT1 ULT */ \ + MACRO__(0x160B, ## __VA_ARGS__) /* GT1 Iris */ + +#define INTEL_BDW_ULX_GT1_IDS(MACRO__, ...) \ + MACRO__(0x160E, ## __VA_ARGS__) /* GT1 ULX */ + +#define INTEL_BDW_GT1_IDS(MACRO__, ...) \ + INTEL_BDW_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x1602, ## __VA_ARGS__), /* GT1 ULT */ \ + MACRO__(0x160A, ## __VA_ARGS__), /* GT1 Server */ \ + MACRO__(0x160D, ## __VA_ARGS__) /* GT1 Workstation */ + +#define INTEL_BDW_ULT_GT2_IDS(MACRO__, ...) \ + MACRO__(0x1616, ## __VA_ARGS__), /* GT2 ULT */ \ + MACRO__(0x161B, ## __VA_ARGS__) /* GT2 ULT */ + +#define INTEL_BDW_ULX_GT2_IDS(MACRO__, ...) \ + MACRO__(0x161E, ## __VA_ARGS__) /* GT2 ULX */ + +#define INTEL_BDW_GT2_IDS(MACRO__, ...) \ + INTEL_BDW_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x1612, ## __VA_ARGS__), /* GT2 Halo */ \ + MACRO__(0x161A, ## __VA_ARGS__), /* GT2 Server */ \ + MACRO__(0x161D, ## __VA_ARGS__) /* GT2 Workstation */ + +#define INTEL_BDW_ULT_GT3_IDS(MACRO__, ...) \ + MACRO__(0x1626, ## __VA_ARGS__), /* ULT */ \ + MACRO__(0x162B, ## __VA_ARGS__) /* Iris */ \ + +#define INTEL_BDW_ULX_GT3_IDS(MACRO__, ...) \ + MACRO__(0x162E, ## __VA_ARGS__) /* ULX */ + +#define INTEL_BDW_GT3_IDS(MACRO__, ...) \ + INTEL_BDW_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_ULX_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x1622, ## __VA_ARGS__), /* ULT */ \ + MACRO__(0x162A, ## __VA_ARGS__), /* Server */ \ + MACRO__(0x162D, ## __VA_ARGS__) /* Workstation */ + +#define INTEL_BDW_ULT_RSVD_IDS(MACRO__, ...) \ + MACRO__(0x1636, ## __VA_ARGS__), /* ULT */ \ + MACRO__(0x163B, ## __VA_ARGS__) /* Iris */ + +#define INTEL_BDW_ULX_RSVD_IDS(MACRO__, ...) \ + MACRO__(0x163E, ## __VA_ARGS__) /* ULX */ + +#define INTEL_BDW_RSVD_IDS(MACRO__, ...) \ + INTEL_BDW_ULT_RSVD_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_ULX_RSVD_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x1632, ## __VA_ARGS__), /* ULT */ \ + MACRO__(0x163A, ## __VA_ARGS__), /* Server */ \ + MACRO__(0x163D, ## __VA_ARGS__) /* Workstation */ + +#define INTEL_BDW_IDS(MACRO__, ...) \ + INTEL_BDW_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_BDW_RSVD_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_CHV_IDS(MACRO__, ...) \ + MACRO__(0x22b0, ## __VA_ARGS__), \ + MACRO__(0x22b1, ## __VA_ARGS__), \ + MACRO__(0x22b2, ## __VA_ARGS__), \ + MACRO__(0x22b3, ## __VA_ARGS__) + +#define INTEL_SKL_ULT_GT1_IDS(MACRO__, ...) \ + MACRO__(0x1906, ## __VA_ARGS__), /* ULT GT1 */ \ + MACRO__(0x1913, ## __VA_ARGS__) /* ULT GT1.5 */ + +#define INTEL_SKL_ULX_GT1_IDS(MACRO__, ...) \ + MACRO__(0x190E, ## __VA_ARGS__), /* ULX GT1 */ \ + MACRO__(0x1915, ## __VA_ARGS__) /* ULX GT1.5 */ + +#define INTEL_SKL_GT1_IDS(MACRO__, ...) \ + INTEL_SKL_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SKL_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x1902, ## __VA_ARGS__), /* DT GT1 */ \ + MACRO__(0x190A, ## __VA_ARGS__), /* SRV GT1 */ \ + MACRO__(0x190B, ## __VA_ARGS__), /* Halo GT1 */ \ + MACRO__(0x1917, ## __VA_ARGS__) /* DT GT1.5 */ + +#define INTEL_SKL_ULT_GT2_IDS(MACRO__, ...) \ + MACRO__(0x1916, ## __VA_ARGS__), /* ULT GT2 */ \ + MACRO__(0x1921, ## __VA_ARGS__) /* ULT GT2F */ + +#define INTEL_SKL_ULX_GT2_IDS(MACRO__, ...) \ + MACRO__(0x191E, ## __VA_ARGS__) /* ULX GT2 */ + +#define INTEL_SKL_GT2_IDS(MACRO__, ...) \ + INTEL_SKL_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SKL_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x1912, ## __VA_ARGS__), /* DT GT2 */ \ + MACRO__(0x191A, ## __VA_ARGS__), /* SRV GT2 */ \ + MACRO__(0x191B, ## __VA_ARGS__), /* Halo GT2 */ \ + MACRO__(0x191D, ## __VA_ARGS__) /* WKS GT2 */ + +#define INTEL_SKL_ULT_GT3_IDS(MACRO__, ...) \ + MACRO__(0x1923, ## __VA_ARGS__), /* ULT GT3 */ \ + MACRO__(0x1926, ## __VA_ARGS__), /* ULT GT3e */ \ + MACRO__(0x1927, ## __VA_ARGS__) /* ULT GT3e */ + +#define INTEL_SKL_GT3_IDS(MACRO__, ...) \ + INTEL_SKL_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x192A, ## __VA_ARGS__), /* SRV GT3 */ \ + MACRO__(0x192B, ## __VA_ARGS__), /* Halo GT3e */ \ + MACRO__(0x192D, ## __VA_ARGS__) /* SRV GT3e */ + +#define INTEL_SKL_GT4_IDS(MACRO__, ...) \ + MACRO__(0x1932, ## __VA_ARGS__), /* DT GT4 */ \ + MACRO__(0x193A, ## __VA_ARGS__), /* SRV GT4e */ \ + MACRO__(0x193B, ## __VA_ARGS__), /* Halo GT4e */ \ + MACRO__(0x193D, ## __VA_ARGS__) /* WKS GT4e */ + +#define INTEL_SKL_IDS(MACRO__, ...) \ + INTEL_SKL_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SKL_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SKL_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_SKL_GT4_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_BXT_IDS(MACRO__, ...) \ + MACRO__(0x0A84, ## __VA_ARGS__), \ + MACRO__(0x1A84, ## __VA_ARGS__), \ + MACRO__(0x1A85, ## __VA_ARGS__), \ + MACRO__(0x5A84, ## __VA_ARGS__), /* APL HD Graphics 505 */ \ + MACRO__(0x5A85, ## __VA_ARGS__) /* APL HD Graphics 500 */ + +#define INTEL_GLK_IDS(MACRO__, ...) \ + MACRO__(0x3184, ## __VA_ARGS__), \ + MACRO__(0x3185, ## __VA_ARGS__) + +#define INTEL_KBL_ULT_GT1_IDS(MACRO__, ...) \ + MACRO__(0x5906, ## __VA_ARGS__), /* ULT GT1 */ \ + MACRO__(0x5913, ## __VA_ARGS__) /* ULT GT1.5 */ + +#define INTEL_KBL_ULX_GT1_IDS(MACRO__, ...) \ + MACRO__(0x590E, ## __VA_ARGS__), /* ULX GT1 */ \ + MACRO__(0x5915, ## __VA_ARGS__) /* ULX GT1.5 */ + +#define INTEL_KBL_GT1_IDS(MACRO__, ...) \ + INTEL_KBL_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_KBL_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x5902, ## __VA_ARGS__), /* DT GT1 */ \ + MACRO__(0x5908, ## __VA_ARGS__), /* Halo GT1 */ \ + MACRO__(0x590A, ## __VA_ARGS__), /* SRV GT1 */ \ + MACRO__(0x590B, ## __VA_ARGS__) /* Halo GT1 */ + +#define INTEL_KBL_ULT_GT2_IDS(MACRO__, ...) \ + MACRO__(0x5916, ## __VA_ARGS__), /* ULT GT2 */ \ + MACRO__(0x5921, ## __VA_ARGS__) /* ULT GT2F */ + +#define INTEL_KBL_ULX_GT2_IDS(MACRO__, ...) \ + MACRO__(0x591E, ## __VA_ARGS__) /* ULX GT2 */ + +#define INTEL_KBL_GT2_IDS(MACRO__, ...) \ + INTEL_KBL_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_KBL_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x5912, ## __VA_ARGS__), /* DT GT2 */ \ + MACRO__(0x5917, ## __VA_ARGS__), /* Mobile GT2 */ \ + MACRO__(0x591A, ## __VA_ARGS__), /* SRV GT2 */ \ + MACRO__(0x591B, ## __VA_ARGS__), /* Halo GT2 */ \ + MACRO__(0x591D, ## __VA_ARGS__) /* WKS GT2 */ + +#define INTEL_KBL_ULT_GT3_IDS(MACRO__, ...) \ + MACRO__(0x5926, ## __VA_ARGS__) /* ULT GT3 */ + +#define INTEL_KBL_GT3_IDS(MACRO__, ...) \ + INTEL_KBL_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x5923, ## __VA_ARGS__), /* ULT GT3 */ \ + MACRO__(0x5927, ## __VA_ARGS__) /* ULT GT3 */ + +#define INTEL_KBL_GT4_IDS(MACRO__, ...) \ + MACRO__(0x593B, ## __VA_ARGS__) /* Halo GT4 */ + +/* AML/KBL Y GT2 */ +#define INTEL_AML_KBL_GT2_IDS(MACRO__, ...) \ + MACRO__(0x591C, ## __VA_ARGS__), /* ULX GT2 */ \ + MACRO__(0x87C0, ## __VA_ARGS__) /* ULX GT2 */ + +/* AML/CFL Y GT2 */ +#define INTEL_AML_CFL_GT2_IDS(MACRO__, ...) \ + MACRO__(0x87CA, ## __VA_ARGS__) + +/* CML GT1 */ +#define INTEL_CML_GT1_IDS(MACRO__, ...) \ + MACRO__(0x9BA2, ## __VA_ARGS__), \ + MACRO__(0x9BA4, ## __VA_ARGS__), \ + MACRO__(0x9BA5, ## __VA_ARGS__), \ + MACRO__(0x9BA8, ## __VA_ARGS__) + +#define INTEL_CML_U_GT1_IDS(MACRO__, ...) \ + MACRO__(0x9B21, ## __VA_ARGS__), \ + MACRO__(0x9BAA, ## __VA_ARGS__), \ + MACRO__(0x9BAC, ## __VA_ARGS__) + +/* CML GT2 */ +#define INTEL_CML_GT2_IDS(MACRO__, ...) \ + MACRO__(0x9BC2, ## __VA_ARGS__), \ + MACRO__(0x9BC4, ## __VA_ARGS__), \ + MACRO__(0x9BC5, ## __VA_ARGS__), \ + MACRO__(0x9BC6, ## __VA_ARGS__), \ + MACRO__(0x9BC8, ## __VA_ARGS__), \ + MACRO__(0x9BE6, ## __VA_ARGS__), \ + MACRO__(0x9BF6, ## __VA_ARGS__) + +#define INTEL_CML_U_GT2_IDS(MACRO__, ...) \ + MACRO__(0x9B41, ## __VA_ARGS__), \ + MACRO__(0x9BCA, ## __VA_ARGS__), \ + MACRO__(0x9BCC, ## __VA_ARGS__) + +#define INTEL_CML_IDS(MACRO__, ...) \ + INTEL_CML_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CML_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CML_U_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CML_U_GT2_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_KBL_IDS(MACRO__, ...) \ + INTEL_KBL_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_KBL_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_KBL_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_KBL_GT4_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_AML_KBL_GT2_IDS(MACRO__, ## __VA_ARGS__) + +/* CFL S */ +#define INTEL_CFL_S_GT1_IDS(MACRO__, ...) \ + MACRO__(0x3E90, ## __VA_ARGS__), /* SRV GT1 */ \ + MACRO__(0x3E93, ## __VA_ARGS__), /* SRV GT1 */ \ + MACRO__(0x3E99, ## __VA_ARGS__) /* SRV GT1 */ + +#define INTEL_CFL_S_GT2_IDS(MACRO__, ...) \ + MACRO__(0x3E91, ## __VA_ARGS__), /* SRV GT2 */ \ + MACRO__(0x3E92, ## __VA_ARGS__), /* SRV GT2 */ \ + MACRO__(0x3E96, ## __VA_ARGS__), /* SRV GT2 */ \ + MACRO__(0x3E98, ## __VA_ARGS__), /* SRV GT2 */ \ + MACRO__(0x3E9A, ## __VA_ARGS__) /* SRV GT2 */ + +/* CFL H */ +#define INTEL_CFL_H_GT1_IDS(MACRO__, ...) \ + MACRO__(0x3E9C, ## __VA_ARGS__) + +#define INTEL_CFL_H_GT2_IDS(MACRO__, ...) \ + MACRO__(0x3E94, ## __VA_ARGS__), /* Halo GT2 */ \ + MACRO__(0x3E9B, ## __VA_ARGS__) /* Halo GT2 */ + +/* CFL U GT2 */ +#define INTEL_CFL_U_GT2_IDS(MACRO__, ...) \ + MACRO__(0x3EA9, ## __VA_ARGS__) + +/* CFL U GT3 */ +#define INTEL_CFL_U_GT3_IDS(MACRO__, ...) \ + MACRO__(0x3EA5, ## __VA_ARGS__), /* ULT GT3 */ \ + MACRO__(0x3EA6, ## __VA_ARGS__), /* ULT GT3 */ \ + MACRO__(0x3EA7, ## __VA_ARGS__), /* ULT GT3 */ \ + MACRO__(0x3EA8, ## __VA_ARGS__) /* ULT GT3 */ + +#define INTEL_CFL_IDS(MACRO__, ...) \ + INTEL_CFL_S_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CFL_S_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CFL_H_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CFL_H_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CFL_U_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_CFL_U_GT3_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_AML_CFL_GT2_IDS(MACRO__, ## __VA_ARGS__) + +/* WHL/CFL U GT1 */ +#define INTEL_WHL_U_GT1_IDS(MACRO__, ...) \ + MACRO__(0x3EA1, ## __VA_ARGS__), \ + MACRO__(0x3EA4, ## __VA_ARGS__) + +/* WHL/CFL U GT2 */ +#define INTEL_WHL_U_GT2_IDS(MACRO__, ...) \ + MACRO__(0x3EA0, ## __VA_ARGS__), \ + MACRO__(0x3EA3, ## __VA_ARGS__) + +/* WHL/CFL U GT3 */ +#define INTEL_WHL_U_GT3_IDS(MACRO__, ...) \ + MACRO__(0x3EA2, ## __VA_ARGS__) + +#define INTEL_WHL_IDS(MACRO__, ...) \ + INTEL_WHL_U_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_WHL_U_GT2_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_WHL_U_GT3_IDS(MACRO__, ## __VA_ARGS__) + +/* CNL */ +#define INTEL_CNL_PORT_F_IDS(MACRO__, ...) \ + MACRO__(0x5A44, ## __VA_ARGS__), \ + MACRO__(0x5A4C, ## __VA_ARGS__), \ + MACRO__(0x5A54, ## __VA_ARGS__), \ + MACRO__(0x5A5C, ## __VA_ARGS__) + +#define INTEL_CNL_IDS(MACRO__, ...) \ + INTEL_CNL_PORT_F_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x5A40, ## __VA_ARGS__), \ + MACRO__(0x5A41, ## __VA_ARGS__), \ + MACRO__(0x5A42, ## __VA_ARGS__), \ + MACRO__(0x5A49, ## __VA_ARGS__), \ + MACRO__(0x5A4A, ## __VA_ARGS__), \ + MACRO__(0x5A50, ## __VA_ARGS__), \ + MACRO__(0x5A51, ## __VA_ARGS__), \ + MACRO__(0x5A52, ## __VA_ARGS__), \ + MACRO__(0x5A59, ## __VA_ARGS__), \ + MACRO__(0x5A5A, ## __VA_ARGS__) + +/* ICL */ +#define INTEL_ICL_PORT_F_IDS(MACRO__, ...) \ + MACRO__(0x8A50, ## __VA_ARGS__), \ + MACRO__(0x8A52, ## __VA_ARGS__), \ + MACRO__(0x8A53, ## __VA_ARGS__), \ + MACRO__(0x8A54, ## __VA_ARGS__), \ + MACRO__(0x8A56, ## __VA_ARGS__), \ + MACRO__(0x8A57, ## __VA_ARGS__), \ + MACRO__(0x8A58, ## __VA_ARGS__), \ + MACRO__(0x8A59, ## __VA_ARGS__), \ + MACRO__(0x8A5A, ## __VA_ARGS__), \ + MACRO__(0x8A5B, ## __VA_ARGS__), \ + MACRO__(0x8A5C, ## __VA_ARGS__), \ + MACRO__(0x8A70, ## __VA_ARGS__), \ + MACRO__(0x8A71, ## __VA_ARGS__) + +#define INTEL_ICL_IDS(MACRO__, ...) \ + INTEL_ICL_PORT_F_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x8A51, ## __VA_ARGS__), \ + MACRO__(0x8A5D, ## __VA_ARGS__) + +/* EHL */ +#define INTEL_EHL_IDS(MACRO__, ...) \ + MACRO__(0x4541, ## __VA_ARGS__), \ + MACRO__(0x4551, ## __VA_ARGS__), \ + MACRO__(0x4555, ## __VA_ARGS__), \ + MACRO__(0x4557, ## __VA_ARGS__), \ + MACRO__(0x4570, ## __VA_ARGS__), \ + MACRO__(0x4571, ## __VA_ARGS__) + +/* JSL */ +#define INTEL_JSL_IDS(MACRO__, ...) \ + MACRO__(0x4E51, ## __VA_ARGS__), \ + MACRO__(0x4E55, ## __VA_ARGS__), \ + MACRO__(0x4E57, ## __VA_ARGS__), \ + MACRO__(0x4E61, ## __VA_ARGS__), \ + MACRO__(0x4E71, ## __VA_ARGS__) + +/* TGL */ +#define INTEL_TGL_GT1_IDS(MACRO__, ...) \ + MACRO__(0x9A60, ## __VA_ARGS__), \ + MACRO__(0x9A68, ## __VA_ARGS__), \ + MACRO__(0x9A70, ## __VA_ARGS__) + +#define INTEL_TGL_GT2_IDS(MACRO__, ...) \ + MACRO__(0x9A40, ## __VA_ARGS__), \ + MACRO__(0x9A49, ## __VA_ARGS__), \ + MACRO__(0x9A59, ## __VA_ARGS__), \ + MACRO__(0x9A78, ## __VA_ARGS__), \ + MACRO__(0x9AC0, ## __VA_ARGS__), \ + MACRO__(0x9AC9, ## __VA_ARGS__), \ + MACRO__(0x9AD9, ## __VA_ARGS__), \ + MACRO__(0x9AF8, ## __VA_ARGS__) + +#define INTEL_TGL_IDS(MACRO__, ...) \ + INTEL_TGL_GT1_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_TGL_GT2_IDS(MACRO__, ## __VA_ARGS__) + +/* RKL */ +#define INTEL_RKL_IDS(MACRO__, ...) \ + MACRO__(0x4C80, ## __VA_ARGS__), \ + MACRO__(0x4C8A, ## __VA_ARGS__), \ + MACRO__(0x4C8B, ## __VA_ARGS__), \ + MACRO__(0x4C8C, ## __VA_ARGS__), \ + MACRO__(0x4C90, ## __VA_ARGS__), \ + MACRO__(0x4C9A, ## __VA_ARGS__) + +/* DG1 */ +#define INTEL_DG1_IDS(MACRO__, ...) \ + MACRO__(0x4905, ## __VA_ARGS__), \ + MACRO__(0x4906, ## __VA_ARGS__), \ + MACRO__(0x4907, ## __VA_ARGS__), \ + MACRO__(0x4908, ## __VA_ARGS__), \ + MACRO__(0x4909, ## __VA_ARGS__) + +/* ADL-S */ +#define INTEL_ADLS_IDS(MACRO__, ...) \ + MACRO__(0x4680, ## __VA_ARGS__), \ + MACRO__(0x4682, ## __VA_ARGS__), \ + MACRO__(0x4688, ## __VA_ARGS__), \ + MACRO__(0x468A, ## __VA_ARGS__), \ + MACRO__(0x468B, ## __VA_ARGS__), \ + MACRO__(0x4690, ## __VA_ARGS__), \ + MACRO__(0x4692, ## __VA_ARGS__), \ + MACRO__(0x4693, ## __VA_ARGS__) + +/* ADL-P */ +#define INTEL_ADLP_IDS(MACRO__, ...) \ + MACRO__(0x46A0, ## __VA_ARGS__), \ + MACRO__(0x46A1, ## __VA_ARGS__), \ + MACRO__(0x46A2, ## __VA_ARGS__), \ + MACRO__(0x46A3, ## __VA_ARGS__), \ + MACRO__(0x46A6, ## __VA_ARGS__), \ + MACRO__(0x46A8, ## __VA_ARGS__), \ + MACRO__(0x46AA, ## __VA_ARGS__), \ + MACRO__(0x462A, ## __VA_ARGS__), \ + MACRO__(0x4626, ## __VA_ARGS__), \ + MACRO__(0x4628, ## __VA_ARGS__), \ + MACRO__(0x46B0, ## __VA_ARGS__), \ + MACRO__(0x46B1, ## __VA_ARGS__), \ + MACRO__(0x46B2, ## __VA_ARGS__), \ + MACRO__(0x46B3, ## __VA_ARGS__), \ + MACRO__(0x46C0, ## __VA_ARGS__), \ + MACRO__(0x46C1, ## __VA_ARGS__), \ + MACRO__(0x46C2, ## __VA_ARGS__), \ + MACRO__(0x46C3, ## __VA_ARGS__) + +/* ADL-N */ +#define INTEL_ADLN_IDS(MACRO__, ...) \ + MACRO__(0x46D0, ## __VA_ARGS__), \ + MACRO__(0x46D1, ## __VA_ARGS__), \ + MACRO__(0x46D2, ## __VA_ARGS__), \ + MACRO__(0x46D3, ## __VA_ARGS__), \ + MACRO__(0x46D4, ## __VA_ARGS__) + +/* RPL-S */ +#define INTEL_RPLS_IDS(MACRO__, ...) \ + MACRO__(0xA780, ## __VA_ARGS__), \ + MACRO__(0xA781, ## __VA_ARGS__), \ + MACRO__(0xA782, ## __VA_ARGS__), \ + MACRO__(0xA783, ## __VA_ARGS__), \ + MACRO__(0xA788, ## __VA_ARGS__), \ + MACRO__(0xA789, ## __VA_ARGS__), \ + MACRO__(0xA78A, ## __VA_ARGS__), \ + MACRO__(0xA78B, ## __VA_ARGS__) + +/* RPL-U */ +#define INTEL_RPLU_IDS(MACRO__, ...) \ + MACRO__(0xA721, ## __VA_ARGS__), \ + MACRO__(0xA7A1, ## __VA_ARGS__), \ + MACRO__(0xA7A9, ## __VA_ARGS__), \ + MACRO__(0xA7AC, ## __VA_ARGS__), \ + MACRO__(0xA7AD, ## __VA_ARGS__) + +/* RPL-P */ +#define INTEL_RPLP_IDS(MACRO__, ...) \ + MACRO__(0xA720, ## __VA_ARGS__), \ + MACRO__(0xA7A0, ## __VA_ARGS__), \ + MACRO__(0xA7A8, ## __VA_ARGS__), \ + MACRO__(0xA7AA, ## __VA_ARGS__), \ + MACRO__(0xA7AB, ## __VA_ARGS__) + +/* DG2 */ +#define INTEL_DG2_G10_D_IDS(MACRO__, ...) \ + MACRO__(0x56A0, ## __VA_ARGS__), \ + MACRO__(0x56A1, ## __VA_ARGS__), \ + MACRO__(0x56A2, ## __VA_ARGS__) + +#define INTEL_DG2_G10_E_IDS(MACRO__, ...) \ + MACRO__(0x56BE, ## __VA_ARGS__), \ + MACRO__(0x56BF, ## __VA_ARGS__) + +#define INTEL_DG2_G10_M_IDS(MACRO__, ...) \ + MACRO__(0x5690, ## __VA_ARGS__), \ + MACRO__(0x5691, ## __VA_ARGS__), \ + MACRO__(0x5692, ## __VA_ARGS__) + +#define INTEL_DG2_G10_IDS(MACRO__, ...) \ + INTEL_DG2_G10_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G10_E_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G10_M_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_DG2_G11_D_IDS(MACRO__, ...) \ + MACRO__(0x56A5, ## __VA_ARGS__), \ + MACRO__(0x56A6, ## __VA_ARGS__), \ + MACRO__(0x56B0, ## __VA_ARGS__), \ + MACRO__(0x56B1, ## __VA_ARGS__) + +#define INTEL_DG2_G11_E_IDS(MACRO__, ...) \ + MACRO__(0x56BA, ## __VA_ARGS__), \ + MACRO__(0x56BB, ## __VA_ARGS__), \ + MACRO__(0x56BC, ## __VA_ARGS__), \ + MACRO__(0x56BD, ## __VA_ARGS__) + +#define INTEL_DG2_G11_M_IDS(MACRO__, ...) \ + MACRO__(0x5693, ## __VA_ARGS__), \ + MACRO__(0x5694, ## __VA_ARGS__), \ + MACRO__(0x5695, ## __VA_ARGS__) + +#define INTEL_DG2_G11_IDS(MACRO__, ...) \ + INTEL_DG2_G11_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G11_E_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G11_M_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_DG2_G12_D_IDS(MACRO__, ...) \ + MACRO__(0x56A3, ## __VA_ARGS__), \ + MACRO__(0x56A4, ## __VA_ARGS__), \ + MACRO__(0x56B2, ## __VA_ARGS__), \ + MACRO__(0x56B3, ## __VA_ARGS__) + +#define INTEL_DG2_G12_M_IDS(MACRO__, ...) \ + MACRO__(0x5696, ## __VA_ARGS__), \ + MACRO__(0x5697, ## __VA_ARGS__) + +#define INTEL_DG2_G12_IDS(MACRO__, ...) \ + INTEL_DG2_G12_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G12_M_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_DG2_D_IDS(MACRO__, ...) \ + INTEL_DG2_G10_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G11_D_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G12_D_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_DG2_IDS(MACRO__, ...) \ + INTEL_DG2_G10_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G11_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_DG2_G12_IDS(MACRO__, ## __VA_ARGS__) + +#define INTEL_ATS_M150_IDS(MACRO__, ...) \ + MACRO__(0x56C0, ## __VA_ARGS__), \ + MACRO__(0x56C2, ## __VA_ARGS__) + +#define INTEL_ATS_M75_IDS(MACRO__, ...) \ + MACRO__(0x56C1, ## __VA_ARGS__) + +#define INTEL_ATS_M_IDS(MACRO__, ...) \ + INTEL_ATS_M150_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_ATS_M75_IDS(MACRO__, ## __VA_ARGS__) + +/* ARL */ +#define INTEL_ARL_H_IDS(MACRO__, ...) \ + MACRO__(0x7D51, ## __VA_ARGS__), \ + MACRO__(0x7DD1, ## __VA_ARGS__) + +#define INTEL_ARL_U_IDS(MACRO__, ...) \ + MACRO__(0x7D41, ## __VA_ARGS__) \ + +#define INTEL_ARL_S_IDS(MACRO__, ...) \ + MACRO__(0x7D67, ## __VA_ARGS__), \ + MACRO__(0xB640, ## __VA_ARGS__) + +#define INTEL_ARL_IDS(MACRO__, ...) \ + INTEL_ARL_H_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_ARL_U_IDS(MACRO__, ## __VA_ARGS__), \ + INTEL_ARL_S_IDS(MACRO__, ## __VA_ARGS__) + +/* MTL */ +#define INTEL_MTL_U_IDS(MACRO__, ...) \ + MACRO__(0x7D40, ## __VA_ARGS__), \ + MACRO__(0x7D45, ## __VA_ARGS__) + +#define INTEL_MTL_IDS(MACRO__, ...) \ + INTEL_MTL_U_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0x7D55, ## __VA_ARGS__), \ + MACRO__(0x7D60, ## __VA_ARGS__), \ + MACRO__(0x7DD5, ## __VA_ARGS__) + +/* PVC */ +#define INTEL_PVC_IDS(MACRO__, ...) \ + MACRO__(0x0B69, ## __VA_ARGS__), \ + MACRO__(0x0B6E, ## __VA_ARGS__), \ + MACRO__(0x0BD4, ## __VA_ARGS__), \ + MACRO__(0x0BD5, ## __VA_ARGS__), \ + MACRO__(0x0BD6, ## __VA_ARGS__), \ + MACRO__(0x0BD7, ## __VA_ARGS__), \ + MACRO__(0x0BD8, ## __VA_ARGS__), \ + MACRO__(0x0BD9, ## __VA_ARGS__), \ + MACRO__(0x0BDA, ## __VA_ARGS__), \ + MACRO__(0x0BDB, ## __VA_ARGS__), \ + MACRO__(0x0BE0, ## __VA_ARGS__), \ + MACRO__(0x0BE1, ## __VA_ARGS__), \ + MACRO__(0x0BE5, ## __VA_ARGS__) + +/* LNL */ +#define INTEL_LNL_IDS(MACRO__, ...) \ + MACRO__(0x6420, ## __VA_ARGS__), \ + MACRO__(0x64A0, ## __VA_ARGS__), \ + MACRO__(0x64B0, ## __VA_ARGS__) + +/* BMG */ +#define INTEL_BMG_G21_IDS(MACRO__, ...) \ + MACRO__(0xE202, ## __VA_ARGS__), \ + MACRO__(0xE209, ## __VA_ARGS__), \ + MACRO__(0xE20B, ## __VA_ARGS__), \ + MACRO__(0xE20C, ## __VA_ARGS__), \ + MACRO__(0xE20D, ## __VA_ARGS__), \ + MACRO__(0xE210, ## __VA_ARGS__), \ + MACRO__(0xE211, ## __VA_ARGS__), \ + MACRO__(0xE212, ## __VA_ARGS__), \ + MACRO__(0xE216, ## __VA_ARGS__) + +#define INTEL_BMG_IDS(MACRO__, ...) \ + INTEL_BMG_G21_IDS(MACRO__, ## __VA_ARGS__), \ + MACRO__(0xE220, ## __VA_ARGS__), \ + MACRO__(0xE221, ## __VA_ARGS__), \ + MACRO__(0xE222, ## __VA_ARGS__), \ + MACRO__(0xE223, ## __VA_ARGS__) + +/* PTL */ +#define INTEL_PTL_IDS(MACRO__, ...) \ + MACRO__(0xB080, ## __VA_ARGS__), \ + MACRO__(0xB081, ## __VA_ARGS__), \ + MACRO__(0xB082, ## __VA_ARGS__), \ + MACRO__(0xB083, ## __VA_ARGS__), \ + MACRO__(0xB084, ## __VA_ARGS__), \ + MACRO__(0xB085, ## __VA_ARGS__), \ + MACRO__(0xB086, ## __VA_ARGS__), \ + MACRO__(0xB087, ## __VA_ARGS__), \ + MACRO__(0xB08F, ## __VA_ARGS__), \ + MACRO__(0xB090, ## __VA_ARGS__), \ + MACRO__(0xB0A0, ## __VA_ARGS__), \ + MACRO__(0xB0B0, ## __VA_ARGS__) + +/* WCL */ +#define INTEL_WCL_IDS(MACRO__, ...) \ + MACRO__(0xFD80, ## __VA_ARGS__), \ + MACRO__(0xFD81, ## __VA_ARGS__) + +/* NVL-S */ +#define INTEL_NVLS_IDS(MACRO__, ...) \ + MACRO__(0xD740, ## __VA_ARGS__), \ + MACRO__(0xD741, ## __VA_ARGS__), \ + MACRO__(0xD742, ## __VA_ARGS__), \ + MACRO__(0xD743, ## __VA_ARGS__), \ + MACRO__(0xD744, ## __VA_ARGS__), \ + MACRO__(0xD745, ## __VA_ARGS__) + +/* CRI */ +#define INTEL_CRI_IDS(MACRO__, ...) \ + MACRO__(0x674C, ## __VA_ARGS__) + +#endif /* __PCIIDS_H__ */ diff --git a/include/drm/intel/xe_sriov_vfio.h b/include/drm/intel/xe_sriov_vfio.h new file mode 100644 index 000000000000..e9814e8149fd --- /dev/null +++ b/include/drm/intel/xe_sriov_vfio.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_SRIOV_VFIO_H_ +#define _XE_SRIOV_VFIO_H_ + +#include <linux/types.h> + +struct pci_dev; +struct xe_device; + +/** + * xe_sriov_vfio_get_pf() - Get PF &xe_device. + * @pdev: the VF &pci_dev device + * + * Return: pointer to PF &xe_device, NULL otherwise. + */ +struct xe_device *xe_sriov_vfio_get_pf(struct pci_dev *pdev); + +/** + * xe_sriov_vfio_migration_supported() - Check if migration is supported. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * + * Return: true if migration is supported, false otherwise. + */ +bool xe_sriov_vfio_migration_supported(struct xe_device *xe); + +/** + * xe_sriov_vfio_wait_flr_done() - Wait for VF FLR completion. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * This function will wait until VF FLR is processed by PF on all tiles (or + * until timeout occurs). + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_wait_flr_done(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_suspend_device() - Suspend VF. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * This function will pause VF on all tiles/GTs. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_suspend_device(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_resume_device() - Resume VF. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * This function will resume VF on all tiles. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_resume_device(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_stop_copy_enter() - Initiate a VF device migration data save. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_stop_copy_enter(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_stop_copy_exit() - Finish a VF device migration data save. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_stop_copy_exit(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_resume_data_enter() - Initiate a VF device migration data restore. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_resume_data_enter(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_resume_data_exit() - Finish a VF device migration data restore. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_resume_data_exit(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_error() - Move VF device to error state. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * Reset is needed to move it out of error state. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_sriov_vfio_error(struct xe_device *xe, unsigned int vfid); + +/** + * xe_sriov_vfio_data_read() - Read migration data from the VF device. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * @buf: start address of userspace buffer + * @len: requested read size from userspace + * + * Return: number of bytes that has been successfully read, + * 0 if no more migration data is available, -errno on failure. + */ +ssize_t xe_sriov_vfio_data_read(struct xe_device *xe, unsigned int vfid, + char __user *buf, size_t len); +/** + * xe_sriov_vfio_data_write() - Write migration data to the VF device. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * @buf: start address of userspace buffer + * @len: requested write size from userspace + * + * Return: number of bytes that has been successfully written, -errno on failure. + */ +ssize_t xe_sriov_vfio_data_write(struct xe_device *xe, unsigned int vfid, + const char __user *buf, size_t len); +/** + * xe_sriov_vfio_stop_copy_size() - Get a size estimate of VF device migration data. + * @xe: the PF &xe_device obtained by calling xe_sriov_vfio_get_pf() + * @vfid: the VF identifier (can't be 0) + * + * Return: migration data size in bytes or a negative error code on failure. + */ +ssize_t xe_sriov_vfio_stop_copy_size(struct xe_device *xe, unsigned int vfid); + +#endif diff --git a/include/drm/spsc_queue.h b/include/drm/spsc_queue.h index 125f096c88cb..ee9df8cc67b7 100644 --- a/include/drm/spsc_queue.h +++ b/include/drm/spsc_queue.h @@ -70,9 +70,11 @@ static inline bool spsc_queue_push(struct spsc_queue *queue, struct spsc_node *n preempt_disable(); + atomic_inc(&queue->job_count); + smp_mb__after_atomic(); + tail = (struct spsc_node **)atomic_long_xchg(&queue->tail, (long)&node->next); WRITE_ONCE(*tail, node); - atomic_inc(&queue->job_count); /* * In case of first element verify new node will be visible to the consumer diff --git a/include/drm/task_barrier.h b/include/drm/task_barrier.h new file mode 100644 index 000000000000..f6e6ed529681 --- /dev/null +++ b/include/drm/task_barrier.h @@ -0,0 +1,107 @@ +/* + * Copyright 2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include <linux/semaphore.h> +#include <linux/atomic.h> + +/* + * Reusable 2 PHASE task barrier (rendez-vous point) implementation for N tasks. + * Based on the Little book of semaphores - https://greenteapress.com/wp/semaphores/ + */ + + + +#ifndef DRM_TASK_BARRIER_H_ +#define DRM_TASK_BARRIER_H_ + +/* + * Represents an instance of a task barrier. + */ +struct task_barrier { + unsigned int n; + atomic_t count; + struct semaphore enter_turnstile; + struct semaphore exit_turnstile; +}; + +static inline void task_barrier_signal_turnstile(struct semaphore *turnstile, + unsigned int n) +{ + int i; + + for (i = 0 ; i < n; i++) + up(turnstile); +} + +static inline void task_barrier_init(struct task_barrier *tb) +{ + tb->n = 0; + atomic_set(&tb->count, 0); + sema_init(&tb->enter_turnstile, 0); + sema_init(&tb->exit_turnstile, 0); +} + +static inline void task_barrier_add_task(struct task_barrier *tb) +{ + tb->n++; +} + +static inline void task_barrier_rem_task(struct task_barrier *tb) +{ + tb->n--; +} + +/* + * Lines up all the threads BEFORE the critical point. + * + * When all thread passed this code the entry barrier is back to locked state. + */ +static inline void task_barrier_enter(struct task_barrier *tb) +{ + if (atomic_inc_return(&tb->count) == tb->n) + task_barrier_signal_turnstile(&tb->enter_turnstile, tb->n); + + down(&tb->enter_turnstile); +} + +/* + * Lines up all the threads AFTER the critical point. + * + * This function is used to avoid any one thread running ahead if the barrier is + * used repeatedly . + */ +static inline void task_barrier_exit(struct task_barrier *tb) +{ + if (atomic_dec_return(&tb->count) == 0) + task_barrier_signal_turnstile(&tb->exit_turnstile, tb->n); + + down(&tb->exit_turnstile); +} + +/* Convinieince function when nothing to be done in between entry and exit */ +static inline void task_barrier_full(struct task_barrier *tb) +{ + task_barrier_enter(tb); + task_barrier_exit(tb); +} + +#endif diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h deleted file mode 100644 index b8ba58861986..000000000000 --- a/include/drm/tinydrm/mipi-dbi.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * MIPI Display Bus Interface (DBI) LCD controller support - * - * Copyright 2016 Noralf Trønnes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __LINUX_MIPI_DBI_H -#define __LINUX_MIPI_DBI_H - -#include <drm/tinydrm/tinydrm.h> - -struct spi_device; -struct gpio_desc; -struct regulator; - -/** - * struct mipi_dbi - MIPI DBI controller - * @tinydrm: tinydrm base - * @spi: SPI device - * @enabled: Pipeline is enabled - * @cmdlock: Command lock - * @command: Bus specific callback executing commands. - * @read_commands: Array of read commands terminated by a zero entry. - * Reading is disabled if this is NULL. - * @dc: Optional D/C gpio. - * @tx_buf: Buffer used for transfer (copy clip rect area) - * @tx_buf9: Buffer used for Option 1 9-bit conversion - * @tx_buf9_len: Size of tx_buf9. - * @swap_bytes: Swap bytes in buffer before transfer - * @reset: Optional reset gpio - * @rotation: initial rotation in degrees Counter Clock Wise - * @backlight: backlight device (optional) - * @regulator: power regulator (optional) - */ -struct mipi_dbi { - struct tinydrm_device tinydrm; - struct spi_device *spi; - bool enabled; - struct mutex cmdlock; - int (*command)(struct mipi_dbi *mipi, u8 cmd, u8 *param, size_t num); - const u8 *read_commands; - struct gpio_desc *dc; - u16 *tx_buf; - void *tx_buf9; - size_t tx_buf9_len; - bool swap_bytes; - struct gpio_desc *reset; - unsigned int rotation; - struct backlight_device *backlight; - struct regulator *regulator; -}; - -static inline struct mipi_dbi * -mipi_dbi_from_tinydrm(struct tinydrm_device *tdev) -{ - return container_of(tdev, struct mipi_dbi, tinydrm); -} - -int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi, - struct gpio_desc *dc); -int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi, - const struct drm_simple_display_pipe_funcs *pipe_funcs, - struct drm_driver *driver, - const struct drm_display_mode *mode, unsigned int rotation); -void mipi_dbi_enable_flush(struct mipi_dbi *mipi, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plan_state); -void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe); -void mipi_dbi_hw_reset(struct mipi_dbi *mipi); -bool mipi_dbi_display_is_on(struct mipi_dbi *mipi); -int mipi_dbi_poweron_reset(struct mipi_dbi *mipi); -int mipi_dbi_poweron_conditional_reset(struct mipi_dbi *mipi); -u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len); - -int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val); -int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len); -int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, - struct drm_clip_rect *clip, bool swap); -/** - * mipi_dbi_command - MIPI DCS command with optional parameter(s) - * @mipi: MIPI structure - * @cmd: Command - * @seq...: Optional parameter(s) - * - * Send MIPI DCS command to the controller. Use mipi_dbi_command_read() for - * get/read. - * - * Returns: - * Zero on success, negative error code on failure. - */ -#define mipi_dbi_command(mipi, cmd, seq...) \ -({ \ - u8 d[] = { seq }; \ - mipi_dbi_command_buf(mipi, cmd, d, ARRAY_SIZE(d)); \ -}) - -#ifdef CONFIG_DEBUG_FS -int mipi_dbi_debugfs_init(struct drm_minor *minor); -#else -#define mipi_dbi_debugfs_init NULL -#endif - -#endif /* __LINUX_MIPI_DBI_H */ diff --git a/include/drm/tinydrm/tinydrm-helpers.h b/include/drm/tinydrm/tinydrm-helpers.h deleted file mode 100644 index 5b96f0b12c8c..000000000000 --- a/include/drm/tinydrm/tinydrm-helpers.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2016 Noralf Trønnes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __LINUX_TINYDRM_HELPERS_H -#define __LINUX_TINYDRM_HELPERS_H - -struct backlight_device; -struct tinydrm_device; -struct drm_clip_rect; -struct spi_transfer; -struct spi_message; -struct spi_device; -struct device; - -/** - * tinydrm_machine_little_endian - Machine is little endian - * - * Returns: - * true if *defined(__LITTLE_ENDIAN)*, false otherwise - */ -static inline bool tinydrm_machine_little_endian(void) -{ -#if defined(__LITTLE_ENDIAN) - return true; -#else - return false; -#endif -} - -bool tinydrm_merge_clips(struct drm_clip_rect *dst, - struct drm_clip_rect *src, unsigned int num_clips, - unsigned int flags, u32 max_width, u32 max_height); -int tinydrm_fb_dirty(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int flags, unsigned int color, - struct drm_clip_rect *clips, - unsigned int num_clips); -void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, - struct drm_clip_rect *clip); -void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, - struct drm_clip_rect *clip); -void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr, - struct drm_framebuffer *fb, - struct drm_clip_rect *clip, bool swap); -void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, - struct drm_clip_rect *clip); - -size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len); -bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw); -int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz, - struct spi_transfer *header, u8 bpw, const void *buf, - size_t len); -void _tinydrm_dbg_spi_message(struct spi_device *spi, struct spi_message *m); - -#ifdef DEBUG -/** - * tinydrm_dbg_spi_message - Dump SPI message - * @spi: SPI device - * @m: SPI message - * - * Dumps info about the transfers in a SPI message including buffer content. - * DEBUG has to be defined for this function to be enabled alongside setting - * the DRM_UT_DRIVER bit of &drm_debug. - */ -static inline void tinydrm_dbg_spi_message(struct spi_device *spi, - struct spi_message *m) -{ - if (drm_debug & DRM_UT_DRIVER) - _tinydrm_dbg_spi_message(spi, m); -} -#else -static inline void tinydrm_dbg_spi_message(struct spi_device *spi, - struct spi_message *m) -{ -} -#endif /* DEBUG */ - -#endif /* __LINUX_TINYDRM_HELPERS_H */ diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h deleted file mode 100644 index 448aa5ea4722..000000000000 --- a/include/drm/tinydrm/tinydrm.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2016 Noralf Trønnes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __LINUX_TINYDRM_H -#define __LINUX_TINYDRM_H - -#include <linux/mutex.h> -#include <drm/drm_simple_kms_helper.h> - -struct drm_clip_rect; -struct drm_driver; -struct drm_file; -struct drm_framebuffer; -struct drm_framebuffer_funcs; - -/** - * struct tinydrm_device - tinydrm device - */ -struct tinydrm_device { - /** - * @drm: DRM device - */ - struct drm_device *drm; - - /** - * @pipe: Display pipe structure - */ - struct drm_simple_display_pipe pipe; - - /** - * @dirty_lock: Serializes framebuffer flushing - */ - struct mutex dirty_lock; - - /** - * @fb_funcs: Framebuffer functions used when creating framebuffers - */ - const struct drm_framebuffer_funcs *fb_funcs; - - /** - * @fb_dirty: Framebuffer dirty callback - */ - int (*fb_dirty)(struct drm_framebuffer *framebuffer, - struct drm_file *file_priv, unsigned flags, - unsigned color, struct drm_clip_rect *clips, - unsigned num_clips); -}; - -static inline struct tinydrm_device * -pipe_to_tinydrm(struct drm_simple_display_pipe *pipe) -{ - return container_of(pipe, struct tinydrm_device, pipe); -} - -/** - * TINYDRM_MODE - tinydrm display mode - * @hd: Horizontal resolution, width - * @vd: Vertical resolution, height - * @hd_mm: Display width in millimeters - * @vd_mm: Display height in millimeters - * - * This macro creates a &drm_display_mode for use with tinydrm. - */ -#define TINYDRM_MODE(hd, vd, hd_mm, vd_mm) \ - .hdisplay = (hd), \ - .hsync_start = (hd), \ - .hsync_end = (hd), \ - .htotal = (hd), \ - .vdisplay = (vd), \ - .vsync_start = (vd), \ - .vsync_end = (vd), \ - .vtotal = (vd), \ - .width_mm = (hd_mm), \ - .height_mm = (vd_mm), \ - .type = DRM_MODE_TYPE_DRIVER, \ - .clock = 1 /* pass validation */ - -int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev, - const struct drm_framebuffer_funcs *fb_funcs, - struct drm_driver *driver); -int devm_tinydrm_register(struct tinydrm_device *tdev); -void tinydrm_shutdown(struct tinydrm_device *tdev); - -void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state); -int -tinydrm_display_pipe_init(struct tinydrm_device *tdev, - const struct drm_simple_display_pipe_funcs *funcs, - int connector_type, - const uint32_t *formats, - unsigned int format_count, - const struct drm_display_mode *mode, - unsigned int rotation); - -#endif /* __LINUX_TINYDRM_H */ diff --git a/include/drm/ttm/ttm_allocation.h b/include/drm/ttm/ttm_allocation.h new file mode 100644 index 000000000000..655d1e44aba7 --- /dev/null +++ b/include/drm/ttm/ttm_allocation.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* Copyright (c) 2025 Valve Corporation */ + +#ifndef _TTM_ALLOCATION_H_ +#define _TTM_ALLOCATION_H_ + +#define TTM_ALLOCATION_POOL_BENEFICIAL_ORDER(n) ((n) & 0xff) /* Max order which caller can benefit from */ +#define TTM_ALLOCATION_POOL_USE_DMA_ALLOC BIT(8) /* Use coherent DMA allocations. */ +#define TTM_ALLOCATION_POOL_USE_DMA32 BIT(9) /* Use GFP_DMA32 allocations. */ +#define TTM_ALLOCATION_PROPAGATE_ENOSPC BIT(10) /* Do not convert ENOSPC from resource managers to ENOMEM. */ + +#endif diff --git a/include/drm/ttm/ttm_backup.h b/include/drm/ttm/ttm_backup.h new file mode 100644 index 000000000000..c33cba111171 --- /dev/null +++ b/include/drm/ttm/ttm_backup.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2024 Intel Corporation + */ + +#ifndef _TTM_BACKUP_H_ +#define _TTM_BACKUP_H_ + +#include <linux/mm_types.h> +#include <linux/shmem_fs.h> + +/** + * ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer + * @handle: The handle to convert. + * + * Converts an opaque handle received from the + * ttm_backup_backup_page() function to an (invalid) + * struct page pointer suitable for a struct page array. + * + * Return: An (invalid) struct page pointer. + */ +static inline struct page * +ttm_backup_handle_to_page_ptr(unsigned long handle) +{ + return (struct page *)(handle << 1 | 1); +} + +/** + * ttm_backup_page_ptr_is_handle() - Whether a struct page pointer is a handle + * @page: The struct page pointer to check. + * + * Return: true if the struct page pointer is a handld returned from + * ttm_backup_handle_to_page_ptr(). False otherwise. + */ +static inline bool ttm_backup_page_ptr_is_handle(const struct page *page) +{ + return (unsigned long)page & 1; +} + +/** + * ttm_backup_page_ptr_to_handle() - Convert a struct page pointer to a handle + * @page: The struct page pointer to convert + * + * Return: The handle that was previously used in + * ttm_backup_handle_to_page_ptr() to obtain a struct page pointer, suitable + * for use as argument in the struct ttm_backup_drop() or + * ttm_backup_copy_page() functions. + */ +static inline unsigned long +ttm_backup_page_ptr_to_handle(const struct page *page) +{ + WARN_ON(!ttm_backup_page_ptr_is_handle(page)); + return (unsigned long)page >> 1; +} + +void ttm_backup_drop(struct file *backup, pgoff_t handle); + +int ttm_backup_copy_page(struct file *backup, struct page *dst, + pgoff_t handle, bool intr); + +s64 +ttm_backup_backup_page(struct file *backup, struct page *page, + bool writeback, pgoff_t idx, gfp_t page_gfp, + gfp_t alloc_gfp); + +void ttm_backup_fini(struct file *backup); + +u64 ttm_backup_bytes_avail(void); + +struct file *ttm_backup_shmem_create(loff_t size); + +#endif diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h new file mode 100644 index 000000000000..bca3a8849d47 --- /dev/null +++ b/include/drm/ttm/ttm_bo.h @@ -0,0 +1,546 @@ +/************************************************************************** + * + * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> + */ + +#ifndef _TTM_BO_API_H_ +#define _TTM_BO_API_H_ + +#include <drm/drm_gem.h> + +#include <linux/kref.h> +#include <linux/list.h> + +#include "ttm_device.h" + +/* Default number of pre-faulted pages in the TTM fault handler */ +#define TTM_BO_VM_NUM_PREFAULT 16 + +struct iosys_map; + +struct ttm_global; +struct ttm_device; +struct ttm_placement; +struct ttm_place; +struct ttm_resource; +struct ttm_resource_manager; +struct ttm_tt; + +/** + * enum ttm_bo_type + * + * @ttm_bo_type_device: These are 'normal' buffers that can + * be mmapped by user space. Each of these bos occupy a slot in the + * device address space, that can be used for normal vm operations. + * + * @ttm_bo_type_kernel: These buffers are like ttm_bo_type_device buffers, + * but they cannot be accessed from user-space. For kernel-only use. + * + * @ttm_bo_type_sg: Buffer made from dmabuf sg table shared with another + * driver. + */ +enum ttm_bo_type { + ttm_bo_type_device, + ttm_bo_type_kernel, + ttm_bo_type_sg +}; + +/** + * struct ttm_buffer_object + * + * @base: drm_gem_object superclass data. + * @bdev: Pointer to the buffer object device structure. + * @type: The bo type. + * @page_alignment: Page alignment. + * @destroy: Destruction function. If NULL, kfree is used. + * @kref: Reference count of this buffer object. When this refcount reaches + * zero, the object is destroyed or put on the delayed delete list. + * @resource: structure describing current placement. + * @ttm: TTM structure holding system pages. + * @deleted: True if the object is only a zombie and already deleted. + * @bulk_move: The bulk move object. + * @priority: Priority for LRU, BOs with lower priority are evicted first. + * @pin_count: Pin count. + * + * Base class for TTM buffer object, that deals with data placement and CPU + * mappings. GPU mappings are really up to the driver, but for simpler GPUs + * the driver can usually use the placement offset @offset directly as the + * GPU virtual address. For drivers implementing multiple + * GPU memory manager contexts, the driver should manage the address space + * in these contexts separately and use these objects to get the correct + * placement and caching for these GPU maps. This makes it possible to use + * these objects for even quite elaborate memory management schemes. + * The destroy member, the API visibility of this object makes it possible + * to derive driver specific types. + */ +struct ttm_buffer_object { + struct drm_gem_object base; + + /* + * Members constant at init. + */ + struct ttm_device *bdev; + enum ttm_bo_type type; + uint32_t page_alignment; + void (*destroy) (struct ttm_buffer_object *); + + /* + * Members not needing protection. + */ + struct kref kref; + + /* + * Members protected by the bo::resv::reserved lock. + */ + struct ttm_resource *resource; + struct ttm_tt *ttm; + bool deleted; + struct ttm_lru_bulk_move *bulk_move; + unsigned priority; + unsigned pin_count; + + /** + * @delayed_delete: Work item used when we can't delete the BO + * immediately + */ + struct work_struct delayed_delete; + + /** + * @sg: external source of pages and DMA addresses, protected by the + * reservation lock. + */ + struct sg_table *sg; +}; + +#define TTM_BO_MAP_IOMEM_MASK 0x80 + +/** + * struct ttm_bo_kmap_obj + * + * @virtual: The current kernel virtual address. + * @page: The page when kmap'ing a single page. + * @bo_kmap_type: Type of bo_kmap. + * @bo: The TTM BO. + * + * Object describing a kernel mapping. Since a TTM bo may be located + * in various memory types with various caching policies, the + * mapping can either be an ioremap, a vmap, a kmap or part of a + * premapped region. + */ +struct ttm_bo_kmap_obj { + void *virtual; + struct page *page; + enum { + ttm_bo_map_iomap = 1 | TTM_BO_MAP_IOMEM_MASK, + ttm_bo_map_vmap = 2, + ttm_bo_map_kmap = 3, + ttm_bo_map_premapped = 4 | TTM_BO_MAP_IOMEM_MASK, + } bo_kmap_type; + struct ttm_buffer_object *bo; +}; + +/** + * struct ttm_operation_ctx + * + * @interruptible: Sleep interruptible if sleeping. + * @no_wait_gpu: Return immediately if the GPU is busy. + * @gfp_retry_mayfail: Set the __GFP_RETRY_MAYFAIL when allocation pages. + * @allow_res_evict: Allow eviction of reserved BOs. Can be used when multiple + * BOs share the same reservation object. + * faults. Should only be used by TTM internally. + * @resv: Reservation object to allow reserved evictions with. + * @bytes_moved: Statistics on how many bytes have been moved. + * + * Context for TTM operations like changing buffer placement or general memory + * allocation. + */ +struct ttm_operation_ctx { + bool interruptible; + bool no_wait_gpu; + bool gfp_retry_mayfail; + bool allow_res_evict; + struct dma_resv *resv; + uint64_t bytes_moved; +}; + +struct ttm_lru_walk; + +/** struct ttm_lru_walk_ops - Operations for a LRU walk. */ +struct ttm_lru_walk_ops { + /** + * process_bo - Process this bo. + * @walk: struct ttm_lru_walk describing the walk. + * @bo: A locked and referenced buffer object. + * + * Return: Negative error code on error, User-defined positive value + * (typically, but not always, size of the processed bo) on success. + * On success, the returned values are summed by the walk and the + * walk exits when its target is met. + * 0 also indicates success, -EBUSY means this bo was skipped. + */ + s64 (*process_bo)(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo); +}; + +/** + * struct ttm_lru_walk_arg - Common part for the variants of BO LRU walk. + */ +struct ttm_lru_walk_arg { + /** @ctx: Pointer to the struct ttm_operation_ctx. */ + struct ttm_operation_ctx *ctx; + /** @ticket: The struct ww_acquire_ctx if any. */ + struct ww_acquire_ctx *ticket; + /** @trylock_only: Only use trylock for locking. */ + bool trylock_only; +}; + +/** + * struct ttm_lru_walk - Structure describing a LRU walk. + */ +struct ttm_lru_walk { + /** @ops: Pointer to the ops structure. */ + const struct ttm_lru_walk_ops *ops; + /** @arg: Common bo LRU walk arguments. */ + struct ttm_lru_walk_arg arg; +}; + +s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev, + struct ttm_resource_manager *man, s64 target); + +/** + * struct ttm_bo_shrink_flags - flags to govern the bo shrinking behaviour + * @purge: Purge the content rather than backing it up. + * @writeback: Attempt to immediately write content to swap space. + * @allow_move: Allow moving to system before shrinking. This is typically + * not desired for zombie- or ghost objects (with zombie object meaning + * objects with a zero gem object refcount) + */ +struct ttm_bo_shrink_flags { + u32 purge : 1; + u32 writeback : 1; + u32 allow_move : 1; +}; + +long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, + const struct ttm_bo_shrink_flags flags); + +bool ttm_bo_shrink_suitable(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx); + +bool ttm_bo_shrink_avoid_wait(void); + +/** + * ttm_bo_reserve: + * + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY. + * @ticket: ticket used to acquire the ww_mutex. + * + * Locks a buffer object for validation. (Or prevents other processes from + * locking it for validation), while taking a number of measures to prevent + * deadlocks. + * + * Returns: + * -EDEADLK: The reservation may cause a deadlock. + * Release all buffer reservations, wait for @bo to become unreserved and + * try again. + * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by + * a signal. Release all buffer reservations and return to user-space. + * -EBUSY: The function needed to sleep, but @no_wait was true + * -EALREADY: Bo already reserved using @ticket. This error code will only + * be returned if @use_ticket is set to true. + */ +static inline int ttm_bo_reserve(struct ttm_buffer_object *bo, + bool interruptible, bool no_wait, + struct ww_acquire_ctx *ticket) +{ + int ret = 0; + + if (no_wait) { + bool success; + + if (WARN_ON(ticket)) + return -EBUSY; + + success = dma_resv_trylock(bo->base.resv); + return success ? 0 : -EBUSY; + } + + if (interruptible) + ret = dma_resv_lock_interruptible(bo->base.resv, ticket); + else + ret = dma_resv_lock(bo->base.resv, ticket); + if (ret == -EINTR) + return -ERESTARTSYS; + return ret; +} + +/** + * ttm_bo_reserve_slowpath: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @ticket: Ticket used to acquire the ww_mutex. + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + */ +static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, + struct ww_acquire_ctx *ticket) +{ + if (interruptible) { + int ret = dma_resv_lock_slow_interruptible(bo->base.resv, + ticket); + if (ret == -EINTR) + ret = -ERESTARTSYS; + return ret; + } + dma_resv_lock_slow(bo->base.resv, ticket); + return 0; +} + +void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo); + +static inline void +ttm_bo_move_to_lru_tail_unlocked(struct ttm_buffer_object *bo) +{ + spin_lock(&bo->bdev->lru_lock); + ttm_bo_move_to_lru_tail(bo); + spin_unlock(&bo->bdev->lru_lock); +} + +static inline void ttm_bo_assign_mem(struct ttm_buffer_object *bo, + struct ttm_resource *new_mem) +{ + WARN_ON(bo->resource); + bo->resource = new_mem; +} + +/** + * ttm_bo_move_null - assign memory for a buffer object. + * @bo: The bo to assign the memory to + * @new_mem: The memory to be assigned. + * + * Assign the memory from new_mem to the memory of the buffer object bo. + */ +static inline void ttm_bo_move_null(struct ttm_buffer_object *bo, + struct ttm_resource *new_mem) +{ + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); +} + +/** + * ttm_bo_unreserve + * + * @bo: A pointer to a struct ttm_buffer_object. + * + * Unreserve a previous reservation of @bo. + */ +static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) +{ + ttm_bo_move_to_lru_tail_unlocked(bo); + dma_resv_unlock(bo->base.resv); +} + +/** + * ttm_kmap_obj_virtual + * + * @map: A struct ttm_bo_kmap_obj returned from ttm_bo_kmap. + * @is_iomem: Pointer to an integer that on return indicates 1 if the + * virtual map is io memory, 0 if normal memory. + * + * Returns the virtual address of a buffer object area mapped by ttm_bo_kmap. + * If *is_iomem is 1 on return, the virtual address points to an io memory area, + * that should strictly be accessed by the iowriteXX() and similar functions. + */ +static inline void *ttm_kmap_obj_virtual(struct ttm_bo_kmap_obj *map, + bool *is_iomem) +{ + *is_iomem = !!(map->bo_kmap_type & TTM_BO_MAP_IOMEM_MASK); + return map->virtual; +} + +int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, + struct ttm_operation_ctx *ctx); +int ttm_bo_validate(struct ttm_buffer_object *bo, + struct ttm_placement *placement, + struct ttm_operation_ctx *ctx); +void ttm_bo_fini(struct ttm_buffer_object *bo); +void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo, + struct ttm_lru_bulk_move *bulk); +bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, + const struct ttm_place *place); +int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, struct ttm_operation_ctx *ctx, + struct sg_table *sg, struct dma_resv *resv, + void (*destroy)(struct ttm_buffer_object *)); +int ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, bool interruptible, + struct sg_table *sg, struct dma_resv *resv, + void (*destroy)(struct ttm_buffer_object *)); +int ttm_bo_kmap(struct ttm_buffer_object *bo, unsigned long start_page, + unsigned long num_pages, struct ttm_bo_kmap_obj *map); +void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map); +void *ttm_bo_kmap_try_from_panic(struct ttm_buffer_object *bo, unsigned long page); +int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map); +void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map); +int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object *bo); +s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, + struct ttm_resource_manager *man, gfp_t gfp_flags, + s64 target); +void ttm_bo_pin(struct ttm_buffer_object *bo); +void ttm_bo_unpin(struct ttm_buffer_object *bo); +int ttm_bo_evict_first(struct ttm_device *bdev, + struct ttm_resource_manager *man, + struct ttm_operation_ctx *ctx); +int ttm_bo_access(struct ttm_buffer_object *bo, unsigned long offset, + void *buf, int len, int write); +vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, + struct vm_fault *vmf); +vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, + pgprot_t prot, + pgoff_t num_prefault); +vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf); +void ttm_bo_vm_open(struct vm_area_struct *vma); +void ttm_bo_vm_close(struct vm_area_struct *vma); +int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write); +vm_fault_t ttm_bo_vm_dummy_page(struct vm_fault *vmf, pgprot_t prot); + +int ttm_bo_mem_space(struct ttm_buffer_object *bo, + struct ttm_placement *placement, + struct ttm_resource **mem, + struct ttm_operation_ctx *ctx); + +void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo); +/* + * ttm_bo_util.c + */ +int ttm_mem_io_reserve(struct ttm_device *bdev, + struct ttm_resource *mem); +void ttm_mem_io_free(struct ttm_device *bdev, + struct ttm_resource *mem); +void ttm_move_memcpy(bool clear, u32 num_pages, + struct ttm_kmap_iter *dst_iter, + struct ttm_kmap_iter *src_iter); +int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem); +int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, + struct dma_fence *fence, bool evict, + bool pipeline, + struct ttm_resource *new_mem); +void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, + struct ttm_resource *new_mem); +int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo); +pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res, + pgprot_t tmp); +void ttm_bo_tt_destroy(struct ttm_buffer_object *bo); +int ttm_bo_populate(struct ttm_buffer_object *bo, + struct ttm_operation_ctx *ctx); +int ttm_bo_setup_export(struct ttm_buffer_object *bo, + struct ttm_operation_ctx *ctx); + +/* Driver LRU walk helpers initially targeted for shrinking. */ + +/** + * struct ttm_bo_lru_cursor - Iterator cursor for TTM LRU list looping + */ +struct ttm_bo_lru_cursor { + /** @res_curs: Embedded struct ttm_resource_cursor. */ + struct ttm_resource_cursor res_curs; + /** + * @bo: Buffer object pointer if a buffer object is refcounted, + * NULL otherwise. + */ + struct ttm_buffer_object *bo; + /** + * @needs_unlock: Valid iff @bo != NULL. The bo resv needs + * unlock before the next iteration or after loop exit. + */ + bool needs_unlock; + /** @arg: Pointer to common BO LRU walk arguments. */ + struct ttm_lru_walk_arg *arg; +}; + +void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs); + +struct ttm_bo_lru_cursor * +ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs, + struct ttm_resource_manager *man, + struct ttm_lru_walk_arg *arg); + +struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs); + +struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs); + +/* + * Defines needed to use autocleanup (linux/cleanup.h) with struct ttm_bo_lru_cursor. + */ +DEFINE_CLASS(ttm_bo_lru_cursor, struct ttm_bo_lru_cursor *, + if (_T) {ttm_bo_lru_cursor_fini(_T); }, + ttm_bo_lru_cursor_init(curs, man, arg), + struct ttm_bo_lru_cursor *curs, struct ttm_resource_manager *man, + struct ttm_lru_walk_arg *arg); +static inline void * +class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T) +{ return *_T; } +#define class_ttm_bo_lru_cursor_is_conditional false + +/** + * ttm_bo_lru_for_each_reserved_guarded() - Iterate over buffer objects owning + * resources on LRU lists. + * @_cursor: struct ttm_bo_lru_cursor to use for the iteration. + * @_man: The resource manager whose LRU lists to iterate over. + * @_arg: The struct ttm_lru_walk_arg to govern the LRU walk. + * @_bo: The struct ttm_buffer_object pointer pointing to the buffer object + * for the current iteration. + * + * Iterate over all resources of @_man and for each resource, attempt to + * reference and lock (using the locking mode detailed in @_ctx) the buffer + * object it points to. If successful, assign @_bo to the address of the + * buffer object and update @_cursor. The iteration is guarded in the + * sense that @_cursor will be initialized before looping start and cleaned + * up at looping termination, even if terminated prematurely by, for + * example a return or break statement. Exiting the loop will also unlock + * (if needed) and unreference @_bo. + * + * Return: If locking of a bo returns an error, then iteration is terminated + * and @_bo is set to a corresponding error pointer. It's illegal to + * dereference @_bo after loop exit. + */ +#define ttm_bo_lru_for_each_reserved_guarded(_cursor, _man, _arg, _bo) \ + scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _arg) \ + for ((_bo) = ttm_bo_lru_cursor_first(_cursor); \ + !IS_ERR_OR_NULL(_bo); \ + (_bo) = ttm_bo_lru_cursor_next(_cursor)) + +#endif diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h deleted file mode 100644 index 3fc4854dce49..000000000000 --- a/include/drm/ttm/ttm_bo_api.h +++ /dev/null @@ -1,799 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> - */ - -#ifndef _TTM_BO_API_H_ -#define _TTM_BO_API_H_ - -#include <drm/drm_hashtab.h> -#include <drm/drm_vma_manager.h> -#include <linux/kref.h> -#include <linux/list.h> -#include <linux/wait.h> -#include <linux/mutex.h> -#include <linux/mm.h> -#include <linux/bitmap.h> -#include <linux/reservation.h> - -struct ttm_bo_global; - -struct ttm_bo_device; - -struct drm_mm_node; - -struct ttm_placement; - -struct ttm_place; - -struct ttm_lru_bulk_move; - -/** - * struct ttm_bus_placement - * - * @addr: mapped virtual address - * @base: bus base address - * @is_iomem: is this io memory ? - * @size: size in byte - * @offset: offset from the base address - * @io_reserved_vm: The VM system has a refcount in @io_reserved_count - * @io_reserved_count: Refcounting the numbers of callers to ttm_mem_io_reserve - * - * Structure indicating the bus placement of an object. - */ -struct ttm_bus_placement { - void *addr; - phys_addr_t base; - unsigned long size; - unsigned long offset; - bool is_iomem; - bool io_reserved_vm; - uint64_t io_reserved_count; -}; - - -/** - * struct ttm_mem_reg - * - * @mm_node: Memory manager node. - * @size: Requested size of memory region. - * @num_pages: Actual size of memory region in pages. - * @page_alignment: Page alignment. - * @placement: Placement flags. - * @bus: Placement on io bus accessible to the CPU - * - * Structure indicating the placement and space resources used by a - * buffer object. - */ - -struct ttm_mem_reg { - void *mm_node; - unsigned long start; - unsigned long size; - unsigned long num_pages; - uint32_t page_alignment; - uint32_t mem_type; - uint32_t placement; - struct ttm_bus_placement bus; -}; - -/** - * enum ttm_bo_type - * - * @ttm_bo_type_device: These are 'normal' buffers that can - * be mmapped by user space. Each of these bos occupy a slot in the - * device address space, that can be used for normal vm operations. - * - * @ttm_bo_type_kernel: These buffers are like ttm_bo_type_device buffers, - * but they cannot be accessed from user-space. For kernel-only use. - * - * @ttm_bo_type_sg: Buffer made from dmabuf sg table shared with another - * driver. - */ - -enum ttm_bo_type { - ttm_bo_type_device, - ttm_bo_type_kernel, - ttm_bo_type_sg -}; - -struct ttm_tt; - -/** - * struct ttm_buffer_object - * - * @bdev: Pointer to the buffer object device structure. - * @type: The bo type. - * @destroy: Destruction function. If NULL, kfree is used. - * @num_pages: Actual number of pages. - * @acc_size: Accounted size for this object. - * @kref: Reference count of this buffer object. When this refcount reaches - * zero, the object is put on the delayed delete list. - * @list_kref: List reference count of this buffer object. This member is - * used to avoid destruction while the buffer object is still on a list. - * Lru lists may keep one refcount, the delayed delete list, and kref != 0 - * keeps one refcount. When this refcount reaches zero, - * the object is destroyed. - * @mem: structure describing current placement. - * @persistent_swap_storage: Usually the swap storage is deleted for buffers - * pinned in physical memory. If this behaviour is not desired, this member - * holds a pointer to a persistent shmem object. - * @ttm: TTM structure holding system pages. - * @evicted: Whether the object was evicted without user-space knowing. - * @cpu_writes: For synchronization. Number of cpu writers. - * @lru: List head for the lru list. - * @ddestroy: List head for the delayed destroy list. - * @swap: List head for swap LRU list. - * @moving: Fence set when BO is moving - * @vma_node: Address space manager node. - * @offset: The current GPU offset, which can have different meanings - * depending on the memory type. For SYSTEM type memory, it should be 0. - * @cur_placement: Hint of current placement. - * @wu_mutex: Wait unreserved mutex. - * - * Base class for TTM buffer object, that deals with data placement and CPU - * mappings. GPU mappings are really up to the driver, but for simpler GPUs - * the driver can usually use the placement offset @offset directly as the - * GPU virtual address. For drivers implementing multiple - * GPU memory manager contexts, the driver should manage the address space - * in these contexts separately and use these objects to get the correct - * placement and caching for these GPU maps. This makes it possible to use - * these objects for even quite elaborate memory management schemes. - * The destroy member, the API visibility of this object makes it possible - * to derive driver specific types. - */ - -struct ttm_buffer_object { - /** - * Members constant at init. - */ - - struct ttm_bo_device *bdev; - enum ttm_bo_type type; - void (*destroy) (struct ttm_buffer_object *); - unsigned long num_pages; - size_t acc_size; - - /** - * Members not needing protection. - */ - - struct kref kref; - struct kref list_kref; - - /** - * Members protected by the bo::resv::reserved lock. - */ - - struct ttm_mem_reg mem; - struct file *persistent_swap_storage; - struct ttm_tt *ttm; - bool evicted; - - /** - * Members protected by the bo::reserved lock only when written to. - */ - - atomic_t cpu_writers; - - /** - * Members protected by the bdev::lru_lock. - */ - - struct list_head lru; - struct list_head ddestroy; - struct list_head swap; - struct list_head io_reserve_lru; - - /** - * Members protected by a bo reservation. - */ - - struct dma_fence *moving; - - struct drm_vma_offset_node vma_node; - - unsigned priority; - - /** - * Special members that are protected by the reserve lock - * and the bo::lock when written to. Can be read with - * either of these locks held. - */ - - uint64_t offset; /* GPU address space is independent of CPU word size */ - - struct sg_table *sg; - - struct reservation_object *resv; - struct reservation_object ttm_resv; - struct mutex wu_mutex; -}; - -/** - * struct ttm_bo_kmap_obj - * - * @virtual: The current kernel virtual address. - * @page: The page when kmap'ing a single page. - * @bo_kmap_type: Type of bo_kmap. - * - * Object describing a kernel mapping. Since a TTM bo may be located - * in various memory types with various caching policies, the - * mapping can either be an ioremap, a vmap, a kmap or part of a - * premapped region. - */ - -#define TTM_BO_MAP_IOMEM_MASK 0x80 -struct ttm_bo_kmap_obj { - void *virtual; - struct page *page; - enum { - ttm_bo_map_iomap = 1 | TTM_BO_MAP_IOMEM_MASK, - ttm_bo_map_vmap = 2, - ttm_bo_map_kmap = 3, - ttm_bo_map_premapped = 4 | TTM_BO_MAP_IOMEM_MASK, - } bo_kmap_type; - struct ttm_buffer_object *bo; -}; - -/** - * struct ttm_operation_ctx - * - * @interruptible: Sleep interruptible if sleeping. - * @no_wait_gpu: Return immediately if the GPU is busy. - * @resv: Reservation object to allow reserved evictions with. - * @flags: Including the following flags - * - * Context for TTM operations like changing buffer placement or general memory - * allocation. - */ -struct ttm_operation_ctx { - bool interruptible; - bool no_wait_gpu; - struct reservation_object *resv; - uint64_t bytes_moved; - uint32_t flags; -}; - -/* Allow eviction of reserved BOs */ -#define TTM_OPT_FLAG_ALLOW_RES_EVICT 0x1 -/* when serving page fault or suspend, allow alloc anyway */ -#define TTM_OPT_FLAG_FORCE_ALLOC 0x2 - -/** - * ttm_bo_get - reference a struct ttm_buffer_object - * - * @bo: The buffer object. - */ -static inline void ttm_bo_get(struct ttm_buffer_object *bo) -{ - kref_get(&bo->kref); -} - -/** - * ttm_bo_reference - reference a struct ttm_buffer_object - * - * @bo: The buffer object. - * - * Returns a refcounted pointer to a buffer object. - * - * This function is deprecated. Use @ttm_bo_get instead. - */ - -static inline struct ttm_buffer_object * -ttm_bo_reference(struct ttm_buffer_object *bo) -{ - ttm_bo_get(bo); - return bo; -} - -/** - * ttm_bo_get_unless_zero - reference a struct ttm_buffer_object unless - * its refcount has already reached zero. - * @bo: The buffer object. - * - * Used to reference a TTM buffer object in lookups where the object is removed - * from the lookup structure during the destructor and for RCU lookups. - * - * Returns: @bo if the referencing was successful, NULL otherwise. - */ -static inline __must_check struct ttm_buffer_object * -ttm_bo_get_unless_zero(struct ttm_buffer_object *bo) -{ - if (!kref_get_unless_zero(&bo->kref)) - return NULL; - return bo; -} - -/** - * ttm_bo_wait - wait for buffer idle. - * - * @bo: The buffer object. - * @interruptible: Use interruptible wait. - * @no_wait: Return immediately if buffer is busy. - * - * This function must be called with the bo::mutex held, and makes - * sure any previous rendering to the buffer is completed. - * Note: It might be necessary to block validations before the - * wait by reserving the buffer. - * Returns -EBUSY if no_wait is true and the buffer is busy. - * Returns -ERESTARTSYS if interrupted by a signal. - */ -int ttm_bo_wait(struct ttm_buffer_object *bo, bool interruptible, bool no_wait); - -/** - * ttm_bo_mem_compat - Check if proposed placement is compatible with a bo - * - * @placement: Return immediately if buffer is busy. - * @mem: The struct ttm_mem_reg indicating the region where the bo resides - * @new_flags: Describes compatible placement found - * - * Returns true if the placement is compatible - */ -bool ttm_bo_mem_compat(struct ttm_placement *placement, struct ttm_mem_reg *mem, - uint32_t *new_flags); - -/** - * ttm_bo_validate - * - * @bo: The buffer object. - * @placement: Proposed placement for the buffer object. - * @ctx: validation parameters. - * - * Changes placement and caching policy of the buffer object - * according proposed placement. - * Returns - * -EINVAL on invalid proposed placement. - * -ENOMEM on out-of-memory condition. - * -EBUSY if no_wait is true and buffer busy. - * -ERESTARTSYS if interrupted by a signal. - */ -int ttm_bo_validate(struct ttm_buffer_object *bo, - struct ttm_placement *placement, - struct ttm_operation_ctx *ctx); - -/** - * ttm_bo_put - * - * @bo: The buffer object. - * - * Unreference a buffer object. - */ -void ttm_bo_put(struct ttm_buffer_object *bo); - -/** - * ttm_bo_unref - * - * @bo: The buffer object. - * - * Unreference and clear a pointer to a buffer object. - * - * This function is deprecated. Use @ttm_bo_put instead. - */ -void ttm_bo_unref(struct ttm_buffer_object **bo); - -/** - * ttm_bo_add_to_lru - * - * @bo: The buffer object. - * - * Add this bo to the relevant mem type lru and, if it's backed by - * system pages (ttms) to the swap list. - * This function must be called with struct ttm_bo_global::lru_lock held, and - * is typically called immediately prior to unreserving a bo. - */ -void ttm_bo_add_to_lru(struct ttm_buffer_object *bo); - -/** - * ttm_bo_del_from_lru - * - * @bo: The buffer object. - * - * Remove this bo from all lru lists used to lookup and reserve an object. - * This function must be called with struct ttm_bo_global::lru_lock held, - * and is usually called just immediately after the bo has been reserved to - * avoid recursive reservation from lru lists. - */ -void ttm_bo_del_from_lru(struct ttm_buffer_object *bo); - -/** - * ttm_bo_move_to_lru_tail - * - * @bo: The buffer object. - * @bulk: optional bulk move structure to remember BO positions - * - * Move this BO to the tail of all lru lists used to lookup and reserve an - * object. This function must be called with struct ttm_bo_global::lru_lock - * held, and is used to make a BO less likely to be considered for eviction. - */ -void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo, - struct ttm_lru_bulk_move *bulk); - -/** - * ttm_bo_bulk_move_lru_tail - * - * @bulk: bulk move structure - * - * Bulk move BOs to the LRU tail, only valid to use when driver makes sure that - * BO order never changes. Should be called with ttm_bo_global::lru_lock held. - */ -void ttm_bo_bulk_move_lru_tail(struct ttm_lru_bulk_move *bulk); - -/** - * ttm_bo_lock_delayed_workqueue - * - * Prevent the delayed workqueue from running. - * Returns - * True if the workqueue was queued at the time - */ -int ttm_bo_lock_delayed_workqueue(struct ttm_bo_device *bdev); - -/** - * ttm_bo_unlock_delayed_workqueue - * - * Allows the delayed workqueue to run. - */ -void ttm_bo_unlock_delayed_workqueue(struct ttm_bo_device *bdev, int resched); - -/** - * ttm_bo_eviction_valuable - * - * @bo: The buffer object to evict - * @place: the placement we need to make room for - * - * Check if it is valuable to evict the BO to make room for the given placement. - */ -bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, - const struct ttm_place *place); - -/** - * ttm_bo_synccpu_write_grab - * - * @bo: The buffer object: - * @no_wait: Return immediately if buffer is busy. - * - * Synchronizes a buffer object for CPU RW access. This means - * command submission that affects the buffer will return -EBUSY - * until ttm_bo_synccpu_write_release is called. - * - * Returns - * -EBUSY if the buffer is busy and no_wait is true. - * -ERESTARTSYS if interrupted by a signal. - */ -int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait); - -/** - * ttm_bo_synccpu_write_release: - * - * @bo : The buffer object. - * - * Releases a synccpu lock. - */ -void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo); - -/** - * ttm_bo_acc_size - * - * @bdev: Pointer to a ttm_bo_device struct. - * @bo_size: size of the buffer object in byte. - * @struct_size: size of the structure holding buffer object datas - * - * Returns size to account for a buffer object - */ -size_t ttm_bo_acc_size(struct ttm_bo_device *bdev, - unsigned long bo_size, - unsigned struct_size); -size_t ttm_bo_dma_acc_size(struct ttm_bo_device *bdev, - unsigned long bo_size, - unsigned struct_size); - -/** - * ttm_bo_init_reserved - * - * @bdev: Pointer to a ttm_bo_device struct. - * @bo: Pointer to a ttm_buffer_object to be initialized. - * @size: Requested size of buffer object. - * @type: Requested type of buffer object. - * @flags: Initial placement flags. - * @page_alignment: Data alignment in pages. - * @ctx: TTM operation context for memory allocation. - * @acc_size: Accounted size for this object. - * @resv: Pointer to a reservation_object, or NULL to let ttm allocate one. - * @destroy: Destroy function. Use NULL for kfree(). - * - * This function initializes a pre-allocated struct ttm_buffer_object. - * As this object may be part of a larger structure, this function, - * together with the @destroy function, - * enables driver-specific objects derived from a ttm_buffer_object. - * - * On successful return, the caller owns an object kref to @bo. The kref and - * list_kref are usually set to 1, but note that in some situations, other - * tasks may already be holding references to @bo as well. - * Furthermore, if resv == NULL, the buffer's reservation lock will be held, - * and it is the caller's responsibility to call ttm_bo_unreserve. - * - * If a failure occurs, the function will call the @destroy function, or - * kfree() if @destroy is NULL. Thus, after a failure, dereferencing @bo is - * illegal and will likely cause memory corruption. - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid placement flags. - * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. - */ - -int ttm_bo_init_reserved(struct ttm_bo_device *bdev, - struct ttm_buffer_object *bo, - unsigned long size, - enum ttm_bo_type type, - struct ttm_placement *placement, - uint32_t page_alignment, - struct ttm_operation_ctx *ctx, - size_t acc_size, - struct sg_table *sg, - struct reservation_object *resv, - void (*destroy) (struct ttm_buffer_object *)); - -/** - * ttm_bo_init - * - * @bdev: Pointer to a ttm_bo_device struct. - * @bo: Pointer to a ttm_buffer_object to be initialized. - * @size: Requested size of buffer object. - * @type: Requested type of buffer object. - * @flags: Initial placement flags. - * @page_alignment: Data alignment in pages. - * @interruptible: If needing to sleep to wait for GPU resources, - * sleep interruptible. - * pinned in physical memory. If this behaviour is not desired, this member - * holds a pointer to a persistent shmem object. Typically, this would - * point to the shmem object backing a GEM object if TTM is used to back a - * GEM user interface. - * @acc_size: Accounted size for this object. - * @resv: Pointer to a reservation_object, or NULL to let ttm allocate one. - * @destroy: Destroy function. Use NULL for kfree(). - * - * This function initializes a pre-allocated struct ttm_buffer_object. - * As this object may be part of a larger structure, this function, - * together with the @destroy function, - * enables driver-specific objects derived from a ttm_buffer_object. - * - * On successful return, the caller owns an object kref to @bo. The kref and - * list_kref are usually set to 1, but note that in some situations, other - * tasks may already be holding references to @bo as well. - * - * If a failure occurs, the function will call the @destroy function, or - * kfree() if @destroy is NULL. Thus, after a failure, dereferencing @bo is - * illegal and will likely cause memory corruption. - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid placement flags. - * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. - */ -int ttm_bo_init(struct ttm_bo_device *bdev, struct ttm_buffer_object *bo, - unsigned long size, enum ttm_bo_type type, - struct ttm_placement *placement, - uint32_t page_alignment, bool interrubtible, size_t acc_size, - struct sg_table *sg, struct reservation_object *resv, - void (*destroy) (struct ttm_buffer_object *)); - -/** - * ttm_bo_create - * - * @bdev: Pointer to a ttm_bo_device struct. - * @size: Requested size of buffer object. - * @type: Requested type of buffer object. - * @placement: Initial placement. - * @page_alignment: Data alignment in pages. - * @interruptible: If needing to sleep while waiting for GPU resources, - * sleep interruptible. - * @p_bo: On successful completion *p_bo points to the created object. - * - * This function allocates a ttm_buffer_object, and then calls ttm_bo_init - * on that object. The destroy function is set to kfree(). - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid placement flags. - * -ERESTARTSYS: Interrupted by signal while waiting for resources. - */ -int ttm_bo_create(struct ttm_bo_device *bdev, unsigned long size, - enum ttm_bo_type type, struct ttm_placement *placement, - uint32_t page_alignment, bool interruptible, - struct ttm_buffer_object **p_bo); - -/** - * ttm_bo_init_mm - * - * @bdev: Pointer to a ttm_bo_device struct. - * @mem_type: The memory type. - * @p_size: size managed area in pages. - * - * Initialize a manager for a given memory type. - * Note: if part of driver firstopen, it must be protected from a - * potentially racing lastclose. - * Returns: - * -EINVAL: invalid size or memory type. - * -ENOMEM: Not enough memory. - * May also return driver-specified errors. - */ -int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, - unsigned long p_size); - -/** - * ttm_bo_clean_mm - * - * @bdev: Pointer to a ttm_bo_device struct. - * @mem_type: The memory type. - * - * Take down a manager for a given memory type after first walking - * the LRU list to evict any buffers left alive. - * - * Normally, this function is part of lastclose() or unload(), and at that - * point there shouldn't be any buffers left created by user-space, since - * there should've been removed by the file descriptor release() method. - * However, before this function is run, make sure to signal all sync objects, - * and verify that the delayed delete queue is empty. The driver must also - * make sure that there are no NO_EVICT buffers present in this memory type - * when the call is made. - * - * If this function is part of a VT switch, the caller must make sure that - * there are no appications currently validating buffers before this - * function is called. The caller can do that by first taking the - * struct ttm_bo_device::ttm_lock in write mode. - * - * Returns: - * -EINVAL: invalid or uninitialized memory type. - * -EBUSY: There are still buffers left in this memory type. - */ -int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type); - -/** - * ttm_bo_evict_mm - * - * @bdev: Pointer to a ttm_bo_device struct. - * @mem_type: The memory type. - * - * Evicts all buffers on the lru list of the memory type. - * This is normally part of a VT switch or an - * out-of-memory-space-due-to-fragmentation handler. - * The caller must make sure that there are no other processes - * currently validating buffers, and can do that by taking the - * struct ttm_bo_device::ttm_lock in write mode. - * - * Returns: - * -EINVAL: Invalid or uninitialized memory type. - * -ERESTARTSYS: The call was interrupted by a signal while waiting to - * evict a buffer. - */ -int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type); - -/** - * ttm_kmap_obj_virtual - * - * @map: A struct ttm_bo_kmap_obj returned from ttm_bo_kmap. - * @is_iomem: Pointer to an integer that on return indicates 1 if the - * virtual map is io memory, 0 if normal memory. - * - * Returns the virtual address of a buffer object area mapped by ttm_bo_kmap. - * If *is_iomem is 1 on return, the virtual address points to an io memory area, - * that should strictly be accessed by the iowriteXX() and similar functions. - */ -static inline void *ttm_kmap_obj_virtual(struct ttm_bo_kmap_obj *map, - bool *is_iomem) -{ - *is_iomem = !!(map->bo_kmap_type & TTM_BO_MAP_IOMEM_MASK); - return map->virtual; -} - -/** - * ttm_bo_kmap - * - * @bo: The buffer object. - * @start_page: The first page to map. - * @num_pages: Number of pages to map. - * @map: pointer to a struct ttm_bo_kmap_obj representing the map. - * - * Sets up a kernel virtual mapping, using ioremap, vmap or kmap to the - * data in the buffer object. The ttm_kmap_obj_virtual function can then be - * used to obtain a virtual address to the data. - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid range. - */ -int ttm_bo_kmap(struct ttm_buffer_object *bo, unsigned long start_page, - unsigned long num_pages, struct ttm_bo_kmap_obj *map); - -/** - * ttm_bo_kunmap - * - * @map: Object describing the map to unmap. - * - * Unmaps a kernel map set up by ttm_bo_kmap. - */ -void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map); - -/** - * ttm_fbdev_mmap - mmap fbdev memory backed by a ttm buffer object. - * - * @vma: vma as input from the fbdev mmap method. - * @bo: The bo backing the address space. The address space will - * have the same size as the bo, and start at offset 0. - * - * This function is intended to be called by the fbdev mmap method - * if the fbdev address space is to be backed by a bo. - */ -int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo); - -/** - * ttm_bo_mmap - mmap out of the ttm device address space. - * - * @filp: filp as input from the mmap method. - * @vma: vma as input from the mmap method. - * @bdev: Pointer to the ttm_bo_device with the address space manager. - * - * This function is intended to be called by the device mmap method. - * if the device address space is to be backed by the bo manager. - */ -int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, - struct ttm_bo_device *bdev); - -void *ttm_kmap_atomic_prot(struct page *page, pgprot_t prot); - -void ttm_kunmap_atomic_prot(void *addr, pgprot_t prot); - -/** - * ttm_bo_io - * - * @bdev: Pointer to the struct ttm_bo_device. - * @filp: Pointer to the struct file attempting to read / write. - * @wbuf: User-space pointer to address of buffer to write. NULL on read. - * @rbuf: User-space pointer to address of buffer to read into. - * Null on write. - * @count: Number of bytes to read / write. - * @f_pos: Pointer to current file position. - * @write: 1 for read, 0 for write. - * - * This function implements read / write into ttm buffer objects, and is - * intended to - * be called from the fops::read and fops::write method. - * Returns: - * See man (2) write, man(2) read. In particular, - * the function may return -ERESTARTSYS if - * interrupted by a signal. - */ -ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, - const char __user *wbuf, char __user *rbuf, - size_t count, loff_t *f_pos, bool write); - -int ttm_bo_swapout(struct ttm_bo_global *glob, - struct ttm_operation_ctx *ctx); -void ttm_bo_swapout_all(struct ttm_bo_device *bdev); -int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo); -#endif diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h deleted file mode 100644 index 1021106438b2..000000000000 --- a/include/drm/ttm/ttm_bo_driver.h +++ /dev/null @@ -1,887 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2006-2009 Vmware, Inc., Palo Alto, CA., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> - */ -#ifndef _TTM_BO_DRIVER_H_ -#define _TTM_BO_DRIVER_H_ - -#include <drm/drm_mm.h> -#include <drm/drm_vma_manager.h> -#include <linux/workqueue.h> -#include <linux/fs.h> -#include <linux/spinlock.h> -#include <linux/reservation.h> - -#include "ttm_bo_api.h" -#include "ttm_memory.h" -#include "ttm_module.h" -#include "ttm_placement.h" -#include "ttm_tt.h" - -#define TTM_MAX_BO_PRIORITY 4U - -#define TTM_MEMTYPE_FLAG_FIXED (1 << 0) /* Fixed (on-card) PCI memory */ -#define TTM_MEMTYPE_FLAG_MAPPABLE (1 << 1) /* Memory mappable */ -#define TTM_MEMTYPE_FLAG_CMA (1 << 3) /* Can't map aperture */ - -struct ttm_mem_type_manager; - -struct ttm_mem_type_manager_func { - /** - * struct ttm_mem_type_manager member init - * - * @man: Pointer to a memory type manager. - * @p_size: Implementation dependent, but typically the size of the - * range to be managed in pages. - * - * Called to initialize a private range manager. The function is - * expected to initialize the man::priv member. - * Returns 0 on success, negative error code on failure. - */ - int (*init)(struct ttm_mem_type_manager *man, unsigned long p_size); - - /** - * struct ttm_mem_type_manager member takedown - * - * @man: Pointer to a memory type manager. - * - * Called to undo the setup done in init. All allocated resources - * should be freed. - */ - int (*takedown)(struct ttm_mem_type_manager *man); - - /** - * struct ttm_mem_type_manager member get_node - * - * @man: Pointer to a memory type manager. - * @bo: Pointer to the buffer object we're allocating space for. - * @placement: Placement details. - * @flags: Additional placement flags. - * @mem: Pointer to a struct ttm_mem_reg to be filled in. - * - * This function should allocate space in the memory type managed - * by @man. Placement details if - * applicable are given by @placement. If successful, - * @mem::mm_node should be set to a non-null value, and - * @mem::start should be set to a value identifying the beginning - * of the range allocated, and the function should return zero. - * If the memory region accommodate the buffer object, @mem::mm_node - * should be set to NULL, and the function should return 0. - * If a system error occurred, preventing the request to be fulfilled, - * the function should return a negative error code. - * - * Note that @mem::mm_node will only be dereferenced by - * struct ttm_mem_type_manager functions and optionally by the driver, - * which has knowledge of the underlying type. - * - * This function may not be called from within atomic context, so - * an implementation can and must use either a mutex or a spinlock to - * protect any data structures managing the space. - */ - int (*get_node)(struct ttm_mem_type_manager *man, - struct ttm_buffer_object *bo, - const struct ttm_place *place, - struct ttm_mem_reg *mem); - - /** - * struct ttm_mem_type_manager member put_node - * - * @man: Pointer to a memory type manager. - * @mem: Pointer to a struct ttm_mem_reg to be filled in. - * - * This function frees memory type resources previously allocated - * and that are identified by @mem::mm_node and @mem::start. May not - * be called from within atomic context. - */ - void (*put_node)(struct ttm_mem_type_manager *man, - struct ttm_mem_reg *mem); - - /** - * struct ttm_mem_type_manager member debug - * - * @man: Pointer to a memory type manager. - * @printer: Prefix to be used in printout to identify the caller. - * - * This function is called to print out the state of the memory - * type manager to aid debugging of out-of-memory conditions. - * It may not be called from within atomic context. - */ - void (*debug)(struct ttm_mem_type_manager *man, - struct drm_printer *printer); -}; - -/** - * struct ttm_mem_type_manager - * - * @has_type: The memory type has been initialized. - * @use_type: The memory type is enabled. - * @flags: TTM_MEMTYPE_XX flags identifying the traits of the memory - * managed by this memory type. - * @gpu_offset: If used, the GPU offset of the first managed page of - * fixed memory or the first managed location in an aperture. - * @size: Size of the managed region. - * @available_caching: A mask of available caching types, TTM_PL_FLAG_XX, - * as defined in ttm_placement_common.h - * @default_caching: The default caching policy used for a buffer object - * placed in this memory type if the user doesn't provide one. - * @func: structure pointer implementing the range manager. See above - * @priv: Driver private closure for @func. - * @io_reserve_mutex: Mutex optionally protecting shared io_reserve structures - * @use_io_reserve_lru: Use an lru list to try to unreserve io_mem_regions - * reserved by the TTM vm system. - * @io_reserve_lru: Optional lru list for unreserving io mem regions. - * @io_reserve_fastpath: Only use bdev::driver::io_mem_reserve to obtain - * @move_lock: lock for move fence - * static information. bdev::driver::io_mem_free is never used. - * @lru: The lru list for this memory type. - * @move: The fence of the last pipelined move operation. - * - * This structure is used to identify and manage memory types for a device. - * It's set up by the ttm_bo_driver::init_mem_type method. - */ - - - -struct ttm_mem_type_manager { - struct ttm_bo_device *bdev; - - /* - * No protection. Constant from start. - */ - - bool has_type; - bool use_type; - uint32_t flags; - uint64_t gpu_offset; /* GPU address space is independent of CPU word size */ - uint64_t size; - uint32_t available_caching; - uint32_t default_caching; - const struct ttm_mem_type_manager_func *func; - void *priv; - struct mutex io_reserve_mutex; - bool use_io_reserve_lru; - bool io_reserve_fastpath; - spinlock_t move_lock; - - /* - * Protected by @io_reserve_mutex: - */ - - struct list_head io_reserve_lru; - - /* - * Protected by the global->lru_lock. - */ - - struct list_head lru[TTM_MAX_BO_PRIORITY]; - - /* - * Protected by @move_lock. - */ - struct dma_fence *move; -}; - -/** - * struct ttm_bo_driver - * - * @create_ttm_backend_entry: Callback to create a struct ttm_backend. - * @invalidate_caches: Callback to invalidate read caches when a buffer object - * has been evicted. - * @init_mem_type: Callback to initialize a struct ttm_mem_type_manager - * structure. - * @evict_flags: Callback to obtain placement flags when a buffer is evicted. - * @move: Callback for a driver to hook in accelerated functions to - * move a buffer. - * If set to NULL, a potentially slow memcpy() move is used. - */ - -struct ttm_bo_driver { - /** - * ttm_tt_create - * - * @bo: The buffer object to create the ttm for. - * @page_flags: Page flags as identified by TTM_PAGE_FLAG_XX flags. - * - * Create a struct ttm_tt to back data with system memory pages. - * No pages are actually allocated. - * Returns: - * NULL: Out of memory. - */ - struct ttm_tt *(*ttm_tt_create)(struct ttm_buffer_object *bo, - uint32_t page_flags); - - /** - * ttm_tt_populate - * - * @ttm: The struct ttm_tt to contain the backing pages. - * - * Allocate all backing pages - * Returns: - * -ENOMEM: Out of memory. - */ - int (*ttm_tt_populate)(struct ttm_tt *ttm, - struct ttm_operation_ctx *ctx); - - /** - * ttm_tt_unpopulate - * - * @ttm: The struct ttm_tt to contain the backing pages. - * - * Free all backing page - */ - void (*ttm_tt_unpopulate)(struct ttm_tt *ttm); - - /** - * struct ttm_bo_driver member invalidate_caches - * - * @bdev: the buffer object device. - * @flags: new placement of the rebound buffer object. - * - * A previosly evicted buffer has been rebound in a - * potentially new location. Tell the driver that it might - * consider invalidating read (texture) caches on the next command - * submission as a consequence. - */ - - int (*invalidate_caches)(struct ttm_bo_device *bdev, uint32_t flags); - int (*init_mem_type)(struct ttm_bo_device *bdev, uint32_t type, - struct ttm_mem_type_manager *man); - - /** - * struct ttm_bo_driver member eviction_valuable - * - * @bo: the buffer object to be evicted - * @place: placement we need room for - * - * Check with the driver if it is valuable to evict a BO to make room - * for a certain placement. - */ - bool (*eviction_valuable)(struct ttm_buffer_object *bo, - const struct ttm_place *place); - /** - * struct ttm_bo_driver member evict_flags: - * - * @bo: the buffer object to be evicted - * - * Return the bo flags for a buffer which is not mapped to the hardware. - * These will be placed in proposed_flags so that when the move is - * finished, they'll end up in bo->mem.flags - */ - - void (*evict_flags)(struct ttm_buffer_object *bo, - struct ttm_placement *placement); - - /** - * struct ttm_bo_driver member move: - * - * @bo: the buffer to move - * @evict: whether this motion is evicting the buffer from - * the graphics address space - * @ctx: context for this move with parameters - * @new_mem: the new memory region receiving the buffer - * - * Move a buffer between two memory regions. - */ - int (*move)(struct ttm_buffer_object *bo, bool evict, - struct ttm_operation_ctx *ctx, - struct ttm_mem_reg *new_mem); - - /** - * struct ttm_bo_driver_member verify_access - * - * @bo: Pointer to a buffer object. - * @filp: Pointer to a struct file trying to access the object. - * - * Called from the map / write / read methods to verify that the - * caller is permitted to access the buffer object. - * This member may be set to NULL, which will refuse this kind of - * access for all buffer objects. - * This function should return 0 if access is granted, -EPERM otherwise. - */ - int (*verify_access)(struct ttm_buffer_object *bo, - struct file *filp); - - /** - * Hook to notify driver about a driver move so it - * can do tiling things and book-keeping. - * - * @evict: whether this move is evicting the buffer from the graphics - * address space - */ - void (*move_notify)(struct ttm_buffer_object *bo, - bool evict, - struct ttm_mem_reg *new_mem); - /* notify the driver we are taking a fault on this BO - * and have reserved it */ - int (*fault_reserve_notify)(struct ttm_buffer_object *bo); - - /** - * notify the driver that we're about to swap out this bo - */ - void (*swap_notify)(struct ttm_buffer_object *bo); - - /** - * Driver callback on when mapping io memory (for bo_move_memcpy - * for instance). TTM will take care to call io_mem_free whenever - * the mapping is not use anymore. io_mem_reserve & io_mem_free - * are balanced. - */ - int (*io_mem_reserve)(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem); - void (*io_mem_free)(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem); - - /** - * Return the pfn for a given page_offset inside the BO. - * - * @bo: the BO to look up the pfn for - * @page_offset: the offset to look up - */ - unsigned long (*io_mem_pfn)(struct ttm_buffer_object *bo, - unsigned long page_offset); - - /** - * Read/write memory buffers for ptrace access - * - * @bo: the BO to access - * @offset: the offset from the start of the BO - * @buf: pointer to source/destination buffer - * @len: number of bytes to copy - * @write: whether to read (0) from or write (non-0) to BO - * - * If successful, this function should return the number of - * bytes copied, -EIO otherwise. If the number of bytes - * returned is < len, the function may be called again with - * the remainder of the buffer to copy. - */ - int (*access_memory)(struct ttm_buffer_object *bo, unsigned long offset, - void *buf, int len, int write); -}; - -/** - * struct ttm_bo_global - Buffer object driver global data. - * - * @mem_glob: Pointer to a struct ttm_mem_global object for accounting. - * @dummy_read_page: Pointer to a dummy page used for mapping requests - * of unpopulated pages. - * @shrink: A shrink callback object used for buffer object swap. - * @device_list_mutex: Mutex protecting the device list. - * This mutex is held while traversing the device list for pm options. - * @lru_lock: Spinlock protecting the bo subsystem lru lists. - * @device_list: List of buffer object devices. - * @swap_lru: Lru list of buffer objects used for swapping. - */ - -extern struct ttm_bo_global { - - /** - * Constant after init. - */ - - struct kobject kobj; - struct ttm_mem_global *mem_glob; - struct page *dummy_read_page; - spinlock_t lru_lock; - - /** - * Protected by ttm_global_mutex. - */ - unsigned int use_count; - struct list_head device_list; - - /** - * Protected by the lru_lock. - */ - struct list_head swap_lru[TTM_MAX_BO_PRIORITY]; - - /** - * Internal protection. - */ - atomic_t bo_count; -} ttm_bo_glob; - - -#define TTM_NUM_MEM_TYPES 8 - -/** - * struct ttm_bo_device - Buffer object driver device-specific data. - * - * @driver: Pointer to a struct ttm_bo_driver struct setup by the driver. - * @man: An array of mem_type_managers. - * @vma_manager: Address space manager - * lru_lock: Spinlock that protects the buffer+device lru lists and - * ddestroy lists. - * @dev_mapping: A pointer to the struct address_space representing the - * device address space. - * @wq: Work queue structure for the delayed delete workqueue. - * @no_retry: Don't retry allocation if it fails - * - */ - -struct ttm_bo_device { - - /* - * Constant after bo device init / atomic. - */ - struct list_head device_list; - struct ttm_bo_global *glob; - struct ttm_bo_driver *driver; - struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES]; - - /* - * Protected by internal locks. - */ - struct drm_vma_offset_manager vma_manager; - - /* - * Protected by the global:lru lock. - */ - struct list_head ddestroy; - - /* - * Protected by load / firstopen / lastclose /unload sync. - */ - - struct address_space *dev_mapping; - - /* - * Internal protection. - */ - - struct delayed_work wq; - - bool need_dma32; - - bool no_retry; -}; - -/** - * struct ttm_lru_bulk_move_pos - * - * @first: first BO in the bulk move range - * @last: last BO in the bulk move range - * - * Positions for a lru bulk move. - */ -struct ttm_lru_bulk_move_pos { - struct ttm_buffer_object *first; - struct ttm_buffer_object *last; -}; - -/** - * struct ttm_lru_bulk_move - * - * @tt: first/last lru entry for BOs in the TT domain - * @vram: first/last lru entry for BOs in the VRAM domain - * @swap: first/last lru entry for BOs on the swap list - * - * Helper structure for bulk moves on the LRU list. - */ -struct ttm_lru_bulk_move { - struct ttm_lru_bulk_move_pos tt[TTM_MAX_BO_PRIORITY]; - struct ttm_lru_bulk_move_pos vram[TTM_MAX_BO_PRIORITY]; - struct ttm_lru_bulk_move_pos swap[TTM_MAX_BO_PRIORITY]; -}; - -/** - * ttm_flag_masked - * - * @old: Pointer to the result and original value. - * @new: New value of bits. - * @mask: Mask of bits to change. - * - * Convenience function to change a number of bits identified by a mask. - */ - -static inline uint32_t -ttm_flag_masked(uint32_t *old, uint32_t new, uint32_t mask) -{ - *old ^= (*old ^ new) & mask; - return *old; -} - -/* - * ttm_bo.c - */ - -/** - * ttm_mem_reg_is_pci - * - * @bdev: Pointer to a struct ttm_bo_device. - * @mem: A valid struct ttm_mem_reg. - * - * Returns true if the memory described by @mem is PCI memory, - * false otherwise. - */ -bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem); - -/** - * ttm_bo_mem_space - * - * @bo: Pointer to a struct ttm_buffer_object. the data of which - * we want to allocate space for. - * @proposed_placement: Proposed new placement for the buffer object. - * @mem: A struct ttm_mem_reg. - * @interruptible: Sleep interruptible when sliping. - * @no_wait_gpu: Return immediately if the GPU is busy. - * - * Allocate memory space for the buffer object pointed to by @bo, using - * the placement flags in @mem, potentially evicting other idle buffer objects. - * This function may sleep while waiting for space to become available. - * Returns: - * -EBUSY: No space available (only if no_wait == 1). - * -ENOMEM: Could not allocate memory for the buffer object, either due to - * fragmentation or concurrent allocators. - * -ERESTARTSYS: An interruptible sleep was interrupted by a signal. - */ -int ttm_bo_mem_space(struct ttm_buffer_object *bo, - struct ttm_placement *placement, - struct ttm_mem_reg *mem, - struct ttm_operation_ctx *ctx); - -void ttm_bo_mem_put(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem); -void ttm_bo_mem_put_locked(struct ttm_buffer_object *bo, - struct ttm_mem_reg *mem); - -int ttm_bo_device_release(struct ttm_bo_device *bdev); - -/** - * ttm_bo_device_init - * - * @bdev: A pointer to a struct ttm_bo_device to initialize. - * @glob: A pointer to an initialized struct ttm_bo_global. - * @driver: A pointer to a struct ttm_bo_driver set up by the caller. - * @mapping: The address space to use for this bo. - * @file_page_offset: Offset into the device address space that is available - * for buffer data. This ensures compatibility with other users of the - * address space. - * - * Initializes a struct ttm_bo_device: - * Returns: - * !0: Failure. - */ -int ttm_bo_device_init(struct ttm_bo_device *bdev, - struct ttm_bo_driver *driver, - struct address_space *mapping, - uint64_t file_page_offset, bool need_dma32); - -/** - * ttm_bo_unmap_virtual - * - * @bo: tear down the virtual mappings for this BO - */ -void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo); - -/** - * ttm_bo_unmap_virtual - * - * @bo: tear down the virtual mappings for this BO - * - * The caller must take ttm_mem_io_lock before calling this function. - */ -void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo); - -int ttm_mem_io_reserve_vm(struct ttm_buffer_object *bo); -void ttm_mem_io_free_vm(struct ttm_buffer_object *bo); -int ttm_mem_io_lock(struct ttm_mem_type_manager *man, bool interruptible); -void ttm_mem_io_unlock(struct ttm_mem_type_manager *man); - -void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo); -void ttm_bo_add_to_lru(struct ttm_buffer_object *bo); - -/** - * __ttm_bo_reserve: - * - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY. - * @ticket: ticket used to acquire the ww_mutex. - * - * Will not remove reserved buffers from the lru lists. - * Otherwise identical to ttm_bo_reserve. - * - * Returns: - * -EDEADLK: The reservation may cause a deadlock. - * Release all buffer reservations, wait for @bo to become unreserved and - * try again. (only if use_sequence == 1). - * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by - * a signal. Release all buffer reservations and return to user-space. - * -EBUSY: The function needed to sleep, but @no_wait was true - * -EALREADY: Bo already reserved using @ticket. This error code will only - * be returned if @use_ticket is set to true. - */ -static inline int __ttm_bo_reserve(struct ttm_buffer_object *bo, - bool interruptible, bool no_wait, - struct ww_acquire_ctx *ticket) -{ - int ret = 0; - - if (no_wait) { - bool success; - if (WARN_ON(ticket)) - return -EBUSY; - - success = reservation_object_trylock(bo->resv); - return success ? 0 : -EBUSY; - } - - if (interruptible) - ret = reservation_object_lock_interruptible(bo->resv, ticket); - else - ret = reservation_object_lock(bo->resv, ticket); - if (ret == -EINTR) - return -ERESTARTSYS; - return ret; -} - -/** - * ttm_bo_reserve: - * - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY. - * @ticket: ticket used to acquire the ww_mutex. - * - * Locks a buffer object for validation. (Or prevents other processes from - * locking it for validation) and removes it from lru lists, while taking - * a number of measures to prevent deadlocks. - * - * Deadlocks may occur when two processes try to reserve multiple buffers in - * different order, either by will or as a result of a buffer being evicted - * to make room for a buffer already reserved. (Buffers are reserved before - * they are evicted). The following algorithm prevents such deadlocks from - * occurring: - * Processes attempting to reserve multiple buffers other than for eviction, - * (typically execbuf), should first obtain a unique 32-bit - * validation sequence number, - * and call this function with @use_ticket == 1 and @ticket->stamp == the unique - * sequence number. If upon call of this function, the buffer object is already - * reserved, the validation sequence is checked against the validation - * sequence of the process currently reserving the buffer, - * and if the current validation sequence is greater than that of the process - * holding the reservation, the function returns -EDEADLK. Otherwise it sleeps - * waiting for the buffer to become unreserved, after which it retries - * reserving. - * The caller should, when receiving an -EDEADLK error - * release all its buffer reservations, wait for @bo to become unreserved, and - * then rerun the validation with the same validation sequence. This procedure - * will always guarantee that the process with the lowest validation sequence - * will eventually succeed, preventing both deadlocks and starvation. - * - * Returns: - * -EDEADLK: The reservation may cause a deadlock. - * Release all buffer reservations, wait for @bo to become unreserved and - * try again. (only if use_sequence == 1). - * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by - * a signal. Release all buffer reservations and return to user-space. - * -EBUSY: The function needed to sleep, but @no_wait was true - * -EALREADY: Bo already reserved using @ticket. This error code will only - * be returned if @use_ticket is set to true. - */ -static inline int ttm_bo_reserve(struct ttm_buffer_object *bo, - bool interruptible, bool no_wait, - struct ww_acquire_ctx *ticket) -{ - int ret; - - WARN_ON(!kref_read(&bo->kref)); - - ret = __ttm_bo_reserve(bo, interruptible, no_wait, ticket); - if (likely(ret == 0)) - ttm_bo_del_sub_from_lru(bo); - - return ret; -} - -/** - * ttm_bo_reserve_slowpath: - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @sequence: Set (@bo)->sequence to this value after lock - * - * This is called after ttm_bo_reserve returns -EAGAIN and we backed off - * from all our other reservations. Because there are no other reservations - * held by us, this function cannot deadlock any more. - */ -static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, - bool interruptible, - struct ww_acquire_ctx *ticket) -{ - int ret = 0; - - WARN_ON(!kref_read(&bo->kref)); - - if (interruptible) - ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock, - ticket); - else - ww_mutex_lock_slow(&bo->resv->lock, ticket); - - if (likely(ret == 0)) - ttm_bo_del_sub_from_lru(bo); - else if (ret == -EINTR) - ret = -ERESTARTSYS; - - return ret; -} - -/** - * ttm_bo_unreserve - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Unreserve a previous reservation of @bo. - */ -static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) -{ - if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { - spin_lock(&bo->bdev->glob->lru_lock); - ttm_bo_add_to_lru(bo); - spin_unlock(&bo->bdev->glob->lru_lock); - } - reservation_object_unlock(bo->resv); -} - -/* - * ttm_bo_util.c - */ - -int ttm_mem_io_reserve(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem); -void ttm_mem_io_free(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem); -/** - * ttm_bo_move_ttm - * - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @no_wait_gpu: Return immediately if the GPU is busy. - * @new_mem: struct ttm_mem_reg indicating where to move. - * - * Optimized move function for a buffer object with both old and - * new placement backed by a TTM. The function will, if successful, - * free any old aperture space, and set (@new_mem)->mm_node to NULL, - * and update the (@bo)->mem placement flags. If unsuccessful, the old - * data remains untouched, and it's up to the caller to free the - * memory space indicated by @new_mem. - * Returns: - * !0: Failure. - */ - -int ttm_bo_move_ttm(struct ttm_buffer_object *bo, - struct ttm_operation_ctx *ctx, - struct ttm_mem_reg *new_mem); - -/** - * ttm_bo_move_memcpy - * - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @no_wait_gpu: Return immediately if the GPU is busy. - * @new_mem: struct ttm_mem_reg indicating where to move. - * - * Fallback move function for a mappable buffer object in mappable memory. - * The function will, if successful, - * free any old aperture space, and set (@new_mem)->mm_node to NULL, - * and update the (@bo)->mem placement flags. If unsuccessful, the old - * data remains untouched, and it's up to the caller to free the - * memory space indicated by @new_mem. - * Returns: - * !0: Failure. - */ - -int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, - struct ttm_operation_ctx *ctx, - struct ttm_mem_reg *new_mem); - -/** - * ttm_bo_free_old_node - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Utility function to free an old placement after a successful move. - */ -void ttm_bo_free_old_node(struct ttm_buffer_object *bo); - -/** - * ttm_bo_move_accel_cleanup. - * - * @bo: A pointer to a struct ttm_buffer_object. - * @fence: A fence object that signals when moving is complete. - * @evict: This is an evict move. Don't return until the buffer is idle. - * @new_mem: struct ttm_mem_reg indicating where to move. - * - * Accelerated move function to be called when an accelerated move - * has been scheduled. The function will create a new temporary buffer object - * representing the old placement, and put the sync object on both buffer - * objects. After that the newly created buffer object is unref'd to be - * destroyed when the move is complete. This will help pipeline - * buffer moves. - */ -int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, - struct dma_fence *fence, bool evict, - struct ttm_mem_reg *new_mem); - -/** - * ttm_bo_pipeline_move. - * - * @bo: A pointer to a struct ttm_buffer_object. - * @fence: A fence object that signals when moving is complete. - * @evict: This is an evict move. Don't return until the buffer is idle. - * @new_mem: struct ttm_mem_reg indicating where to move. - * - * Function for pipelining accelerated moves. Either free the memory - * immediately or hang it on a temporary buffer object. - */ -int ttm_bo_pipeline_move(struct ttm_buffer_object *bo, - struct dma_fence *fence, bool evict, - struct ttm_mem_reg *new_mem); - -/** - * ttm_bo_pipeline_gutting. - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Pipelined gutting a BO of it's backing store. - */ -int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo); - -/** - * ttm_io_prot - * - * @c_state: Caching state. - * @tmp: Page protection flag for a normal, cached mapping. - * - * Utility function that returns the pgprot_t that should be used for - * setting up a PTE with the caching model indicated by @c_state. - */ -pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp); - -extern const struct ttm_mem_type_manager_func ttm_bo_manager_func; - -#endif diff --git a/include/drm/ttm/ttm_caching.h b/include/drm/ttm/ttm_caching.h new file mode 100644 index 000000000000..a18f43e93aba --- /dev/null +++ b/include/drm/ttm/ttm_caching.h @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + */ + +#ifndef _TTM_CACHING_H_ +#define _TTM_CACHING_H_ + +#include <linux/pgtable.h> + +#define TTM_NUM_CACHING_TYPES 3 + +/** + * enum ttm_caching - CPU caching and BUS snooping behavior. + */ +enum ttm_caching { + /** + * @ttm_uncached: Most defensive option for device mappings, + * don't even allow write combining. + */ + ttm_uncached, + + /** + * @ttm_write_combined: Don't cache read accesses, but allow at least + * writes to be combined. + */ + ttm_write_combined, + + /** + * @ttm_cached: Fully cached like normal system memory, requires that + * devices snoop the CPU cache on accesses. + */ + ttm_cached +}; + +pgprot_t ttm_prot_from_caching(enum ttm_caching caching, pgprot_t tmp); + +#endif diff --git a/include/drm/ttm/ttm_debug.h b/include/drm/ttm/ttm_debug.h deleted file mode 100644 index b5e460fa5086..000000000000 --- a/include/drm/ttm/ttm_debug.h +++ /dev/null @@ -1,31 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2017 Advanced Micro Devices, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Tom St Denis <tom.stdenis@amd.com> - */ -extern void ttm_trace_dma_map(struct device *dev, struct ttm_dma_tt *tt); -extern void ttm_trace_dma_unmap(struct device *dev, struct ttm_dma_tt *tt); diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h new file mode 100644 index 000000000000..5618aef462f2 --- /dev/null +++ b/include/drm/ttm/ttm_device.h @@ -0,0 +1,305 @@ +/* + * Copyright 2020 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + */ + +#ifndef _TTM_DEVICE_H_ +#define _TTM_DEVICE_H_ + +#include <linux/types.h> +#include <linux/workqueue.h> +#include <drm/ttm/ttm_allocation.h> +#include <drm/ttm/ttm_resource.h> +#include <drm/ttm/ttm_pool.h> + +struct ttm_device; +struct ttm_placement; +struct ttm_buffer_object; +struct ttm_operation_ctx; + +/** + * struct ttm_global - Buffer object driver global data. + */ +extern struct ttm_global { + + /** + * @dummy_read_page: Pointer to a dummy page used for mapping requests + * of unpopulated pages. Constant after init. + */ + struct page *dummy_read_page; + + /** + * @device_list: List of buffer object devices. Protected by + * ttm_global_mutex. + */ + struct list_head device_list; + + /** + * @bo_count: Number of buffer objects allocated by devices. + */ + atomic_t bo_count; +} ttm_glob; + +struct ttm_device_funcs { + /** + * ttm_tt_create + * + * @bo: The buffer object to create the ttm for. + * @page_flags: Page flags as identified by TTM_TT_FLAG_XX flags. + * + * Create a struct ttm_tt to back data with system memory pages. + * No pages are actually allocated. + * Returns: + * NULL: Out of memory. + */ + struct ttm_tt *(*ttm_tt_create)(struct ttm_buffer_object *bo, + uint32_t page_flags); + + /** + * ttm_tt_populate + * + * @ttm: The struct ttm_tt to contain the backing pages. + * + * Allocate all backing pages + * Returns: + * -ENOMEM: Out of memory. + */ + int (*ttm_tt_populate)(struct ttm_device *bdev, + struct ttm_tt *ttm, + struct ttm_operation_ctx *ctx); + + /** + * ttm_tt_unpopulate + * + * @ttm: The struct ttm_tt to contain the backing pages. + * + * Free all backing page + */ + void (*ttm_tt_unpopulate)(struct ttm_device *bdev, + struct ttm_tt *ttm); + + /** + * ttm_tt_destroy + * + * @bdev: Pointer to a ttm device + * @ttm: Pointer to a struct ttm_tt. + * + * Destroy the backend. This will be call back from ttm_tt_destroy so + * don't call ttm_tt_destroy from the callback or infinite loop. + */ + void (*ttm_tt_destroy)(struct ttm_device *bdev, struct ttm_tt *ttm); + + /** + * struct ttm_bo_driver member eviction_valuable + * + * @bo: the buffer object to be evicted + * @place: placement we need room for + * + * Check with the driver if it is valuable to evict a BO to make room + * for a certain placement. + */ + bool (*eviction_valuable)(struct ttm_buffer_object *bo, + const struct ttm_place *place); + /** + * struct ttm_bo_driver member evict_flags: + * + * @bo: the buffer object to be evicted + * + * Return the bo flags for a buffer which is not mapped to the hardware. + * These will be placed in proposed_flags so that when the move is + * finished, they'll end up in bo->mem.flags + * This should not cause multihop evictions, and the core will warn + * if one is proposed. + */ + + void (*evict_flags)(struct ttm_buffer_object *bo, + struct ttm_placement *placement); + + /** + * struct ttm_bo_driver member move: + * + * @bo: the buffer to move + * @evict: whether this motion is evicting the buffer from + * the graphics address space + * @ctx: context for this move with parameters + * @new_mem: the new memory region receiving the buffer + * @hop: placement for driver directed intermediate hop + * + * Move a buffer between two memory regions. + * Returns errno -EMULTIHOP if driver requests a hop + */ + int (*move)(struct ttm_buffer_object *bo, bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem, + struct ttm_place *hop); + + /** + * Hook to notify driver about a resource delete. + */ + void (*delete_mem_notify)(struct ttm_buffer_object *bo); + + /** + * notify the driver that we're about to swap out this bo + */ + void (*swap_notify)(struct ttm_buffer_object *bo); + + /** + * Driver callback on when mapping io memory (for bo_move_memcpy + * for instance). TTM will take care to call io_mem_free whenever + * the mapping is not use anymore. io_mem_reserve & io_mem_free + * are balanced. + */ + int (*io_mem_reserve)(struct ttm_device *bdev, + struct ttm_resource *mem); + void (*io_mem_free)(struct ttm_device *bdev, + struct ttm_resource *mem); + + /** + * Return the pfn for a given page_offset inside the BO. + * + * @bo: the BO to look up the pfn for + * @page_offset: the offset to look up + */ + unsigned long (*io_mem_pfn)(struct ttm_buffer_object *bo, + unsigned long page_offset); + + /** + * Read/write memory buffers for ptrace access + * + * @bo: the BO to access + * @offset: the offset from the start of the BO + * @buf: pointer to source/destination buffer + * @len: number of bytes to copy + * @write: whether to read (0) from or write (non-0) to BO + * + * If successful, this function should return the number of + * bytes copied, -EIO otherwise. If the number of bytes + * returned is < len, the function may be called again with + * the remainder of the buffer to copy. + */ + int (*access_memory)(struct ttm_buffer_object *bo, unsigned long offset, + void *buf, int len, int write); + + /** + * Notify the driver that we're about to release a BO + * + * @bo: BO that is about to be released + * + * Gives the driver a chance to do any cleanup, including + * adding fences that may force a delayed delete + */ + void (*release_notify)(struct ttm_buffer_object *bo); +}; + +/** + * struct ttm_device - Buffer object driver device-specific data. + */ +struct ttm_device { + /** + * @device_list: Our entry in the global device list. + * Constant after bo device init + */ + struct list_head device_list; + + /** + * @alloc_flags: TTM_ALLOCATION_* flags. + */ + unsigned int alloc_flags; + + /** + * @funcs: Function table for the device. + * Constant after bo device init + */ + const struct ttm_device_funcs *funcs; + + /** + * @sysman: Resource manager for the system domain. + * Access via ttm_manager_type. + */ + struct ttm_resource_manager sysman; + + /** + * @man_drv: An array of resource_managers, one per resource type. + */ + struct ttm_resource_manager *man_drv[TTM_NUM_MEM_TYPES]; + + /** + * @vma_manager: Address space manager for finding BOs to mmap. + */ + struct drm_vma_offset_manager *vma_manager; + + /** + * @pool: page pool for the device. + */ + struct ttm_pool pool; + + /** + * @lru_lock: Protection for the per manager LRU and ddestroy lists. + */ + spinlock_t lru_lock; + + /** + * @unevictable: Buffer objects which are pinned or swapped and as such + * not on an LRU list. + */ + struct list_head unevictable; + + /** + * @dev_mapping: A pointer to the struct address_space for invalidating + * CPU mappings on buffer move. Protected by load/unload sync. + */ + struct address_space *dev_mapping; + + /** + * @wq: Work queue structure for the delayed delete workqueue. + */ + struct workqueue_struct *wq; +}; + +int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags); +int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, + gfp_t gfp_flags); +int ttm_device_prepare_hibernation(struct ttm_device *bdev); + +static inline struct ttm_resource_manager * +ttm_manager_type(struct ttm_device *bdev, int mem_type) +{ + BUILD_BUG_ON(__builtin_constant_p(mem_type) + && mem_type >= TTM_NUM_MEM_TYPES); + return bdev->man_drv[mem_type]; +} + +static inline void ttm_set_driver_manager(struct ttm_device *bdev, int type, + struct ttm_resource_manager *manager) +{ + BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); + bdev->man_drv[type] = manager; +} + +int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *funcs, + struct device *dev, struct address_space *mapping, + struct drm_vma_offset_manager *vma_manager, + unsigned int alloc_flags); +void ttm_device_fini(struct ttm_device *bdev); +void ttm_device_clear_dma_mappings(struct ttm_device *bdev); + +#endif diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h index 621615fa7728..fac1e3e57ebd 100644 --- a/include/drm/ttm/ttm_execbuf_util.h +++ b/include/drm/ttm/ttm_execbuf_util.h @@ -33,7 +33,9 @@ #include <linux/list.h> -#include "ttm_bo_api.h" +struct ww_acquire_ctx; +struct dma_fence; +struct ttm_buffer_object; /** * struct ttm_validate_buffer @@ -50,7 +52,7 @@ struct ttm_validate_buffer { }; /** - * function ttm_eu_backoff_reservation + * ttm_eu_backoff_reservation * * @ticket: ww_acquire_ctx from reserve call * @list: thread private list of ttm_validate_buffer structs. @@ -58,12 +60,11 @@ struct ttm_validate_buffer { * Undoes all buffer validation reservations for bos pointed to by * the list entries. */ - -extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, - struct list_head *list); +void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, + struct list_head *list); /** - * function ttm_eu_reserve_buffers + * ttm_eu_reserve_buffers * * @ticket: [out] ww_acquire_ctx filled in by call, or NULL if only * non-blocking reserves should be tried. @@ -95,13 +96,12 @@ extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, * ttm_eu_fence_buffer_objects() when command submission is complete or * has failed. */ - -extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, - struct list_head *list, bool intr, - struct list_head *dups); +int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, + struct list_head *list, bool intr, + struct list_head *dups); /** - * function ttm_eu_fence_buffer_objects. + * ttm_eu_fence_buffer_objects * * @ticket: ww_acquire_ctx from reserve call * @list: thread private list of ttm_validate_buffer structs. @@ -112,9 +112,8 @@ extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, * It also unreserves all buffers, putting them on lru lists. * */ - -extern void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket, - struct list_head *list, - struct dma_fence *fence); +void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket, + struct list_head *list, + struct dma_fence *fence); #endif diff --git a/include/drm/ttm/ttm_kmap_iter.h b/include/drm/ttm/ttm_kmap_iter.h new file mode 100644 index 000000000000..fe72631a6e93 --- /dev/null +++ b/include/drm/ttm/ttm_kmap_iter.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2021 Intel Corporation + */ +#ifndef __TTM_KMAP_ITER_H__ +#define __TTM_KMAP_ITER_H__ + +#include <linux/types.h> + +struct ttm_kmap_iter; +struct iosys_map; + +/** + * struct ttm_kmap_iter_ops - Ops structure for a struct + * ttm_kmap_iter. + * @maps_tt: Whether the iterator maps TT memory directly, as opposed + * mapping a TT through an aperture. Both these modes have + * struct ttm_resource_manager::use_tt set, but the latter typically + * returns is_iomem == true from ttm_mem_io_reserve. + */ +struct ttm_kmap_iter_ops { + /** + * @map_local: Map a PAGE_SIZE part of the resource using + * kmap_local semantics. + * @res_iter: Pointer to the struct ttm_kmap_iter representing + * the resource. + * @dmap: The struct iosys_map holding the virtual address after + * the operation. + * @i: The location within the resource to map. PAGE_SIZE granularity. + */ + void (*map_local)(struct ttm_kmap_iter *res_iter, + struct iosys_map *dmap, pgoff_t i); + /** + * @unmap_local: Unmap a PAGE_SIZE part of the resource previously + * mapped using kmap_local. + * @res_iter: Pointer to the struct ttm_kmap_iter representing + * the resource. + * @dmap: The struct iosys_map holding the virtual address after + * the operation. + */ + void (*unmap_local)(struct ttm_kmap_iter *res_iter, + struct iosys_map *dmap); + bool maps_tt; +}; + +/** + * struct ttm_kmap_iter - Iterator for kmap_local type operations on a + * resource. + * @ops: Pointer to the operations struct. + * + * This struct is intended to be embedded in a resource-specific specialization + * implementing operations for the resource. + * + * Nothing stops us from extending the operations to vmap, vmap_pfn etc, + * replacing some or parts of the ttm_bo_util. cpu-map functionality. + */ +struct ttm_kmap_iter { + const struct ttm_kmap_iter_ops *ops; +}; + +#endif /* __TTM_KMAP_ITER_H__ */ diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h deleted file mode 100644 index 3ff48a0a2d7b..000000000000 --- a/include/drm/ttm/ttm_memory.h +++ /dev/null @@ -1,98 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ - -#ifndef TTM_MEMORY_H -#define TTM_MEMORY_H - -#include <linux/workqueue.h> -#include <linux/spinlock.h> -#include <linux/bug.h> -#include <linux/wait.h> -#include <linux/errno.h> -#include <linux/kobject.h> -#include <linux/mm.h> -#include "ttm_bo_api.h" - -/** - * struct ttm_mem_global - Global memory accounting structure. - * - * @shrink: A single callback to shrink TTM memory usage. Extend this - * to a linked list to be able to handle multiple callbacks when needed. - * @swap_queue: A workqueue to handle shrinking in low memory situations. We - * need a separate workqueue since it will spend a lot of time waiting - * for the GPU, and this will otherwise block other workqueue tasks(?) - * At this point we use only a single-threaded workqueue. - * @work: The workqueue callback for the shrink queue. - * @lock: Lock to protect the @shrink - and the memory accounting members, - * that is, essentially the whole structure with some exceptions. - * @lower_mem_limit: include lower limit of swap space and lower limit of - * system memory. - * @zones: Array of pointers to accounting zones. - * @num_zones: Number of populated entries in the @zones array. - * @zone_kernel: Pointer to the kernel zone. - * @zone_highmem: Pointer to the highmem zone if there is one. - * @zone_dma32: Pointer to the dma32 zone if there is one. - * - * Note that this structure is not per device. It should be global for all - * graphics devices. - */ - -#define TTM_MEM_MAX_ZONES 2 -struct ttm_mem_zone; -extern struct ttm_mem_global { - struct kobject kobj; - struct ttm_bo_global *bo_glob; - struct workqueue_struct *swap_queue; - struct work_struct work; - spinlock_t lock; - uint64_t lower_mem_limit; - struct ttm_mem_zone *zones[TTM_MEM_MAX_ZONES]; - unsigned int num_zones; - struct ttm_mem_zone *zone_kernel; -#ifdef CONFIG_HIGHMEM - struct ttm_mem_zone *zone_highmem; -#else - struct ttm_mem_zone *zone_dma32; -#endif -} ttm_mem_glob; - -extern int ttm_mem_global_init(struct ttm_mem_global *glob); -extern void ttm_mem_global_release(struct ttm_mem_global *glob); -extern int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, - struct ttm_operation_ctx *ctx); -extern void ttm_mem_global_free(struct ttm_mem_global *glob, - uint64_t amount); -extern int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, - struct page *page, uint64_t size, - struct ttm_operation_ctx *ctx); -extern void ttm_mem_global_free_page(struct ttm_mem_global *glob, - struct page *page, uint64_t size); -extern size_t ttm_round_pot(size_t size); -extern uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob); -extern bool ttm_check_under_lowerlimit(struct ttm_mem_global *glob, - uint64_t num_pages, struct ttm_operation_ctx *ctx); -#endif diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h deleted file mode 100644 index 4d9b019d253c..000000000000 --- a/include/drm/ttm/ttm_page_alloc.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) Red Hat Inc. - - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: Dave Airlie <airlied@redhat.com> - * Jerome Glisse <jglisse@redhat.com> - */ -#ifndef TTM_PAGE_ALLOC -#define TTM_PAGE_ALLOC - -#include <drm/ttm/ttm_bo_driver.h> -#include <drm/ttm/ttm_memory.h> - -struct device; - -/** - * Initialize pool allocator. - */ -int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages); -/** - * Free pool allocator. - */ -void ttm_page_alloc_fini(void); - -/** - * ttm_pool_populate: - * - * @ttm: The struct ttm_tt to contain the backing pages. - * - * Add backing pages to all of @ttm - */ -int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); - -/** - * ttm_pool_unpopulate: - * - * @ttm: The struct ttm_tt which to free backing pages. - * - * Free all pages of @ttm - */ -void ttm_pool_unpopulate(struct ttm_tt *ttm); - -/** - * Populates and DMA maps pages to fullfil a ttm_dma_populate() request - */ -int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt, - struct ttm_operation_ctx *ctx); - -/** - * Unpopulates and DMA unmaps pages as part of a - * ttm_dma_unpopulate() request */ -void ttm_unmap_and_unpopulate_pages(struct device *dev, struct ttm_dma_tt *tt); - -/** - * Output the state of pools to debugfs file - */ -int ttm_page_alloc_debugfs(struct seq_file *m, void *data); - -#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU) -/** - * Initialize pool allocator. - */ -int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages); - -/** - * Free pool allocator. - */ -void ttm_dma_page_alloc_fini(void); - -/** - * Output the state of pools to debugfs file - */ -int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data); - -int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev, - struct ttm_operation_ctx *ctx); -void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev); - -#else -static inline int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, - unsigned max_pages) -{ - return -ENODEV; -} - -static inline void ttm_dma_page_alloc_fini(void) { return; } - -static inline int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) -{ - return 0; -} -static inline int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, - struct device *dev, - struct ttm_operation_ctx *ctx) -{ - return -ENOMEM; -} -static inline void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, - struct device *dev) -{ -} -#endif - -#endif diff --git a/include/drm/ttm/ttm_placement.h b/include/drm/ttm/ttm_placement.h index e88a8e39767b..b510a4812609 100644 --- a/include/drm/ttm/ttm_placement.h +++ b/include/drm/ttm/ttm_placement.h @@ -35,6 +35,17 @@ /* * Memory regions for data placement. + * + * Buffers placed in TTM_PL_SYSTEM are considered under TTMs control and can + * be swapped out whenever TTMs thinks it is a good idea. + * In cases where drivers would like to use TTM_PL_SYSTEM as a valid + * placement they need to be able to handle the issues that arise due to the + * above manually. + * + * For BO's which reside in system memory but for which the accelerator + * requires direct access (i.e. their usage needs to be synchronized + * between the CPU and accelerator via fences) a new, driver private + * placement that can handle such scenarios is a good idea. */ #define TTM_PL_SYSTEM 0 @@ -42,42 +53,29 @@ #define TTM_PL_VRAM 2 #define TTM_PL_PRIV 3 -#define TTM_PL_FLAG_SYSTEM (1 << TTM_PL_SYSTEM) -#define TTM_PL_FLAG_TT (1 << TTM_PL_TT) -#define TTM_PL_FLAG_VRAM (1 << TTM_PL_VRAM) -#define TTM_PL_FLAG_PRIV (1 << TTM_PL_PRIV) -#define TTM_PL_MASK_MEM 0x0000FFFF - /* - * Other flags that affects data placement. - * TTM_PL_FLAG_CACHED indicates cache-coherent mappings - * if available. - * TTM_PL_FLAG_SHARED means that another application may - * reference the buffer. - * TTM_PL_FLAG_NO_EVICT means that the buffer may never - * be evicted to make room for other buffers. * TTM_PL_FLAG_TOPDOWN requests to be placed from the * top of the memory area, instead of the bottom. */ -#define TTM_PL_FLAG_CACHED (1 << 16) -#define TTM_PL_FLAG_UNCACHED (1 << 17) -#define TTM_PL_FLAG_WC (1 << 18) -#define TTM_PL_FLAG_CONTIGUOUS (1 << 19) -#define TTM_PL_FLAG_NO_EVICT (1 << 21) -#define TTM_PL_FLAG_TOPDOWN (1 << 22) +#define TTM_PL_FLAG_CONTIGUOUS (1 << 0) +#define TTM_PL_FLAG_TOPDOWN (1 << 1) + +/* For multihop handling */ +#define TTM_PL_FLAG_TEMPORARY (1 << 2) -#define TTM_PL_MASK_CACHING (TTM_PL_FLAG_CACHED | \ - TTM_PL_FLAG_UNCACHED | \ - TTM_PL_FLAG_WC) +/* Placement is never used during eviction */ +#define TTM_PL_FLAG_DESIRED (1 << 3) -#define TTM_PL_MASK_MEMTYPE (TTM_PL_MASK_MEM | TTM_PL_MASK_CACHING) +/* Placement is only used during eviction */ +#define TTM_PL_FLAG_FALLBACK (1 << 4) /** * struct ttm_place * * @fpfn: first valid page frame number to put the object * @lpfn: last valid page frame number to put the object + * @mem_type: One of TTM_PL_* where the resource should be allocated from. * @flags: memory domain and caching flags for the object * * Structure indicating a possible place to put an object. @@ -85,6 +83,7 @@ struct ttm_place { unsigned fpfn; unsigned lpfn; + uint32_t mem_type; uint32_t flags; }; @@ -93,16 +92,12 @@ struct ttm_place { * * @num_placement: number of preferred placements * @placement: preferred placements - * @num_busy_placement: number of preferred placements when need to evict buffer - * @busy_placement: preferred placements when need to evict buffer * * Structure indicating the placement you request for an object. */ struct ttm_placement { unsigned num_placement; const struct ttm_place *placement; - unsigned num_busy_placement; - const struct ttm_place *busy_placement; }; #endif diff --git a/include/drm/ttm/ttm_pool.h b/include/drm/ttm/ttm_pool.h new file mode 100644 index 000000000000..233581670e78 --- /dev/null +++ b/include/drm/ttm/ttm_pool.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2020 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + */ + +#ifndef _TTM_PAGE_POOL_H_ +#define _TTM_PAGE_POOL_H_ + +#include <linux/mmzone.h> +#include <linux/llist.h> +#include <linux/spinlock.h> +#include <drm/ttm/ttm_caching.h> + +struct device; +struct seq_file; +struct ttm_backup_flags; +struct ttm_operation_ctx; +struct ttm_pool; +struct ttm_tt; + +/** + * struct ttm_pool_type - Pool for a certain memory type + * + * @pool: the pool we belong to, might be NULL for the global ones + * @order: the allocation order our pages have + * @caching: the caching type our pages have + * @shrinker_list: our place on the global shrinker list + * @lock: protection of the page list + * @pages: the list of pages in the pool + */ +struct ttm_pool_type { + struct ttm_pool *pool; + unsigned int order; + enum ttm_caching caching; + + struct list_head shrinker_list; + + spinlock_t lock; + struct list_head pages; +}; + +/** + * struct ttm_pool - Pool for all caching and orders + * + * @dev: the device we allocate pages for + * @nid: which numa node to use + * @alloc_flags: TTM_ALLOCATION_POOL_* flags + * @caching: pools for each caching/order + */ +struct ttm_pool { + struct device *dev; + int nid; + + unsigned int alloc_flags; + + struct { + struct ttm_pool_type orders[NR_PAGE_ORDERS]; + } caching[TTM_NUM_CACHING_TYPES]; +}; + +int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt, + struct ttm_operation_ctx *ctx); +void ttm_pool_free(struct ttm_pool *pool, struct ttm_tt *tt); + +void ttm_pool_init(struct ttm_pool *pool, struct device *dev, + int nid, unsigned int alloc_flags); +void ttm_pool_fini(struct ttm_pool *pool); + +int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m); + +void ttm_pool_drop_backed_up(struct ttm_tt *tt); + +long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *ttm, + const struct ttm_backup_flags *flags); +int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, + const struct ttm_operation_ctx *ctx); + +int ttm_pool_mgr_init(unsigned long num_pages); +void ttm_pool_mgr_fini(void); + +#endif diff --git a/include/drm/ttm/ttm_range_manager.h b/include/drm/ttm/ttm_range_manager.h new file mode 100644 index 000000000000..7963b957e9ef --- /dev/null +++ b/include/drm/ttm/ttm_range_manager.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +#ifndef _TTM_RANGE_MANAGER_H_ +#define _TTM_RANGE_MANAGER_H_ + +#include <drm/ttm/ttm_resource.h> +#include <drm/ttm/ttm_device.h> +#include <drm/drm_mm.h> + +/** + * struct ttm_range_mgr_node + * + * @base: base clase we extend + * @mm_nodes: MM nodes, usually 1 + * + * Extending the ttm_resource object to manage an address space allocation with + * one or more drm_mm_nodes. + */ +struct ttm_range_mgr_node { + struct ttm_resource base; + struct drm_mm_node mm_nodes[]; +}; + +/** + * to_ttm_range_mgr_node + * + * @res: the resource to upcast + * + * Upcast the ttm_resource object into a ttm_range_mgr_node object. + */ +static inline struct ttm_range_mgr_node * +to_ttm_range_mgr_node(struct ttm_resource *res) +{ + return container_of(res, struct ttm_range_mgr_node, base); +} + +int ttm_range_man_init_nocheck(struct ttm_device *bdev, + unsigned type, bool use_tt, + unsigned long p_size); +int ttm_range_man_fini_nocheck(struct ttm_device *bdev, + unsigned type); +static __always_inline int ttm_range_man_init(struct ttm_device *bdev, + unsigned int type, bool use_tt, + unsigned long p_size) +{ + BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); + return ttm_range_man_init_nocheck(bdev, type, use_tt, p_size); +} + +static __always_inline int ttm_range_man_fini(struct ttm_device *bdev, + unsigned int type) +{ + BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); + return ttm_range_man_fini_nocheck(bdev, type); +} +#endif diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h new file mode 100644 index 000000000000..33e80f30b8b8 --- /dev/null +++ b/include/drm/ttm/ttm_resource.h @@ -0,0 +1,524 @@ +/* + * Copyright 2020 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + */ + +#ifndef _TTM_RESOURCE_H_ +#define _TTM_RESOURCE_H_ + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/iosys-map.h> +#include <linux/dma-fence.h> + +#include <drm/ttm/ttm_caching.h> +#include <drm/ttm/ttm_kmap_iter.h> + +#define TTM_MAX_BO_PRIORITY 4U +#define TTM_NUM_MEM_TYPES 9 + +struct dentry; +struct dmem_cgroup_device; +struct drm_printer; +struct ttm_device; +struct ttm_resource_manager; +struct ttm_resource; +struct ttm_place; +struct ttm_buffer_object; +struct ttm_placement; +struct iosys_map; +struct io_mapping; +struct sg_table; +struct scatterlist; + +/** + * define TTM_NUM_MOVE_FENCES - How many entities can be used for evictions + * + * Pipelined evictions can be spread on multiple entities. This + * is the max number of entities that can be used by the driver + * for that purpose. + */ +#define TTM_NUM_MOVE_FENCES 8 + +/** + * enum ttm_lru_item_type - enumerate ttm_lru_item subclasses + */ +enum ttm_lru_item_type { + /** @TTM_LRU_RESOURCE: The resource subclass */ + TTM_LRU_RESOURCE, + /** @TTM_LRU_HITCH: The iterator hitch subclass */ + TTM_LRU_HITCH +}; + +/** + * struct ttm_lru_item - The TTM lru list node base class + * @link: The list link + * @type: The subclass type + */ +struct ttm_lru_item { + struct list_head link; + enum ttm_lru_item_type type; +}; + +/** + * ttm_lru_item_init() - initialize a struct ttm_lru_item + * @item: The item to initialize + * @type: The subclass type + */ +static inline void ttm_lru_item_init(struct ttm_lru_item *item, + enum ttm_lru_item_type type) +{ + item->type = type; + INIT_LIST_HEAD(&item->link); +} + +static inline bool ttm_lru_item_is_res(const struct ttm_lru_item *item) +{ + return item->type == TTM_LRU_RESOURCE; +} + +struct ttm_resource_manager_func { + /** + * struct ttm_resource_manager_func member alloc + * + * @man: Pointer to a memory type manager. + * @bo: Pointer to the buffer object we're allocating space for. + * @place: Placement details. + * @res: Resulting pointer to the ttm_resource. + * + * This function should allocate space in the memory type managed + * by @man. Placement details if applicable are given by @place. If + * successful, a filled in ttm_resource object should be returned in + * @res. @res::start should be set to a value identifying the beginning + * of the range allocated, and the function should return zero. + * If the manager can't fulfill the request -ENOSPC should be returned. + * If a system error occurred, preventing the request to be fulfilled, + * the function should return a negative error code. + * + * This function may not be called from within atomic context and needs + * to take care of its own locking to protect any data structures + * managing the space. + */ + int (*alloc)(struct ttm_resource_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_resource **res); + + /** + * struct ttm_resource_manager_func member free + * + * @man: Pointer to a memory type manager. + * @res: Pointer to a struct ttm_resource to be freed. + * + * This function frees memory type resources previously allocated. + * May not be called from within atomic context. + */ + void (*free)(struct ttm_resource_manager *man, + struct ttm_resource *res); + + /** + * struct ttm_resource_manager_func member intersects + * + * @man: Pointer to a memory type manager. + * @res: Pointer to a struct ttm_resource to be checked. + * @place: Placement to check against. + * @size: Size of the check. + * + * Test if @res intersects with @place + @size. Used to judge if + * evictions are valueable or not. + */ + bool (*intersects)(struct ttm_resource_manager *man, + struct ttm_resource *res, + const struct ttm_place *place, + size_t size); + + /** + * struct ttm_resource_manager_func member compatible + * + * @man: Pointer to a memory type manager. + * @res: Pointer to a struct ttm_resource to be checked. + * @place: Placement to check against. + * @size: Size of the check. + * + * Test if @res compatible with @place + @size. Used to check of + * the need to move the backing store or not. + */ + bool (*compatible)(struct ttm_resource_manager *man, + struct ttm_resource *res, + const struct ttm_place *place, + size_t size); + + /** + * struct ttm_resource_manager_func member debug + * + * @man: Pointer to a memory type manager. + * @printer: Prefix to be used in printout to identify the caller. + * + * This function is called to print out the state of the memory + * type manager to aid debugging of out-of-memory conditions. + * It may not be called from within atomic context. + */ + void (*debug)(struct ttm_resource_manager *man, + struct drm_printer *printer); +}; + +/** + * struct ttm_resource_manager + * + * @use_type: The memory type is enabled. + * @use_tt: If a TT object should be used for the backing store. + * @size: Size of the managed region. + * @bdev: ttm device this manager belongs to + * @func: structure pointer implementing the range manager. See above + * @eviction_lock: lock for eviction fences + * @eviction_fences: The fences of the last pipelined move operation. + * @lru: The lru list for this memory type. + * + * This structure is used to identify and manage memory types for a device. + */ +struct ttm_resource_manager { + /* + * No protection. Constant from start. + */ + bool use_type; + bool use_tt; + struct ttm_device *bdev; + uint64_t size; + const struct ttm_resource_manager_func *func; + + /* This is very similar to a dma_resv object, but locking rules make + * it difficult to use one in this context. + */ + spinlock_t eviction_lock; + struct dma_fence *eviction_fences[TTM_NUM_MOVE_FENCES]; + + /* + * Protected by the bdev->lru_lock. + */ + struct list_head lru[TTM_MAX_BO_PRIORITY]; + + /** + * @usage: How much of the resources are used, protected by the + * bdev->lru_lock. + */ + uint64_t usage; + + /** + * @cg: &dmem_cgroup_region used for memory accounting, if not NULL. + */ + struct dmem_cgroup_region *cg; +}; + +/** + * struct ttm_bus_placement + * + * @addr: mapped virtual address + * @offset: physical addr + * @is_iomem: is this io memory ? + * @caching: See enum ttm_caching + * + * Structure indicating the bus placement of an object. + */ +struct ttm_bus_placement { + void *addr; + phys_addr_t offset; + bool is_iomem; + enum ttm_caching caching; +}; + +/** + * struct ttm_resource + * + * @start: Start of the allocation. + * @size: Actual size of resource in bytes. + * @mem_type: Resource type of the allocation. + * @placement: Placement flags. + * @bus: Placement on io bus accessible to the CPU + * @bo: weak reference to the BO, protected by ttm_device::lru_lock + * @css: cgroup state this resource is charged to + * + * Structure indicating the placement and space resources used by a + * buffer object. + */ +struct ttm_resource { + unsigned long start; + size_t size; + uint32_t mem_type; + uint32_t placement; + struct ttm_bus_placement bus; + struct ttm_buffer_object *bo; + + struct dmem_cgroup_pool_state *css; + + /** + * @lru: Least recently used list, see &ttm_resource_manager.lru + */ + struct ttm_lru_item lru; +}; + +/** + * ttm_lru_item_to_res() - Downcast a struct ttm_lru_item to a struct ttm_resource + * @item: The struct ttm_lru_item to downcast + * + * Return: Pointer to the embedding struct ttm_resource + */ +static inline struct ttm_resource * +ttm_lru_item_to_res(struct ttm_lru_item *item) +{ + return container_of(item, struct ttm_resource, lru); +} + +/** + * struct ttm_lru_bulk_move_pos + * + * @first: first res in the bulk move range + * @last: last res in the bulk move range + * + * Range of resources for a lru bulk move. + */ +struct ttm_lru_bulk_move_pos { + struct ttm_resource *first; + struct ttm_resource *last; +}; + +/** + * struct ttm_lru_bulk_move + * @pos: first/last lru entry for resources in the each domain/priority + * @cursor_list: The list of cursors currently traversing any of + * the sublists of @pos. Protected by the ttm device's lru_lock. + * + * Container for the current bulk move state. Should be used with + * ttm_lru_bulk_move_init() and ttm_bo_set_bulk_move(). + * All BOs in a bulk_move structure need to share the same reservation object to + * ensure that the bulk as a whole is locked for eviction even if only one BO of + * the bulk is evicted. + */ +struct ttm_lru_bulk_move { + struct ttm_lru_bulk_move_pos pos[TTM_NUM_MEM_TYPES][TTM_MAX_BO_PRIORITY]; + struct list_head cursor_list; +}; + +/** + * struct ttm_resource_cursor + * @man: The resource manager currently being iterated over + * @hitch: A hitch list node inserted before the next resource + * to iterate over. + * @bulk_link: A list link for the list of cursors traversing the + * bulk sublist of @bulk. Protected by the ttm device's lru_lock. + * @bulk: Pointer to struct ttm_lru_bulk_move whose subrange @hitch is + * inserted to. NULL if none. Never dereference this pointer since + * the struct ttm_lru_bulk_move object pointed to might have been + * freed. The pointer is only for comparison. + * @mem_type: The memory type of the LRU list being traversed. + * This field is valid iff @bulk != NULL. + * @priority: the current priority + * + * Cursor to iterate over the resources in a manager. + */ +struct ttm_resource_cursor { + struct ttm_resource_manager *man; + struct ttm_lru_item hitch; + struct list_head bulk_link; + struct ttm_lru_bulk_move *bulk; + unsigned int mem_type; + unsigned int priority; +}; + +void ttm_resource_cursor_init(struct ttm_resource_cursor *cursor, + struct ttm_resource_manager *man); + +void ttm_resource_cursor_fini(struct ttm_resource_cursor *cursor); + +/** + * struct ttm_kmap_iter_iomap - Specialization for a struct io_mapping + + * struct sg_table backed struct ttm_resource. + * @base: Embedded struct ttm_kmap_iter providing the usage interface. + * @iomap: struct io_mapping representing the underlying linear io_memory. + * @st: sg_table into @iomap, representing the memory of the struct ttm_resource. + * @start: Offset that needs to be subtracted from @st to make + * sg_dma_address(st->sgl) - @start == 0 for @iomap start. + * @cache: Scatterlist traversal cache for fast lookups. + * @cache.sg: Pointer to the currently cached scatterlist segment. + * @cache.i: First index of @sg. PAGE_SIZE granularity. + * @cache.end: Last index + 1 of @sg. PAGE_SIZE granularity. + * @cache.offs: First offset into @iomap of @sg. PAGE_SIZE granularity. + */ +struct ttm_kmap_iter_iomap { + struct ttm_kmap_iter base; + struct io_mapping *iomap; + struct sg_table *st; + resource_size_t start; + struct { + struct scatterlist *sg; + pgoff_t i; + pgoff_t end; + pgoff_t offs; + } cache; +}; + +/** + * struct ttm_kmap_iter_linear_io - Iterator specialization for linear io + * @base: The base iterator + * @dmap: Points to the starting address of the region + * @needs_unmap: Whether we need to unmap on fini + */ +struct ttm_kmap_iter_linear_io { + struct ttm_kmap_iter base; + struct iosys_map dmap; + bool needs_unmap; +}; + +/** + * ttm_resource_manager_set_used + * + * @man: A memory manager object. + * @used: usage state to set. + * + * Set the manager in use flag. If disabled the manager is no longer + * used for object placement. + */ +static inline void +ttm_resource_manager_set_used(struct ttm_resource_manager *man, bool used) +{ + int i; + + for (i = 0; i < TTM_MAX_BO_PRIORITY; i++) + WARN_ON(!list_empty(&man->lru[i])); + man->use_type = used; +} + +/** + * ttm_resource_manager_used + * + * @man: Manager to get used state for + * + * Get the in use flag for a manager. + * Returns: + * true is used, false if not. + */ +static inline bool ttm_resource_manager_used(struct ttm_resource_manager *man) +{ + return man->use_type; +} + +/** + * ttm_resource_manager_cleanup + * + * @man: A memory manager object. + * + * Cleanup the move fences from the memory manager object. + */ +static inline void +ttm_resource_manager_cleanup(struct ttm_resource_manager *man) +{ + int i; + + for (i = 0; i < TTM_NUM_MOVE_FENCES; i++) { + dma_fence_put(man->eviction_fences[i]); + man->eviction_fences[i] = NULL; + } +} + +void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk); +void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk); +void ttm_lru_bulk_move_fini(struct ttm_device *bdev, + struct ttm_lru_bulk_move *bulk); + +void ttm_resource_add_bulk_move(struct ttm_resource *res, + struct ttm_buffer_object *bo); +void ttm_resource_del_bulk_move(struct ttm_resource *res, + struct ttm_buffer_object *bo); +void ttm_resource_move_to_lru_tail(struct ttm_resource *res); + +void ttm_resource_init(struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_resource *res); +void ttm_resource_fini(struct ttm_resource_manager *man, + struct ttm_resource *res); + +int ttm_resource_alloc(struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_resource **res, + struct dmem_cgroup_pool_state **ret_limit_pool); +void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res); +bool ttm_resource_intersects(struct ttm_device *bdev, + struct ttm_resource *res, + const struct ttm_place *place, + size_t size); +bool ttm_resource_compatible(struct ttm_resource *res, + struct ttm_placement *placement, + bool evicting); +void ttm_resource_set_bo(struct ttm_resource *res, + struct ttm_buffer_object *bo); + +void ttm_resource_manager_init(struct ttm_resource_manager *man, + struct ttm_device *bdev, + uint64_t size); + +int ttm_resource_manager_evict_all(struct ttm_device *bdev, + struct ttm_resource_manager *man); + +uint64_t ttm_resource_manager_usage(struct ttm_resource_manager *man); +void ttm_resource_manager_debug(struct ttm_resource_manager *man, + struct drm_printer *p); + +struct ttm_resource * +ttm_resource_manager_first(struct ttm_resource_cursor *cursor); +struct ttm_resource * +ttm_resource_manager_next(struct ttm_resource_cursor *cursor); + +struct ttm_resource * +ttm_lru_first_res_or_null(struct list_head *head); + +/** + * ttm_resource_manager_for_each_res - iterate over all resources + * @cursor: struct ttm_resource_cursor for the current position + * @res: the current resource + * + * Iterate over all the evictable resources in a resource manager. + */ +#define ttm_resource_manager_for_each_res(cursor, res) \ + for (res = ttm_resource_manager_first(cursor); res; \ + res = ttm_resource_manager_next(cursor)) + +struct ttm_kmap_iter * +ttm_kmap_iter_iomap_init(struct ttm_kmap_iter_iomap *iter_io, + struct io_mapping *iomap, + struct sg_table *st, + resource_size_t start); + +struct ttm_kmap_iter_linear_io; + +struct ttm_kmap_iter * +ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, + struct ttm_device *bdev, + struct ttm_resource *mem); + +void ttm_kmap_iter_linear_io_fini(struct ttm_kmap_iter_linear_io *iter_io, + struct ttm_device *bdev, + struct ttm_resource *mem); + +void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man, + struct dentry * parent, + const char *name); +#endif diff --git a/include/drm/ttm/ttm_set_memory.h b/include/drm/ttm/ttm_set_memory.h deleted file mode 100644 index 7c492b49e38c..000000000000 --- a/include/drm/ttm/ttm_set_memory.h +++ /dev/null @@ -1,150 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2018 Advanced Micro Devices, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Huang Rui <ray.huang@amd.com> - */ - -#ifndef TTM_SET_MEMORY -#define TTM_SET_MEMORY - -#include <linux/mm.h> - -#ifdef CONFIG_X86 - -#include <asm/set_memory.h> - -static inline int ttm_set_pages_array_wb(struct page **pages, int addrinarray) -{ - return set_pages_array_wb(pages, addrinarray); -} - -static inline int ttm_set_pages_array_wc(struct page **pages, int addrinarray) -{ - return set_pages_array_wc(pages, addrinarray); -} - -static inline int ttm_set_pages_array_uc(struct page **pages, int addrinarray) -{ - return set_pages_array_uc(pages, addrinarray); -} - -static inline int ttm_set_pages_wb(struct page *page, int numpages) -{ - return set_pages_wb(page, numpages); -} - -static inline int ttm_set_pages_wc(struct page *page, int numpages) -{ - unsigned long addr = (unsigned long)page_address(page); - - return set_memory_wc(addr, numpages); -} - -static inline int ttm_set_pages_uc(struct page *page, int numpages) -{ - return set_pages_uc(page, numpages); -} - -#else /* for CONFIG_X86 */ - -#if IS_ENABLED(CONFIG_AGP) - -#include <asm/agp.h> - -static inline int ttm_set_pages_array_wb(struct page **pages, int addrinarray) -{ - int i; - - for (i = 0; i < addrinarray; i++) - unmap_page_from_agp(pages[i]); - return 0; -} - -static inline int ttm_set_pages_array_wc(struct page **pages, int addrinarray) -{ - int i; - - for (i = 0; i < addrinarray; i++) - map_page_into_agp(pages[i]); - return 0; -} - -static inline int ttm_set_pages_array_uc(struct page **pages, int addrinarray) -{ - int i; - - for (i = 0; i < addrinarray; i++) - map_page_into_agp(pages[i]); - return 0; -} - -static inline int ttm_set_pages_wb(struct page *page, int numpages) -{ - int i; - - for (i = 0; i < numpages; i++) - unmap_page_from_agp(page++); - return 0; -} - -#else /* for CONFIG_AGP */ - -static inline int ttm_set_pages_array_wb(struct page **pages, int addrinarray) -{ - return 0; -} - -static inline int ttm_set_pages_array_wc(struct page **pages, int addrinarray) -{ - return 0; -} - -static inline int ttm_set_pages_array_uc(struct page **pages, int addrinarray) -{ - return 0; -} - -static inline int ttm_set_pages_wb(struct page *page, int numpages) -{ - return 0; -} - -#endif /* for CONFIG_AGP */ - -static inline int ttm_set_pages_wc(struct page *page, int numpages) -{ - return 0; -} - -static inline int ttm_set_pages_uc(struct page *page, int numpages) -{ - return 0; -} - -#endif /* for CONFIG_X86 */ - -#endif diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h index c0e928abf592..406437ad674b 100644 --- a/include/drm/ttm/ttm_tt.h +++ b/include/drm/ttm/ttm_tt.h @@ -27,113 +27,157 @@ #ifndef _TTM_TT_H_ #define _TTM_TT_H_ +#include <linux/pagemap.h> #include <linux/types.h> +#include <drm/ttm/ttm_caching.h> +#include <drm/ttm/ttm_kmap_iter.h> +struct ttm_backup; +struct ttm_device; struct ttm_tt; -struct ttm_mem_reg; +struct ttm_resource; struct ttm_buffer_object; struct ttm_operation_ctx; +struct ttm_pool_tt_restore; -#define TTM_PAGE_FLAG_WRITE (1 << 3) -#define TTM_PAGE_FLAG_SWAPPED (1 << 4) -#define TTM_PAGE_FLAG_PERSISTENT_SWAP (1 << 5) -#define TTM_PAGE_FLAG_ZERO_ALLOC (1 << 6) -#define TTM_PAGE_FLAG_DMA32 (1 << 7) -#define TTM_PAGE_FLAG_SG (1 << 8) -#define TTM_PAGE_FLAG_NO_RETRY (1 << 9) - -enum ttm_caching_state { - tt_uncached, - tt_wc, - tt_cached -}; - -struct ttm_backend_func { +/** + * struct ttm_tt - This is a structure holding the pages, caching- and aperture + * binding status for a buffer object that isn't backed by fixed (VRAM / AGP) + * memory. + */ +struct ttm_tt { + /** @pages: Array of pages backing the data. */ + struct page **pages; /** - * struct ttm_backend_func member bind + * @page_flags: The page flags. * - * @ttm: Pointer to a struct ttm_tt. - * @bo_mem: Pointer to a struct ttm_mem_reg describing the - * memory type and location for binding. + * Supported values: * - * Bind the backend pages into the aperture in the location - * indicated by @bo_mem. This function should be able to handle - * differences between aperture and system page sizes. - */ - int (*bind) (struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem); - - /** - * struct ttm_backend_func member unbind + * TTM_TT_FLAG_SWAPPED: Set by TTM when the pages have been unpopulated + * and swapped out by TTM. Calling ttm_tt_populate() will then swap the + * pages back in, and unset the flag. Drivers should in general never + * need to touch this. + * + * TTM_TT_FLAG_ZERO_ALLOC: Set if the pages will be zeroed on + * allocation. + * + * TTM_TT_FLAG_EXTERNAL: Set if the underlying pages were allocated + * externally, like with dma-buf or userptr. This effectively disables + * TTM swapping out such pages. Also important is to prevent TTM from + * ever directly mapping these pages. + * + * Note that enum ttm_bo_type.ttm_bo_type_sg objects will always enable + * this flag. + * + * TTM_TT_FLAG_EXTERNAL_MAPPABLE: Same behaviour as + * TTM_TT_FLAG_EXTERNAL, but with the reduced restriction that it is + * still valid to use TTM to map the pages directly. This is useful when + * implementing a ttm_tt backend which still allocates driver owned + * pages underneath(say with shmem). + * + * Note that since this also implies TTM_TT_FLAG_EXTERNAL, the usage + * here should always be: + * + * page_flags = TTM_TT_FLAG_EXTERNAL | + * TTM_TT_FLAG_EXTERNAL_MAPPABLE; + * + * TTM_TT_FLAG_DECRYPTED: The mapped ttm pages should be marked as + * not encrypted. The framework will try to match what the dma layer + * is doing, but note that it is a little fragile because ttm page + * fault handling abuses the DMA api a bit and dma_map_attrs can't be + * used to assure pgprot always matches. + * + * TTM_TT_FLAG_BACKED_UP: TTM internal only. This is set if the + * struct ttm_tt has been (possibly partially) backed up. * - * @ttm: Pointer to a struct ttm_tt. + * TTM_TT_FLAG_PRIV_POPULATED: TTM internal only. DO NOT USE. This is + * set by TTM after ttm_tt_populate() has successfully returned, and is + * then unset when TTM calls ttm_tt_unpopulate(). * - * Unbind previously bound backend pages. This function should be - * able to handle differences between aperture and system page sizes. */ - int (*unbind) (struct ttm_tt *ttm); +#define TTM_TT_FLAG_SWAPPED BIT(0) +#define TTM_TT_FLAG_ZERO_ALLOC BIT(1) +#define TTM_TT_FLAG_EXTERNAL BIT(2) +#define TTM_TT_FLAG_EXTERNAL_MAPPABLE BIT(3) +#define TTM_TT_FLAG_DECRYPTED BIT(4) +#define TTM_TT_FLAG_BACKED_UP BIT(5) +#define TTM_TT_FLAG_PRIV_POPULATED BIT(6) + uint32_t page_flags; + /** @num_pages: Number of pages in the page array. */ + uint32_t num_pages; + /** @sg: for SG objects via dma-buf. */ + struct sg_table *sg; + /** @dma_address: The DMA (bus) addresses of the pages. */ + dma_addr_t *dma_address; + /** @swap_storage: Pointer to shmem struct file for swap storage. */ + struct file *swap_storage; /** - * struct ttm_backend_func member destroy - * - * @ttm: Pointer to a struct ttm_tt. - * - * Destroy the backend. This will be call back from ttm_tt_destroy so - * don't call ttm_tt_destroy from the callback or infinite loop. + * @backup: Pointer to backup struct for backed up tts. + * Could be unified with @swap_storage. Meanwhile, the driver's + * ttm_tt_create() callback is responsible for assigning + * this field. */ - void (*destroy) (struct ttm_tt *ttm); + struct file *backup; + /** + * @caching: The current caching state of the pages, see enum + * ttm_caching. + */ + enum ttm_caching caching; + /** @restore: Partial restoration from backup state. TTM private */ + struct ttm_pool_tt_restore *restore; }; /** - * struct ttm_tt - * - * @bdev: Pointer to a struct ttm_bo_device. - * @func: Pointer to a struct ttm_backend_func that describes - * the backend methods. - * pointer. - * @pages: Array of pages backing the data. - * @num_pages: Number of pages in the page array. - * @bdev: Pointer to the current struct ttm_bo_device. - * @be: Pointer to the ttm backend. - * @swap_storage: Pointer to shmem struct file for swap storage. - * @caching_state: The current caching state of the pages. - * @state: The current binding state of the pages. - * - * This is a structure holding the pages, caching- and aperture binding - * status for a buffer object that isn't backed by fixed (VRAM / AGP) - * memory. + * struct ttm_kmap_iter_tt - Specialization of a mappig iterator for a tt. + * @base: Embedded struct ttm_kmap_iter providing the usage interface + * @tt: Cached struct ttm_tt. + * @prot: Cached page protection for mapping. */ -struct ttm_tt { - struct ttm_bo_device *bdev; - struct ttm_backend_func *func; - struct page **pages; - uint32_t page_flags; - unsigned long num_pages; - struct sg_table *sg; /* for SG objects via dma-buf */ - struct file *swap_storage; - enum ttm_caching_state caching_state; - enum { - tt_bound, - tt_unbound, - tt_unpopulated, - } state; +struct ttm_kmap_iter_tt { + struct ttm_kmap_iter base; + struct ttm_tt *tt; + pgprot_t prot; }; +static inline bool ttm_tt_is_populated(struct ttm_tt *tt) +{ + return tt->page_flags & TTM_TT_FLAG_PRIV_POPULATED; +} + /** - * struct ttm_dma_tt + * ttm_tt_is_swapped() - Whether the ttm_tt is swapped out or backed up + * @tt: The struct ttm_tt. * - * @ttm: Base ttm_tt struct. - * @dma_address: The DMA (bus) addresses of the pages - * @pages_list: used by some page allocation backend + * Return: true if swapped or backed up, false otherwise. + */ +static inline bool ttm_tt_is_swapped(const struct ttm_tt *tt) +{ + return tt->page_flags & (TTM_TT_FLAG_SWAPPED | TTM_TT_FLAG_BACKED_UP); +} + +/** + * ttm_tt_is_backed_up() - Whether the ttm_tt backed up + * @tt: The struct ttm_tt. * - * This is a structure holding the pages, caching- and aperture binding - * status for a buffer object that isn't backed by fixed (VRAM / AGP) - * memory. + * Return: true if swapped or backed up, false otherwise. */ -struct ttm_dma_tt { - struct ttm_tt ttm; - dma_addr_t *dma_address; - struct list_head pages_list; -}; +static inline bool ttm_tt_is_backed_up(const struct ttm_tt *tt) +{ + return tt->page_flags & TTM_TT_FLAG_BACKED_UP; +} + +/** + * ttm_tt_clear_backed_up() - Clear the ttm_tt backed-up status + * @tt: The struct ttm_tt. + * + * Drivers can use this functionto clear the backed-up status, + * for example before destroying or re-validating a purged tt. + */ +static inline void ttm_tt_clear_backed_up(struct ttm_tt *tt) +{ + tt->page_flags &= ~TTM_TT_FLAG_BACKED_UP; +} /** * ttm_tt_create @@ -151,7 +195,9 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc); * * @ttm: The struct ttm_tt. * @bo: The buffer object we create the ttm for. - * @page_flags: Page flags as identified by TTM_PAGE_FLAG_XX flags. + * @page_flags: Page flags as identified by TTM_TT_FLAG_XX flags. + * @caching: the desired caching state of the pages + * @extra_pages: Extra pages needed for the driver. * * Create a struct ttm_tt to back data with system memory pages. * No pages are actually allocated. @@ -159,11 +205,10 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc); * NULL: Out of memory. */ int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo, - uint32_t page_flags); -int ttm_dma_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_buffer_object *bo, - uint32_t page_flags); -int ttm_sg_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_buffer_object *bo, - uint32_t page_flags); + uint32_t page_flags, enum ttm_caching caching, + unsigned long extra_pages); +int ttm_sg_tt_init(struct ttm_tt *ttm_dma, struct ttm_buffer_object *bo, + uint32_t page_flags, enum ttm_caching caching); /** * ttm_tt_fini @@ -173,36 +218,16 @@ int ttm_sg_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_buffer_object *bo, * Free memory of ttm_tt structure */ void ttm_tt_fini(struct ttm_tt *ttm); -void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma); - -/** - * ttm_ttm_bind: - * - * @ttm: The struct ttm_tt containing backing pages. - * @bo_mem: The struct ttm_mem_reg identifying the binding location. - * - * Bind the pages of @ttm to an aperture location identified by @bo_mem - */ -int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem, - struct ttm_operation_ctx *ctx); /** - * ttm_ttm_destroy: + * ttm_tt_destroy: * + * @bdev: the ttm_device this object belongs to * @ttm: The struct ttm_tt. * * Unbind, unpopulate and destroy common struct ttm_tt. */ -void ttm_tt_destroy(struct ttm_tt *ttm); - -/** - * ttm_ttm_unbind: - * - * @ttm: The struct ttm_tt. - * - * Unbind a struct ttm_tt. - */ -void ttm_tt_unbind(struct ttm_tt *ttm); +void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm); /** * ttm_tt_swapin: @@ -212,40 +237,68 @@ void ttm_tt_unbind(struct ttm_tt *ttm); * Swap in a previously swap out ttm_tt. */ int ttm_tt_swapin(struct ttm_tt *ttm); +int ttm_tt_swapout(struct ttm_device *bdev, struct ttm_tt *ttm, + gfp_t gfp_flags); /** - * ttm_tt_set_placement_caching: + * ttm_tt_populate - allocate pages for a ttm * - * @ttm A struct ttm_tt the backing pages of which will change caching policy. - * @placement: Flag indicating the desired caching policy. + * @bdev: the ttm_device this object belongs to + * @ttm: Pointer to the ttm_tt structure + * @ctx: operation context for populating the tt object. * - * This function will change caching policy of any default kernel mappings of - * the pages backing @ttm. If changing from cached to uncached or - * write-combined, - * all CPU caches will first be flushed to make sure the data of the pages - * hit RAM. This function may be very costly as it involves global TLB - * and cache flushes and potential page splitting / combining. + * Calls the driver method to allocate pages for a ttm */ -int ttm_tt_set_placement_caching(struct ttm_tt *ttm, uint32_t placement); -int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage); +int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, + struct ttm_operation_ctx *ctx); /** - * ttm_tt_populate - allocate pages for a ttm + * ttm_tt_unpopulate - free pages from a ttm * + * @bdev: the ttm_device this object belongs to * @ttm: Pointer to the ttm_tt structure * - * Calls the driver method to allocate pages for a ttm + * Calls the driver method to free all pages from a ttm */ -int ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); /** - * ttm_tt_unpopulate - free pages from a ttm + * ttm_tt_mark_for_clear - Mark pages for clearing on populate. * * @ttm: Pointer to the ttm_tt structure * - * Calls the driver method to free all pages from a ttm + * Marks pages for clearing so that the next time the page vector is + * populated, the pages will be cleared. */ -void ttm_tt_unpopulate(struct ttm_tt *ttm); +static inline void ttm_tt_mark_for_clear(struct ttm_tt *ttm) +{ + ttm->page_flags |= TTM_TT_FLAG_ZERO_ALLOC; +} + +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); + +struct ttm_kmap_iter *ttm_kmap_iter_tt_init(struct ttm_kmap_iter_tt *iter_tt, + struct ttm_tt *tt); +unsigned long ttm_tt_pages_limit(void); + +/** + * struct ttm_backup_flags - Flags to govern backup behaviour. + * @purge: Free pages without backing up. Bypass pools. + * @writeback: Attempt to copy contents directly to swap space, even + * if that means blocking on writes to external memory. + */ +struct ttm_backup_flags { + u32 purge : 1; + u32 writeback : 1; +}; + +long ttm_tt_backup(struct ttm_device *bdev, struct ttm_tt *tt, + const struct ttm_backup_flags flags); + +int ttm_tt_restore(struct ttm_device *bdev, struct ttm_tt *tt, + const struct ttm_operation_ctx *ctx); + +int ttm_tt_setup_backup(struct ttm_tt *tt); #if IS_ENABLED(CONFIG_AGP) #include <linux/agp_backend.h> @@ -255,7 +308,7 @@ void ttm_tt_unpopulate(struct ttm_tt *ttm); * * @bo: Buffer object we allocate the ttm for. * @bridge: The agp bridge this device is sitting on. - * @page_flags: Page flags as identified by TTM_PAGE_FLAG_XX flags. + * @page_flags: Page flags as identified by TTM_TT_FLAG_XX flags. * * * Create a TTM backend that uses the indicated AGP bridge as an aperture @@ -265,8 +318,10 @@ void ttm_tt_unpopulate(struct ttm_tt *ttm); struct ttm_tt *ttm_agp_tt_create(struct ttm_buffer_object *bo, struct agp_bridge_data *bridge, uint32_t page_flags); -int ttm_agp_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); -void ttm_agp_tt_unpopulate(struct ttm_tt *ttm); +int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem); +void ttm_agp_unbind(struct ttm_tt *ttm); +void ttm_agp_destroy(struct ttm_tt *ttm); +bool ttm_agp_is_bound(struct ttm_tt *ttm); #endif #endif |
