diff options
| author | Thomas Zimmermann <tzimmermann@suse.de> | 2026-02-05 12:33:06 +0300 |
|---|---|---|
| committer | Thomas Zimmermann <tzimmermann@suse.de> | 2026-02-05 12:33:06 +0300 |
| commit | 2bebc88d5e37ddcb5ea5039a39f39527662b27f0 (patch) | |
| tree | cd7213334fe604969486b693e44750fe19ecd925 /include/drm | |
| parent | 96f30ee0fb9db1663eb8fd55c12e4c67da8c4a90 (diff) | |
| parent | 3cc9398a9ea69b77de01b370463b706c354e52fb (diff) | |
| download | linux-2bebc88d5e37ddcb5ea5039a39f39527662b27f0.tar.xz | |
Merge drm/drm-next into drm-misc-next
Backmerging to get bug fixes from v6.19-rc7.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Diffstat (limited to 'include/drm')
| -rw-r--r-- | include/drm/bridge/dw_hdmi_qp.h | 1 | ||||
| -rw-r--r-- | include/drm/display/drm_dp_helper.h | 60 | ||||
| -rw-r--r-- | include/drm/drm_atomic_helper.h | 22 | ||||
| -rw-r--r-- | include/drm/drm_bridge.h | 249 | ||||
| -rw-r--r-- | include/drm/drm_gpusvm.h | 29 | ||||
| -rw-r--r-- | include/drm/drm_pagemap.h | 142 | ||||
| -rw-r--r-- | include/drm/drm_pagemap_util.h | 92 | ||||
| -rw-r--r-- | include/drm/intel/display_parent_interface.h | 104 | ||||
| -rw-r--r-- | include/drm/intel/intel_lb_mei_interface.h | 3 |
9 files changed, 489 insertions, 213 deletions
diff --git a/include/drm/bridge/dw_hdmi_qp.h b/include/drm/bridge/dw_hdmi_qp.h index 3f461f6b9bbf..3af12f82da2c 100644 --- a/include/drm/bridge/dw_hdmi_qp.h +++ b/include/drm/bridge/dw_hdmi_qp.h @@ -34,5 +34,6 @@ struct dw_hdmi_qp_plat_data { 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_suspend(struct device *dev, struct dw_hdmi_qp *hdmi); void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); #endif /* __DW_HDMI_QP__ */ diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index df2f24b950e4..1d0acd58f486 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -206,6 +206,9 @@ drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) /* DP/eDP DSC support */ u8 drm_dp_dsc_sink_bpp_incr(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]); +u32 drm_dp_dsc_slice_count_to_mask(int slice_count); +u32 drm_dp_dsc_sink_slice_count_mask(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], + bool is_edp); 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]); @@ -552,6 +555,22 @@ 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 + * + * 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_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 @@ -570,12 +589,29 @@ static inline int drm_dp_dpcd_read_data(struct drm_dp_aux *aux, void *buffer, size_t size) { int ret; + size_t i; + u8 *buf = buffer; ret = drm_dp_dpcd_read(aux, offset, buffer, size); - if (ret < 0) - return ret; - if (ret < size) - return -EPROTO; + if (ret >= 0) { + if (ret < size) + return -EPROTO; + return 0; + } + + /* + * Workaround for USB-C hubs/adapters with buggy firmware that fail + * multi-byte AUX reads but work with single-byte reads. + * Known affected devices: + * - Lenovo USB-C to VGA adapter (VIA VL817, idVendor=17ef, idProduct=7217) + * - Dell DA310 USB-C hub (idVendor=413c, idProduct=c010) + * Attempt byte-by-byte reading as a fallback. + */ + for (i = 0; i < size; i++) { + ret = drm_dp_dpcd_readb(aux, offset + i, &buf[i]); + if (ret < 0) + return ret; + } return 0; } @@ -610,22 +646,6 @@ static inline int drm_dp_dpcd_write_data(struct drm_dp_aux *aux, } /** - * 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 diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 53382fe93537..e154ee4f0696 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -60,6 +60,12 @@ int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state, 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); +void drm_atomic_helper_commit_encoder_bridge_disable(struct drm_device *dev, + struct drm_atomic_state *state); +void drm_atomic_helper_commit_crtc_disable(struct drm_device *dev, + struct drm_atomic_state *state); +void drm_atomic_helper_commit_encoder_bridge_post_disable(struct drm_device *dev, + struct drm_atomic_state *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); @@ -89,8 +95,24 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, void drm_atomic_helper_calc_timestamping_constants(struct drm_atomic_state *state); +void drm_atomic_helper_commit_crtc_set_mode(struct drm_device *dev, + 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_writebacks(struct drm_device *dev, + struct drm_atomic_state *state); + +void drm_atomic_helper_commit_encoder_bridge_pre_enable(struct drm_device *dev, + struct drm_atomic_state *state); + +void drm_atomic_helper_commit_crtc_enable(struct drm_device *dev, + struct drm_atomic_state *state); + +void drm_atomic_helper_commit_encoder_bridge_enable(struct drm_device *dev, + struct drm_atomic_state *state); + void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *old_state); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 73c23fece792..4f19f7064ee3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -176,33 +176,17 @@ struct drm_bridge_funcs { /** * @disable: * - * The @disable callback should disable the bridge. + * 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 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 @disable callback is optional. * * NOTE: @@ -215,34 +199,17 @@ struct drm_bridge_funcs { /** * @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 - * @post_disable is called. + * 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. * - * 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 bridge must assume that the display pipe (i.e. clocks and timing + * signals) feeding it is no longer running when this callback is + * called. * * The @post_disable callback is optional. * @@ -285,30 +252,18 @@ struct drm_bridge_funcs { /** * @pre_enable: * - * The display pipe (i.e. clocks and timing signals) feeding this bridge - * 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 - * - * and the encoder is enabled via one of: + * 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. * - * - &drm_encoder_helper_funcs.atomic_enable - * - &drm_encoder_helper_funcs.enable - * - &drm_encoder_helper_funcs.commit + * 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. * * The @pre_enable callback is optional. * @@ -322,31 +277,19 @@ struct drm_bridge_funcs { /** * @enable: * - * The @enable callback should enable the bridge. + * 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 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 @enable callback is optional. * * NOTE: @@ -359,30 +302,17 @@ struct drm_bridge_funcs { /** * @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 + * 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 @atomic_pre_enable or @pre_enable function. If the preceding + * element is a &drm_encoder it's called right before the encoder's + * &drm_encoder_helper_funcs.atomic_enable hook. * - * 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 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. * * The @atomic_pre_enable callback is optional. */ @@ -392,31 +322,18 @@ struct drm_bridge_funcs { /** * @atomic_enable: * - * The @atomic_enable callback should enable the bridge. + * 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 @atomic_enable or @enable function. If the preceding element + * is a &drm_encoder it's called right after the encoder's + * &drm_encoder_helper_funcs.atomic_enable hook. * * 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, @@ -424,32 +341,16 @@ struct drm_bridge_funcs { /** * @atomic_disable: * - * The @atomic_disable callback should disable the bridge. + * 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 @atomic_disable or @disable vfunc. If the preceding element + * is a &drm_encoder it's called right before the + * &drm_encoder_helper_funcs.atomic_disable hook. * * 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, @@ -458,34 +359,16 @@ struct drm_bridge_funcs { /** * @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. + * 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 + * @atomic_post_disable or @post_disable function. If the preceding + * element is a &drm_encoder it's called right after the encoder's + * &drm_encoder_helper_funcs.atomic_disable hook. * - * 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 bridge must assume that the display pipe (i.e. clocks and timing + * signals) feeding it is no longer running when this callback is + * called. * * The @atomic_post_disable callback is optional. */ diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h index 632e100e6efb..2578ac92a8d4 100644 --- a/include/drm/drm_gpusvm.h +++ b/include/drm/drm_gpusvm.h @@ -328,6 +328,35 @@ void drm_gpusvm_free_pages(struct drm_gpusvm *gpusvm, struct drm_gpusvm_pages *svm_pages, unsigned long npages); +/** + * enum drm_gpusvm_scan_result - Scan result from the drm_gpusvm_scan_mm() function. + * @DRM_GPUSVM_SCAN_UNPOPULATED: At least one page was not present or inaccessible. + * @DRM_GPUSVM_SCAN_EQUAL: All pages belong to the struct dev_pagemap indicated as + * the @pagemap argument to the drm_gpusvm_scan_mm() function. + * @DRM_GPUSVM_SCAN_OTHER: All pages belong to exactly one dev_pagemap, which is + * *NOT* the @pagemap argument to the drm_gpusvm_scan_mm(). All pages belong to + * the same device private owner. + * @DRM_GPUSVM_SCAN_SYSTEM: All pages are present and system pages. + * @DRM_GPUSVM_SCAN_MIXED_DEVICE: All pages are device pages and belong to at least + * two different struct dev_pagemaps. All pages belong to the same device private + * owner. + * @DRM_GPUSVM_SCAN_MIXED: Pages are present and are a mix of system pages + * and device-private pages. All device-private pages belong to the same device + * private owner. + */ +enum drm_gpusvm_scan_result { + DRM_GPUSVM_SCAN_UNPOPULATED, + DRM_GPUSVM_SCAN_EQUAL, + DRM_GPUSVM_SCAN_OTHER, + DRM_GPUSVM_SCAN_SYSTEM, + DRM_GPUSVM_SCAN_MIXED_DEVICE, + DRM_GPUSVM_SCAN_MIXED, +}; + +enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range, + void *dev_private_owner, + const struct dev_pagemap *pagemap); + #ifdef CONFIG_LOCKDEP /** * drm_gpusvm_driver_set_lock() - Set the lock protecting accesses to GPU SVM diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h index f6e7e234c089..2baf0861f78f 100644 --- a/include/drm/drm_pagemap.h +++ b/include/drm/drm_pagemap.h @@ -8,7 +8,10 @@ #define NR_PAGES(order) (1U << (order)) +struct dma_fence; struct drm_pagemap; +struct drm_pagemap_cache; +struct drm_pagemap_dev_hold; struct drm_pagemap_zdd; struct device; @@ -123,17 +126,49 @@ struct drm_pagemap_ops { unsigned long start, unsigned long end, struct mm_struct *mm, unsigned long timeslice_ms); + /** + * @destroy: Destroy the drm_pagemap and associated resources. + * @dpagemap: The drm_pagemap to destroy. + * @is_atomic_or_reclaim: The function may be called from + * atomic- or reclaim context. + * + * The implementation should take care not to attempt to + * destroy resources that may already have been destroyed + * using devm_ callbacks, since this function may be called + * after the underlying struct device has been unbound. + * If the implementation defers the execution to a work item + * to avoid locking issues, then it must make sure the work + * items are flushed before module exit. If the destroy call + * happens after the provider's pci_remove() callback has + * been executed, a module reference and drm device reference is + * held across the destroy callback. + */ + void (*destroy)(struct drm_pagemap *dpagemap, + bool is_atomic_or_reclaim); }; /** * 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. + * @ref: Reference count. + * @drm: The struct drm device owning the device-private memory. + * @pagemap: Pointer to the underlying dev_pagemap. + * @dev_hold: Pointer to a struct drm_pagemap_dev_hold for + * device referencing. + * @cache: Back-pointer to the &struct drm_pagemap_cache used for this + * &struct drm_pagemap. May be NULL if no cache is used. + * @shrink_link: Link into the shrinker's list of drm_pagemaps. Only + * used if also using a pagemap cache. */ struct drm_pagemap { const struct drm_pagemap_ops *ops; - struct device *dev; + struct kref ref; + struct drm_device *drm; + struct dev_pagemap *pagemap; + struct drm_pagemap_dev_hold *dev_hold; + struct drm_pagemap_cache *cache; + struct list_head shrink_link; }; struct drm_pagemap_devmem; @@ -174,6 +209,8 @@ struct drm_pagemap_devmem_ops { * @pages: Pointer to array of device memory pages (destination) * @pagemap_addr: Pointer to array of DMA information (source) * @npages: Number of pages to copy + * @pre_migrate_fence: dma-fence to wait for before migration start. + * May be NULL. * * Copy pages to device memory. If the order of a @pagemap_addr entry * is greater than 0, the entry is populated but subsequent entries @@ -183,13 +220,16 @@ struct drm_pagemap_devmem_ops { */ int (*copy_to_devmem)(struct page **pages, struct drm_pagemap_addr *pagemap_addr, - unsigned long npages); + unsigned long npages, + struct dma_fence *pre_migrate_fence); /** * @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 + * @pre_migrate_fence: dma-fence to wait for before migration start. + * May be NULL. * * Copy pages to system RAM. If the order of a @pagemap_addr entry * is greater than 0, the entry is populated but subsequent entries @@ -199,9 +239,67 @@ struct drm_pagemap_devmem_ops { */ int (*copy_to_ram)(struct page **pages, struct drm_pagemap_addr *pagemap_addr, - unsigned long npages); + unsigned long npages, + struct dma_fence *pre_migrate_fence); }; +#if IS_ENABLED(CONFIG_ZONE_DEVICE) + +int drm_pagemap_init(struct drm_pagemap *dpagemap, + struct dev_pagemap *pagemap, + struct drm_device *drm, + const struct drm_pagemap_ops *ops); + +struct drm_pagemap *drm_pagemap_create(struct drm_device *drm, + struct dev_pagemap *pagemap, + const struct drm_pagemap_ops *ops); + +struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page); + +void drm_pagemap_put(struct drm_pagemap *dpagemap); + +#else + +static inline struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page) +{ + return NULL; +} + +static inline void drm_pagemap_put(struct drm_pagemap *dpagemap) +{ +} + +#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */ + +/** + * drm_pagemap_get() - Obtain a reference on a struct drm_pagemap + * @dpagemap: Pointer to the struct drm_pagemap, or NULL. + * + * Return: Pointer to the struct drm_pagemap, or NULL. + */ +static inline struct drm_pagemap * +drm_pagemap_get(struct drm_pagemap *dpagemap) +{ + if (likely(dpagemap)) + kref_get(&dpagemap->ref); + + return dpagemap; +} + +/** + * drm_pagemap_get_unless_zero() - Obtain a reference on a struct drm_pagemap + * unless the current reference count is zero. + * @dpagemap: Pointer to the drm_pagemap or NULL. + * + * Return: A pointer to @dpagemap if the reference count was successfully + * incremented. NULL if @dpagemap was NULL, or its refcount was 0. + */ +static inline struct drm_pagemap * __must_check +drm_pagemap_get_unless_zero(struct drm_pagemap *dpagemap) +{ + return (dpagemap && kref_get_unless_zero(&dpagemap->ref)) ? dpagemap : NULL; +} + /** * struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation * @@ -212,6 +310,8 @@ struct drm_pagemap_devmem_ops { * @dpagemap: The struct drm_pagemap of the pages this allocation belongs to. * @size: Size of device memory allocation * @timeslice_expiration: Timeslice expiration in jiffies + * @pre_migrate_fence: Fence to wait for or pipeline behind before migration starts. + * (May be NULL). */ struct drm_pagemap_devmem { struct device *dev; @@ -221,28 +321,52 @@ struct drm_pagemap_devmem { struct drm_pagemap *dpagemap; size_t size; u64 timeslice_expiration; + struct dma_fence *pre_migrate_fence; }; +/** + * struct drm_pagemap_migrate_details - Details to govern migration. + * @timeslice_ms: The time requested for the migrated pagemap pages to + * be present in @mm before being allowed to be migrated back. + * @can_migrate_same_pagemap: Whether the copy function as indicated by + * the @source_peer_migrates flag, can migrate device pages within a + * single drm_pagemap. + * @source_peer_migrates: Whether on p2p migration, The source drm_pagemap + * should use the copy_to_ram() callback rather than the destination + * drm_pagemap should use the copy_to_devmem() callback. + */ +struct drm_pagemap_migrate_details { + unsigned long timeslice_ms; + u32 can_migrate_same_pagemap : 1; + u32 source_peer_migrates : 1; +}; + +#if IS_ENABLED(CONFIG_ZONE_DEVICE) + 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); + const struct drm_pagemap_migrate_details *mdetails); 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); + struct drm_pagemap *dpagemap, size_t size, + struct dma_fence *pre_migrate_fence); int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, unsigned long start, unsigned long end, struct mm_struct *mm, unsigned long timeslice_ms); +void drm_pagemap_destroy(struct drm_pagemap *dpagemap, bool is_atomic_or_reclaim); + +int drm_pagemap_reinit(struct drm_pagemap *dpagemap); + +#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */ + #endif diff --git a/include/drm/drm_pagemap_util.h b/include/drm/drm_pagemap_util.h new file mode 100644 index 000000000000..19169b42b891 --- /dev/null +++ b/include/drm/drm_pagemap_util.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _DRM_PAGEMAP_UTIL_H_ +#define _DRM_PAGEMAP_UTIL_H_ + +#include <linux/list.h> +#include <linux/mutex.h> + +struct drm_device; +struct drm_pagemap; +struct drm_pagemap_cache; +struct drm_pagemap_owner; +struct drm_pagemap_shrinker; + +/** + * struct drm_pagemap_peer - Structure representing a fast interconnect peer + * @list: Pointer to a &struct drm_pagemap_owner_list used to keep track of peers + * @link: List link for @list's list of peers. + * @owner: Pointer to a &struct drm_pagemap_owner, common for a set of peers having + * fast interconnects. + * @private: Pointer private to the struct embedding this struct. + */ +struct drm_pagemap_peer { + struct drm_pagemap_owner_list *list; + struct list_head link; + struct drm_pagemap_owner *owner; + void *private; +}; + +/** + * struct drm_pagemap_owner_list - Keeping track of peers and owners + * @peer: List of peers. + * + * The owner list defines the scope where we identify peers having fast interconnects + * and a common owner. Typically a driver has a single global owner list to + * keep track of common owners for the driver's pagemaps. + */ +struct drm_pagemap_owner_list { + /** @lock: Mutex protecting the @peers list. */ + struct mutex lock; + /** @peers: List of peers. */ + struct list_head peers; +}; + +/* + * Convenience macro to define an owner list. + * Typically the owner list statically declared + * driver-wide. + */ +#define DRM_PAGEMAP_OWNER_LIST_DEFINE(_name) \ + struct drm_pagemap_owner_list _name = { \ + .lock = __MUTEX_INITIALIZER((_name).lock), \ + .peers = LIST_HEAD_INIT((_name).peers) } + +void drm_pagemap_shrinker_add(struct drm_pagemap *dpagemap); + +int drm_pagemap_cache_lock_lookup(struct drm_pagemap_cache *cache); + +void drm_pagemap_cache_unlock_lookup(struct drm_pagemap_cache *cache); + +struct drm_pagemap_shrinker *drm_pagemap_shrinker_create_devm(struct drm_device *drm); + +struct drm_pagemap_cache *drm_pagemap_cache_create_devm(struct drm_pagemap_shrinker *shrinker); + +struct drm_pagemap *drm_pagemap_get_from_cache(struct drm_pagemap_cache *cache); + +void drm_pagemap_cache_set_pagemap(struct drm_pagemap_cache *cache, struct drm_pagemap *dpagemap); + +struct drm_pagemap *drm_pagemap_get_from_cache_if_active(struct drm_pagemap_cache *cache); + +#ifdef CONFIG_PROVE_LOCKING + +void drm_pagemap_shrinker_might_lock(struct drm_pagemap *dpagemap); + +#else + +static inline void drm_pagemap_shrinker_might_lock(struct drm_pagemap *dpagemap) +{ +} + +#endif /* CONFIG_PROVE_LOCKING */ + +void drm_pagemap_release_owner(struct drm_pagemap_peer *peer); + +int drm_pagemap_acquire_owner(struct drm_pagemap_peer *peer, + struct drm_pagemap_owner_list *owner_list, + bool (*has_interconnect)(struct drm_pagemap_peer *peer1, + struct drm_pagemap_peer *peer2)); +#endif diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index 26bedc360044..ce946859a3a9 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -6,9 +6,55 @@ #include <linux/types.h> +struct dma_fence; +struct drm_crtc; struct drm_device; +struct drm_framebuffer; +struct drm_gem_object; +struct drm_plane_state; +struct drm_scanout_buffer; +struct i915_vma; +struct intel_hdcp_gsc_context; +struct intel_initial_plane_config; +struct intel_panic; +struct intel_stolen_node; struct ref_tracker; +/* Keep struct definitions sorted */ + +struct intel_display_hdcp_interface { + ssize_t (*gsc_msg_send)(struct intel_hdcp_gsc_context *gsc_context, + void *msg_in, size_t msg_in_len, + void *msg_out, size_t msg_out_len); + bool (*gsc_check_status)(struct drm_device *drm); + struct intel_hdcp_gsc_context *(*gsc_context_alloc)(struct drm_device *drm); + void (*gsc_context_free)(struct intel_hdcp_gsc_context *gsc_context); +}; + +struct intel_display_initial_plane_interface { + void (*vblank_wait)(struct drm_crtc *crtc); + struct drm_gem_object *(*alloc_obj)(struct drm_device *drm, struct intel_initial_plane_config *plane_config); + int (*setup)(struct drm_plane_state *plane_state, struct intel_initial_plane_config *plane_config, + struct drm_framebuffer *fb, struct i915_vma *vma); + void (*config_fini)(struct intel_initial_plane_config *plane_configs); +}; + +struct intel_display_irq_interface { + bool (*enabled)(struct drm_device *drm); + void (*synchronize)(struct drm_device *drm); +}; + +struct intel_display_panic_interface { + struct intel_panic *(*alloc)(void); + int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb); + void (*finish)(struct intel_panic *panic); +}; + +struct intel_display_pc8_interface { + void (*block)(struct drm_device *drm); + void (*unblock)(struct drm_device *drm); +}; + struct intel_display_rpm_interface { struct ref_tracker *(*get)(const struct drm_device *drm); struct ref_tracker *(*get_raw)(const struct drm_device *drm); @@ -25,6 +71,28 @@ struct intel_display_rpm_interface { void (*assert_unblock)(const struct drm_device *drm); }; +struct intel_display_rps_interface { + void (*boost_if_not_started)(struct dma_fence *fence); + void (*mark_interactive)(struct drm_device *drm, bool interactive); + void (*ilk_irq_handler)(struct drm_device *drm); +}; + +struct intel_display_stolen_interface { + int (*insert_node_in_range)(struct intel_stolen_node *node, u64 size, + unsigned int align, u64 start, u64 end); + int (*insert_node)(struct intel_stolen_node *node, u64 size, unsigned int align); /* Optional */ + void (*remove_node)(struct intel_stolen_node *node); + bool (*initialized)(struct drm_device *drm); + bool (*node_allocated)(const struct intel_stolen_node *node); + u64 (*node_offset)(const struct intel_stolen_node *node); + u64 (*area_address)(struct drm_device *drm); /* Optional */ + u64 (*area_size)(struct drm_device *drm); /* Optional */ + u64 (*node_address)(const struct intel_stolen_node *node); + u64 (*node_size)(const struct intel_stolen_node *node); + struct intel_stolen_node *(*node_alloc)(struct drm_device *drm); + void (*node_free)(const struct intel_stolen_node *node); +}; + /** * struct intel_display_parent_interface - services parent driver provides to display * @@ -38,8 +106,44 @@ struct intel_display_rpm_interface { * check the optional pointers. */ struct intel_display_parent_interface { + /** @hdcp: HDCP GSC interface */ + const struct intel_display_hdcp_interface *hdcp; + + /** @initial_plane: Initial plane interface */ + const struct intel_display_initial_plane_interface *initial_plane; + + /** @irq: IRQ interface */ + const struct intel_display_irq_interface *irq; + + /** @panic: Panic interface */ + const struct intel_display_panic_interface *panic; + + /** @pc8: PC8 interface. Optional. */ + const struct intel_display_pc8_interface *pc8; + /** @rpm: Runtime PM functions */ const struct intel_display_rpm_interface *rpm; + + /** @rps: RPS interface. Optional. */ + const struct intel_display_rps_interface *rps; + + /** @stolen: Stolen memory. */ + const struct intel_display_stolen_interface *stolen; + + /* Generic independent functions */ + struct { + /** @fence_priority_display: Set display priority. Optional. */ + void (*fence_priority_display)(struct dma_fence *fence); + + /** @has_auxccs: Are AuxCCS formats supported by the parent. Optional. */ + bool (*has_auxccs)(struct drm_device *drm); + + /** @has_fenced_regions: Support legacy fencing? Optional. */ + bool (*has_fenced_regions)(struct drm_device *drm); + + /** @vgpu_active: Is vGPU active? Optional. */ + bool (*vgpu_active)(struct drm_device *drm); + }; }; #endif diff --git a/include/drm/intel/intel_lb_mei_interface.h b/include/drm/intel/intel_lb_mei_interface.h index d65be2cba2ab..0850738a30fc 100644 --- a/include/drm/intel/intel_lb_mei_interface.h +++ b/include/drm/intel/intel_lb_mei_interface.h @@ -53,7 +53,8 @@ enum intel_lb_status { */ struct intel_lb_component_ops { /** - * push_payload - Sends a payload to the authentication firmware + * @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) |
