[PATCH] backlight: pwm_bl: Avoid open coded arithmetic in memory allocation
kmalloc_array()/kcalloc() should be used to avoid potential overflow when a multiplication is needed to compute the size of the requested memory. So turn a kzalloc()+explicit size computation into an equivalent kcalloc(). Signed-off-by: Christophe JAILLET --- drivers/video/backlight/pwm_bl.c | 9 - 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 8d8959a70e44..c0523a0269ee 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -263,9 +263,8 @@ static int pwm_backlight_parse_dt(struct device *dev, /* read brightness levels from DT property */ if (num_levels > 0) { - size_t size = sizeof(*data->levels) * num_levels; - - data->levels = devm_kzalloc(dev, size, GFP_KERNEL); + data->levels = devm_kcalloc(dev, num_levels, + sizeof(*data->levels), GFP_KERNEL); if (!data->levels) return -ENOMEM; @@ -320,8 +319,8 @@ static int pwm_backlight_parse_dt(struct device *dev, * Create a new table of brightness levels with all the * interpolated steps. */ - size = sizeof(*table) * num_levels; - table = devm_kzalloc(dev, size, GFP_KERNEL); + table = devm_kcalloc(dev, num_levels, sizeof(*table), +GFP_KERNEL); if (!table) return -ENOMEM; /* -- 2.32.0
Re: [PATCH] iommu/vt-d: Remove comment reference to iommu_dev_has_feature
On 2/2/22 10:37 AM, Akeem G Abodunrin wrote: iommu_dev_has_feature() api has been removed by the commit 262948f8ba573 ("iommu: Delete iommu_dev_has_feature()") - So this patch removes comment about the api to avoid any confusion. Signed-off-by: Akeem G Abodunrin Cc: Lu Baolu It's not a change for iommu/vt-d, but for iommu core. Please add Joerg in the to list. Best regards, baolu --- include/linux/iommu.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index de0c57a567c8..bea054f2bd4d 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -153,8 +153,7 @@ struct iommu_resv_region { * supported, this feature must be enabled before and * disabled after %IOMMU_DEV_FEAT_SVA. * - * Device drivers query whether a feature is supported using - * iommu_dev_has_feature(), and enable it using iommu_dev_enable_feature(). + * Device drivers enable the feature via iommu_dev_enable_feature(). */ enum iommu_dev_features { IOMMU_DEV_FEAT_AUX,
Re: [BUG] gpu: drm: radeon: two possible deadlocks involving locking and waiting
Hi Christian, Thanks for the reply :) On 2022/2/1 15:56, Christian König wrote: Hi Jia-Ju, interesting that you have found those issues with an automated tool. And yes that is a well design flaw within the radeon driver which can happen on hardware faults, e.g. when radeon_ring_backup() needs to be called. In fact, my tool finds dozens of similar possible deadlocks caused by wait_event_timeout() in radeon_fence_wait_seq_timeout(). There are three other examples in Linux 5.16: #BUG 1 radeon_dpm_change_power_state_locked() mutex_lock(&rdev->ring_lock); --> Line 1133 (Lock A) radeon_fence_wait_empty() radeon_fence_wait_seq_timeout() wait_event_timeout(rdev->fence_queue, ...) --> Line 504 (Wait X) radeon_fence_driver_fini() mutex_lock(&rdev->ring_lock); --> Line 917 (Lock A) wake_up_all(&rdev->fence_queue); --> Line 927 (Wake X) #BUG 2 radeon_set_pm_profile() mutex_lock(&rdev->pm.mutex); --> Line 382 (Lock A) radeon_pm_set_clocks() radeon_fence_wait_empty() radeon_fence_wait_seq_timeout() wait_event_timeout(rdev->fence_queue, ...) --> Line 504 (Wait X) radeon_dynpm_idle_work_handler() mutex_lock(&rdev->pm.mutex); --> Line 1861 (Lock A) radeon_fence_count_emitted() radeon_fence_process() wake_up_all(&rdev->fence_queue); --> Line 323 (Wake X) #BUG 3 radeon_pm_fini_old() mutex_lock(&rdev->pm.mutex); --> Line 1642 (Lock A) radeon_pm_set_clocks() radeon_fence_wait_empty() radeon_fence_wait_seq_timeout() wait_event_timeout(rdev->fence_queue, ...) --> Line 504 (Wait X) radeon_dynpm_idle_work_handler() mutex_lock(&rdev->pm.mutex); --> Line 1861 (Lock A) radeon_fence_count_emitted() radeon_fence_process() wake_up_all(&rdev->fence_queue); --> Line 323 (Wake X) Thus, to fix these possible deadlocks, we could moditify the code related to radeon_fence_wait_seq_timeout(). But I am not quite familar with the radeon driver, so I am not sure how to moditify the code properly. But that happens so rarely and the driver is not developed further that we decided to not address this any more. Ah, okay. Regards, Christian. Am 01.02.22 um 08:40 schrieb Jia-Ju Bai: Hello, My static analysis tool reports a possible deadlock in the radeon driver in Linux 5.16: #BUG 1 radeon_dpm_change_power_state_locked() mutex_lock(&rdev->ring_lock); --> Line 1133 (Lock A) radeon_fence_wait_empty() radeon_fence_wait_seq_timeout() wait_event_timeout(rdev->fence_queue, ...) --> Line 504 (Wait X) radeon_ring_backup() mutex_lock(&rdev->ring_lock); --> Line 289(Lock A) radeon_fence_count_emitted() radeon_fence_process() wake_up_all(&rdev->fence_queue); --> Line 323 (Wake X) When radeon_dpm_change_power_state_locked() is executed, "Wait X" is performed by holding "Lock A". If radeon_ring_backup() is executed at this time, "Wake X" cannot be performed to wake up "Wait X" in radeon_dpm_change_power_state_locked(), because "Lock A" has been already hold by radeon_dpm_change_power_state_locked(), causing a possible deadlock. I find that "Wait X" is performed with a timeout MAX_SCHEDULE_TIMEOUT, to relieve the possible deadlock; but I think this timeout can cause inefficient execution. #BUG 2 radeon_ring_lock() mutex_lock(&rdev->ring_lock); --> Line 147 (Lock A) radeon_ring_alloc() radeon_fence_wait_next() radeon_fence_wait_seq_timeout() wait_event_timeout(rdev->fence_queue, ...) --> Line 504 (Wait X) radeon_ring_backup() mutex_lock(&rdev->ring_lock); --> Line 289(Lock A) radeon_fence_count_emitted() radeon_fence_process() wake_up_all(&rdev->fence_queue); --> Line 323 (Wake X) When radeon_ring_lock() is executed, "Wait X" is performed by holding "Lock A". If radeon_ring_backup() is executed at this time, "Wake X" cannot be performed to wake up "Wait X" in radeon_ring_lock(), because "Lock A" has been already hold by radeon_ring_lock(), causing a possible deadlock. I find that "Wait X" is performed with a timeout MAX_SCHEDULE_TIMEOUT, to relieve the possible deadlock; but I think this timeout can cause inefficient execution. I am not quite sure whether these possible problems are real and how to fix them if they are real. Any feedback would be appreciated, thanks :) Best wishes, Jia-Ju Bai
Re: [PATCH] drm/dp: Remove common Post Cursor2 register handling
Ping, This is a OOB read fix. Can someone please pick this up? -Kees On Wed, Jan 05, 2022 at 09:35:07AM -0800, Kees Cook wrote: > The link_status array was not large enough to read the Adjust Request > Post Cursor2 register, so remove the common helper function to avoid > an OOB read, found with a -Warray-bounds build: > > drivers/gpu/drm/drm_dp_helper.c: In function > 'drm_dp_get_adjust_request_post_cursor': > drivers/gpu/drm/drm_dp_helper.c:59:27: error: array subscript 10 is outside > array bounds of 'const u8[6]' {aka 'const unsigned char[6]'} > [-Werror=array-bounds] >59 | return link_status[r - DP_LANE0_1_STATUS]; > |~~~^~~ > drivers/gpu/drm/drm_dp_helper.c:147:51: note: while referencing 'link_status' > 147 | u8 drm_dp_get_adjust_request_post_cursor(const u8 > link_status[DP_LINK_STATUS_SIZE], > | > ~^~~~ > > Replace the only user of the helper with an open-coded fetch and decode, > similar to drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c. > > Fixes: 79465e0ffeb9 ("drm/dp: Add helper to get post-cursor adjustments") > Cc: Maarten Lankhorst > Cc: Maxime Ripard > Cc: Thomas Zimmermann > Cc: David Airlie > Cc: Daniel Vetter > Cc: dri-devel@lists.freedesktop.org > Signed-off-by: Kees Cook > --- > This is the alternative to: > https://lore.kernel.org/lkml/20211203084354.3105253-1-keesc...@chromium.org/ > --- > drivers/gpu/drm/drm_dp_helper.c | 10 -- > drivers/gpu/drm/tegra/dp.c | 11 ++- > include/drm/drm_dp_helper.h | 2 -- > 3 files changed, 10 insertions(+), 13 deletions(-) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c > index 23f9073bc473..c9528aa62c9c 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -144,16 +144,6 @@ u8 drm_dp_get_adjust_tx_ffe_preset(const u8 > link_status[DP_LINK_STATUS_SIZE], > } > EXPORT_SYMBOL(drm_dp_get_adjust_tx_ffe_preset); > > -u8 drm_dp_get_adjust_request_post_cursor(const u8 > link_status[DP_LINK_STATUS_SIZE], > - unsigned int lane) > -{ > - unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2; > - u8 value = dp_link_status(link_status, offset); > - > - return (value >> (lane << 1)) & 0x3; > -} > -EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor); > - > static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 > rd_interval) > { > if (rd_interval > 4) > diff --git a/drivers/gpu/drm/tegra/dp.c b/drivers/gpu/drm/tegra/dp.c > index 70dfb7d1dec5..f5535eb04c6b 100644 > --- a/drivers/gpu/drm/tegra/dp.c > +++ b/drivers/gpu/drm/tegra/dp.c > @@ -549,6 +549,15 @@ static void drm_dp_link_get_adjustments(struct > drm_dp_link *link, > { > struct drm_dp_link_train_set *adjust = &link->train.adjust; > unsigned int i; > + u8 post_cursor; > + int err; > + > + err = drm_dp_dpcd_read(link->aux, DP_ADJUST_REQUEST_POST_CURSOR2, > +&post_cursor, sizeof(post_cursor)); > + if (err < 0) { > + DRM_ERROR("failed to read post_cursor2: %d\n", err); > + post_cursor = 0; > + } > > for (i = 0; i < link->lanes; i++) { > adjust->voltage_swing[i] = > @@ -560,7 +569,7 @@ static void drm_dp_link_get_adjustments(struct > drm_dp_link *link, > DP_TRAIN_PRE_EMPHASIS_SHIFT; > > adjust->post_cursor[i] = > - drm_dp_get_adjust_request_post_cursor(status, i); > + (post_cursor >> (i << 1)) & 0x3; > } > } > > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 472dac376284..fdf3cf6ccc02 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1528,8 +1528,6 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 > link_status[DP_LINK_STATUS_SI > int lane); > u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE], > int lane); > -u8 drm_dp_get_adjust_request_post_cursor(const u8 > link_status[DP_LINK_STATUS_SIZE], > - unsigned int lane); > > #define DP_BRANCH_OUI_HEADER_SIZE0xc > #define DP_RECEIVER_CAP_SIZE 0xf > -- > 2.30.2 > -- Kees Cook
Re: [PATCH v2] drm/dp: Fix off-by-one in register cache size
Ping. This is a OOB read fix. Can something send this to Linus please? -Kees On Wed, Jan 05, 2022 at 09:33:10AM -0800, Kees Cook wrote: > The pcon_dsc_dpcd array holds 13 registers (0x92 through 0x9E). Fix the > math to calculate the max size. Found from a -Warray-bounds build: > > drivers/gpu/drm/drm_dp_helper.c: In function 'drm_dp_pcon_dsc_bpp_incr': > drivers/gpu/drm/drm_dp_helper.c:3130:28: error: array subscript 12 is outside > array bounds of 'const u8[12]' {aka 'const unsigned char[12]'} > [-Werror=array-bounds] > 3130 | buf = pcon_dsc_dpcd[DP_PCON_DSC_BPP_INCR - > DP_PCON_DSC_ENCODER]; > | > ~^~~~ > drivers/gpu/drm/drm_dp_helper.c:3126:39: note: while referencing > 'pcon_dsc_dpcd' > 3126 | int drm_dp_pcon_dsc_bpp_incr(const u8 > pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]) > | > ~^~~ > > Cc: Maarten Lankhorst > Cc: Maxime Ripard > Cc: Thomas Zimmermann > Cc: David Airlie > Cc: Daniel Vetter > Cc: dri-devel@lists.freedesktop.org > Fixes: e2e16da398d9 ("drm/dp_helper: Add support for Configuring DSC for > HDMI2.1 Pcon") > Cc: sta...@vger.kernel.org > Reviewed-by: Gustavo A. R. Silva > Link: https://lore.kernel.org/lkml/20211214001849.GA62559@embeddedor/ > Signed-off-by: Kees Cook > --- > v1: > https://lore.kernel.org/lkml/20211203084333.3105038-1-keesc...@chromium.org/ > v2: > - add reviewed-by > - add cc:stable > --- > include/drm/drm_dp_helper.h | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 30359e434c3f..472dac376284 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -456,7 +456,7 @@ struct drm_panel; > #define DP_FEC_CAPABILITY_1 0x091 /* 2.0 */ > > /* DP-HDMI2.1 PCON DSC ENCODER SUPPORT */ > -#define DP_PCON_DSC_ENCODER_CAP_SIZE0xC /* 0x9E - 0x92 */ > +#define DP_PCON_DSC_ENCODER_CAP_SIZE0xD /* 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) > -- > 2.30.2 > -- Kees Cook
Re: [PATCH v6 3/4] drm/bridge: anx7625: Support reading edid through aux channel
Hi Hsin-Yi, Thank you for the patch! Yet something to improve: [auto build test ERROR on drm/drm-next] [cannot apply to robh/for-next drm-intel/for-linux-next v5.17-rc2] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Hsin-Yi-Wang/drm-bridge-anx7625-send-DPCD-command-to-downstream/20220203-221108 base: git://anongit.freedesktop.org/drm/drm drm-next config: arm64-randconfig-c023-20220130 (https://download.01.org/0day-ci/archive/20220205/202202050942.c6c0md0r-...@intel.com/config) compiler: aarch64-linux-gcc (GCC) 11.2.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/45f3728b61c8cb8d53d13d88c33c4f58630dcea6 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Hsin-Yi-Wang/drm-bridge-anx7625-send-DPCD-command-to-downstream/20220203-221108 git checkout 45f3728b61c8cb8d53d13d88c33c4f58630dcea6 # save the config file to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arm64 SHELL=/bin/bash If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All errors (new ones prefixed by >>): aarch64-linux-ld: Unexpected GOT/PLT entries detected! aarch64-linux-ld: Unexpected run-time procedure linkages detected! aarch64-linux-ld: ID map text too big or misaligned aarch64-linux-ld: drivers/gpu/drm/bridge/analogix/anx7625.o: in function `anx7625_bridge_detach': >> anx7625.c:(.text+0x5e0): undefined reference to `drm_dp_aux_unregister' aarch64-linux-ld: drivers/gpu/drm/bridge/analogix/anx7625.o: in function `anx7625_bridge_attach': >> anx7625.c:(.text+0x674): undefined reference to `drm_dp_aux_register' aarch64-linux-ld: drivers/gpu/drm/bridge/analogix/anx7625.o: in function `anx7625_i2c_probe': >> anx7625.c:(.text+0x1b48): undefined reference to `drm_dp_aux_init' --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org
Re: [PATCH v4 2/2] drm/msm/dp: rewrite dss_module_power to use bulk clock functions
On 1/31/2022 1:05 PM, Dmitry Baryshkov wrote: In order to simplify DP code, drop hand-coded loops over clock arrays, replacing them with clk_bulk_* functions. Signed-off-by: Dmitry Baryshkov Tested-by: Jessica Zhang # RB3 (sdm845) and RB5 (qrb5165) Reviewed-by: Jessica Zhang --- drivers/gpu/drm/msm/Makefile | 1 - drivers/gpu/drm/msm/dp/dp_clk_util.c | 120 --- drivers/gpu/drm/msm/dp/dp_clk_util.h | 38 - drivers/gpu/drm/msm/dp/dp_ctrl.c | 19 ++--- drivers/gpu/drm/msm/dp/dp_parser.c | 21 - drivers/gpu/drm/msm/dp/dp_parser.h | 17 +++- drivers/gpu/drm/msm/dp/dp_power.c| 91 7 files changed, 100 insertions(+), 207 deletions(-) delete mode 100644 drivers/gpu/drm/msm/dp/dp_clk_util.c delete mode 100644 drivers/gpu/drm/msm/dp/dp_clk_util.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index a44abf0a7660..ecf01f9989ed 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -101,7 +101,6 @@ msm-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \ dp/dp_catalog.o \ - dp/dp_clk_util.o \ dp/dp_ctrl.o \ dp/dp_display.o \ dp/dp_drm.o \ diff --git a/drivers/gpu/drm/msm/dp/dp_clk_util.c b/drivers/gpu/drm/msm/dp/dp_clk_util.c deleted file mode 100644 index 44a4fc59ff31.. --- a/drivers/gpu/drm/msm/dp/dp_clk_util.c +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2012-2015, 2017-2018, The Linux Foundation. - * All rights reserved. - */ - -#include -#include -#include -#include -#include - -#include - -#include "dp_clk_util.h" - -void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk) -{ - int i; - - for (i = num_clk - 1; i >= 0; i--) { - if (clk_arry[i].clk) - clk_put(clk_arry[i].clk); - clk_arry[i].clk = NULL; - } -} - -int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk) -{ - int i, rc = 0; - - for (i = 0; i < num_clk; i++) { - clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name); - rc = PTR_ERR_OR_ZERO(clk_arry[i].clk); - if (rc) { - DEV_ERR("%pS->%s: '%s' get failed. rc=%d\n", - __builtin_return_address(0), __func__, - clk_arry[i].clk_name, rc); - goto error; - } - } - - return rc; - -error: - for (i--; i >= 0; i--) { - if (clk_arry[i].clk) - clk_put(clk_arry[i].clk); - clk_arry[i].clk = NULL; - } - - return rc; -} - -int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk) -{ - int i, rc = 0; - - for (i = 0; i < num_clk; i++) { - if (clk_arry[i].clk) { - if (clk_arry[i].type != DSS_CLK_AHB) { - DEV_DBG("%pS->%s: '%s' rate %ld\n", - __builtin_return_address(0), __func__, - clk_arry[i].clk_name, - clk_arry[i].rate); - rc = clk_set_rate(clk_arry[i].clk, - clk_arry[i].rate); - if (rc) { - DEV_ERR("%pS->%s: %s failed. rc=%d\n", - __builtin_return_address(0), - __func__, - clk_arry[i].clk_name, rc); - break; - } - } - } else { - DEV_ERR("%pS->%s: '%s' is not available\n", - __builtin_return_address(0), __func__, - clk_arry[i].clk_name); - rc = -EPERM; - break; - } - } - - return rc; -} - -int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable) -{ - int i, rc = 0; - - if (enable) { - for (i = 0; i < num_clk; i++) { - DEV_DBG("%pS->%s: enable '%s'\n", - __builtin_return_address(0), __func__, - clk_arry[i].clk_name); - rc = clk_prepare_enable(clk_arry[i].clk); - if (rc) - DEV_ERR("%pS->%s: %s en fail. rc=%d\n", - __builtin_return_address(0), - __func__, - clk_arry[i].clk_name, rc); - - if (rc && i) { -
Re: [Freedreno] [PATCH v4 1/2] drm/msm/dpu: simplify clocks handling
On 1/31/2022 1:05 PM, Dmitry Baryshkov wrote: DPU driver contains code to parse clock items from device tree into special data struct and then enable/disable/set rate for the clocks using that data struct. However the DPU driver itself uses only parsing and enabling/disabling part (the rate setting is used by DP driver). Move this implementation to the DP driver (which actually uses rate setting) and replace hand-coded enable/disable/get loops in the DPU with the respective clk_bulk operations. Put operation is removed completely because, it is handled using devres instead. DP implementation is unchanged for now. Signed-off-by: Dmitry Baryshkov Tested-by: Jessica Zhang # RB3 (sdm845) and RB5 (qrb5165) Reviewed-by: Jessica Zhang --- drivers/gpu/drm/msm/Makefile | 2 +- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 24 ++- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h | 6 +- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 46 +++-- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 4 +- drivers/gpu/drm/msm/disp/dpu1/dpu_mdss.c | 26 +++ .../dpu1/dpu_io_util.c => dp/dp_clk_util.c} | 69 +-- .../dpu1/dpu_io_util.h => dp/dp_clk_util.h} | 8 +-- drivers/gpu/drm/msm/dp/dp_parser.h| 2 +- 9 files changed, 37 insertions(+), 150 deletions(-) rename drivers/gpu/drm/msm/{disp/dpu1/dpu_io_util.c => dp/dp_clk_util.c} (61%) rename drivers/gpu/drm/msm/{disp/dpu1/dpu_io_util.h => dp/dp_clk_util.h} (85%) diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 03ab55c37beb..a44abf0a7660 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -66,7 +66,6 @@ msm-y := \ disp/dpu1/dpu_hw_top.o \ disp/dpu1/dpu_hw_util.o \ disp/dpu1/dpu_hw_vbif.o \ - disp/dpu1/dpu_io_util.o \ disp/dpu1/dpu_kms.o \ disp/dpu1/dpu_mdss.o \ disp/dpu1/dpu_plane.o \ @@ -102,6 +101,7 @@ msm-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \ dp/dp_catalog.o \ + dp/dp_clk_util.o \ dp/dp_ctrl.o \ dp/dp_display.o \ dp/dp_drm.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c index 60fe06018581..4d184122d63e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -284,17 +284,6 @@ void dpu_core_perf_crtc_release_bw(struct drm_crtc *crtc) } } -static int _dpu_core_perf_set_core_clk_rate(struct dpu_kms *kms, u64 rate) -{ - struct dss_clk *core_clk = kms->perf.core_clk; - - if (core_clk->max_rate && (rate > core_clk->max_rate)) - rate = core_clk->max_rate; - - core_clk->rate = rate; - return dev_pm_opp_set_rate(&kms->pdev->dev, core_clk->rate); -} - static u64 _dpu_core_perf_get_core_clk_rate(struct dpu_kms *kms) { u64 clk_rate = kms->perf.perf_tune.min_core_clk; @@ -306,7 +295,7 @@ static u64 _dpu_core_perf_get_core_clk_rate(struct dpu_kms *kms) dpu_cstate = to_dpu_crtc_state(crtc->state); clk_rate = max(dpu_cstate->new_perf.core_clk_rate, clk_rate); - clk_rate = clk_round_rate(kms->perf.core_clk->clk, + clk_rate = clk_round_rate(kms->perf.core_clk, clk_rate); } } @@ -405,10 +394,11 @@ int dpu_core_perf_crtc_update(struct drm_crtc *crtc, trace_dpu_core_perf_update_clk(kms->dev, stop_req, clk_rate); - ret = _dpu_core_perf_set_core_clk_rate(kms, clk_rate); + if (clk_rate > kms->perf.max_core_clk_rate) + clk_rate = kms->perf.max_core_clk_rate; + ret = dev_pm_opp_set_rate(&kms->pdev->dev, clk_rate); if (ret) { - DPU_ERROR("failed to set %s clock rate %llu\n", - kms->perf.core_clk->clk_name, clk_rate); + DPU_ERROR("failed to set core clock rate %llu\n", clk_rate); return ret; } @@ -529,13 +519,13 @@ void dpu_core_perf_destroy(struct dpu_core_perf *perf) int dpu_core_perf_init(struct dpu_core_perf *perf, struct drm_device *dev, struct dpu_mdss_cfg *catalog, - struct dss_clk *core_clk) + struct clk *core_clk) { perf->dev = dev; perf->catalog = catalog; perf->core_clk = core_clk; - perf->max_core_clk_rate = core_clk->max_rate; + perf->max_core_clk_rate = clk_get_rate(core_clk); if (!perf->max_core_clk_rate) { DPU_DEBUG("optional max core clk rate, use default\n"); perf->max_core_clk_rate = DPU_PERF_DEFAULT_MAX_CORE_CLK_RATE; diff --git a/driver
[PATCH v2 3/3] drm/panel-edp: Allow querying the detected panel via debugfs
Recently we added generic "edp-panel"s probed by EDID. To support panels in this way we look at the panel ID in the EDID and look up the panel in a table that has power sequence timings. If we find a panel that's not in the table we will still attempt to use it but we'll use conservative timings. While it's likely that these conservative timings will work for most nearly all panels, the performance of turning the panel off and on suffers. We'd like to be able to reliably detect the case that we're using the hardcoded timings without relying on parsing dmesg. This allows us to implement tests that ensure that no devices get shipped that are relying on the conservative timings. Let's add a new debugfs entry to panel devices. It will have one of: * UNKNOWN - We tried to detect a panel but it wasn't in our table. * HARDCODED - We're not using generic "edp-panel" probed by EDID. * A panel name - This is the name of the panel from our table. Signed-off-by: Douglas Anderson --- Changes in v2: - Now using debugfs, not sysfs drivers/gpu/drm/panel/panel-edp.c | 37 ++- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index a394a15dc3fb..0fda1eb7b690 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -21,6 +21,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -222,6 +223,8 @@ struct panel_edp { struct gpio_desc *enable_gpio; struct gpio_desc *hpd_gpio; + const struct edp_panel_entry *detected_panel; + struct edid *edid; struct drm_display_mode override_mode; @@ -606,6 +609,28 @@ static int panel_edp_get_timings(struct drm_panel *panel, return p->desc->num_timings; } +static int detected_panel_show(struct seq_file *s, void *data) +{ + struct drm_panel *panel = s->private; + struct panel_edp *p = to_panel_edp(panel); + + if (IS_ERR(p->detected_panel)) + seq_puts(s, "UNKNOWN\n"); + else if (!p->detected_panel) + seq_puts(s, "HARDCODED\n"); + else + seq_printf(s, "%s\n", p->detected_panel->name); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(detected_panel); + +static void panel_edp_debugfs_init(struct drm_panel *panel, struct dentry *root) +{ + debugfs_create_file("detected_panel", 0600, root, panel, &detected_panel_fops); +} + static const struct drm_panel_funcs panel_edp_funcs = { .disable = panel_edp_disable, .unprepare = panel_edp_unprepare, @@ -613,6 +638,7 @@ static const struct drm_panel_funcs panel_edp_funcs = { .enable = panel_edp_enable, .get_modes = panel_edp_get_modes, .get_timings = panel_edp_get_timings, + .debugfs_init = panel_edp_debugfs_init, }; #define PANEL_EDP_BOUNDS_CHECK(to_check, bounds, field) \ @@ -666,7 +692,6 @@ static const struct edp_panel_entry *find_edp_panel(u32 panel_id); static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) { - const struct edp_panel_entry *edp_panel; struct panel_desc *desc; u32 panel_id; char vend[4]; @@ -705,14 +730,14 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) } drm_edid_decode_panel_id(panel_id, vend, &product_id); - edp_panel = find_edp_panel(panel_id); + panel->detected_panel = find_edp_panel(panel_id); /* * We're using non-optimized timings and want it really obvious that * someone needs to add an entry to the table, so we'll do a WARN_ON * splat. */ - if (WARN_ON(!edp_panel)) { + if (WARN_ON(!panel->detected_panel)) { dev_warn(dev, "Unknown panel %s %#06x, using conservative timings\n", vend, product_id); @@ -734,12 +759,14 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) */ desc->delay.unprepare = 2000; desc->delay.enable = 200; + + panel->detected_panel = ERR_PTR(-EINVAL); } else { dev_info(dev, "Detected %s %s (%#06x)\n", -vend, edp_panel->name, product_id); +vend, panel->detected_panel->name, product_id); /* Update the delay; everything else comes from EDID */ - desc->delay = *edp_panel->delay; + desc->delay = *panel->detected_panel->delay; } ret = 0; -- 2.35.0.263.gb82422642f-goog
[PATCH v2 2/3] drm: Plumb debugfs_init through to panels
We'd like panels to be able to add things to debugfs underneath the connector's directory. Let's plumb it through. A panel will be able to put things in a "panel" directory under the connector's directory. Note that debugfs is not ABI and so it's always possible that the location that the panel gets for its debugfs could change in the future. NOTE: this currently only works if you're using a modern architecture. Specifically the plumbing relies on _both_ drm_bridge_connector and drm_panel_bridge. If you're not using one or both of these things then things won't be plumbed through. As a side effect of this change, drm_bridges can also get callbacks to put stuff underneath the connector's debugfs directory. At the moment all bridges in the chain have their debugfs_init() called with the connector's root directory. Signed-off-by: Douglas Anderson --- Changes in v2: - ("drm: Plumb debugfs_init through to panels") new for v2. drivers/gpu/drm/bridge/panel.c | 12 drivers/gpu/drm/drm_bridge_connector.c | 15 +++ drivers/gpu/drm/drm_debugfs.c | 3 +++ include/drm/drm_bridge.h | 7 +++ include/drm/drm_connector.h| 7 +++ include/drm/drm_panel.h| 8 6 files changed, 52 insertions(+) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index b32295abd9e7..5be057575183 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -138,6 +138,17 @@ static int panel_bridge_get_modes(struct drm_bridge *bridge, return drm_panel_get_modes(panel_bridge->panel, connector); } +static void panel_bridge_debugfs_init(struct drm_bridge *bridge, + struct dentry *root) +{ + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); + struct drm_panel *panel = panel_bridge->panel; + + root = debugfs_create_dir("panel", root); + if (panel->funcs->debugfs_init) + panel->funcs->debugfs_init(panel, root); +} + static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .attach = panel_bridge_attach, .detach = panel_bridge_detach, @@ -150,6 +161,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, + .debugfs_init = panel_bridge_debugfs_init, }; /** diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index 791379816837..60923cdfe8e1 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -216,6 +216,20 @@ static void drm_bridge_connector_destroy(struct drm_connector *connector) kfree(bridge_connector); } +static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, + struct dentry *root) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_encoder *encoder = bridge_connector->encoder; + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->debugfs_init) + bridge->funcs->debugfs_init(bridge, root); + } +} + static const struct drm_connector_funcs drm_bridge_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = drm_bridge_connector_detect, @@ -223,6 +237,7 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = { .destroy = drm_bridge_connector_destroy, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .debugfs_init = drm_bridge_connector_debugfs_init, }; /* - diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b0a826489488..7f1b82dbaebb 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -436,6 +436,9 @@ void drm_debugfs_connector_add(struct drm_connector *connector) /* vrr range */ debugfs_create_file("vrr_range", S_IRUGO, root, connector, &vrr_range_fops); + + if (connector->funcs->debugfs_init) + connector->funcs->debugfs_init(connector, root); } void drm_debugfs_connector_remove(struct drm_connector *connector) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 061d87313fac..f27b4060faa2 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -649,6 +649,13 @@ struct drm_bridge_funcs { * the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->o
[PATCH v2 1/3] drm/bridge: ti-sn65dsi86: Use drm_bridge_connector
The ti-sn65dsi86 driver shouldn't hand-roll its own bridge connector. It should use the normal drm_bridge_connector. Let's switch to do that, removing all of the custom code. NOTE: this still _doesn't_ implement DRM_BRIDGE_ATTACH_NO_CONNECTOR support for ti-sn65dsi86 and that would still be a useful thing to do in the future. It was attempted in the past [1] but put on the back burner. However, unless we instantly change ti-sn65dsi86 fully from not supporting DRM_BRIDGE_ATTACH_NO_CONNECTOR at all to _only_ supporting DRM_BRIDGE_ATTACH_NO_CONNECTOR then we'll still need a bit of time when we support both. This is a better way to support the old way where the driver hand rolls things itself. A new notes about the implementation here: * When using the drm_bridge_connector the connector should be created after all the bridges, so we change the ordering a bit. * I'm reasonably certain that we don't need to do anything to "free" the new drm_bridge_connector. If drm_bridge_connector_init() returns success then we know drm_connector_init() was called with the `drm_bridge_connector_funcs`. The `drm_bridge_connector_funcs` has a .destroy() that does all the cleanup. drm_connector_init() calls __drm_mode_object_add() with a drm_connector_free() that will call the .destroy(). * I'm also reasonably certain that I don't need to "undo" the drm_bridge_attach() if drm_bridge_connector_init() fails. The "detach" function is private and other similar code doesn't try to undo the drm_bridge_attach() in error cases. There's also a comment indicating the lack of balance at the top of drm_bridge_attach(). [1] https://lore.kernel.org/r/20210920225801.227211-4-robdcl...@gmail.com Signed-off-by: Douglas Anderson --- Changes in v2: - ("ti-sn65dsi86: Use drm_bridge_connector") new for v2. drivers/gpu/drm/bridge/ti-sn65dsi86.c | 72 ++- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index ba136a188be7..38616aab12ac 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -174,7 +175,7 @@ struct ti_sn65dsi86 { struct regmap *regmap; struct drm_dp_aux aux; struct drm_bridge bridge; - struct drm_connectorconnector; + struct drm_connector*connector; struct device_node *host_node; struct mipi_dsi_device *dsi; struct clk *refclk; @@ -646,54 +647,6 @@ static struct auxiliary_driver ti_sn_aux_driver = { .id_table = ti_sn_aux_id_table, }; -/* - - * DRM Connector Operations - */ - -static struct ti_sn65dsi86 * -connector_to_ti_sn65dsi86(struct drm_connector *connector) -{ - return container_of(connector, struct ti_sn65dsi86, connector); -} - -static int ti_sn_bridge_connector_get_modes(struct drm_connector *connector) -{ - struct ti_sn65dsi86 *pdata = connector_to_ti_sn65dsi86(connector); - - return drm_bridge_get_modes(pdata->next_bridge, connector); -} - -static struct drm_connector_helper_funcs ti_sn_bridge_connector_helper_funcs = { - .get_modes = ti_sn_bridge_connector_get_modes, -}; - -static const struct drm_connector_funcs ti_sn_bridge_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static int ti_sn_bridge_connector_init(struct ti_sn65dsi86 *pdata) -{ - int ret; - - ret = drm_connector_init(pdata->bridge.dev, &pdata->connector, -&ti_sn_bridge_connector_funcs, -DRM_MODE_CONNECTOR_eDP); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } - - drm_connector_helper_add(&pdata->connector, -&ti_sn_bridge_connector_helper_funcs); - drm_connector_attach_encoder(&pdata->connector, pdata->bridge.encoder); - - return 0; -} - /*-- * DRM Bridge */ @@ -757,10 +710,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, return ret; } - ret = ti_sn_bridge_connector_init(pdata); - if (ret < 0) - goto err_conn_init; - /* We never want the next bridge to *also* create a connector: */ flags |= DRM_BRIDGE_ATTACH_NO_CONNECTOR; @@ -768,13 +717,20 @@ static int
[PATCH v2 0/3] drm/panel-edp: Debugfs for panel-edp
The main goal of this series is the final patch in the series: to allow test code to reliably find out if we ended up hitting the "fallback" case of the generic edp-panel driver where we don't recognize a panel and choose to use super conservative timing. Version 1 of the patch actually landed but was quickly reverted since it was pointed out that it should have been done in debugfs, not sysfs. As discussed on IRC [1], we want this support to be under the "connector" directory of debugfs but there was no existing way to do that. Thus patch #2 in the series was born to try to plumb this through. It was asserted that it would be OK to rely on a fairly modern display pipeline for this plumbing and perhaps fail to populate the debugfs file if we're using older/deprecated pipelines. Patch #1 in the series was born because the bridge chip I was using was still using an older/deprecated pipeline. While this doesn't get us fully to a modern pipeline for ti-sn65dsi86 (it still doesn't move to "NO_CONNECTOR") it hopefully moves us in the right direction. This was tested on sc7180-trogdor devices with _both_ the ti-sn65dsi86 and the parade-ps8640 bridge chips (since some devices have one, some the other). The parade-ps8640 already uses supports "NO_CONNECTOR", luckily. [1] https://people.freedesktop.org/~cbrill/dri-log/?channel=dri-devel&date=2022-02-02 Changes in v2: - ("ti-sn65dsi86: Use drm_bridge_connector") new for v2. - ("drm: Plumb debugfs_init through to panels") new for v2. - Now using debugfs, not sysfs Douglas Anderson (3): drm/bridge: ti-sn65dsi86: Use drm_bridge_connector drm: Plumb debugfs_init through to panels drm/panel-edp: Allow querying the detected panel via debugfs drivers/gpu/drm/bridge/panel.c | 12 + drivers/gpu/drm/bridge/ti-sn65dsi86.c | 72 +- drivers/gpu/drm/drm_bridge_connector.c | 15 ++ drivers/gpu/drm/drm_debugfs.c | 3 ++ drivers/gpu/drm/panel/panel-edp.c | 37 +++-- include/drm/drm_bridge.h | 7 +++ include/drm/drm_connector.h| 7 +++ include/drm/drm_panel.h| 8 +++ 8 files changed, 98 insertions(+), 63 deletions(-) -- 2.35.0.263.gb82422642f-goog
Re: [PATCH 05/12] drm/msm/dpu: add an API to reset the encoder related hw blocks
On 05/02/2022 00:17, Abhinav Kumar wrote: Add an API to reset the encoder related hw blocks to ensure a proper teardown of the pipeline. At the moment this is being used only for the writeback encoder but eventually we can start using this for all interfaces. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 92 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 10 +++ 2 files changed, 102 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 1e648db..e977c05 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -21,6 +22,7 @@ #include "dpu_hw_intf.h" #include "dpu_hw_ctl.h" #include "dpu_hw_dspp.h" +#include "dpu_hw_merge3d.h" #include "dpu_formats.h" #include "dpu_encoder_phys.h" #include "dpu_crtc.h" @@ -1813,6 +1815,96 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc) DPU_ATRACE_END("encoder_kickoff"); } +static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) +{ + struct dpu_hw_mixer_cfg mixer; + int i, num_lm; + u32 flush_mask = 0; + struct dpu_global_state *global_state; + struct dpu_hw_blk *hw_lm[2]; + struct dpu_hw_mixer *hw_mixer[2]; + struct dpu_hw_ctl *ctl = phys_enc->hw_ctl; + + memset(&mixer, 0, sizeof(mixer)); + + /* reset all mixers for this encoder */ + if (phys_enc->hw_ctl->ops.clear_all_blendstages) + phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl); + + global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms); + + num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state, + phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); + + for (i = 0; i < num_lm; i++) { + hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]); + flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, hw_mixer[i]->idx); + if (phys_enc->hw_ctl->ops.update_pending_flush) + phys_enc->hw_ctl->ops.update_pending_flush(ctl, flush_mask); + + /* clear all blendstages */ + if (phys_enc->hw_ctl->ops.setup_blendstage) + phys_enc->hw_ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL); + } +} + +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) +{ + struct dpu_hw_ctl *ctl = phys_enc->hw_ctl; + struct dpu_hw_intf_cfg intf_cfg = { 0 }; + int i; + struct dpu_encoder_virt *dpu_enc; + + dpu_enc = to_dpu_encoder_virt(phys_enc->parent); + + phys_enc->hw_ctl->ops.reset(ctl); + + dpu_encoder_helper_reset_mixers(phys_enc); + + if (phys_enc->hw_wb) { + /* disable the PP block */ + if (phys_enc->hw_wb->ops.bind_pingpong_blk) + phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, false, + phys_enc->hw_pp->idx); + + /* mark WB flush as pending */ + if (phys_enc->hw_ctl->ops.update_pending_flush_wb) + phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx); + } else { + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk) + phys_enc->hw_intf->ops.bind_pingpong_blk( + dpu_enc->phys_encs[i]->hw_intf, false, + dpu_enc->phys_encs[i]->hw_pp->idx); + /* mark INTF flush as pending */ + if (phys_enc->hw_ctl->ops.update_pending_flush_intf) + phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl, + dpu_enc->phys_encs[i]->hw_intf->idx); + } + } + + /* reset the merge 3D HW block */ + if (phys_enc->hw_pp->merge_3d) { + phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d, + BLEND_3D_NONE); + if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d) + phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl, + phys_enc->hw_pp->merge_3d->idx); + } + + intf_cfg.stream_sel = 0; /* Don't care value for video mode */ + intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); + if
Re: [PATCH 03/12] drm/msm/dpu: add writeback blocks to DPU RM
On 05/02/2022 00:17, Abhinav Kumar wrote: Add writeback blocks to DPU resource manager so that writeback encoders can request for writeback hardware blocks through RM and their usage can be tracked. Signed-off-by: Abhinav Kumar [please excuse me for the duplicate, I've sent the email without the proper distribution list] We have WB blocks being allocated manually. Could you please consider following the ideas from https://patchwork.freedesktop.org/patch/470394/?series=99175&rev=1 ? I think it simplifies the code and shows exact correspondence between WB and dpu_encoder. --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 3 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 71 + drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 2 + 4 files changed, 78 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index e241914..cc10436 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -21,9 +22,11 @@ /** * Encoder functions and data types * @intfs:Interfaces this encoder is using, INTF_MODE_NONE if unused + * @wbs:Writeback blocks this encoder is using */ struct dpu_encoder_hw_resources { enum dpu_intf_mode intfs[INTF_MAX]; + enum dpu_intf_mode wbs[WB_MAX]; }; /** diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h index 2d385b4..1e00804 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -146,6 +147,7 @@ struct dpu_global_state { uint32_t ctl_to_enc_id[CTL_MAX - CTL_0]; uint32_t intf_to_enc_id[INTF_MAX - INTF_0]; uint32_t dspp_to_enc_id[DSPP_MAX - DSPP_0]; + uint32_t wb_to_enc_id[WB_MAX - WB_0]; }; struct dpu_global_state diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index f9c83d6..edd0b7a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ @@ -9,6 +10,7 @@ #include "dpu_hw_ctl.h" #include "dpu_hw_pingpong.h" #include "dpu_hw_intf.h" +#include "dpu_hw_wb.h" #include "dpu_hw_dspp.h" #include "dpu_hw_merge3d.h" #include "dpu_encoder.h" @@ -75,6 +77,14 @@ int dpu_rm_destroy(struct dpu_rm *rm) dpu_hw_intf_destroy(hw); } } + for (i = 0; i < ARRAY_SIZE(rm->wb_blks); i++) { + struct dpu_hw_wb *hw; + + if (rm->wb_blks[i]) { + hw = to_dpu_hw_wb(rm->wb_blks[i]); + dpu_hw_wb_destroy(hw); + } + } return 0; } @@ -187,6 +197,24 @@ int dpu_rm_init(struct dpu_rm *rm, rm->intf_blks[intf->id - INTF_0] = &hw->base; } + for (i = 0; i < cat->wb_count; i++) { + struct dpu_hw_wb *hw; + const struct dpu_wb_cfg *wb = &cat->wb[i]; + + if (wb->id < WB_0 || wb->id >= WB_MAX) { + DPU_ERROR("skip intf %d with invalid id\n", wb->id); + continue; + } + + hw = dpu_hw_wb_init(wb->id, mmio, cat); + if (IS_ERR_OR_NULL(hw)) { + rc = PTR_ERR(hw); + DPU_ERROR("failed wb object creation: err %d\n", rc); + goto fail; + } + rm->wb_blks[wb->id - WB_0] = &hw->base; + } + for (i = 0; i < cat->ctl_count; i++) { struct dpu_hw_ctl *hw; const struct dpu_ctl_cfg *ctl = &cat->ctl[i]; @@ -479,6 +507,33 @@ static int _dpu_rm_reserve_intf( return 0; } +static int _dpu_rm_reserve_wb( + struct dpu_rm *rm, + struct dpu_global_state *global_state, + uint32_t enc_id, + uint32_t id) +{ + int idx = id - WB_0; + + if (idx < 0 || idx >= ARRAY_SIZE(rm->wb_blks)) { + DPU_ERROR("invalid intf id: %d", id); + return -EINVAL; + } + + if (!rm->wb_blks[idx]) { +
Re: [PATCH 07/12] drm/msm/dpu: add encoder operations to prepare/cleanup wb job
On 05/02/2022 00:17, Abhinav Kumar wrote: add dpu encoder APIs to prepare and cleanup writeback job for the writeback encoder. These shall be invoked from the prepare_wb_job/cleanup_wb_job hooks of the drm_writeback framework. Having dpu-encoder-wide API for the single case of WB looks like an overkill. But I think we have no other choice here. Reviewed-by: Dmitry Baryshkov Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 34 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 16 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 5 3 files changed, 55 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 947069b..b51a677 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -958,6 +958,40 @@ static int dpu_encoder_resource_control(struct drm_encoder *drm_enc, return 0; } +void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job) +{ + struct dpu_encoder_virt *dpu_enc; + int i; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i]; + + if (phys->ops.prepare_wb_job) + phys->ops.prepare_wb_job(phys, job); + + } +} + +void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job) +{ + struct dpu_encoder_virt *dpu_enc; + int i; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i]; + + if (phys->ops.cleanup_wb_job) + phys->ops.cleanup_wb_job(phys, job); + + } +} + static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index cc10436..da5b6d6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -171,4 +171,20 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc); +/** + * dpu_encoder_prepare_wb_job - prepare writeback job for the encoder. + * @drm_enc:Pointer to previously created drm encoder structure + * @job:Pointer to the current drm writeback job + */ +void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job); + +/** + * dpu_encoder_cleanup_wb_job - cleanup writeback job for the encoder. + * @drm_enc:Pointer to previously created drm encoder structure + * @job:Pointer to the current drm writeback job + */ +void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 07c3525..7b3354d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -7,6 +7,7 @@ #ifndef __DPU_ENCODER_PHYS_H__ #define __DPU_ENCODER_PHYS_H__ +#include #include #include "dpu_kms.h" @@ -146,6 +147,10 @@ struct dpu_encoder_phys_ops { void (*restore)(struct dpu_encoder_phys *phys); int (*get_line_count)(struct dpu_encoder_phys *phys); int (*get_frame_count)(struct dpu_encoder_phys *phys); + void (*prepare_wb_job)(struct dpu_encoder_phys *phys_enc, + struct drm_writeback_job *job); + void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc, + struct drm_writeback_job *job); }; /** -- With best wishes Dmitry
Re: [PATCH v3 1/3] drm/msm/dp: revise timing engine programming to support widebus feature
On 05/02/2022 02:26, Kuogee Hsieh wrote: On 2/4/2022 2:05 PM, Dmitry Baryshkov wrote: On 04/02/2022 21:36, Kuogee Hsieh wrote: Widebus feature will transmit two pixel data per pixel clock to interface. Timing engine provides driving force for this purpose. This patch base on HPG (Hardware Programming Guide) to revise timing engine register setting to accommodate both widebus and non widebus application. Also horizontal width parameters need to be reduced by half since two pixel data are clocked out per pixel clock when widebus feature enabled. In addition, revised timing engine function is an generic function and intend to be shared by all platforms to reduce maintenance efforts. Changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 + .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 14 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 99 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 2 + 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0d315b4..0c22839 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -208,6 +208,8 @@ struct dpu_encoder_virt { u32 idle_timeout; + bool wide_bus_en; + struct msm_dp *dp; }; @@ -217,6 +219,14 @@ static u32 dither_matrix[DITHER_MATRIX_SZ] = { 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10 }; + +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); + + return dpu_enc->wide_bus_en; +} + static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc) { struct dpu_hw_dither_cfg dither_cfg = { 0 }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 99a5d73..893d74d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -168,4 +168,6 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc); +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ This chunk does not apply against the msm-next. The conflict is trivial, but it would be nice to know that the code was tested against the current tip. My fault, I forget to cherry-pick to msm-next this time before upload for review diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 185379b..3d6c914 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -110,6 +110,20 @@ static void drm_mode_to_intf_timing_params( timing->v_back_porch += timing->v_front_porch; timing->v_front_porch = 0; } + + timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent); + + /* + * for DP, divide the horizonal parameters by 2 when + * widebus is enabled + */ + if (timing->wide_bus_en) { + timing->width = timing->width >> 1; + timing->xres = timing->xres >> 1; + timing->h_back_porch = timing->h_back_porch >> 1; + timing->h_front_porch = timing->h_front_porch >> 1; + timing->hsync_pulse_width = timing->hsync_pulse_width >> 1; + } } static u32 get_horizontal_total(const struct intf_timing_params *timing) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 116e2b5..35d4aaa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -33,6 +33,7 @@ #define INTF_TP_COLOR1 0x05C #define INTF_CONFIG2 0x060 #define INTF_DISPLAY_DATA_HCTL 0x064 +#define INTF_ACTIVE_DATA_HCTL 0x068 #define INTF_FRAME_LINE_COUNT_EN 0x0A8 #define INTF_FRAME_COUNT 0x0AC #define INTF_LINE_COUNT 0x0B0 @@ -90,68 +91,95 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, u32 hsync_period, vsync_period; u32 display_v_start, display_v_end; u32 hsync_start_x, hsync_end_x; + u32 hsync_data_start_x, hsync_data_end_x; u32 active_h_start, active_h_end; u32 active_v_start, active_v_end; u32 active_hctl, display_hctl, hsync_ctl; u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; u3
Re: [PATCH 06/12] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
On 05/02/2022 00:17, Abhinav Kumar wrote: Make changes to dpu_encoder to support virtual encoder needed to support writeback for dpu. This patch will change significantly if Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 57 + 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index e977c05..947069b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -974,6 +974,7 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC]; struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC]; struct dpu_hw_blk *hw_dspp[MAX_CHANNELS_PER_ENC] = { NULL }; + enum dpu_hw_blk_type blk_type; int num_lm, num_ctl, num_pp; int i, j; @@ -1061,20 +1062,36 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, phys->hw_pp = dpu_enc->hw_pp[i]; phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]); + if (phys->intf_mode == INTF_MODE_WB_LINE) + blk_type = DPU_HW_BLK_WB; + else + blk_type = DPU_HW_BLK_INTF; + num_blk = dpu_rm_get_assigned_resources(&dpu_kms->rm, - global_state, drm_enc->base.id, DPU_HW_BLK_INTF, + global_state, drm_enc->base.id, blk_type, hw_blk, ARRAY_SIZE(hw_blk)); - for (j = 0; j < num_blk; j++) { - struct dpu_hw_intf *hw_intf; - hw_intf = to_dpu_hw_intf(hw_blk[i]); - if (hw_intf->idx == phys->intf_idx) - phys->hw_intf = hw_intf; + if (blk_type == DPU_HW_BLK_WB) { + for (j = 0; j < num_blk; j++) { + struct dpu_hw_wb *hw_wb; + + hw_wb = to_dpu_hw_wb(hw_blk[i]); + if (hw_wb->idx == phys->intf_idx) + phys->hw_wb = hw_wb; + } + } else { + for (j = 0; j < num_blk; j++) { + struct dpu_hw_intf *hw_intf; + + hw_intf = to_dpu_hw_intf(hw_blk[i]); + if (hw_intf->idx == phys->intf_idx) + phys->hw_intf = hw_intf; + } } I think that if we sequentially call dpu_rm_get_assigned_resources(.., DPU_HW_BLK_INTF, ...) and then dpu_rm_get_assigned_resources(.., DPU_HW_BLK_WB, ...), the code would be cleaner. Or even better get the WB direclty using the provided ID. - if (!phys->hw_intf) { + if (!phys->hw_intf && !phys->hw_wb) { DPU_ERROR_ENC(dpu_enc, - "no intf block assigned at idx: %d\n", i); + "no intf or WB block assigned at idx: %d\n", i); return; } @@ -1224,15 +1241,22 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc) mutex_unlock(&dpu_enc->enc_lock); } -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog, +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg *catalog, enum dpu_intf_type type, u32 controller_id) { int i = 0; - for (i = 0; i < catalog->intf_count; i++) { - if (catalog->intf[i].type == type - && catalog->intf[i].controller_id == controller_id) { - return catalog->intf[i].id; + if (type != INTF_WB) { + for (i = 0; i < catalog->intf_count; i++) { + if (catalog->intf[i].type == type + && catalog->intf[i].controller_id == controller_id) { + return catalog->intf[i].id; + } + } + } else { + for (i = 0; i < catalog->wb_count; i++) { + if (catalog->wb[i].id == controller_id) + return catalog->wb[i].id; } } @@ -2096,6 +2120,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, case DRM_MODE_ENCODER_TMDS: intf_type = INTF_DP; break; + case DRM_MODE_ENCODER_VIRTUAL: + intf_type = INTF_WB; + break; } WARN_ON(disp_info->num_of_h_tiles < 1); @@ -2128,11 +2155,11 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n", i, controller_id, phys_params.split_role); - phys_params.intf
Re: [PATCH v3 1/3] drm/msm/dp: revise timing engine programming to support widebus feature
On 2/4/2022 2:05 PM, Dmitry Baryshkov wrote: On 04/02/2022 21:36, Kuogee Hsieh wrote: Widebus feature will transmit two pixel data per pixel clock to interface. Timing engine provides driving force for this purpose. This patch base on HPG (Hardware Programming Guide) to revise timing engine register setting to accommodate both widebus and non widebus application. Also horizontal width parameters need to be reduced by half since two pixel data are clocked out per pixel clock when widebus feature enabled. In addition, revised timing engine function is an generic function and intend to be shared by all platforms to reduce maintenance efforts. Changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 + .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 14 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 99 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 2 + 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0d315b4..0c22839 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -208,6 +208,8 @@ struct dpu_encoder_virt { u32 idle_timeout; + bool wide_bus_en; + struct msm_dp *dp; }; @@ -217,6 +219,14 @@ static u32 dither_matrix[DITHER_MATRIX_SZ] = { 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10 }; + +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); + + return dpu_enc->wide_bus_en; +} + static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc) { struct dpu_hw_dither_cfg dither_cfg = { 0 }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 99a5d73..893d74d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -168,4 +168,6 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc); +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ This chunk does not apply against the msm-next. The conflict is trivial, but it would be nice to know that the code was tested against the current tip. My fault, I forget to cherry-pick to msm-next this time before upload for review diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 185379b..3d6c914 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -110,6 +110,20 @@ static void drm_mode_to_intf_timing_params( timing->v_back_porch += timing->v_front_porch; timing->v_front_porch = 0; } + + timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent); + + /* + * for DP, divide the horizonal parameters by 2 when + * widebus is enabled + */ + if (timing->wide_bus_en) { + timing->width = timing->width >> 1; + timing->xres = timing->xres >> 1; + timing->h_back_porch = timing->h_back_porch >> 1; + timing->h_front_porch = timing->h_front_porch >> 1; + timing->hsync_pulse_width = timing->hsync_pulse_width >> 1; + } } static u32 get_horizontal_total(const struct intf_timing_params *timing) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 116e2b5..35d4aaa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -33,6 +33,7 @@ #define INTF_TP_COLOR1 0x05C #define INTF_CONFIG2 0x060 #define INTF_DISPLAY_DATA_HCTL 0x064 +#define INTF_ACTIVE_DATA_HCTL 0x068 #define INTF_FRAME_LINE_COUNT_EN 0x0A8 #define INTF_FRAME_COUNT 0x0AC #define INTF_LINE_COUNT 0x0B0 @@ -90,68 +91,95 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, u32 hsync_period, vsync_period; u32 display_v_start, display_v_end; u32 hsync_start_x, hsync_end_x; + u32 hsync_data_start_x, hsync_data_end_x; u32 active_h_start, active_h_end; u32 active_v_start, active_v_end; u32 active_hctl, display_hctl, hsync_ctl; u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; u32 panel_format; - u32 intf_cfg, intf_c
Re: [PATCH 09/12] drm/msm/dpu: add the writeback connector layer
On 05/02/2022 00:17, Abhinav Kumar wrote: Introduce the dpu_writeback module which serves as the interface between dpu operations and the drm_writeback. This module manages the connector related operations for dpu writeback. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 71 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 27 ++ 3 files changed, 99 insertions(+) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 3abaf84..05a8515 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -74,6 +74,7 @@ msm-y := \ disp/dpu1/dpu_plane.o \ disp/dpu1/dpu_rm.o \ disp/dpu1/dpu_vbif.o \ + disp/dpu1/dpu_writeback.o \ disp/msm_disp_snapshot.o \ disp/msm_disp_snapshot_util.o \ msm_atomic.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c new file mode 100644 index 000..7b61fad --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "dpu_writeback.h" + +static int dpu_wb_conn_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + + return drm_add_modes_noedid(connector, dev->mode_config.max_width, + dev->mode_config.max_height); +} + +static const struct drm_connector_funcs dpu_wb_conn_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int dpu_wb_conn_prepare_job(struct drm_writeback_connector *connector, + struct drm_writeback_job *job) +{ + if (!job->fb) + return 0; + + dpu_encoder_prepare_wb_job(connector->encoder, job); + + return 0; +} + +static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector *connector, + struct drm_writeback_job *job) +{ + if (!job->fb) + return; + + dpu_encoder_cleanup_wb_job(connector->encoder, job); +} + +static const struct drm_connector_helper_funcs dpu_wb_conn_helper_funcs = { + .get_modes = dpu_wb_conn_get_modes, + .prepare_writeback_job = dpu_wb_conn_prepare_job, + .cleanup_writeback_job = dpu_wb_conn_cleanup_job, +}; + +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, + const struct drm_encoder_helper_funcs *enc_helper_funcs, const u32 *format_list, + u32 num_formats) the enc_helper_funcs is always dpu_encoder_helper_funcs here. And enc->helper_private is already initalized. You can drop the argument. +{ + struct msm_drm_private *priv = dev->dev_private; + struct dpu_wb_connector *dpu_wb_conn; + int rc = 0; + + dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL); + dpu_wb_conn->base.base = &dpu_wb_conn->connector; + dpu_wb_conn->base.encoder = enc; + + drm_connector_helper_add(dpu_wb_conn->base.base, &dpu_wb_conn_helper_funcs); + + rc = drm_writeback_connector_init(dev, &dpu_wb_conn->base, + &dpu_wb_conn_funcs, enc_helper_funcs, + format_list, num_formats); + + priv->connectors[priv->num_connectors++] = dpu_wb_conn->base.base; + + return rc; +} diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h new file mode 100644 index 000..206ce5e --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _DPU_WRITEBACK_H +#define _DPU_WRITEBACK_H + +#include +#include +#include +#include + +#include "msm_drv.h" +#include "dpu_kms.h" +#include "dpu_encoder_phys.h" + +struct dpu_wb_connector { + struct drm_connector connector; + struct drm_writeback_connector base; +}; + +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, + const struct drm_encoder_helper_funcs *enc_helper_funcs, + const u32 *format_list, u32 num_formats); + +#endif /*_DPU_WRITEBACK_H */ -- With best wishes Dmitry
Re: [PATCH 08/12] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
On 05/02/2022 00:17, Abhinav Kumar wrote: Introduce the dpu_encoder_phys_* for the writeback interface to handle writeback specific hardware programming. Signed-off-by: Abhinav Kumar Mostly looks ok, see minor comments bellow. --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 36 +- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c| 813 + 3 files changed, 849 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index c43ef35..3abaf84 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -53,6 +53,7 @@ msm-y := \ disp/dpu1/dpu_encoder.o \ disp/dpu1/dpu_encoder_phys_cmd.o \ disp/dpu1/dpu_encoder_phys_vid.o \ + disp/dpu1/dpu_encoder_phys_wb.o \ disp/dpu1/dpu_formats.o \ disp/dpu1/dpu_hw_catalog.o \ disp/dpu1/dpu_hw_ctl.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 7b3354d..80da0a9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -159,6 +159,7 @@ struct dpu_encoder_phys_ops { * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel * @INTR_IDX_RDPTR:Readpointer done unterrupt for cmd mode panel + * @INTR_IDX_WB_DONE: Writeback fone interrupt for virtual connector */ enum dpu_intr_idx { INTR_IDX_VSYNC, @@ -166,6 +167,7 @@ enum dpu_intr_idx { INTR_IDX_UNDERRUN, INTR_IDX_CTL_START, INTR_IDX_RDPTR, + INTR_IDX_WB_DONE, INTR_IDX_MAX, }; @@ -196,7 +198,7 @@ struct dpu_encoder_irq { * @hw_ctl: Hardware interface to the ctl registers * @hw_pp:Hardware interface to the ping pong registers * @hw_intf: Hardware interface to the intf registers - * @hw_wb: Hardware interface to the wb registers + * @hw_wb: Hardware interface to the wb registers * @dpu_kms: Pointer to the dpu_kms top level * @cached_mode: DRM mode cached at mode_set time, acted on in enable * @enabled: Whether the encoder has enabled and running a mode @@ -250,6 +252,31 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys) } /** + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to handle command + * mode specific operations + * @base: Baseclass physical encoder structure + * @wbirq_refcount: Reference count of writeback interrupt + * @wb_done_timeout_cnt: number of wb done irq timeout errors + * @wb_cfg: writeback block config to store fb related details + * @cdp_cfg: chroma down prefetch block config for wb + * @aspace: address space to be used for wb framebuffer + * @wb_conn: backpointer to writeback connector + * @wb_job: backpointer to current writeback job + * @dest: dpu buffer layout for current writeback output buffer + */ +struct dpu_encoder_phys_wb { + struct dpu_encoder_phys base; + atomic_t wbirq_refcount; + int wb_done_timeout_cnt; + struct dpu_hw_wb_cfg wb_cfg; + struct dpu_hw_wb_cdp_cfg cdp_cfg; What about moving them to the stack rather storing them in the structure? + struct msm_gem_address_space *aspace; + struct drm_writeback_connector *wb_conn; + struct drm_writeback_job *wb_job; + struct dpu_hw_fmt_layout dest; +}; + +/** * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command *mode specific operations * @base: Baseclass physical encoder structure @@ -317,6 +344,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init( struct dpu_enc_phys_init_params *p); /** + * dpu_encoder_phys_wb_init - initialize writeback encoder + * @init: Pointer to init info structure with initialization params + */ +struct dpu_encoder_phys *dpu_encoder_phys_wb_init( + struct dpu_enc_phys_init_params *p); + +/** * dpu_encoder_helper_trigger_start - control start helper function *This helper function may be optionally specified by physical *encoders if they require ctl_start triggering. diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c new file mode 100644 index 000..783f83e --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt)"[drm:%s:%d] " fmt, __func__, __LINE__ + +#include the header is unused + +#include "dpu_encoder_phys.h" +#include "dpu_formats.h" +#include "dpu_hw_top.h" +#include "dpu
Re: [PATCH 02/12] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
On 05/02/2022 00:17, Abhinav Kumar wrote: Add the dpu_hw_wb abstraction to program registers related to the writeback block. These will be invoked once all the configuration is set and ready to be programmed to the registers. Reviewed-by: Dmitry Baryshkov Few minor comments bellow. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 267 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 145 3 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 03ab55c..c43ef35 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -66,6 +66,7 @@ msm-y := \ disp/dpu1/dpu_hw_top.o \ disp/dpu1/dpu_hw_util.o \ disp/dpu1/dpu_hw_vbif.o \ + disp/dpu1/dpu_hw_wb.o \ disp/dpu1/dpu_io_util.o \ disp/dpu1/dpu_kms.o \ disp/dpu1/dpu_mdss.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c new file mode 100644 index 000..d395475 --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved + */ + +#include "dpu_hw_mdss.h" +#include "dpu_hwio.h" +#include "dpu_hw_catalog.h" +#include "dpu_hw_wb.h" +#include "dpu_formats.h" +#include "dpu_kms.h" + +#define WB_DST_FORMAT 0x000 +#define WB_DST_OP_MODE0x004 +#define WB_DST_PACK_PATTERN 0x008 +#define WB_DST0_ADDR 0x00C +#define WB_DST1_ADDR 0x010 +#define WB_DST2_ADDR 0x014 +#define WB_DST3_ADDR 0x018 +#define WB_DST_YSTRIDE0 0x01C +#define WB_DST_YSTRIDE1 0x020 +#define WB_DST_YSTRIDE1 0x020 +#define WB_DST_DITHER_BITDEPTH0x024 +#define WB_DST_MATRIX_ROW00x030 +#define WB_DST_MATRIX_ROW10x034 +#define WB_DST_MATRIX_ROW20x038 +#define WB_DST_MATRIX_ROW30x03C +#define WB_DST_WRITE_CONFIG 0x048 +#define WB_ROTATION_DNSCALER 0x050 +#define WB_ROTATOR_PIPE_DOWNSCALER0x054 +#define WB_N16_INIT_PHASE_X_C03 0x060 +#define WB_N16_INIT_PHASE_X_C12 0x064 +#define WB_N16_INIT_PHASE_Y_C03 0x068 +#define WB_N16_INIT_PHASE_Y_C12 0x06C +#define WB_OUT_SIZE 0x074 +#define WB_ALPHA_X_VALUE 0x078 +#define WB_DANGER_LUT 0x084 +#define WB_SAFE_LUT 0x088 +#define WB_QOS_CTRL 0x090 +#define WB_CREQ_LUT_0 0x098 +#define WB_CREQ_LUT_1 0x09C +#define WB_UBWC_STATIC_CTRL 0x144 +#define WB_MUX0x150 +#define WB_CROP_CTRL 0x154 +#define WB_CROP_OFFSET0x158 +#define WB_CSC_BASE 0x260 +#define WB_DST_ADDR_SW_STATUS 0x2B0 +#define WB_CDP_CNTL 0x2B4 +#define WB_OUT_IMAGE_SIZE 0x2C0 +#define WB_OUT_XY 0x2C4 + +/* WB_QOS_CTRL */ +#define WB_QOS_CTRL_DANGER_SAFE_ENBIT(0) + +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb, + const struct dpu_mdss_cfg *m, void __iomem *addr, + struct dpu_hw_blk_reg_map *b) +{ + int i; + + for (i = 0; i < m->wb_count; i++) { + if (wb == m->wb[i].id) { + b->base_off = addr; + b->blk_off = m->wb[i].base; + b->length = m->wb[i].len; + b->hwversion = m->hwversion; + return &m->wb[i]; + } + } + return ERR_PTR(-EINVAL); +} + +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx, + struct dpu_hw_wb_cfg *data) +{ + struct dpu_hw_blk_reg_map *c = &ctx->hw; + + DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]); + DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]); + DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]); + DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]); +} + +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx, + struct dpu_hw_wb_cfg *data) +{ + struct dpu_hw_blk_reg_map *c = &ctx->hw; + const struct dpu_format *fmt = data->dest.format; + u32 dst_format, pattern, ystride0, ystride1,
Re: [PATCH 01/12] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog
On 05/02/2022 00:17, Abhinav Kumar wrote: Add writeback blocks to the sm8250 DPU hardware catalog. Other chipsets support writeback too but add it to sm8250 to prototype the feature so that it can be easily extended to other chipsets. Signed-off-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 73 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 66 ++- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index aa75991..fdd878d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -90,6 +91,15 @@ BIT(MDP_INTF3_INTR) | \ BIT(MDP_INTF4_INTR)) +#define WB_SM8250_MASK (BIT(DPU_WB_LINE_MODE) | \ +BIT(DPU_WB_UBWC) | \ +BIT(DPU_WB_YUV_CONFIG) | \ +BIT(DPU_WB_PIPE_ALPHA) | \ +BIT(DPU_WB_XY_ROI_OFFSET) | \ +BIT(DPU_WB_QOS) | \ +BIT(DPU_WB_QOS_8LVL) | \ +BIT(DPU_WB_CDP) | \ +BIT(DPU_WB_INPUT_CTRL)) #define DEFAULT_PIXEL_RAM_SIZE (50 * 1024) #define DEFAULT_DPU_LINE_WIDTH2048 @@ -177,6 +187,40 @@ static const uint32_t plane_formats_yuv[] = { DRM_FORMAT_YVU420, }; +static const uint32_t wb2_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_RGB888, + DRM_FORMAT_ARGB, + DRM_FORMAT_RGBA, + DRM_FORMAT_ABGR, + DRM_FORMAT_XRGB, + DRM_FORMAT_RGBX, + DRM_FORMAT_XBGR, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_ARGB, + DRM_FORMAT_RGBA, + DRM_FORMAT_RGBX, + DRM_FORMAT_XRGB, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGR888, + DRM_FORMAT_ABGR, + DRM_FORMAT_BGRA, + DRM_FORMAT_BGRX, + DRM_FORMAT_XBGR, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_ABGR, + DRM_FORMAT_BGRA, + DRM_FORMAT_BGRX, + DRM_FORMAT_XBGR, +}; + /* * DPU sub blocks config */ @@ -317,6 +361,8 @@ static const struct dpu_mdp_cfg sm8250_mdp[] = { .reg_off = 0x2C4, .bit_off = 8}, .clk_ctrls[DPU_CLK_CTRL_REG_DMA] = { .reg_off = 0x2BC, .bit_off = 20}, + .clk_ctrls[DPU_CLK_CTRL_WB2] = { + .reg_off = 0x3B8, .bit_off = 24}, }, }; @@ -862,6 +908,29 @@ static const struct dpu_intf_cfg sc7280_intf[] = { }; /* + * Writeback blocks config + */ +#define WB_BLK(_name, _id, _base, _features, _clk_ctrl, \ + __xin_id, vbif_id, _reg, _wb_done_bit) \ + { \ + .name = _name, .id = _id, \ + .base = _base, .len = 0x2c8, \ + .features = _features, \ + .format_list = wb2_formats, \ + .num_formats = ARRAY_SIZE(wb2_formats), \ + .clk_ctrl = _clk_ctrl, \ + .xin_id = __xin_id, \ + .vbif_idx = vbif_id, \ + .maxlinewidth = DEFAULT_DPU_LINE_WIDTH, \ + .intr_wb_done = DPU_IRQ_IDX(_reg, _wb_done_bit) \ + } + +static const struct dpu_wb_cfg sm8250_wb[] = { + WB_BLK("wb_2", WB_2, 0x65000, WB_SM8250_MASK, DPU_CLK_CTRL_WB2, 6, + VBIF_RT, MDP_SSPP_TOP0_INTR, 4), +}; + +/* * VBIF sub blocks config */ /* VBIF QOS remap */ @@ -1225,6 +1294,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg) .intf = sm8150_intf, .vbif_count = ARRAY_SIZE(sdm845_vbif), .vbif = sdm845_vbif, + .wb_count = ARRAY_SIZE(sm8250_wb), + .wb = sm8250_wb, .reg_dma_count = 1, .dma_cfg = sm8250_regdma, .perf = sm8250_perf_data, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h ind
Re: [PATCH 11/12] drm/msm/dpu: gracefully handle null fb commits for writeback
On 05/02/2022 00:17, Abhinav Kumar wrote: kms_writeback test cases also verify with a null fb for the writeback connector job. In addition there are also other commit paths which can result in kickoffs without a valid framebuffer like while closing the fb which results in the callback to drm_atomic_helper_dirtyfb() which internally triggers a commit. Add protection in the dpu driver to ensure that commits for writeback encoders without a valid fb are gracefully skipped. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c| 9 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 21 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 6 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h| 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 12 5 files changed, 49 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index e7c9fe1..f7963b0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -869,6 +869,13 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) DPU_ATRACE_BEGIN("crtc_commit"); + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + if (!dpu_encoder_has_valid_fb(encoder)) { Two small comments here. First, let's probably rename the function to dpu_encoder_is_valid() or dpu_encoder_is_valid_for_commit() (ugh, too long). There might be other cases (in theory), why encoder is invalid at this moment. Also (a minor nit): I think that we should commit if at least one of encoders is valid. So we might want to create an encoder_valid_mask basing on the calls to dpu_encoder. And then use it later for calling dpu_encoder_prepare_for_kickoff() and dpu_encoder_kickoff(). + DRM_DEBUG_ATOMIC("invalid FB not kicking off crtc\n"); + goto end; + } + } /* * Encoder will flush/start now, unless it has a tx pending. If so, it * may delay and flush at an irq event (e.g. ppdone) @@ -891,6 +898,8 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) dpu_encoder_kickoff(encoder); reinit_completion(&dpu_crtc->frame_done_comp); + +end: DPU_ATRACE_END("crtc_commit"); } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 3746432..e990dbc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -1832,6 +1832,27 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) } } +bool dpu_encoder_has_valid_fb(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc; + unsigned int i; + struct dpu_encoder_phys *phys; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + + if (drm_enc->encoder_type == DRM_MODE_ENCODER_VIRTUAL) { + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + phys = dpu_enc->phys_encs[i]; + if (phys->ops.has_valid_output_fb && !phys->ops.has_valid_output_fb(phys)) { + DPU_DEBUG("invalid FB not kicking off\n"); + return false; + } + } + } + + return true; +} + void dpu_encoder_kickoff(struct drm_encoder *drm_enc) { struct dpu_encoder_virt *dpu_enc; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index da5b6d6..63d90b8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -187,4 +187,10 @@ void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, struct drm_writeback_job *job); +/** + * dpu_encoder_has_valid_fb - cleanup writeback job for the encoder. + * @drm_enc:Pointer to drm encoder structure + */ +bool dpu_encoder_has_valid_fb(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 80da0a9..5b45b3c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -151,6 +151,7 @@ struct dpu_encoder_phys_ops { struct drm_writeback_job *job); void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc, struct drm_writeback_job *job); + bool (*has_valid_output_fb)(struct dpu_encoder_phys *phys_enc); }; /** diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index 783f83e..7eeed79 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_ph
Re: [v5 1/3] dt-bindings: msm/dsi: Add 10nm dsi phy tuning properties
On Mon, 31 Jan 2022 01:45:17 +0530, Rajeev Nandan wrote: > In most cases, the default values of DSI PHY tuning registers should be > sufficient as they are fully optimized. However, in some cases where > extreme board parasitics cause the eye shape to degrade, the override > bits can be used to improve the signal quality. > > The general guidelines for DSI PHY tuning include: > - High and moderate data rates may benefit from the drive strength and > drive level tuning. > - Drive strength tuning will affect the output impedance and may be used > for matching optimization. > - Drive level tuning will affect the output levels without affecting the > impedance. > > The clock and data lanes have a calibration circuitry feature. The drive > strength tuning can be done by adjusting rescode offset for hstop/hsbot, > and the drive level tuning can be done by adjusting the LDO output level > for the HSTX drive. > > Signed-off-by: Rajeev Nandan > --- > > Changes in v2: > - More details in the commit text (Stephen Boyd) > - Use human understandable values (Stephen Boyd, Dmitry Baryshkov) > - Do not take values that are going to be unused (Dmitry Baryshkov) > > Changes in v3: > - Use "qcom," prefix (Dmitry Baryshkov) > - Remove encoding from phy-drive-ldo-level (Dmitry Baryshkov) > - Use negative values instead of two's complement (Dmitry, Rob Herring) > > Changes in v4: > - Fix dt_binding_check error (Rob Herring's bot) > > Changes in v5: > - None > > .../bindings/display/msm/dsi-phy-10nm.yaml | 36 > ++ > 1 file changed, 36 insertions(+) > Reviewed-by: Rob Herring
Re: [PATCH 12/12] drm/msm/dpu: add writeback blocks to the display snapshot
On 05/02/2022 00:17, Abhinav Kumar wrote: Add writeback block information while capturing the display snapshot. Signed-off-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 6327ba9..e227b35 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -987,6 +987,11 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k msm_disp_snapshot_add_block(disp_state, cat->mixer[i].len, dpu_kms->mmio + cat->mixer[i].base, "lm_%d", i); + /* dump WB sub-blocks HW regs info */ + for (i = 0; i < cat->wb_count; i++) + msm_disp_snapshot_add_block(disp_state, cat->wb[i].len, + dpu_kms->mmio + cat->wb[i].base, "wb_%d", i); + msm_disp_snapshot_add_block(disp_state, top->hw.length, dpu_kms->mmio + top->hw.blk_off, "top"); -- With best wishes Dmitry
Re: [PATCH 10/12] drm/msm/dpu: initialize dpu encoder and connector for writeback
On 05/02/2022 00:17, Abhinav Kumar wrote: Initialize dpu encoder and connector for writeback if the target supports it in the catalog. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 37 - drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 62 + 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index b51a677..3746432 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2066,7 +2066,7 @@ static void dpu_encoder_early_unregister(struct drm_encoder *encoder) } static int dpu_encoder_virt_add_phys_encs( - u32 display_caps, + struct msm_display_info *disp_info, struct dpu_encoder_virt *dpu_enc, struct dpu_enc_phys_init_params *params) { @@ -2085,7 +2085,7 @@ static int dpu_encoder_virt_add_phys_encs( return -EINVAL; } - if (display_caps & MSM_DISPLAY_CAP_VID_MODE) { + if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE) { enc = dpu_encoder_phys_vid_init(params); if (IS_ERR_OR_NULL(enc)) { @@ -2098,7 +2098,7 @@ static int dpu_encoder_virt_add_phys_encs( ++dpu_enc->num_phys_encs; } - if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) { + if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) { enc = dpu_encoder_phys_cmd_init(params); if (IS_ERR_OR_NULL(enc)) { @@ -2111,6 +2111,19 @@ static int dpu_encoder_virt_add_phys_encs( ++dpu_enc->num_phys_encs; } + if (disp_info->intf_type == DRM_MODE_ENCODER_VIRTUAL) { + enc = dpu_encoder_phys_wb_init(params); + + if (IS_ERR_OR_NULL(enc)) { + DPU_ERROR_ENC(dpu_enc, "failed to init wb enc: %ld\n", + PTR_ERR(enc)); + return enc == NULL ? -EINVAL : PTR_ERR(enc); + } + + dpu_enc->phys_encs[dpu_enc->num_phys_encs] = enc; + ++dpu_enc->num_phys_encs; + } + if (params->split_role == ENC_ROLE_SLAVE) dpu_enc->cur_slave = enc; else @@ -2199,9 +2212,8 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, } if (!ret) { - ret = dpu_encoder_virt_add_phys_encs(disp_info->capabilities, - dpu_enc, - &phys_params); + ret = dpu_encoder_virt_add_phys_encs(disp_info, + dpu_enc, &phys_params); if (ret) DPU_ERROR_ENC(dpu_enc, "failed to add phys encs\n"); } @@ -2317,11 +2329,14 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev, if (!dpu_enc) return ERR_PTR(-ENOMEM); - rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs, - drm_enc_mode, NULL); - if (rc) { - devm_kfree(dev->dev, dpu_enc); - return ERR_PTR(rc); + /* this is handled by drm_writeback_connector_init for virtual encoder */ + if (drm_enc_mode != DRM_MODE_ENCODER_VIRTUAL) { + rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs, + drm_enc_mode, NULL); + if (rc) { + devm_kfree(dev->dev, dpu_enc); + return ERR_PTR(rc); + } } drm_encoder_helper_add(&dpu_enc->base, &dpu_encoder_helper_funcs); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 47fe11a..6327ba9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -15,6 +16,7 @@ #include #include #include +#include #include "msm_drv.h" #include "msm_mmu.h" @@ -29,6 +31,7 @@ #include "dpu_kms.h" #include "dpu_plane.h" #include "dpu_vbif.h" +#include "dpu_writeback.h" #define CREATE_TRACE_POINTS #include "dpu_trace.h" @@ -642,6 +645,56 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev, return 0; } +static int _dpu_kms_initialize_writeback(struct drm_device *dev, + struct msm_drm_private *priv, struct dpu_kms *dpu_kms) +{ + struct drm_encoder *encoder
Re: [PATCH v6 4/4] dt-bindings: drm/bridge: anx7625: Add aux-bus node
On Thu, 03 Feb 2022 22:10:23 +0800, Hsin-Yi Wang wrote: > List panel under aux-bus node if it's connected to anx7625's aux bus. > > Signed-off-by: Hsin-Yi Wang > --- > .../display/bridge/analogix,anx7625.yaml| 17 + > 1 file changed, 17 insertions(+) > Reviewed-by: Rob Herring
Re: [PATCH 04/12] drm/msm/dpu: add changes to support writeback in hw_ctl
On 05/02/2022 00:17, Abhinav Kumar wrote: Add changes to support writeback module in the dpu_hw_ctl interface. In addition inroduce a reset_intf_cfg op to reset the interface bits for the currently active interfaces in the ctl path. Signed-off-by: Abhinav Kumar --- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 3 +- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 6 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 65 -- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 27 - 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index 34a6940..4cb72fa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved. */ @@ -70,7 +71,7 @@ static void _dpu_encoder_phys_cmd_update_intf_cfg( intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_CMD; intf_cfg.stream_sel = cmd_enc->stream_sel; intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); - ctl->ops.setup_intf_cfg(ctl, &intf_cfg); + ctl->ops.setup_intf_cfg(ctl, &intf_cfg, false); } static void dpu_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index ddd9d89..950fcd6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved. +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -290,7 +292,7 @@ static void dpu_encoder_phys_vid_setup_timing_engine( spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, &timing_params, fmt); - phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg); + phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg, false); /* setup which pp blk will connect to this intf */ if (phys_enc->hw_intf->ops.bind_pingpong_blk) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index 02da9ec..a2069af 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. */ #include @@ -23,8 +24,10 @@ #define CTL_SW_RESET 0x030 #define CTL_LAYER_EXTN_OFFSET 0x40 #define CTL_MERGE_3D_ACTIVE 0x0E4 +#define CTL_WB_ACTIVE 0x0EC #define CTL_INTF_ACTIVE 0x0F4 #define CTL_MERGE_3D_FLUSH0x100 +#define CTL_WB_FLUSH 0x108 #define CTL_INTF_FLUSH0x110 #define CTL_INTF_MASTER 0x134 #define CTL_FETCH_PIPE_ACTIVE 0x0FC @@ -35,6 +38,7 @@ #define DPU_REG_RESET_TIMEOUT_US2000 #define MERGE_3D_IDX 23 #define INTF_IDX 31 +#define WB_IDX 16 #define CTL_INVALID_BIT 0x #define CTL_DEFAULT_GROUP_ID 0xf @@ -128,6 +132,9 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx) if (ctx->pending_flush_mask & BIT(INTF_IDX)) DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH, ctx->pending_intf_flush_mask); + if (ctx->pending_flush_mask & BIT(WB_IDX)) + DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH, + ctx->pending_wb_flush_mask); DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask); } @@ -248,6 +255,13 @@ static void dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx, } } +static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl *ctx, + enum dpu_wb wb) +{ + ctx->pending_wb_flush_mask |= BIT(wb - WB_0); + ctx->pending_flush_mask |= BIT(WB_IDX); +} + static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx, enum dpu_intf intf) { @@ -493,10 +507,11 @@ static void dpu_hw_ctl_setup_blendstage(struct
Re: [PATCH v3 1/3] drm/msm/dp: revise timing engine programming to support widebus feature
On 04/02/2022 21:36, Kuogee Hsieh wrote: Widebus feature will transmit two pixel data per pixel clock to interface. Timing engine provides driving force for this purpose. This patch base on HPG (Hardware Programming Guide) to revise timing engine register setting to accommodate both widebus and non widebus application. Also horizontal width parameters need to be reduced by half since two pixel data are clocked out per pixel clock when widebus feature enabled. In addition, revised timing engine function is an generic function and intend to be shared by all platforms to reduce maintenance efforts. Changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c| 10 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h| 2 + .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 14 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c| 99 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h| 2 + 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0d315b4..0c22839 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -208,6 +208,8 @@ struct dpu_encoder_virt { u32 idle_timeout; + bool wide_bus_en; + struct msm_dp *dp; }; @@ -217,6 +219,14 @@ static u32 dither_matrix[DITHER_MATRIX_SZ] = { 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10 }; + +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); + + return dpu_enc->wide_bus_en; +} + static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc) { struct dpu_hw_dither_cfg dither_cfg = { 0 }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 99a5d73..893d74d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -168,4 +168,6 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc); +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ This chunk does not apply against the msm-next. The conflict is trivial, but it would be nice to know that the code was tested against the current tip. diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 185379b..3d6c914 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -110,6 +110,20 @@ static void drm_mode_to_intf_timing_params( timing->v_back_porch += timing->v_front_porch; timing->v_front_porch = 0; } + + timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent); + + /* +* for DP, divide the horizonal parameters by 2 when +* widebus is enabled +*/ + if (timing->wide_bus_en) { + timing->width = timing->width >> 1; + timing->xres = timing->xres >> 1; + timing->h_back_porch = timing->h_back_porch >> 1; + timing->h_front_porch = timing->h_front_porch >> 1; + timing->hsync_pulse_width = timing->hsync_pulse_width >> 1; + } } static u32 get_horizontal_total(const struct intf_timing_params *timing) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 116e2b5..35d4aaa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -33,6 +33,7 @@ #define INTF_TP_COLOR1 0x05C #define INTF_CONFIG20x060 #define INTF_DISPLAY_DATA_HCTL 0x064 +#define INTF_ACTIVE_DATA_HCTL 0x068 #define INTF_FRAME_LINE_COUNT_EN0x0A8 #define INTF_FRAME_COUNT0x0AC #define INTF_LINE_COUNT 0x0B0 @@ -90,68 +91,95 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, u32 hsync_period, vsync_period; u32 display_v_start, display_v_end; u32 hsync_start_x, hsync_end_x; + u32 hsync_data_start_x, hsync_data_end_x; u32 active_h_start, active_h_end; u32 active_v_start, active_v_end; u32 active_hctl, display_hctl, hsync_ctl; u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; u32 panel_format; - u32 intf_cfg, intf_cfg2 = 0, display_data_hctl = 0; +
Re: [PATCH v3 2/3] drm/msm/dp: revise timing engine programming to support compression (DSC)
On 04/02/2022 21:36, Kuogee Hsieh wrote: Divides horizontal width by 3 at timing engine of interface. There are major part of compression (DSC) programming have to be done at DSC controller which is not covered by this patch. Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 22 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 3 +++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 35d4aaa..ee7ca34 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -128,7 +128,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, * video timing. It is recommended to enable it for all cases, except * if compression is enabled in 1 pixel per clock mode */ - if (p->wide_bus_en) + if (!p->compression_en || p->wide_bus_en) intf_cfg2 |= BIT(4); So, what exactly is BIT(4)? Is it WIDE_BUS_ENABLE of some kind? Then why do you disable it for compression_en? Please, add defines for all the respective BIT(n) values. if (p->wide_bus_en) @@ -150,10 +150,16 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, */ data_width = p->width; - if (!dp_intf && p->wide_bus_en) + if (p->compression_en) { + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3); + + if (p->wide_bus_en) + data_width >>= 1; + } else if (!dp_intf && p->wide_bus_en) { data_width = p->width >> 1; - else + } else { data_width = p->width; + } hsync_data_start_x = hsync_start_x; hsync_data_end_x = hsync_start_x + data_width - 1; @@ -178,8 +184,16 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, active_hctl = (active_h_end << 16) | active_h_start; - if (dp_intf) + if (dp_intf) { display_hctl = active_hctl; + if (p->compression_en) { + active_data_hctl = (hsync_start_x + + p->extra_dto_cycles) << 16; + active_data_hctl += hsync_start_x; + + display_data_hctl = active_data_hctl; + } + } den_polarity = 0; if (ctx->cap->type == INTF_HDMI) { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h index e4a518a..8fc71ce 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h @@ -32,6 +32,9 @@ struct intf_timing_params { u32 hsync_skew; bool wide_bus_en; + bool compression_en; + u32 extra_dto_cycles; /* for DP only */ + u32 dce_bytes_per_line; }; struct intf_prog_fetch { -- With best wishes Dmitry
Re: [PATCH v3 1/3] drm/msm/dp: revise timing engine programming to support widebus feature
On 04/02/2022 21:36, Kuogee Hsieh wrote: Widebus feature will transmit two pixel data per pixel clock to interface. Timing engine provides driving force for this purpose. This patch base on HPG (Hardware Programming Guide) to revise timing engine register setting to accommodate both widebus and non widebus application. Also horizontal width parameters need to be reduced by half since two pixel data are clocked out per pixel clock when widebus feature enabled. In addition, revised timing engine function is an generic function and intend to be shared by all platforms to reduce maintenance efforts. Changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c| 10 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h| 2 + .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 14 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c| 99 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h| 2 + 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0d315b4..0c22839 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -208,6 +208,8 @@ struct dpu_encoder_virt { u32 idle_timeout; + bool wide_bus_en; + struct msm_dp *dp; }; @@ -217,6 +219,14 @@ static u32 dither_matrix[DITHER_MATRIX_SZ] = { 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10 }; + +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); + + return dpu_enc->wide_bus_en; +} + static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc) { struct dpu_hw_dither_cfg dither_cfg = { 0 }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 99a5d73..893d74d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -168,4 +168,6 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc); +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 185379b..3d6c914 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -110,6 +110,20 @@ static void drm_mode_to_intf_timing_params( timing->v_back_porch += timing->v_front_porch; timing->v_front_porch = 0; } + + timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent); + + /* +* for DP, divide the horizonal parameters by 2 when +* widebus is enabled +*/ + if (timing->wide_bus_en) { + timing->width = timing->width >> 1; + timing->xres = timing->xres >> 1; + timing->h_back_porch = timing->h_back_porch >> 1; + timing->h_front_porch = timing->h_front_porch >> 1; + timing->hsync_pulse_width = timing->hsync_pulse_width >> 1; + } } static u32 get_horizontal_total(const struct intf_timing_params *timing) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 116e2b5..35d4aaa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -33,6 +33,7 @@ #define INTF_TP_COLOR1 0x05C #define INTF_CONFIG20x060 #define INTF_DISPLAY_DATA_HCTL 0x064 +#define INTF_ACTIVE_DATA_HCTL 0x068 #define INTF_FRAME_LINE_COUNT_EN0x0A8 #define INTF_FRAME_COUNT0x0AC #define INTF_LINE_COUNT 0x0B0 @@ -90,68 +91,95 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, u32 hsync_period, vsync_period; u32 display_v_start, display_v_end; u32 hsync_start_x, hsync_end_x; + u32 hsync_data_start_x, hsync_data_end_x; u32 active_h_start, active_h_end; u32 active_v_start, active_v_end; u32 active_hctl, display_hctl, hsync_ctl; u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; u32 panel_format; - u32 intf_cfg, intf_cfg2 = 0, display_data_hctl = 0; + u32 intf_cfg, intf_cfg2 = 0; + u32 display_data_hctl = 0, active_data_hctl = 0; + u32 data_width; + bool dp_intf = false; /* read
Re: [PATCH v3 3/3] drm/msm/dp: enable widebus feature for display port
On 04/02/2022 21:36, Kuogee Hsieh wrote: Widebus feature will transmit two pixel data per pixel clock to interface. This feature now is required to be enabled to easy migrant to higher resolution applications in future. However since some legacy chipsets does not support this feature, this feature is enabled base on chip's hardware revision. changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches -- enable widebus feature base on chip hardware revision Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 4 +++- drivers/gpu/drm/msm/dp/dp_catalog.c | 36 +++-- drivers/gpu/drm/msm/dp/dp_catalog.h | 3 ++- drivers/gpu/drm/msm/dp/dp_ctrl.c| 13 +++ drivers/gpu/drm/msm/dp/dp_ctrl.h| 1 + drivers/gpu/drm/msm/dp/dp_display.c | 30 drivers/gpu/drm/msm/dp/dp_display.h | 2 ++ drivers/gpu/drm/msm/dp/dp_panel.c | 4 ++-- drivers/gpu/drm/msm/dp/dp_panel.h | 2 +- drivers/gpu/drm/msm/msm_drv.h | 6 + 10 files changed, 90 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0c22839..b2d23c2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2167,8 +2167,10 @@ int dpu_encoder_setup(struct drm_device *dev, struct drm_encoder *enc, timer_setup(&dpu_enc->vsync_event_timer, dpu_encoder_vsync_event_handler, 0); - else if (disp_info->intf_type == DRM_MODE_ENCODER_TMDS) + else if (disp_info->intf_type == DRM_MODE_ENCODER_TMDS) { dpu_enc->dp = priv->dp[disp_info->h_tile_instance[0]]; + dpu_enc->wide_bus_en = msm_dp_wide_bus_enable(dpu_enc->dp); + } INIT_DELAYED_WORK(&dpu_enc->delayed_off_work, dpu_encoder_off_work); If this patch is moved to #1, this hunk can go into the respective DPU patch. Could you please change the order? diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index 64f0b26..99d087e 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -483,6 +483,27 @@ int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, } /** + * dp_catalog_hw_revision() - retrieve DP hw revision + * + * @dp_catalog: DP catalog structure + * + * return: u32 + * + * This function return the DP controller hw revision + * + */ +u32 dp_catalog_hw_revision(struct dp_catalog *dp_catalog) +{ + u32 revision; + struct dp_catalog_private *catalog = container_of(dp_catalog, + struct dp_catalog_private, dp_catalog); + + revision = dp_read_ahb(catalog, REG_DP_HW_VERSION); + + return revision; +} + +/** * dp_catalog_ctrl_reset() - reset DP controller * * @dp_catalog: DP catalog structure @@ -739,10 +760,11 @@ u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog) } /* panel related catalog functions */ -int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog) +int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog, bool wide_bus_en) { struct dp_catalog_private *catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog); + u32 reg; dp_write_link(catalog, REG_DP_TOTAL_HOR_VER, dp_catalog->total); @@ -751,7 +773,17 @@ int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog) dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, dp_catalog->width_blanking); dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, dp_catalog->dp_active); - dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, 0); + + reg = dp_read_p0(catalog, MMSS_DP_INTF_CONFIG); + + if (wide_bus_en) + reg |= BIT(4); /* DATABUS_WIDEN */ + else + reg &= ~BIT(4); + + DRM_DEBUG_DP("wide_bus_en=%d reg=%x\n", wide_bus_en, reg); + + dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, reg); return 0; } diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 7dea101..a3a0129 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -95,6 +95,7 @@ void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb); void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate, u32 stream_rate_khz, bool fixed_nvid); int dp_catalog_ctrl_set_pattern_state_bit(struct
Re: [PATCH] drm/panel: simple: Assign data from panel_dpi_probe() correctly
On Tue, Feb 01, 2022 at 12:01:53PM +0100, Christoph Niedermaier wrote: > In the function panel_simple_probe() the pointer panel->desc is > assigned to the passed pointer desc. If function panel_dpi_probe() > is called panel->desc will be updated, but further on only desc > will be evaluated. So update the desc pointer to be able to use > the data from the function panel_dpi_probe(). > > Fixes: 4a1d0dbc8332 ("drm/panel: simple: add panel-dpi support") > > Signed-off-by: Christoph Niedermaier > Cc: Marek Vasut > Cc: Thierry Reding > Cc: Sam Ravnborg > Cc: David Airlie > Cc: Daniel Vetter > To: dri-devel@lists.freedesktop.org Thanks for fixing this Reviewed-by: Sam Ravnborg > --- > drivers/gpu/drm/panel/panel-simple.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/drivers/gpu/drm/panel/panel-simple.c > b/drivers/gpu/drm/panel/panel-simple.c > index 9e46db5e359c..3c08f9827acf 100644 > --- a/drivers/gpu/drm/panel/panel-simple.c > +++ b/drivers/gpu/drm/panel/panel-simple.c > @@ -588,6 +588,7 @@ static int panel_simple_probe(struct device *dev, const > struct panel_desc *desc) > err = panel_dpi_probe(dev, panel); > if (err) > goto free_ddc; > + desc = panel->desc; > } else { > if (!of_get_display_timing(dev->of_node, "panel-timing", &dt)) > panel_simple_parse_panel_timing_node(dev, panel, &dt); > -- > 2.11.0
[PATCH 12/12] drm/msm/dpu: add writeback blocks to the display snapshot
Add writeback block information while capturing the display snapshot. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 6327ba9..e227b35 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -987,6 +987,11 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k msm_disp_snapshot_add_block(disp_state, cat->mixer[i].len, dpu_kms->mmio + cat->mixer[i].base, "lm_%d", i); + /* dump WB sub-blocks HW regs info */ + for (i = 0; i < cat->wb_count; i++) + msm_disp_snapshot_add_block(disp_state, cat->wb[i].len, + dpu_kms->mmio + cat->wb[i].base, "wb_%d", i); + msm_disp_snapshot_add_block(disp_state, top->hw.length, dpu_kms->mmio + top->hw.blk_off, "top"); -- 2.7.4
[PATCH 10/12] drm/msm/dpu: initialize dpu encoder and connector for writeback
Initialize dpu encoder and connector for writeback if the target supports it in the catalog. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 37 - drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 62 + 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index b51a677..3746432 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2066,7 +2066,7 @@ static void dpu_encoder_early_unregister(struct drm_encoder *encoder) } static int dpu_encoder_virt_add_phys_encs( - u32 display_caps, + struct msm_display_info *disp_info, struct dpu_encoder_virt *dpu_enc, struct dpu_enc_phys_init_params *params) { @@ -2085,7 +2085,7 @@ static int dpu_encoder_virt_add_phys_encs( return -EINVAL; } - if (display_caps & MSM_DISPLAY_CAP_VID_MODE) { + if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE) { enc = dpu_encoder_phys_vid_init(params); if (IS_ERR_OR_NULL(enc)) { @@ -2098,7 +2098,7 @@ static int dpu_encoder_virt_add_phys_encs( ++dpu_enc->num_phys_encs; } - if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) { + if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) { enc = dpu_encoder_phys_cmd_init(params); if (IS_ERR_OR_NULL(enc)) { @@ -2111,6 +2111,19 @@ static int dpu_encoder_virt_add_phys_encs( ++dpu_enc->num_phys_encs; } + if (disp_info->intf_type == DRM_MODE_ENCODER_VIRTUAL) { + enc = dpu_encoder_phys_wb_init(params); + + if (IS_ERR_OR_NULL(enc)) { + DPU_ERROR_ENC(dpu_enc, "failed to init wb enc: %ld\n", + PTR_ERR(enc)); + return enc == NULL ? -EINVAL : PTR_ERR(enc); + } + + dpu_enc->phys_encs[dpu_enc->num_phys_encs] = enc; + ++dpu_enc->num_phys_encs; + } + if (params->split_role == ENC_ROLE_SLAVE) dpu_enc->cur_slave = enc; else @@ -2199,9 +2212,8 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, } if (!ret) { - ret = dpu_encoder_virt_add_phys_encs(disp_info->capabilities, - dpu_enc, - &phys_params); + ret = dpu_encoder_virt_add_phys_encs(disp_info, + dpu_enc, &phys_params); if (ret) DPU_ERROR_ENC(dpu_enc, "failed to add phys encs\n"); } @@ -2317,11 +2329,14 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev, if (!dpu_enc) return ERR_PTR(-ENOMEM); - rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs, - drm_enc_mode, NULL); - if (rc) { - devm_kfree(dev->dev, dpu_enc); - return ERR_PTR(rc); + /* this is handled by drm_writeback_connector_init for virtual encoder */ + if (drm_enc_mode != DRM_MODE_ENCODER_VIRTUAL) { + rc = drm_encoder_init(dev, &dpu_enc->base, &dpu_encoder_funcs, + drm_enc_mode, NULL); + if (rc) { + devm_kfree(dev->dev, dpu_enc); + return ERR_PTR(rc); + } } drm_encoder_helper_add(&dpu_enc->base, &dpu_encoder_helper_funcs); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 47fe11a..6327ba9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -15,6 +16,7 @@ #include #include #include +#include #include "msm_drv.h" #include "msm_mmu.h" @@ -29,6 +31,7 @@ #include "dpu_kms.h" #include "dpu_plane.h" #include "dpu_vbif.h" +#include "dpu_writeback.h" #define CREATE_TRACE_POINTS #include "dpu_trace.h" @@ -642,6 +645,56 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev, return 0; } +static int _dpu_kms_initialize_writeback(struct drm_device *dev, + struct msm_drm_private *priv, struct dpu_kms *dpu_kms) +{ + struct drm_encoder *encoder = NULL; + struct ms
[PATCH 11/12] drm/msm/dpu: gracefully handle null fb commits for writeback
kms_writeback test cases also verify with a null fb for the writeback connector job. In addition there are also other commit paths which can result in kickoffs without a valid framebuffer like while closing the fb which results in the callback to drm_atomic_helper_dirtyfb() which internally triggers a commit. Add protection in the dpu driver to ensure that commits for writeback encoders without a valid fb are gracefully skipped. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c| 9 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 21 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 6 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h| 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 12 5 files changed, 49 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index e7c9fe1..f7963b0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -869,6 +869,13 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) DPU_ATRACE_BEGIN("crtc_commit"); + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + if (!dpu_encoder_has_valid_fb(encoder)) { + DRM_DEBUG_ATOMIC("invalid FB not kicking off crtc\n"); + goto end; + } + } /* * Encoder will flush/start now, unless it has a tx pending. If so, it * may delay and flush at an irq event (e.g. ppdone) @@ -891,6 +898,8 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) dpu_encoder_kickoff(encoder); reinit_completion(&dpu_crtc->frame_done_comp); + +end: DPU_ATRACE_END("crtc_commit"); } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 3746432..e990dbc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -1832,6 +1832,27 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) } } +bool dpu_encoder_has_valid_fb(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc; + unsigned int i; + struct dpu_encoder_phys *phys; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + + if (drm_enc->encoder_type == DRM_MODE_ENCODER_VIRTUAL) { + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + phys = dpu_enc->phys_encs[i]; + if (phys->ops.has_valid_output_fb && !phys->ops.has_valid_output_fb(phys)) { + DPU_DEBUG("invalid FB not kicking off\n"); + return false; + } + } + } + + return true; +} + void dpu_encoder_kickoff(struct drm_encoder *drm_enc) { struct dpu_encoder_virt *dpu_enc; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index da5b6d6..63d90b8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -187,4 +187,10 @@ void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, struct drm_writeback_job *job); +/** + * dpu_encoder_has_valid_fb - cleanup writeback job for the encoder. + * @drm_enc:Pointer to drm encoder structure + */ +bool dpu_encoder_has_valid_fb(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 80da0a9..5b45b3c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -151,6 +151,7 @@ struct dpu_encoder_phys_ops { struct drm_writeback_job *job); void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc, struct drm_writeback_job *job); + bool (*has_valid_output_fb)(struct dpu_encoder_phys *phys_enc); }; /** diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index 783f83e..7eeed79 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -717,6 +717,16 @@ static void dpu_encoder_phys_wb_cleanup_wb_job(struct dpu_encoder_phys *phys_enc wb_enc->wb_conn = NULL; } +static bool dpu_encoder_phys_wb_has_valid_fb(struct dpu_encoder_phys *phys_enc) +{ + struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc); + + if (wb_enc->wb_job) + return true; + else + return false; +} + /** * dpu_encoder_phys_wb_init_ops - initialize writeback operations * @ops:
[PATCH 07/12] drm/msm/dpu: add encoder operations to prepare/cleanup wb job
add dpu encoder APIs to prepare and cleanup writeback job for the writeback encoder. These shall be invoked from the prepare_wb_job/cleanup_wb_job hooks of the drm_writeback framework. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 34 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 16 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 5 3 files changed, 55 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 947069b..b51a677 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -958,6 +958,40 @@ static int dpu_encoder_resource_control(struct drm_encoder *drm_enc, return 0; } +void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job) +{ + struct dpu_encoder_virt *dpu_enc; + int i; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i]; + + if (phys->ops.prepare_wb_job) + phys->ops.prepare_wb_job(phys, job); + + } +} + +void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job) +{ + struct dpu_encoder_virt *dpu_enc; + int i; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i]; + + if (phys->ops.cleanup_wb_job) + phys->ops.cleanup_wb_job(phys, job); + + } +} + static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index cc10436..da5b6d6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -171,4 +171,20 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc); +/** + * dpu_encoder_prepare_wb_job - prepare writeback job for the encoder. + * @drm_enc:Pointer to previously created drm encoder structure + * @job:Pointer to the current drm writeback job + */ +void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job); + +/** + * dpu_encoder_cleanup_wb_job - cleanup writeback job for the encoder. + * @drm_enc:Pointer to previously created drm encoder structure + * @job:Pointer to the current drm writeback job + */ +void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, + struct drm_writeback_job *job); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 07c3525..7b3354d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -7,6 +7,7 @@ #ifndef __DPU_ENCODER_PHYS_H__ #define __DPU_ENCODER_PHYS_H__ +#include #include #include "dpu_kms.h" @@ -146,6 +147,10 @@ struct dpu_encoder_phys_ops { void (*restore)(struct dpu_encoder_phys *phys); int (*get_line_count)(struct dpu_encoder_phys *phys); int (*get_frame_count)(struct dpu_encoder_phys *phys); + void (*prepare_wb_job)(struct dpu_encoder_phys *phys_enc, + struct drm_writeback_job *job); + void (*cleanup_wb_job)(struct dpu_encoder_phys *phys_enc, + struct drm_writeback_job *job); }; /** -- 2.7.4
[PATCH 09/12] drm/msm/dpu: add the writeback connector layer
Introduce the dpu_writeback module which serves as the interface between dpu operations and the drm_writeback. This module manages the connector related operations for dpu writeback. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 71 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 27 ++ 3 files changed, 99 insertions(+) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 3abaf84..05a8515 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -74,6 +74,7 @@ msm-y := \ disp/dpu1/dpu_plane.o \ disp/dpu1/dpu_rm.o \ disp/dpu1/dpu_vbif.o \ + disp/dpu1/dpu_writeback.o \ disp/msm_disp_snapshot.o \ disp/msm_disp_snapshot_util.o \ msm_atomic.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c new file mode 100644 index 000..7b61fad --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "dpu_writeback.h" + +static int dpu_wb_conn_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + + return drm_add_modes_noedid(connector, dev->mode_config.max_width, + dev->mode_config.max_height); +} + +static const struct drm_connector_funcs dpu_wb_conn_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int dpu_wb_conn_prepare_job(struct drm_writeback_connector *connector, + struct drm_writeback_job *job) +{ + if (!job->fb) + return 0; + + dpu_encoder_prepare_wb_job(connector->encoder, job); + + return 0; +} + +static void dpu_wb_conn_cleanup_job(struct drm_writeback_connector *connector, + struct drm_writeback_job *job) +{ + if (!job->fb) + return; + + dpu_encoder_cleanup_wb_job(connector->encoder, job); +} + +static const struct drm_connector_helper_funcs dpu_wb_conn_helper_funcs = { + .get_modes = dpu_wb_conn_get_modes, + .prepare_writeback_job = dpu_wb_conn_prepare_job, + .cleanup_writeback_job = dpu_wb_conn_cleanup_job, +}; + +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, + const struct drm_encoder_helper_funcs *enc_helper_funcs, const u32 *format_list, + u32 num_formats) +{ + struct msm_drm_private *priv = dev->dev_private; + struct dpu_wb_connector *dpu_wb_conn; + int rc = 0; + + dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL); + dpu_wb_conn->base.base = &dpu_wb_conn->connector; + dpu_wb_conn->base.encoder = enc; + + drm_connector_helper_add(dpu_wb_conn->base.base, &dpu_wb_conn_helper_funcs); + + rc = drm_writeback_connector_init(dev, &dpu_wb_conn->base, + &dpu_wb_conn_funcs, enc_helper_funcs, + format_list, num_formats); + + priv->connectors[priv->num_connectors++] = dpu_wb_conn->base.base; + + return rc; +} diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h new file mode 100644 index 000..206ce5e --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _DPU_WRITEBACK_H +#define _DPU_WRITEBACK_H + +#include +#include +#include +#include + +#include "msm_drv.h" +#include "dpu_kms.h" +#include "dpu_encoder_phys.h" + +struct dpu_wb_connector { + struct drm_connector connector; + struct drm_writeback_connector base; +}; + +int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, + const struct drm_encoder_helper_funcs *enc_helper_funcs, + const u32 *format_list, u32 num_formats); + +#endif /*_DPU_WRITEBACK_H */ -- 2.7.4
[PATCH 08/12] drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback
Introduce the dpu_encoder_phys_* for the writeback interface to handle writeback specific hardware programming. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 36 +- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c| 813 + 3 files changed, 849 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index c43ef35..3abaf84 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -53,6 +53,7 @@ msm-y := \ disp/dpu1/dpu_encoder.o \ disp/dpu1/dpu_encoder_phys_cmd.o \ disp/dpu1/dpu_encoder_phys_vid.o \ + disp/dpu1/dpu_encoder_phys_wb.o \ disp/dpu1/dpu_formats.o \ disp/dpu1/dpu_hw_catalog.o \ disp/dpu1/dpu_hw_ctl.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 7b3354d..80da0a9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -159,6 +159,7 @@ struct dpu_encoder_phys_ops { * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel * @INTR_IDX_RDPTR:Readpointer done unterrupt for cmd mode panel + * @INTR_IDX_WB_DONE: Writeback fone interrupt for virtual connector */ enum dpu_intr_idx { INTR_IDX_VSYNC, @@ -166,6 +167,7 @@ enum dpu_intr_idx { INTR_IDX_UNDERRUN, INTR_IDX_CTL_START, INTR_IDX_RDPTR, + INTR_IDX_WB_DONE, INTR_IDX_MAX, }; @@ -196,7 +198,7 @@ struct dpu_encoder_irq { * @hw_ctl:Hardware interface to the ctl registers * @hw_pp: Hardware interface to the ping pong registers * @hw_intf: Hardware interface to the intf registers - * @hw_wb: Hardware interface to the wb registers + * @hw_wb: Hardware interface to the wb registers * @dpu_kms: Pointer to the dpu_kms top level * @cached_mode: DRM mode cached at mode_set time, acted on in enable * @enabled: Whether the encoder has enabled and running a mode @@ -250,6 +252,31 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys) } /** + * struct dpu_encoder_phys_wb - sub-class of dpu_encoder_phys to handle command + * mode specific operations + * @base: Baseclass physical encoder structure + * @wbirq_refcount: Reference count of writeback interrupt + * @wb_done_timeout_cnt: number of wb done irq timeout errors + * @wb_cfg: writeback block config to store fb related details + * @cdp_cfg: chroma down prefetch block config for wb + * @aspace: address space to be used for wb framebuffer + * @wb_conn: backpointer to writeback connector + * @wb_job: backpointer to current writeback job + * @dest: dpu buffer layout for current writeback output buffer + */ +struct dpu_encoder_phys_wb { + struct dpu_encoder_phys base; + atomic_t wbirq_refcount; + int wb_done_timeout_cnt; + struct dpu_hw_wb_cfg wb_cfg; + struct dpu_hw_wb_cdp_cfg cdp_cfg; + struct msm_gem_address_space *aspace; + struct drm_writeback_connector *wb_conn; + struct drm_writeback_job *wb_job; + struct dpu_hw_fmt_layout dest; +}; + +/** * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command * mode specific operations * @base: Baseclass physical encoder structure @@ -317,6 +344,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init( struct dpu_enc_phys_init_params *p); /** + * dpu_encoder_phys_wb_init - initialize writeback encoder + * @init: Pointer to init info structure with initialization params + */ +struct dpu_encoder_phys *dpu_encoder_phys_wb_init( + struct dpu_enc_phys_init_params *p); + +/** * dpu_encoder_helper_trigger_start - control start helper function * This helper function may be optionally specified by physical * encoders if they require ctl_start triggering. diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c new file mode 100644 index 000..783f83e --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt)"[drm:%s:%d] " fmt, __func__, __LINE__ + +#include + +#include "dpu_encoder_phys.h" +#include "dpu_formats.h" +#include "dpu_hw_top.h" +#include "dpu_hw_wb.h" +#include "dpu_hw_lm.h" +#include "dpu_hw_blk.h" +#include "dpu_hw_merge3d.h" +#include "dpu_hw_interrupts.h" +#include "dpu_core_irq.h" +#include "dpu_vbif.h" +#include "dpu_crtc.h" +#include "disp/msm_disp
[PATCH 05/12] drm/msm/dpu: add an API to reset the encoder related hw blocks
Add an API to reset the encoder related hw blocks to ensure a proper teardown of the pipeline. At the moment this is being used only for the writeback encoder but eventually we can start using this for all interfaces. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 92 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 10 +++ 2 files changed, 102 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 1e648db..e977c05 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -21,6 +22,7 @@ #include "dpu_hw_intf.h" #include "dpu_hw_ctl.h" #include "dpu_hw_dspp.h" +#include "dpu_hw_merge3d.h" #include "dpu_formats.h" #include "dpu_encoder_phys.h" #include "dpu_crtc.h" @@ -1813,6 +1815,96 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc) DPU_ATRACE_END("encoder_kickoff"); } +static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) +{ + struct dpu_hw_mixer_cfg mixer; + int i, num_lm; + u32 flush_mask = 0; + struct dpu_global_state *global_state; + struct dpu_hw_blk *hw_lm[2]; + struct dpu_hw_mixer *hw_mixer[2]; + struct dpu_hw_ctl *ctl = phys_enc->hw_ctl; + + memset(&mixer, 0, sizeof(mixer)); + + /* reset all mixers for this encoder */ + if (phys_enc->hw_ctl->ops.clear_all_blendstages) + phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl); + + global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms); + + num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state, + phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); + + for (i = 0; i < num_lm; i++) { + hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]); + flush_mask = phys_enc->hw_ctl->ops.get_bitmask_mixer(ctl, hw_mixer[i]->idx); + if (phys_enc->hw_ctl->ops.update_pending_flush) + phys_enc->hw_ctl->ops.update_pending_flush(ctl, flush_mask); + + /* clear all blendstages */ + if (phys_enc->hw_ctl->ops.setup_blendstage) + phys_enc->hw_ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL); + } +} + +void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) +{ + struct dpu_hw_ctl *ctl = phys_enc->hw_ctl; + struct dpu_hw_intf_cfg intf_cfg = { 0 }; + int i; + struct dpu_encoder_virt *dpu_enc; + + dpu_enc = to_dpu_encoder_virt(phys_enc->parent); + + phys_enc->hw_ctl->ops.reset(ctl); + + dpu_encoder_helper_reset_mixers(phys_enc); + + if (phys_enc->hw_wb) { + /* disable the PP block */ + if (phys_enc->hw_wb->ops.bind_pingpong_blk) + phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, false, + phys_enc->hw_pp->idx); + + /* mark WB flush as pending */ + if (phys_enc->hw_ctl->ops.update_pending_flush_wb) + phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx); + } else { + for (i = 0; i < dpu_enc->num_phys_encs; i++) { + if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk) + phys_enc->hw_intf->ops.bind_pingpong_blk( + dpu_enc->phys_encs[i]->hw_intf, false, + dpu_enc->phys_encs[i]->hw_pp->idx); + /* mark INTF flush as pending */ + if (phys_enc->hw_ctl->ops.update_pending_flush_intf) + phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl, + dpu_enc->phys_encs[i]->hw_intf->idx); + } + } + + /* reset the merge 3D HW block */ + if (phys_enc->hw_pp->merge_3d) { + phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d, + BLEND_3D_NONE); + if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d) + phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl, + phys_enc->hw_pp->merge_3d->idx); + } + + intf_cfg.stream_sel = 0; /* Don't care value for video mode */ + intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); + if (phys_enc->hw_pp->merge_3d) + intf_cfg.merge_3d
[PATCH 04/12] drm/msm/dpu: add changes to support writeback in hw_ctl
Add changes to support writeback module in the dpu_hw_ctl interface. In addition inroduce a reset_intf_cfg op to reset the interface bits for the currently active interfaces in the ctl path. Signed-off-by: Abhinav Kumar --- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 3 +- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 6 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 65 -- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 27 - 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index 34a6940..4cb72fa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved. */ @@ -70,7 +71,7 @@ static void _dpu_encoder_phys_cmd_update_intf_cfg( intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_CMD; intf_cfg.stream_sel = cmd_enc->stream_sel; intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); - ctl->ops.setup_intf_cfg(ctl, &intf_cfg); + ctl->ops.setup_intf_cfg(ctl, &intf_cfg, false); } static void dpu_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index ddd9d89..950fcd6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved. +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt)"[drm:%s:%d] " fmt, __func__, __LINE__ @@ -290,7 +292,7 @@ static void dpu_encoder_phys_vid_setup_timing_engine( spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, &timing_params, fmt); - phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg); + phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg, false); /* setup which pp blk will connect to this intf */ if (phys_enc->hw_intf->ops.bind_pingpong_blk) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index 02da9ec..a2069af 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. */ #include @@ -23,8 +24,10 @@ #define CTL_SW_RESET 0x030 #define CTL_LAYER_EXTN_OFFSET 0x40 #define CTL_MERGE_3D_ACTIVE 0x0E4 +#define CTL_WB_ACTIVE 0x0EC #define CTL_INTF_ACTIVE 0x0F4 #define CTL_MERGE_3D_FLUSH0x100 +#define CTL_WB_FLUSH 0x108 #define CTL_INTF_FLUSH0x110 #define CTL_INTF_MASTER 0x134 #define CTL_FETCH_PIPE_ACTIVE 0x0FC @@ -35,6 +38,7 @@ #define DPU_REG_RESET_TIMEOUT_US2000 #define MERGE_3D_IDX 23 #define INTF_IDX 31 +#define WB_IDX 16 #define CTL_INVALID_BIT 0x #define CTL_DEFAULT_GROUP_ID 0xf @@ -128,6 +132,9 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx) if (ctx->pending_flush_mask & BIT(INTF_IDX)) DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH, ctx->pending_intf_flush_mask); + if (ctx->pending_flush_mask & BIT(WB_IDX)) + DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH, + ctx->pending_wb_flush_mask); DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask); } @@ -248,6 +255,13 @@ static void dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx, } } +static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl *ctx, + enum dpu_wb wb) +{ + ctx->pending_wb_flush_mask |= BIT(wb - WB_0); + ctx->pending_flush_mask |= BIT(WB_IDX); +} + static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx, enum dpu_intf intf) { @@ -493,10 +507,11 @@ static void dpu_hw_ctl_setup_blendstage(struct dpu_hw_ctl *ctx, static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *c
[PATCH 06/12] drm/msm/dpu: make changes to dpu_encoder to support virtual encoder
Make changes to dpu_encoder to support virtual encoder needed to support writeback for dpu. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 57 + 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index e977c05..947069b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -974,6 +974,7 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC]; struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC]; struct dpu_hw_blk *hw_dspp[MAX_CHANNELS_PER_ENC] = { NULL }; + enum dpu_hw_blk_type blk_type; int num_lm, num_ctl, num_pp; int i, j; @@ -1061,20 +1062,36 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, phys->hw_pp = dpu_enc->hw_pp[i]; phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]); + if (phys->intf_mode == INTF_MODE_WB_LINE) + blk_type = DPU_HW_BLK_WB; + else + blk_type = DPU_HW_BLK_INTF; + num_blk = dpu_rm_get_assigned_resources(&dpu_kms->rm, - global_state, drm_enc->base.id, DPU_HW_BLK_INTF, + global_state, drm_enc->base.id, blk_type, hw_blk, ARRAY_SIZE(hw_blk)); - for (j = 0; j < num_blk; j++) { - struct dpu_hw_intf *hw_intf; - hw_intf = to_dpu_hw_intf(hw_blk[i]); - if (hw_intf->idx == phys->intf_idx) - phys->hw_intf = hw_intf; + if (blk_type == DPU_HW_BLK_WB) { + for (j = 0; j < num_blk; j++) { + struct dpu_hw_wb *hw_wb; + + hw_wb = to_dpu_hw_wb(hw_blk[i]); + if (hw_wb->idx == phys->intf_idx) + phys->hw_wb = hw_wb; + } + } else { + for (j = 0; j < num_blk; j++) { + struct dpu_hw_intf *hw_intf; + + hw_intf = to_dpu_hw_intf(hw_blk[i]); + if (hw_intf->idx == phys->intf_idx) + phys->hw_intf = hw_intf; + } } - if (!phys->hw_intf) { + if (!phys->hw_intf && !phys->hw_wb) { DPU_ERROR_ENC(dpu_enc, - "no intf block assigned at idx: %d\n", i); + "no intf or WB block assigned at idx: %d\n", i); return; } @@ -1224,15 +1241,22 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc) mutex_unlock(&dpu_enc->enc_lock); } -static enum dpu_intf dpu_encoder_get_intf(struct dpu_mdss_cfg *catalog, +static enum dpu_intf dpu_encoder_get_intf_or_wb(struct dpu_mdss_cfg *catalog, enum dpu_intf_type type, u32 controller_id) { int i = 0; - for (i = 0; i < catalog->intf_count; i++) { - if (catalog->intf[i].type == type - && catalog->intf[i].controller_id == controller_id) { - return catalog->intf[i].id; + if (type != INTF_WB) { + for (i = 0; i < catalog->intf_count; i++) { + if (catalog->intf[i].type == type + && catalog->intf[i].controller_id == controller_id) { + return catalog->intf[i].id; + } + } + } else { + for (i = 0; i < catalog->wb_count; i++) { + if (catalog->wb[i].id == controller_id) + return catalog->wb[i].id; } } @@ -2096,6 +2120,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, case DRM_MODE_ENCODER_TMDS: intf_type = INTF_DP; break; + case DRM_MODE_ENCODER_VIRTUAL: + intf_type = INTF_WB; + break; } WARN_ON(disp_info->num_of_h_tiles < 1); @@ -2128,11 +2155,11 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n", i, controller_id, phys_params.split_role); - phys_params.intf_idx = dpu_encoder_get_intf(dpu_kms->catalog, + phys_params.intf_idx = dpu_encoder_get_intf_or_wb(dpu_kms->catalog, intf_type,
[PATCH 03/12] drm/msm/dpu: add writeback blocks to DPU RM
Add writeback blocks to DPU resource manager so that writeback encoders can request for writeback hardware blocks through RM and their usage can be tracked. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 3 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 71 + drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 2 + 4 files changed, 78 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index e241914..cc10436 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -21,9 +22,11 @@ /** * Encoder functions and data types * @intfs: Interfaces this encoder is using, INTF_MODE_NONE if unused + * @wbs:Writeback blocks this encoder is using */ struct dpu_encoder_hw_resources { enum dpu_intf_mode intfs[INTF_MAX]; + enum dpu_intf_mode wbs[WB_MAX]; }; /** diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h index 2d385b4..1e00804 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -146,6 +147,7 @@ struct dpu_global_state { uint32_t ctl_to_enc_id[CTL_MAX - CTL_0]; uint32_t intf_to_enc_id[INTF_MAX - INTF_0]; uint32_t dspp_to_enc_id[DSPP_MAX - DSPP_0]; + uint32_t wb_to_enc_id[WB_MAX - WB_0]; }; struct dpu_global_state diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index f9c83d6..edd0b7a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ @@ -9,6 +10,7 @@ #include "dpu_hw_ctl.h" #include "dpu_hw_pingpong.h" #include "dpu_hw_intf.h" +#include "dpu_hw_wb.h" #include "dpu_hw_dspp.h" #include "dpu_hw_merge3d.h" #include "dpu_encoder.h" @@ -75,6 +77,14 @@ int dpu_rm_destroy(struct dpu_rm *rm) dpu_hw_intf_destroy(hw); } } + for (i = 0; i < ARRAY_SIZE(rm->wb_blks); i++) { + struct dpu_hw_wb *hw; + + if (rm->wb_blks[i]) { + hw = to_dpu_hw_wb(rm->wb_blks[i]); + dpu_hw_wb_destroy(hw); + } + } return 0; } @@ -187,6 +197,24 @@ int dpu_rm_init(struct dpu_rm *rm, rm->intf_blks[intf->id - INTF_0] = &hw->base; } + for (i = 0; i < cat->wb_count; i++) { + struct dpu_hw_wb *hw; + const struct dpu_wb_cfg *wb = &cat->wb[i]; + + if (wb->id < WB_0 || wb->id >= WB_MAX) { + DPU_ERROR("skip intf %d with invalid id\n", wb->id); + continue; + } + + hw = dpu_hw_wb_init(wb->id, mmio, cat); + if (IS_ERR_OR_NULL(hw)) { + rc = PTR_ERR(hw); + DPU_ERROR("failed wb object creation: err %d\n", rc); + goto fail; + } + rm->wb_blks[wb->id - WB_0] = &hw->base; + } + for (i = 0; i < cat->ctl_count; i++) { struct dpu_hw_ctl *hw; const struct dpu_ctl_cfg *ctl = &cat->ctl[i]; @@ -479,6 +507,33 @@ static int _dpu_rm_reserve_intf( return 0; } +static int _dpu_rm_reserve_wb( + struct dpu_rm *rm, + struct dpu_global_state *global_state, + uint32_t enc_id, + uint32_t id) +{ + int idx = id - WB_0; + + if (idx < 0 || idx >= ARRAY_SIZE(rm->wb_blks)) { + DPU_ERROR("invalid intf id: %d", id); + return -EINVAL; + } + + if (!rm->wb_blks[idx]) { + DPU_ERROR("couldn't find wb id %d\n", id); + return -EINVAL; + } + + if (reserved_by_other(global_state->wb_to_enc_id, idx, enc_id)) { + DPU_ERROR("intf id %d already reserved\n", id); + return -ENAVAIL; + } + + global_state->wb_to_enc_id[idx] = enc_id; + return 0; +} + static int _dpu_rm_reserve_intf_related_hw( struct dpu_rm *rm,
[PATCH 02/12] drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks
Add the dpu_hw_wb abstraction to program registers related to the writeback block. These will be invoked once all the configuration is set and ready to be programmed to the registers. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 267 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 145 3 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 03ab55c..c43ef35 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -66,6 +66,7 @@ msm-y := \ disp/dpu1/dpu_hw_top.o \ disp/dpu1/dpu_hw_util.o \ disp/dpu1/dpu_hw_vbif.o \ + disp/dpu1/dpu_hw_wb.o \ disp/dpu1/dpu_io_util.o \ disp/dpu1/dpu_kms.o \ disp/dpu1/dpu_mdss.o \ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c new file mode 100644 index 000..d395475 --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved + */ + +#include "dpu_hw_mdss.h" +#include "dpu_hwio.h" +#include "dpu_hw_catalog.h" +#include "dpu_hw_wb.h" +#include "dpu_formats.h" +#include "dpu_kms.h" + +#define WB_DST_FORMAT 0x000 +#define WB_DST_OP_MODE0x004 +#define WB_DST_PACK_PATTERN 0x008 +#define WB_DST0_ADDR 0x00C +#define WB_DST1_ADDR 0x010 +#define WB_DST2_ADDR 0x014 +#define WB_DST3_ADDR 0x018 +#define WB_DST_YSTRIDE0 0x01C +#define WB_DST_YSTRIDE1 0x020 +#define WB_DST_YSTRIDE1 0x020 +#define WB_DST_DITHER_BITDEPTH0x024 +#define WB_DST_MATRIX_ROW00x030 +#define WB_DST_MATRIX_ROW10x034 +#define WB_DST_MATRIX_ROW20x038 +#define WB_DST_MATRIX_ROW30x03C +#define WB_DST_WRITE_CONFIG 0x048 +#define WB_ROTATION_DNSCALER 0x050 +#define WB_ROTATOR_PIPE_DOWNSCALER0x054 +#define WB_N16_INIT_PHASE_X_C03 0x060 +#define WB_N16_INIT_PHASE_X_C12 0x064 +#define WB_N16_INIT_PHASE_Y_C03 0x068 +#define WB_N16_INIT_PHASE_Y_C12 0x06C +#define WB_OUT_SIZE 0x074 +#define WB_ALPHA_X_VALUE 0x078 +#define WB_DANGER_LUT 0x084 +#define WB_SAFE_LUT 0x088 +#define WB_QOS_CTRL 0x090 +#define WB_CREQ_LUT_0 0x098 +#define WB_CREQ_LUT_1 0x09C +#define WB_UBWC_STATIC_CTRL 0x144 +#define WB_MUX0x150 +#define WB_CROP_CTRL 0x154 +#define WB_CROP_OFFSET0x158 +#define WB_CSC_BASE 0x260 +#define WB_DST_ADDR_SW_STATUS 0x2B0 +#define WB_CDP_CNTL 0x2B4 +#define WB_OUT_IMAGE_SIZE 0x2C0 +#define WB_OUT_XY 0x2C4 + +/* WB_QOS_CTRL */ +#define WB_QOS_CTRL_DANGER_SAFE_ENBIT(0) + +static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb, + const struct dpu_mdss_cfg *m, void __iomem *addr, + struct dpu_hw_blk_reg_map *b) +{ + int i; + + for (i = 0; i < m->wb_count; i++) { + if (wb == m->wb[i].id) { + b->base_off = addr; + b->blk_off = m->wb[i].base; + b->length = m->wb[i].len; + b->hwversion = m->hwversion; + return &m->wb[i]; + } + } + return ERR_PTR(-EINVAL); +} + +static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx, + struct dpu_hw_wb_cfg *data) +{ + struct dpu_hw_blk_reg_map *c = &ctx->hw; + + DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]); + DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]); + DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]); + DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]); +} + +static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx, + struct dpu_hw_wb_cfg *data) +{ + struct dpu_hw_blk_reg_map *c = &ctx->hw; + const struct dpu_format *fmt = data->dest.format; + u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp; + u32 write_config = 0; + u32 opmode = 0; + u32 dst_addr_sw = 0; + +
[PATCH 01/12] drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog
Add writeback blocks to the sm8250 DPU hardware catalog. Other chipsets support writeback too but add it to sm8250 to prototype the feature so that it can be easily extended to other chipsets. Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 73 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 66 ++- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index aa75991..fdd878d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt)"[drm:%s:%d] " fmt, __func__, __LINE__ @@ -90,6 +91,15 @@ BIT(MDP_INTF3_INTR) | \ BIT(MDP_INTF4_INTR)) +#define WB_SM8250_MASK (BIT(DPU_WB_LINE_MODE) | \ +BIT(DPU_WB_UBWC) | \ +BIT(DPU_WB_YUV_CONFIG) | \ +BIT(DPU_WB_PIPE_ALPHA) | \ +BIT(DPU_WB_XY_ROI_OFFSET) | \ +BIT(DPU_WB_QOS) | \ +BIT(DPU_WB_QOS_8LVL) | \ +BIT(DPU_WB_CDP) | \ +BIT(DPU_WB_INPUT_CTRL)) #define DEFAULT_PIXEL_RAM_SIZE (50 * 1024) #define DEFAULT_DPU_LINE_WIDTH 2048 @@ -177,6 +187,40 @@ static const uint32_t plane_formats_yuv[] = { DRM_FORMAT_YVU420, }; +static const uint32_t wb2_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_RGB888, + DRM_FORMAT_ARGB, + DRM_FORMAT_RGBA, + DRM_FORMAT_ABGR, + DRM_FORMAT_XRGB, + DRM_FORMAT_RGBX, + DRM_FORMAT_XBGR, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_ARGB, + DRM_FORMAT_RGBA, + DRM_FORMAT_RGBX, + DRM_FORMAT_XRGB, + DRM_FORMAT_BGR565, + DRM_FORMAT_BGR888, + DRM_FORMAT_ABGR, + DRM_FORMAT_BGRA, + DRM_FORMAT_BGRX, + DRM_FORMAT_XBGR, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_ABGR, + DRM_FORMAT_BGRA, + DRM_FORMAT_BGRX, + DRM_FORMAT_XBGR, +}; + /* * DPU sub blocks config */ @@ -317,6 +361,8 @@ static const struct dpu_mdp_cfg sm8250_mdp[] = { .reg_off = 0x2C4, .bit_off = 8}, .clk_ctrls[DPU_CLK_CTRL_REG_DMA] = { .reg_off = 0x2BC, .bit_off = 20}, + .clk_ctrls[DPU_CLK_CTRL_WB2] = { + .reg_off = 0x3B8, .bit_off = 24}, }, }; @@ -862,6 +908,29 @@ static const struct dpu_intf_cfg sc7280_intf[] = { }; /* + * Writeback blocks config + */ +#define WB_BLK(_name, _id, _base, _features, _clk_ctrl, \ + __xin_id, vbif_id, _reg, _wb_done_bit) \ + { \ + .name = _name, .id = _id, \ + .base = _base, .len = 0x2c8, \ + .features = _features, \ + .format_list = wb2_formats, \ + .num_formats = ARRAY_SIZE(wb2_formats), \ + .clk_ctrl = _clk_ctrl, \ + .xin_id = __xin_id, \ + .vbif_idx = vbif_id, \ + .maxlinewidth = DEFAULT_DPU_LINE_WIDTH, \ + .intr_wb_done = DPU_IRQ_IDX(_reg, _wb_done_bit) \ + } + +static const struct dpu_wb_cfg sm8250_wb[] = { + WB_BLK("wb_2", WB_2, 0x65000, WB_SM8250_MASK, DPU_CLK_CTRL_WB2, 6, + VBIF_RT, MDP_SSPP_TOP0_INTR, 4), +}; + +/* * VBIF sub blocks config */ /* VBIF QOS remap */ @@ -1225,6 +1294,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg) .intf = sm8150_intf, .vbif_count = ARRAY_SIZE(sdm845_vbif), .vbif = sdm845_vbif, + .wb_count = ARRAY_SIZE(sm8250_wb), + .wb = sm8250_wb, .reg_dma_count = 1, .dma_cfg = sm8250_regdma, .perf = sm8250_perf_data, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 31af04a..a3ca695 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/
[PATCH 00/12] Add writeback block support for DPU
This series adds support for writeback block on DPU. Writeback block is extremely useful to validate boards having no physical displays in addition to many other use-cases where we want to get the output of the display pipeline to examine whether issue is with the display pipeline or with the panel. These changes have been validated on SM8250 RB5 boards with IGT KMS writeback test-suite thereby further increasing the IGT test coverage for DPU. I am sharing the test results below. root@linaro-developer:~/igt_repo/igt-gpu-tools/build/tests# ./kms_writeback [ 35.066157] Console: switching to colour dummy device 80x25 [ 35.071964] [IGT] kms_writeback: executing IGT-Version: 1.26-gae2eb9e1 (aarch64) (Linux: 5.16.0-rc2-62171-g132577e2697b aarch64) [ 35.611418] [IGT] kms_writeback: starting subtest writeback-pixel-formats Starting subtest: writeback-pixel-formats [ 35.618528] [IGT] kms_writeback: starting subtest writeback-invalid-parameters Subtest writeback-pixel-formats: SUCCESS (0.000s) Starting subtest: writeback-invalid-parameters Subtest writeback-invalid-parameters: SUCCESS (0.028s) 35.657437] [IGT] kms_writeback: starting subtest writeback-fb-id Starting subtest: writeback-fb-id Subtest writeback-fb-id: SUCCESS (0.030s) [ 35.698957] [IGT] kms_writeback: starting subtest writeback-check-output Starting subtest: writeback-check-output [ 35.852834] [IGT] kms_writeback: exiting, ret=0 Subtest writeback-check-output: SUCCESS (0.142s) [ 35.861291] Console: switching to colour frame buffer device 240x67 root@linaro-developer:~/igt_repo/igt-gpu-tools/build/tests# The changes can easily be extended to support any other chipset using the DPU driver by adding the support in the catalog. Writeback block supports various formats and features. The support for all of them can be incrementally added on top of this framework when validation is improved and the test frameworks are extended to validate them. Abhinav Kumar (12): drm/msm/dpu: add writeback blocks to the sm8250 DPU catalog drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks drm/msm/dpu: add writeback blocks to DPU RM drm/msm/dpu: add changes to support writeback in hw_ctl drm/msm/dpu: add an API to reset the encoder related hw blocks drm/msm/dpu: make changes to dpu_encoder to support virtual encoder drm/msm/dpu: add encoder operations to prepare/cleanup wb job drm/msm/dpu: introduce the dpu_encoder_phys_* for writeback drm/msm/dpu: add the writeback connector layer drm/msm/dpu: initialize dpu encoder and connector for writeback drm/msm/dpu: gracefully handle null fb commits for writeback drm/msm/dpu: add writeback blocks to the display snapshot drivers/gpu/drm/msm/Makefile | 3 + drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 9 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c| 241 +- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h| 25 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 50 ++ .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 3 +- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 6 +- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c| 825 + drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 73 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 66 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 65 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 27 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 267 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h | 145 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c| 67 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h| 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 71 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 71 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h | 27 + 20 files changed, 2007 insertions(+), 38 deletions(-) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c create mode 100644 drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h -- 2.7.4
Re: [PATCH v2 1/4] drm/format-helper: Add drm_fb_{xrgb8888, gray8}_to_mono_reversed()
On Fri, Feb 4, 2022 at 10:53 AM Thomas Zimmermann wrote: > > Hi > > Am 04.02.22 um 14:43 schrieb Javier Martinez Canillas: > > Add support to convert XR24 and 8-bit grayscale to reversed monochrome for > > drivers that control monochromatic panels, that only have 1 bit per pixel. > > > > The drm_fb_gray8_to_mono_reversed() helper was based on the function that > > does the same in the drivers/gpu/drm/tiny/repaper.c driver. > > > > Signed-off-by: Javier Martinez Canillas > > --- > > > > (no changes since v1) > > > > drivers/gpu/drm/drm_format_helper.c | 80 + > > include/drm/drm_format_helper.h | 7 +++ > > 2 files changed, 87 insertions(+) > > > > diff --git a/drivers/gpu/drm/drm_format_helper.c > > b/drivers/gpu/drm/drm_format_helper.c > > index 0f28dd2bdd72..cdce4b7c25d9 100644 > > --- a/drivers/gpu/drm/drm_format_helper.c > > +++ b/drivers/gpu/drm/drm_format_helper.c > > @@ -584,3 +584,83 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int > > dst_pitch, uint32_t dst_for > > return -EINVAL; > > } > > EXPORT_SYMBOL(drm_fb_blit_toio); > > + > > +static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, > > size_t pixels) > > +{ > > + unsigned int xb, i; > > + > > + for (xb = 0; xb < pixels / 8; xb++) { > > In practice, all mode widths are multiples of 8 because VGA mandated it. > So it's ok-ish to assume this here. You should probably at least print a > warning somewhere if (pixels % 8 != 0) Not sure if it's relevant, but 1366x768 was a fairly popular laptop resolution. There's even a dedicated drm_mode_fixup_1366x768 in drm_edid.c. (Would it have killed them to add 2 more horizontal pixels? Apparently.) Cheers, -ilia
Re: [PATCH v2 1/4] drm/format-helper: Add drm_fb_{xrgb8888,gray8}_to_mono_reversed()
Hi Am 04.02.22 um 20:31 schrieb Javier Martinez Canillas: Hello Thomas, Thanks a lot for your feedback. On 2/4/22 16:52, Thomas Zimmermann wrote: [snip] +static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, size_t pixels) +{ + unsigned int xb, i; + + for (xb = 0; xb < pixels / 8; xb++) { In practice, all mode widths are multiples of 8 because VGA mandated it. So it's ok-ish to assume this here. You should probably at least print a warning somewhere if (pixels % 8 != 0) Agreed. After sending the mail, I realized that some code copies only parts of the source around; specifically for damage handling. None of this is aligned to multiple of 8. So the copying could start and end in the middle of bytes. You'd need a pixel-offset value of some sort. If you don't want to handle this now, maybe at least detect this case and put a warning somewhere. [snip] + * DRM doesn't have native monochrome or grayscale support. + * Such drivers can announce the commonly supported XR24 format to userspace + * and use drm_fb_xrgb_to_gray8() to convert to grayscale and then this + * helper function to convert to the native format. + */ +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_rect *clip) There's a bug here. You want to pass in a drm_framebuffer as fourth argument. +{ + + size_t height = drm_rect_height(clip); + size_t width = drm_rect_width(clip); + unsigned int y; + const u8 *gray8 = src; + u8 *mono = dst; + + if (!dst_pitch) + dst_pitch = width; The dst_pitch is given in bytes. You have to device by 8. Here would be a good place to warn if (width % 8 != 0). Ok. + + for (y = 0; y < height; y++) { + drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch); + mono += (dst_pitch / 8); The dst_pitch is already given in bytes. Yes, I know but for reversed mono we want only 1/8 of the width since we are converting from 8 bits per pixel greyscale to 1 bit per pixel mono. Or am I misunderstanding what you meant ? I mean that if there are 80 pixel on a scanline, the value of dst_pitch is already 10. These pitch values are always given in bytes. Best regards Thomas + gray8 += dst_pitch; 'gray8 += fb->pitches[0]' would be correct. Ok. [snip] + */ +void drm_fb_xrgb_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_framebuffer *fb, + const struct drm_rect *clip) +{ + if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB)) + return; + + if (!dst_pitch) + dst_pitch = drm_rect_width(clip); + + drm_fb_xrgb_to_gray8(dst, dst_pitch, src, fb, clip); + drm_fb_gray8_to_mono_reversed(dst, dst_pitch, dst, fb, clip); Converting from dst into dst can give incorrect results. At some point we probably want to add restrict qualifiers to these pointers, to help the compiler with optimizing. A better approach here is to pull the per-line conversion from drm_fb_xrgb_to_gray8() into a separate helper and implement a line-by-line conversion here. something like this: drm_fb_xrgb_to_mono_reversed() { char *tmp = kmalloc(size of a single line of gray8) for (heigth) { drm_fb_xrgb_to_gray8_line(tmp, ..., src, ...); drm_fb_gray8_to_mono_reversed(dst, ..., tmp, ...); src += fb->pitches[0] dst += dst_pitch; } kfree(tmp); } I see. Yes, that sounds a much better approach. I'll change it in v3. Best regards, -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Ivo Totev OpenPGP_signature Description: OpenPGP digital signature
Re: [git pull] drm fixes for 5.17-rc3
The pull request you sent on Fri, 4 Feb 2022 16:09:15 +1000: > git://anongit.freedesktop.org/drm/drm tags/drm-fixes-2022-02-04 has been merged into torvalds/linux.git: https://git.kernel.org/torvalds/c/31462d9e47cf6e2cb10a69c833b5e081fff7086d Thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/prtracker.html
Re: [PATCH 19/21] fbcon: Maintain a private array of fb_info
Hi Daniel, On Mon, Jan 31, 2022 at 10:05:50PM +0100, Daniel Vetter wrote: > Accessing the one in fbmem.c without taking the right locks is a bad > idea. Instead maintain our own private copy, which is fully protected > by console_lock() (like everything else in fbcon.c). That copy is > serialized through fbcon_fb_registered/unregistered() calls. I fail to see why we can make a private copy of registered_fb just like that - are they not somehow shared between fbcon and fbmem. So when fbmem updates it, then fbcon will use the entry or such? I guess I am just ignorant of how registered_fb is used - but please explain. Sam > > Also this means we do not need to hold a full fb_info reference, which > is nice because doing so would mean a refcount loop between the > console and the fb_info. But it's also not nice since it means > console_lock() must be held absolutely everywhere. Well strictly > speaking we could still try to do some refcounting games again by > calling get_fb_info before we drop the console_lock. But things will > get tricky. > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Tetsuo Handa > Cc: Claudio Suarez > Cc: Du Cheng > Cc: Greg Kroah-Hartman > --- > drivers/video/fbdev/core/fbcon.c | 82 +--- > 1 file changed, 43 insertions(+), 39 deletions(-) > > diff --git a/drivers/video/fbdev/core/fbcon.c > b/drivers/video/fbdev/core/fbcon.c > index 22581952b4fd..a0ca34b29615 100644 > --- a/drivers/video/fbdev/core/fbcon.c > +++ b/drivers/video/fbdev/core/fbcon.c > @@ -86,10 +86,6 @@ > * - fbcon state itself is protected by the console_lock, and the code does a > * pretty good job at making sure that lock is held everywhere it's needed. > * > - * - access to the registered_fb array is entirely unprotected. This should > use > - * proper object lifetime handling, i.e. get/put_fb_info. This also means > - * switching from indices to proper pointers for fb_info everywhere. > - * > * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it > * means concurrent access to the same fbdev from both fbcon and userspace > * will blow up. To fix this all fbcon calls from fbmem.c need to be moved > out > @@ -107,6 +103,13 @@ enum { > > static struct fbcon_display fb_display[MAX_NR_CONSOLES]; > > +struct fb_info *fbcon_registered_fb[FB_MAX]; > +int fbcon_num_registered_fb; > + > +#define fbcon_for_each_registered_fb(i) \ > + for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \ > + if (!fbcon_registered_fb[i]) {} else > + > static signed char con2fb_map[MAX_NR_CONSOLES]; > static signed char con2fb_map_boot[MAX_NR_CONSOLES]; > > @@ -114,12 +117,7 @@ static struct fb_info *fbcon_info_from_console(int > console) > { > WARN_CONSOLE_UNLOCKED(); > > - /* > - * Note that only con2fb_map is protected by the console lock, > - * registered_fb is protected by a separate mutex. This lookup can > - * therefore race. > - */ > - return registered_fb[con2fb_map[console]]; > + return fbcon_registered_fb[con2fb_map[console]]; > } > > static int logo_lines; > @@ -516,7 +514,7 @@ static int do_fbcon_takeover(int show_logo) > { > int err, i; > > - if (!num_registered_fb) > + if (!fbcon_num_registered_fb) > return -ENODEV; > > if (!show_logo) > @@ -822,7 +820,7 @@ static int set_con2fb_map(int unit, int newidx, int user) > { > struct vc_data *vc = vc_cons[unit].d; > int oldidx = con2fb_map[unit]; > - struct fb_info *info = registered_fb[newidx]; > + struct fb_info *info = fbcon_registered_fb[newidx]; > struct fb_info *oldinfo = NULL; > int found, err = 0, show_logo; > > @@ -840,7 +838,7 @@ static int set_con2fb_map(int unit, int newidx, int user) > } > > if (oldidx != -1) > - oldinfo = registered_fb[oldidx]; > + oldinfo = fbcon_registered_fb[oldidx]; > > found = search_fb_in_map(newidx); > > @@ -932,13 +930,13 @@ static const char *fbcon_startup(void) >* If num_registered_fb is zero, this is a call for the dummy part. >* The frame buffer devices weren't initialized yet. >*/ > - if (!num_registered_fb || info_idx == -1) > + if (!fbcon_num_registered_fb || info_idx == -1) > return display_desc; > /* >* Instead of blindly using registered_fb[0], we use info_idx, set by >* fbcon_fb_registered(); >*/ > - info = registered_fb[info_idx]; > + info = fbcon_registered_fb[info_idx]; > if (!info) > return NULL; > > @@ -1153,9 +1151,9 @@ static void fbcon_release_all(void) > struct fb_info *info; > int i, j, mapped; > > - for_each_registered_fb(i) { > + fbcon_for_each_registered_fb(i) { > mapped = 0; > - info = registered_fb[i]; > + info = fbcon_registered_fb[i];
Re: [PATCH 18/21] fbcon: untangle fbcon_exit
Hi Daniel, On Mon, Jan 31, 2022 at 10:05:49PM +0100, Daniel Vetter wrote: > There's a bunch of confusions going on here: > - The deferred fbcon setup notifier should only be cleaned up from > fb_console_exit(), to be symmetric with fb_console_init() > - We also need to make sure we don't race with the work, which means > temporarily dropping the console lock (or we can deadlock) > - That also means no point in clearing deferred_takeover, we are > unloading everything anyway. > - Finally rename fbcon_exit to fbcon_release_all and move it, since > that's what's it doing when being called from consw->con_deinit > through fbcon_deinit. > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Greg Kroah-Hartman > Cc: Claudio Suarez > Cc: Du Cheng > Cc: Tetsuo Handa > --- > drivers/video/fbdev/core/fbcon.c | 63 > 1 file changed, 32 insertions(+), 31 deletions(-) > > diff --git a/drivers/video/fbdev/core/fbcon.c > b/drivers/video/fbdev/core/fbcon.c > index 5c14e24d14a1..22581952b4fd 100644 > --- a/drivers/video/fbdev/core/fbcon.c > +++ b/drivers/video/fbdev/core/fbcon.c > @@ -185,7 +185,6 @@ static void fbcon_set_disp(struct fb_info *info, struct > fb_var_screeninfo *var, > int unit); > static void fbcon_modechanged(struct fb_info *info); > static void fbcon_set_all_vcs(struct fb_info *info); > -static void fbcon_exit(void); > > static struct device *fbcon_device; > > @@ -1149,6 +1148,27 @@ static void fbcon_free_font(struct fbcon_display *p, > bool freefont) > > static void set_vc_hi_font(struct vc_data *vc, bool set); > > +static void fbcon_release_all(void) > +{ > + struct fb_info *info; > + int i, j, mapped; > + > + for_each_registered_fb(i) { > + mapped = 0; > + info = registered_fb[i]; > + > + for (j = first_fb_vc; j <= last_fb_vc; j++) { > + if (con2fb_map[j] == i) { > + mapped = 1; > + con2fb_map[j] = -1; > + } > + } > + > + if (mapped) > + fbcon_release(info); > + } > +} > + > static void fbcon_deinit(struct vc_data *vc) > { > struct fbcon_display *p = &fb_display[vc->vc_num]; > @@ -1188,7 +1208,7 @@ static void fbcon_deinit(struct vc_data *vc) > set_vc_hi_font(vc, false); > > if (!con_is_bound(&fb_con)) > - fbcon_exit(); > + fbcon_release_all(); > > if (vc->vc_num == logo_shown) > logo_shown = FBCON_LOGO_CANSHOW; > @@ -3316,34 +3336,6 @@ static void fbcon_start(void) > #endif > } > > -static void fbcon_exit(void) > -{ > - struct fb_info *info; > - int i, j, mapped; > - > -#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER > - if (deferred_takeover) { > - dummycon_unregister_output_notifier(&fbcon_output_nb); > - deferred_takeover = false; > - } > -#endif > - > - for_each_registered_fb(i) { > - mapped = 0; > - info = registered_fb[i]; > - > - for (j = first_fb_vc; j <= last_fb_vc; j++) { > - if (con2fb_map[j] == i) { > - mapped = 1; > - con2fb_map[j] = -1; > - } > - } > - > - if (mapped) > - fbcon_release(info); > - } > -} > - > void __init fb_console_init(void) > { > int i; > @@ -3383,10 +3375,19 @@ static void __exit fbcon_deinit_device(void) > > void __exit fb_console_exit(void) > { > +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER > + console_lock(); > + if (deferred_takeover) > + dummycon_unregister_output_notifier(&fbcon_output_nb); > + console_unlock(); > + > + cancel_work_sync(&fbcon_deferred_takeover_work); > +#endif > + > console_lock(); > fbcon_deinit_device(); > device_destroy(fb_class, MKDEV(0, 0)); > - fbcon_exit(); > + We loose the call to fbcon_release_all() here. We have part of the old fbcon_exit() above, but miss the release parts. Maybe I missed something obvious? The rest looks fine. Sam > do_unregister_con_driver(&fb_con); > console_unlock(); > } > -- > 2.33.0
Re: [PATCH 17/21] fbcon: Move more code into fbcon_release
On Mon, Jan 31, 2022 at 10:05:48PM +0100, Daniel Vetter wrote: > con2fb_release_oldinfo() has a bunch more kfree() calls than > fbcon_exit(), but since kfree() on NULL is harmless doing that in both > places should be ok. This is also a bit more symmetric now again with > fbcon_open also allocating the fbcon_ops structure. > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Tetsuo Handa > Cc: Greg Kroah-Hartman > Cc: Du Cheng > Cc: Claudio Suarez Thanks, like I wrote earlier this makes sense. Acked-by: Sam Ravnborg
Re: [PATCH 16/21] fbcon: Move console_lock for register/unlink/unregister
Hi Daniel. On Mon, Jan 31, 2022 at 10:05:47PM +0100, Daniel Vetter wrote: > Ideally console_lock becomes an implementation detail of fbcon.c and > doesn't show up anywhere in fbmem.c. We're still pretty far from that, > but at least the register/unregister code is there now. > > With this the do_fb_ioctl() handler is the only code in fbmem.c still > calling console_lock(). > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Thomas Zimmermann > Cc: Du Cheng > Cc: Claudio Suarez > Cc: Greg Kroah-Hartman > Cc: Tetsuo Handa > Cc: Matthew Wilcox > Cc: Sam Ravnborg > Cc: Zheyu Ma > Cc: Guenter Roeck > Cc: Alex Deucher > Cc: Zhen Lei > Cc: Xiyu Yang Like how lock_console is now almost local to fbcon. Except the usage outside fbmem + fbcon taht is. Acked-by: Sam Ravnborg > --- > drivers/video/fbdev/core/fbcon.c | 33 ++-- > drivers/video/fbdev/core/fbmem.c | 23 ++ > 2 files changed, 29 insertions(+), 27 deletions(-) > > diff --git a/drivers/video/fbdev/core/fbcon.c > b/drivers/video/fbdev/core/fbcon.c > index 11b9f962af6f..e5e8aaf6f60d 100644 > --- a/drivers/video/fbdev/core/fbcon.c > +++ b/drivers/video/fbdev/core/fbcon.c > @@ -2776,10 +2776,12 @@ void fbcon_fb_unbind(struct fb_info *info) > int i, new_idx = -1; > int idx = info->node; > > - WARN_CONSOLE_UNLOCKED(); > + console_lock(); > > - if (!fbcon_has_console_bind) > + if (!fbcon_has_console_bind) { > + console_unlock(); > return; > + } > > for (i = first_fb_vc; i <= last_fb_vc; i++) { > if (con2fb_map[i] != idx && > @@ -2814,6 +2816,8 @@ void fbcon_fb_unbind(struct fb_info *info) > } > fbcon_unbind(); > } > + > + console_unlock(); > } > > /* called with console_lock held */ > @@ -2821,10 +2825,12 @@ void fbcon_fb_unregistered(struct fb_info *info) > { > int i, idx; > > - WARN_CONSOLE_UNLOCKED(); > + console_lock(); > > - if (deferred_takeover) > + if (deferred_takeover) { > + console_unlock(); > return; > + } > > idx = info->node; > for (i = first_fb_vc; i <= last_fb_vc; i++) { > @@ -2853,6 +2859,7 @@ void fbcon_fb_unregistered(struct fb_info *info) > > if (!num_registered_fb) > do_unregister_con_driver(&fb_con); > + console_unlock(); > } > > void fbcon_remap_all(struct fb_info *info) > @@ -2910,19 +2917,27 @@ static inline void fbcon_select_primary(struct > fb_info *info) > } > #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ > > +static bool lockless_register_fb; > +module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, > 0400); > +MODULE_PARM_DESC(lockless_register_fb, > + "Lockless framebuffer registration for debugging [default=off]"); > + > /* called with console_lock held */ > int fbcon_fb_registered(struct fb_info *info) > { > int ret = 0, i, idx; > > - WARN_CONSOLE_UNLOCKED(); > + if (!lockless_register_fb) > + console_lock(); > + else > + atomic_inc(&ignore_console_lock_warning); > > idx = info->node; > fbcon_select_primary(info); > > if (deferred_takeover) { > pr_info("fbcon: Deferring console take-over\n"); > - return 0; > + goto out; > } > > if (info_idx == -1) { > @@ -2942,6 +2957,12 @@ int fbcon_fb_registered(struct fb_info *info) > } > } > > +out: > + if (!lockless_register_fb) > + console_unlock(); > + else > + atomic_dec(&ignore_console_lock_warning); > + > return ret; > } > > diff --git a/drivers/video/fbdev/core/fbmem.c > b/drivers/video/fbdev/core/fbmem.c > index fd51d12f2702..904ef1250677 100644 > --- a/drivers/video/fbdev/core/fbmem.c > +++ b/drivers/video/fbdev/core/fbmem.c > @@ -1573,14 +1573,9 @@ static void do_remove_conflicting_framebuffers(struct > apertures_struct *a, > } > } > > -static bool lockless_register_fb; > -module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, > 0400); > -MODULE_PARM_DESC(lockless_register_fb, > - "Lockless framebuffer registration for debugging [default=off]"); > - > static int do_register_framebuffer(struct fb_info *fb_info) > { > - int i, ret; > + int i; > struct fb_videomode mode; > > if (fb_check_foreignness(fb_info)) > @@ -1649,17 +1644,7 @@ static int do_register_framebuffer(struct fb_info > *fb_info) > } > #endif > > - if (!lockless_register_fb) > - console_lock(); > - else > - atomic_inc(&ignore_console_lock_warning); > - ret = fbcon_fb_registered(fb_info); > - > - if (!lockless_register_fb) > - console_unlock(); > - else > - atomic_dec(&ignore_console_lock_warning); > - return ret; > + return fbcon_fb_registered(fb_info); > } > > st
Re: [PATCH 15/21] fbcon: Consistently protect deferred_takeover with console_lock()
On Mon, Jan 31, 2022 at 10:05:46PM +0100, Daniel Vetter wrote: > This shouldn't be a problem in practice since until we've actually > taken over the console there's nothing we've registered with the > console/vt subsystem, so the exit/unbind path that check this can't > do the wrong thing. But it's confusing, so fix it by moving it a tad > later. > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Du Cheng > Cc: Tetsuo Handa > Cc: Claudio Suarez > Cc: Thomas Zimmermann It had helped me if the commitlog was explicit that the deferred_takeover falg is moved to the worker function to reset it as late as possible. With the commit log updated: Acked-by: Sam Ravnborg > --- > drivers/video/fbdev/core/fbcon.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/drivers/video/fbdev/core/fbcon.c > b/drivers/video/fbdev/core/fbcon.c > index 496bc5f2133e..11b9f962af6f 100644 > --- a/drivers/video/fbdev/core/fbcon.c > +++ b/drivers/video/fbdev/core/fbcon.c > @@ -3247,6 +3247,9 @@ static void fbcon_register_existing_fbs(struct > work_struct *work) > > console_lock(); > > + deferred_takeover = false; > + logo_shown = FBCON_LOGO_DONTSHOW; > + > for_each_registered_fb(i) > fbcon_fb_registered(registered_fb[i]); > > @@ -3264,8 +3267,6 @@ static int fbcon_output_notifier(struct notifier_block > *nb, > pr_info("fbcon: Taking over console\n"); > > dummycon_unregister_output_notifier(&fbcon_output_nb); > - deferred_takeover = false; > - logo_shown = FBCON_LOGO_DONTSHOW; > > /* We may get called in atomic context */ > schedule_work(&fbcon_deferred_takeover_work); > -- > 2.33.0
Re: [PATCH 03/19] iosys-map: Add a few more helpers
On Fri, Feb 04, 2022 at 08:05:56PM +0100, Thomas Zimmermann wrote: Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: First the simplest ones: - iosys_map_memset(): when abstracting system and I/O memory, just like the memcpy() use case, memset() also has dedicated functions to be called for using IO memory. - iosys_map_memcpy_from(): we may need to copy data from I/O memory, not only to. In certain situations it's useful to be able to read or write to an offset that is calculated by having the memory layout given by a struct declaration. Usually we are going to read/write a u8, u16, u32 or u64. As a pre-requisite for the implementation, add iosys_map_memcpy_from() to be the equivalent of iosys_map_memcpy_to(), but in the other direction. Then add 2 pairs of macros: - iosys_map_rd() / iosys_map_wr() - iosys_map_rd_field() / iosys_map_wr_field() The first pair takes the C-type and offset to read/write. The second pair uses a struct describing the layout of the mapping in order to calculate the offset and size being read/written. We could use readb, readw, readl, readq and the write* counterparts, however due to alignment issues this may not work on all architectures. If alignment needs to be checked to call the right function, it's not possible to decide at compile-time which function to call: so just leave the decision to the memcpy function that will do exactly that. Finally, in order to use the above macros with a map derived from another, add another initializer: IOSYS_MAP_INIT_OFFSET(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi --- include/linux/iosys-map.h | 154 +- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index edd7fa3be9e9..96f8b61ac6fb 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -6,6 +6,7 @@ #ifndef __IOSYS_MAP_H__ #define __IOSYS_MAP_H__ +#include #include #include @@ -133,6 +134,45 @@ static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr) map->is_iomem = false; } +/** + * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map + * @map_: The dma-buf mapping structure to copy from + * @offset_: Offset to add to the other mapping + * + * Initializes a new iosys_map struct based on another passed as argument. It + * does a shallow copy of the struct so it's possible to update the back storage + * without changing where the original map points to. It is the equivalent of + * doing: + * + * .. code-block: c + * + * iosys_map map = other_map; + * iosys_map_incr(&map, &offset); + * + * Example usage: + * + * .. code-block: c + * + * void foo(struct device *dev, struct iosys_map *base_map) + * { + * ... + * struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET); + * ... + * } + * + * The advantage of using the initializer over just increasing the offset with + * ``iosys_map_incr()`` like above is that the new map will always point to the + * right place of the buffer during its scope. It reduces the risk of updating + * the wrong part of the buffer and having no compiler warning about that. If + * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn + * using a uninitialized variable. + */ +#define IOSYS_MAP_INIT_OFFSET(map_, offset_) (struct iosys_map) \ + { \ + .vaddr = (map_)->vaddr + (offset_), \ + .is_iomem = (map_)->is_iomem,\ + } I already nak'ed this macro. This works because of the aliasing rules within the union and the fact that there are only plain pointers. But this is fragile. Do anything more complicated and it breaks. There's not even a test that would tell you that it failed. Therefore, struct iosys_map should only be initialized by the code that creates the stored pointer. I wonder if there is an alternative that is not fragile that allows us to have a macro like that. In that thread I and Daniel continued chatting and after my additional explanations he was convinced about that. I only came up with such a macro after doing the rest of the patches and noticing a pattern that is hard to debug otherwise. I expanded the explanation in the doc above this macro. Maybe something like: #define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({ \ struct iosys_map copy = *(map_);\ iosys_map_incr(©, offset_); \ copy; \ }) Hopefully the compiler elides the additional copy, but I need to check. However, you won't need the offset'ed iosys_m
Re: [PATCH 14/21] fbcon: use lock_fb_info in fbcon_open/release
On Mon, Jan 31, 2022 at 10:05:45PM +0100, Daniel Vetter wrote: > Now we get to the real motiviation, because fbmem.c insists that > that's the right lock for these. > > Ofc fbcon.c has a lot more places where it probably should call > lock_fb_info(). But looking at fbmem.c at least most of these seem to > be protected by console_lock() too, which is probably what papers over > any issues. > > Note that this means we're shuffling around a bit the locking sections > for some of the console takeover and unbind paths, but not all: > - console binding/unbinding from the console layer never with > lock_fb_info > - unbind (as opposed to unlink) never bother with lock_fb_info > > Also the real serialization against set_par and set_pan are still > doing by wrapping the entire ioctl code in console_lock(). So this > shuffling shouldn't be worse than what we had from a "can you trigger > races?" pov, but it's at least clearer. > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Claudio Suarez > Cc: Tetsuo Handa > Cc: Thomas Zimmermann > Cc: Greg Kroah-Hartman > Cc: Du Cheng > Cc: Sam Ravnborg > Cc: Matthew Wilcox > Cc: William Kucharski > Cc: Alex Deucher > Cc: Zheyu Ma > Cc: Zhen Lei > Cc: Xiyu Yang Well, the patch does what the commit log says. Acked-by: Sam Ravnborg
Re: [RFC PATCH 1/3] drm: Extract amdgpu_sa.c as a generic suballocation helper
Hi Am 04.02.22 um 19:29 schrieb Christian König: Oh, that's on my TODO list for years! Am 04.02.22 um 18:48 schrieb Maarten Lankhorst: Suballocating a buffer object is something that is not driver generic, and is useful for other drivers as well. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/Makefile | 4 +- drivers/gpu/drm/drm_suballoc.c | 424 + include/drm/drm_suballoc.h | 78 ++ 3 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_suballoc.c create mode 100644 include/drm/drm_suballoc.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8675c2af7ae1..b848bcf8790c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -57,7 +57,9 @@ drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o \ drm_scdc_helper.o drm_gem_atomic_helper.o \ drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o drm_self_refresh_helper.o drm_rect.o + drm_format_helper.o drm_self_refresh_helper.o drm_rect.o \ + drm_suballoc.o + I think we should put that into a separate module like we now do with other helpers as well. Please. KMS helpers are now likely to be linked into the kernel binary. I've already spent time to reduce the size of the module. Best regard Thomas -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Ivo Totev OpenPGP_signature Description: OpenPGP digital signature
Re: [PATCH 13/21] fbcon: move more common code into fb_open()
On Mon, Jan 31, 2022 at 10:05:44PM +0100, Daniel Vetter wrote: > No idea why con2fb_acquire_newinfo() initializes much less than > fbcon_startup(), but so be it. From a quick look most of the > un-initialized stuff should be fairly harmless, but who knows. > > Signed-off-by: Daniel Vetter > Cc: Daniel Vetter > Cc: Greg Kroah-Hartman > Cc: Tetsuo Handa > Cc: Thomas Zimmermann > Cc: Claudio Suarez > Cc: Du Cheng > --- > drivers/video/fbdev/core/fbcon.c | 74 +--- > 1 file changed, 31 insertions(+), 43 deletions(-) > > diff --git a/drivers/video/fbdev/core/fbcon.c > b/drivers/video/fbdev/core/fbcon.c > index b83a5a77d8a8..5a3391ff038d 100644 > --- a/drivers/video/fbdev/core/fbcon.c > +++ b/drivers/video/fbdev/core/fbcon.c > @@ -680,8 +680,18 @@ static int fbcon_invalid_charcount(struct fb_info *info, > unsigned charcount) > > #endif /* CONFIG_MISC_TILEBLITTING */ > > +static void fbcon_release(struct fb_info *info) > +{ > + if (info->fbops->fb_release) > + info->fbops->fb_release(info, 0); > + > + module_put(info->fbops->owner); > +} > + > static int fbcon_open(struct fb_info *info) > { > + struct fbcon_ops *ops; > + > if (!try_module_get(info->fbops->owner)) > return -ENODEV; > > @@ -691,19 +701,22 @@ static int fbcon_open(struct fb_info *info) > return -ENODEV; > } > > - return 0; > -} > + ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); > + if (!ops) { > + fbcon_release(info); > + return -ENOMEM; > + } > > -static void fbcon_release(struct fb_info *info) > -{ > - if (info->fbops->fb_release) > - info->fbops->fb_release(info, 0); > + INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); > + ops->info = info; > + info->fbcon_par = ops; > + ops->cur_blink_jiffies = HZ / 5; > > - module_put(info->fbops->owner); > + return 0; > } > > static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, > - int unit, int oldidx) > + int unit) > { > struct fbcon_ops *ops = NULL; > int err; > @@ -712,27 +725,10 @@ static int con2fb_acquire_newinfo(struct vc_data *vc, > struct fb_info *info, > if (err) > return err; > > - if (!err) { > - ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); > - if (!ops) > - err = -ENOMEM; > - > - INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); > - } > - > - if (!err) { > - ops->cur_blink_jiffies = HZ / 5; > - ops->info = info; > - info->fbcon_par = ops; > - > - if (vc) > - set_blitting_type(vc, info); > - } > + ops = info->fbcon_par; > > - if (err) { > - con2fb_map[unit] = oldidx; > - fbcon_release(info); > - } > + if (vc) > + set_blitting_type(vc, info); > > return err; > } > @@ -840,9 +836,11 @@ static int set_con2fb_map(int unit, int newidx, int user) > > found = search_fb_in_map(newidx); > > - con2fb_map[unit] = newidx; > - if (!err && !found) > - err = con2fb_acquire_newinfo(vc, info, unit, oldidx); > + if (!err && !found) { > + err = con2fb_acquire_newinfo(vc, info, unit); > + if (!err) > + con2fb_map[unit] = newidx; > + } This looks like an unintentional change of functionality as con2fb_map[unit] is only assigned when we do a con2fb_acquire_newinfo(). Staring at the code I could not say it is wrong, but not nice to hide the change in this patch. Sam > > /* >* If old fb is not mapped to any of the consoles, > @@ -939,20 +937,10 @@ static const char *fbcon_startup(void) > if (fbcon_open(info)) > return NULL; > > - ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); > - if (!ops) { > - fbcon_release(info); > - return NULL; > - } > - > - INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor); > - > + ops = info->fbcon_par; > ops->currcon = -1; > ops->graphics = 1; > ops->cur_rotate = -1; > - ops->cur_blink_jiffies = HZ / 5; > - ops->info = info; > - info->fbcon_par = ops; > > p->con_rotate = initial_rotation; > if (p->con_rotate == -1) > @@ -1022,7 +1010,7 @@ static void fbcon_init(struct vc_data *vc, int init) > return; > > if (!info->fbcon_par) > - con2fb_acquire_newinfo(vc, info, vc->vc_num, -1); > + con2fb_acquire_newinfo(vc, info, vc->vc_num); > > /* If we are not the first console on this > fb, copy the font from that console */ > -- > 2.33.0
[PATCH] drm/nouveau/backlight: Just set all backlight types as RAW
Currently we can get a warning on systems with eDP backlights like so: nv_backlight: invalid backlight type WARNING: CPU: 4 PID: 454 at drivers/video/backlight/backlight.c:420 backlight_device_register+0x226/0x250 This happens as a result of us not filling out props.type for the eDP backlight, even though we do it for all other backlight types. Since nothing in our driver uses anything but BACKLIGHT_RAW, let's take the props\.type assignments out of the codepaths for individual backlight types and just set it unconditionally to prevent this from happening again. Signed-off-by: Lyude Paul Fixes: 6eca310e8924 ("drm/nouveau/kms/nv50-: Add basic DPCD backlight support for nouveau") Cc: # v5.15+ --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 6af12dc99d7f..daf9f87477ba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -101,7 +101,6 @@ nv40_backlight_init(struct nouveau_encoder *encoder, if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) return -ENODEV; - props->type = BACKLIGHT_RAW; props->max_brightness = 31; *ops = &nv40_bl_ops; return 0; @@ -343,7 +342,6 @@ nv50_backlight_init(struct nouveau_backlight *bl, else *ops = &nva3_bl_ops; - props->type = BACKLIGHT_RAW; props->max_brightness = 100; return 0; @@ -411,6 +409,7 @@ nouveau_backlight_init(struct drm_connector *connector) goto fail_alloc; } + props.type = BACKLIGHT_RAW; bl->dev = backlight_device_register(backlight_name, connector->kdev, nv_encoder, ops, &props); if (IS_ERR(bl->dev)) { -- 2.34.1
Re: [PATCH v2 1/4] drm/format-helper: Add drm_fb_{xrgb8888,gray8}_to_mono_reversed()
Hello Thomas, Thanks a lot for your feedback. On 2/4/22 16:52, Thomas Zimmermann wrote: [snip] >> +static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, >> size_t pixels) >> +{ >> +unsigned int xb, i; >> + >> +for (xb = 0; xb < pixels / 8; xb++) { > > In practice, all mode widths are multiples of 8 because VGA mandated it. > So it's ok-ish to assume this here. You should probably at least print a > warning somewhere if (pixels % 8 != 0) > Agreed. [snip] >> + * DRM doesn't have native monochrome or grayscale support. >> + * Such drivers can announce the commonly supported XR24 format to userspace >> + * and use drm_fb_xrgb_to_gray8() to convert to grayscale and then this >> + * helper function to convert to the native format. >> + */ >> +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const >> void *src, >> + const struct drm_rect *clip) > > There's a bug here. You want to pass in a drm_framebuffer as fourth > argument. > >> +{ >> + >> +size_t height = drm_rect_height(clip); >> +size_t width = drm_rect_width(clip); >> +unsigned int y; >> +const u8 *gray8 = src; >> +u8 *mono = dst; >> + >> +if (!dst_pitch) >> +dst_pitch = width; > > The dst_pitch is given in bytes. You have to device by 8. Here would be > a good place to warn if (width % 8 != 0). > Ok. >> + >> +for (y = 0; y < height; y++) { >> +drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch); >> +mono += (dst_pitch / 8); > > The dst_pitch is already given in bytes. > Yes, I know but for reversed mono we want only 1/8 of the width since we are converting from 8 bits per pixel greyscale to 1 bit per pixel mono. Or am I misunderstanding what you meant ? >> +gray8 += dst_pitch; > > 'gray8 += fb->pitches[0]' would be correct. > Ok. [snip] >> + */ >> +void drm_fb_xrgb_to_mono_reversed(void *dst, unsigned int dst_pitch, >> const void *src, >> + const struct drm_framebuffer *fb, >> + const struct drm_rect *clip) >> +{ >> +if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB)) >> +return; >> + >> +if (!dst_pitch) >> +dst_pitch = drm_rect_width(clip); >> + >> +drm_fb_xrgb_to_gray8(dst, dst_pitch, src, fb, clip); >> +drm_fb_gray8_to_mono_reversed(dst, dst_pitch, dst, fb, clip); > > Converting from dst into dst can give incorrect results. At some point > we probably want to add restrict qualifiers to these pointers, to help > the compiler with optimizing. > > A better approach here is to pull the per-line conversion from > drm_fb_xrgb_to_gray8() into a separate helper and implement a > line-by-line conversion here. something like this: > >drm_fb_xrgb_to_mono_reversed() >{ > char *tmp = kmalloc(size of a single line of gray8) > > for (heigth) { > drm_fb_xrgb_to_gray8_line(tmp, ..., src, ...); > drm_fb_gray8_to_mono_reversed(dst, ..., tmp, ...); > > src += fb->pitches[0] > dst += dst_pitch; > } > > kfree(tmp); >} > I see. Yes, that sounds a much better approach. I'll change it in v3. Best regards, -- Javier Martinez Canillas Linux Engineering Red Hat
Re: [PATCH 02/19] iosys-map: Add offset to iosys_map_memcpy_to()
On Fri, Feb 04, 2022 at 07:48:10PM +0100, Thomas Zimmermann wrote: Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: In certain situations it's useful to be able to write to an offset of the mapping. Add a dst_offset to iosys_map_memcpy_to(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/drm_cache.c | 2 +- drivers/gpu/drm/drm_fb_helper.c | 2 +- include/linux/iosys-map.h | 17 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index 66597e411764..c3e6e615bf09 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -218,7 +218,7 @@ static void memcpy_fallback(struct iosys_map *dst, if (!dst->is_iomem && !src->is_iomem) { memcpy(dst->vaddr, src->vaddr, len); } else if (!src->is_iomem) { - iosys_map_memcpy_to(dst, src->vaddr, len); + iosys_map_memcpy_to(dst, 0, src->vaddr, len); } else if (!dst->is_iomem) { memcpy_fromio(dst->vaddr, src->vaddr_iomem, len); } else { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 238f815cb2a0..bf5cc9a42e5a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -385,7 +385,7 @@ static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper, iosys_map_incr(dst, offset); /* go to first pixel within clip rect */ for (y = clip->y1; y < clip->y2; y++) { - iosys_map_memcpy_to(dst, src, len); + iosys_map_memcpy_to(dst, 0, src, len); iosys_map_incr(dst, fb->pitches[0]); src += fb->pitches[0]; } diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index f4186f91caa6..edd7fa3be9e9 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -220,22 +220,23 @@ static inline void iosys_map_clear(struct iosys_map *map) } /** - * iosys_map_memcpy_to - Memcpy into iosys mapping + * iosys_map_memcpy_to_offset - Memcpy into offset of iosys_map 'iosys_map_memcpy_to' With that fixed: Reviewed-by: Thomas Zimmermann thanks, I noticed that, but looks like I squashed to the wrong patch. Lucas De Marchi
Re: [PATCH 09/19] drm/i915/guc: Convert engine record to iosys_map
Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: Use iosys_map to read fields from the dma_blob so access to IO and system memory is abstracted away. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 14 ++ drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h | 3 ++- .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 17 ++--- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 6311b9da87e4..1d21a2d457e0 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -698,18 +698,16 @@ void intel_guc_ads_reset(struct intel_guc *guc) u32 intel_guc_engine_usage_offset(struct intel_guc *guc) { - struct __guc_ads_blob *blob = guc->ads_blob; - u32 base = intel_guc_ggtt_offset(guc, guc->ads_vma); - u32 offset = base + ptr_offset(blob, engine_usage); - - return offset; + return intel_guc_ggtt_offset(guc, guc->ads_vma) + + offsetof(struct __guc_ads_blob, engine_usage); } -struct guc_engine_usage_record *intel_guc_engine_usage(struct intel_engine_cs *engine) +struct iosys_map intel_guc_engine_usage_record_map(struct intel_engine_cs *engine) { struct intel_guc *guc = &engine->gt->uc.guc; - struct __guc_ads_blob *blob = guc->ads_blob; u8 guc_class = engine_class_to_guc_class(engine->class); + size_t offset = offsetof(struct __guc_ads_blob, + engine_usage.engines[guc_class][ilog2(engine->logical_mask)]); - return &blob->engine_usage.engines[guc_class][ilog2(engine->logical_mask)]; + return IOSYS_MAP_INIT_OFFSET(&guc->ads_map, offset); Here's one of the few cases where you can legitimately make a copy of an iosys_map buffer and call iosys_map_incr() on it. Saves you the IOSYS_MAP_INIT_OFFSET(). Best regards Thomas } diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h index e74c110facff..1c64f4d6ea21 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h @@ -7,6 +7,7 @@ #define _INTEL_GUC_ADS_H_ #include +#include struct intel_guc; struct drm_printer; @@ -18,7 +19,7 @@ void intel_guc_ads_init_late(struct intel_guc *guc); void intel_guc_ads_reset(struct intel_guc *guc); void intel_guc_ads_print_policy_info(struct intel_guc *guc, struct drm_printer *p); -struct guc_engine_usage_record *intel_guc_engine_usage(struct intel_engine_cs *engine); +struct iosys_map intel_guc_engine_usage_record_map(struct intel_engine_cs *engine); u32 intel_guc_engine_usage_offset(struct intel_guc *guc); #endif diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c index b3a429a92c0d..6d34842f68b4 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c @@ -1139,6 +1139,9 @@ __extend_last_switch(struct intel_guc *guc, u64 *prev_start, u32 new_start) *prev_start = ((u64)gt_stamp_hi << 32) | new_start; } +#define record_read(map_, field_) \ + iosys_map_rd_field(map_, struct guc_engine_usage_record, field_) + /* * GuC updates shared memory and KMD reads it. Since this is not synchronized, * we run into a race where the value read is inconsistent. Sometimes the @@ -1153,17 +1156,17 @@ __extend_last_switch(struct intel_guc *guc, u64 *prev_start, u32 new_start) static void __get_engine_usage_record(struct intel_engine_cs *engine, u32 *last_in, u32 *id, u32 *total) { - struct guc_engine_usage_record *rec = intel_guc_engine_usage(engine); + struct iosys_map rec_map = intel_guc_engine_usage_record_map(engine); int i = 0; do { - *last_in = READ_ONCE(rec->last_switch_in_stamp); - *id = READ_ONCE(rec->current_context_index); - *total = READ_ONCE(rec->total_runtime); + *last_in = record_read(&rec_map, last_switch_in_stamp); + *id = record_read(&rec_map, current_context_index); + *total = record_read(&rec_map, total_runtime); - if (READ_ONCE(rec->last_switch_in_stamp) == *last_in && - READ_ONCE(rec->current_context_index) == *id && - READ_ONCE(rec->total_runtime) == *total) + if (record_read(&rec_map, last_switch_in_stamp) == *last_in && + record_read(&rec_map, current_context_index) == *id && + record_read(&rec_map, total_runtime) == *total) break; } while (++i < 6); } -- Thomas Zimmermann Graphi
Re: [PATCH 07/19] drm/i915/guc: Convert golden context init to iosys_map
Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: Now the map is saved during creation, so use it to initialize the golden context, reading from shmem and writing to either system or IO memory. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 25 +++--- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 3a0afce7564e..d32b407a2d25 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -473,18 +473,17 @@ static struct intel_engine_cs *find_engine_state(struct intel_gt *gt, u8 engine_ static void guc_init_golden_context(struct intel_guc *guc) { - struct __guc_ads_blob *blob = guc->ads_blob; struct intel_engine_cs *engine; struct intel_gt *gt = guc_to_gt(guc); + struct iosys_map golden_context_map; u32 addr_ggtt, offset; u32 total_size = 0, alloc_size, real_size; u8 engine_class, guc_class; - u8 *ptr; if (!intel_uc_uses_guc_submission(>->uc)) return; - GEM_BUG_ON(!blob); + GEM_BUG_ON(iosys_map_is_null(&guc->ads_map)); /* * Go back and fill in the golden context data now that it is @@ -492,15 +491,15 @@ static void guc_init_golden_context(struct intel_guc *guc) */ offset = guc_ads_golden_ctxt_offset(guc); addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset; - ptr = ((u8 *)blob) + offset; + + golden_context_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map, offset); for (engine_class = 0; engine_class <= MAX_ENGINE_CLASS; ++engine_class) { if (engine_class == OTHER_CLASS) continue; guc_class = engine_class_to_guc_class(engine_class); - - if (!blob->system_info.engine_enabled_masks[guc_class]) + if (!ads_blob_read(guc, system_info.engine_enabled_masks[guc_class])) continue; real_size = intel_engine_context_size(gt, engine_class); @@ -511,18 +510,20 @@ static void guc_init_golden_context(struct intel_guc *guc) if (!engine) { drm_err(>->i915->drm, "No engine state recorded for class %d!\n", engine_class); - blob->ads.eng_state_size[guc_class] = 0; - blob->ads.golden_context_lrca[guc_class] = 0; + ads_blob_write(guc, ads.eng_state_size[guc_class], 0); + ads_blob_write(guc, ads.golden_context_lrca[guc_class], 0); continue; } - GEM_BUG_ON(blob->ads.eng_state_size[guc_class] != + GEM_BUG_ON(ads_blob_read(guc, ads.eng_state_size[guc_class]) != real_size - LRC_SKIP_SIZE); - GEM_BUG_ON(blob->ads.golden_context_lrca[guc_class] != addr_ggtt); + GEM_BUG_ON(ads_blob_read(guc, ads.golden_context_lrca[guc_class]) != addr_ggtt); + addr_ggtt += alloc_size; - shmem_read(engine->default_state, 0, ptr, real_size); - ptr += alloc_size; + shmem_read_to_iosys_map(engine->default_state, 0, + &golden_context_map, real_size); + iosys_map_incr(&golden_context_map, alloc_size); Use an offset to index into iosys_map. Even if that means to add another parameter to shmem_read_to_iosys_map(). This will save you IOSYS_MAP_INIT_OFFSET() and iosys_map_incr(). Best regards Thomas } GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size); -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Ivo Totev OpenPGP_signature Description: OpenPGP digital signature
Re: [PATCH v2 2/4] drm/tiny: Add driver for Solomon SSD130X OLED displays
Hello Andy, Thanks for your feedback. On 2/4/22 15:26, Andy Shevchenko wrote: > On Fri, Feb 04, 2022 at 02:43:45PM +0100, Javier Martinez Canillas wrote: >> Add a DRM driver for SSD1305, SSD1306, SSD1307 and SSD1309 Solomon OLED >> controllers that can be programmed via an I2C interface. This is a port >> of the ssd1307fb driver that already supports these devices. >> >> A Device Tree binding is not added because the DRM driver is compatible >> with the existing binding for the ssd1307fb driver. > > ... > >> +/* >> + * DRM driver for Solomon SSD130X OLED displays >> + * >> + * Copyright 2022 Red Hat Inc. >> + * >> + * Based on drivers/video/fbdev/ssd1307fb.c >> + * Copyright 2012 Free Electrons > >> + * > > No need for this blank line. > Ok. >> + */ > > ... > >> +struct ssd130x_device { >> +struct drm_device drm; >> +struct drm_simple_display_pipe pipe; >> +struct drm_display_mode mode; >> +struct drm_connector connector; > > >> +struct i2c_client *client; > > Can we logically separate hw protocol vs hw interface from day 1, please? > This will allow to add SPI support for this panel much easier. > > Technically I would like to see here > > struct device *dev; > > and probably (I haven't looked into design) > > struct ssd130x_ops *ops; > > or something alike. > Sure. I wanted to keep the driver simple, making the writes bus agnostic and adding a level of indirection would make it more complex. But I agree that it will also make easier to add more buses later. I will do that for v3. [snip] > >> +static inline int ssd130x_write_value(struct i2c_client *client, u8 value) > > Not sure inline does anything useful here. > Ditto for the rest similar cases. > Ok, I'll drop them. > ... > >> +static inline int ssd130x_write_cmd(struct i2c_client *client, int count, >> +/* u8 cmd, u8 value, ... */...) >> +{ >> +va_list ap; >> +u8 value; >> +int ret; >> + >> +va_start(ap, count); > >> +while (count--) { >> +value = va_arg(ap, int); >> +ret = ssd130x_write_value(client, (u8)value); >> +if (ret) >> +goto out_end; >> +} > > I'm wondering if this can be written in a form > > do { > ... > } while (--count); > > In this case it will give a hint that count can't be 0. > Sure, I don't have a strong preference. I will change it. [snip] >> +ssd130x->pwm = pwm_get(dev, NULL); >> +if (IS_ERR(ssd130x->pwm)) { >> +dev_err(dev, "Could not get PWM from device tree!\n"); > > "device tree" is a bit confusing here if I run this on ACPI. > Maybe something like "firmware description"? > Indeed. >> +return PTR_ERR(ssd130x->pwm); >> +} > > ... > >> +/* Set initial contrast */ >> +ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_CONTRAST, >> ssd130x->contrast); > > Creating a local variable for client allows to: > - make lines shorter and might even be less LOCs > - allow to convert struct device to client in one place > (as per my above comment) > > Ditto for other similar cases. > Ok. [snip] >> +/* Switch to horizontal addressing mode */ >> +ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_ADDRESS_MODE, >> +SSD130X_SET_ADDRESS_MODE_HORIZONTAL); >> +if (ret < 0) >> +return ret; >> + >> +return 0; > > Can it be > > return ssd130x_write_cmd(...); > > ? > > ... > Yes. >> +unsigned int line_length = DIV_ROUND_UP(width, 8); >> +unsigned int pages = DIV_ROUND_UP(height, 8); > > For power of two there are more efficient roundup()/rounddown() > (or with _ in the names, I don't remember by heart). > Oh, I didn't know about round_{up,down}(). Thanks a lot for the pointer. > ... > >> +for (k = 0; k < m; k++) { > >> +u8 byte = buf[(8 * i + k) * line_length + >> + j / 8]; > > One line? > Yes. >> +u8 bit = (byte >> (j % 8)) & 1; >> + >> +data |= bit << k; >> +} > > ... > >> +static int ssd130x_display_pipe_mode_valid(struct drm_simple_display_pipe >> *pipe, >> + const struct drm_display_mode *mode) >> +{ >> +struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); >> + >> +if (mode->hdisplay != ssd130x->mode.hdisplay && >> +mode->vdisplay != ssd130x->mode.vdisplay) >> +return MODE_ONE_SIZE; > >> +else if (mode->hdisplay != ssd130x->mode.hdisplay) >> +return MODE_ONE_WIDTH; >> +else if (mode->vdisplay != ssd130x->mode.vdisplay) >> +return MODE_ONE_HEIGHT; > > 'else' in both cases is redundant. > Indeed. >> +return MODE_OK; >> +} > > ... > >> +poweroff: > > out_power_off: ? > Ok. > ... > >> +if (!fb) >> +
Re: [PATCH 04/19] drm/i915/gt: Add helper for shmem copy to iosys_map
Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: Add a variant of shmem_read() that takes a iosys_map pointer rather than a plain pointer as argument. It's mostly a copy __shmem_rw() but adapting the api and removing the write support since there's currently only need to use iosys_map as destination. Reworking __shmem_rw() to share the implementation was tempting, but finding a good balance between reuse and clarity pushed towards a little code duplication. Since the function is small, just add the similar function with a copy/paste/adapt approach. Cc: Matt Roper Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Cc: David Airlie Cc: Daniel Vetter Cc: Matthew Auld Cc: Thomas Hellström Cc: Maarten Lankhorst Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/shmem_utils.c | 33 +++ drivers/gpu/drm/i915/gt/shmem_utils.h | 3 +++ 2 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.c b/drivers/gpu/drm/i915/gt/shmem_utils.c index 0683b27a3890..764adefdb4be 100644 --- a/drivers/gpu/drm/i915/gt/shmem_utils.c +++ b/drivers/gpu/drm/i915/gt/shmem_utils.c @@ -3,6 +3,7 @@ * Copyright © 2020 Intel Corporation */ +#include #include #include #include @@ -123,6 +124,38 @@ static int __shmem_rw(struct file *file, loff_t off, return 0; } Here's a good example of how to avoid iosys_map_incr() and use the memcpy offset: +int shmem_read_to_iosys_map(struct file *file, loff_t off, + struct iosys_map *map, size_t len) +{ + struct iosys_map map_iter = *map; Rather replace map_iter with something like unsigned long map_off = 0; + unsigned long pfn; + + for (pfn = off >> PAGE_SHIFT; len; pfn++) { + unsigned int this = + min_t(size_t, PAGE_SIZE - offset_in_page(off), len); + struct page *page; + void *vaddr; + + page = shmem_read_mapping_page_gfp(file->f_mapping, pfn, + GFP_KERNEL); + if (IS_ERR(page)) + return PTR_ERR(page); + + vaddr = kmap(page); + iosys_map_memcpy_to(&map_iter, 0, vaddr + offset_in_page(off), + this); Use map_off to index into map directly. + mark_page_accessed(page); + kunmap(page); + put_page(page); + + len -= this; + iosys_map_incr(&map_iter, this); Raplace iosys_map_incr() with map_off += this; + off = 0; Maybe off += this ? I think this pattern should be applied to all similar code. As you already noted, iosys_map_incr() is problematic. Best regards Thomas + } + + return 0; +} + int shmem_read(struct file *file, loff_t off, void *dst, size_t len) { return __shmem_rw(file, off, dst, len, false); diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.h b/drivers/gpu/drm/i915/gt/shmem_utils.h index c1669170c351..e1784999faee 100644 --- a/drivers/gpu/drm/i915/gt/shmem_utils.h +++ b/drivers/gpu/drm/i915/gt/shmem_utils.h @@ -8,6 +8,7 @@ #include +struct iosys_map; struct drm_i915_gem_object; struct file; @@ -17,6 +18,8 @@ struct file *shmem_create_from_object(struct drm_i915_gem_object *obj); void *shmem_pin_map(struct file *file); void shmem_unpin_map(struct file *file, void *ptr); +int shmem_read_to_iosys_map(struct file *file, loff_t off, + struct iosys_map *map, size_t len); int shmem_read(struct file *file, loff_t off, void *dst, size_t len); int shmem_write(struct file *file, loff_t off, void *src, size_t len); -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Ivo Totev OpenPGP_signature Description: OpenPGP digital signature
Re: [PATCH 03/19] iosys-map: Add a few more helpers
Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: First the simplest ones: - iosys_map_memset(): when abstracting system and I/O memory, just like the memcpy() use case, memset() also has dedicated functions to be called for using IO memory. - iosys_map_memcpy_from(): we may need to copy data from I/O memory, not only to. In certain situations it's useful to be able to read or write to an offset that is calculated by having the memory layout given by a struct declaration. Usually we are going to read/write a u8, u16, u32 or u64. As a pre-requisite for the implementation, add iosys_map_memcpy_from() to be the equivalent of iosys_map_memcpy_to(), but in the other direction. Then add 2 pairs of macros: - iosys_map_rd() / iosys_map_wr() - iosys_map_rd_field() / iosys_map_wr_field() The first pair takes the C-type and offset to read/write. The second pair uses a struct describing the layout of the mapping in order to calculate the offset and size being read/written. We could use readb, readw, readl, readq and the write* counterparts, however due to alignment issues this may not work on all architectures. If alignment needs to be checked to call the right function, it's not possible to decide at compile-time which function to call: so just leave the decision to the memcpy function that will do exactly that. Finally, in order to use the above macros with a map derived from another, add another initializer: IOSYS_MAP_INIT_OFFSET(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi --- include/linux/iosys-map.h | 154 +- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index edd7fa3be9e9..96f8b61ac6fb 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -6,6 +6,7 @@ #ifndef __IOSYS_MAP_H__ #define __IOSYS_MAP_H__ +#include #include #include @@ -133,6 +134,45 @@ static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr) map->is_iomem = false; } +/** + * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map + * @map_: The dma-buf mapping structure to copy from + * @offset_: Offset to add to the other mapping + * + * Initializes a new iosys_map struct based on another passed as argument. It + * does a shallow copy of the struct so it's possible to update the back storage + * without changing where the original map points to. It is the equivalent of + * doing: + * + * .. code-block: c + * + * iosys_map map = other_map; + * iosys_map_incr(&map, &offset); + * + * Example usage: + * + * .. code-block: c + * + * void foo(struct device *dev, struct iosys_map *base_map) + * { + * ... + * struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET); + * ... + * } + * + * The advantage of using the initializer over just increasing the offset with + * ``iosys_map_incr()`` like above is that the new map will always point to the + * right place of the buffer during its scope. It reduces the risk of updating + * the wrong part of the buffer and having no compiler warning about that. If + * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn + * using a uninitialized variable. + */ +#define IOSYS_MAP_INIT_OFFSET(map_, offset_) (struct iosys_map) \ + { \ + .vaddr = (map_)->vaddr + (offset_), \ + .is_iomem = (map_)->is_iomem,\ + } I already nak'ed this macro. This works because of the aliasing rules within the union and the fact that there are only plain pointers. But this is fragile. Do anything more complicated and it breaks. There's not even a test that would tell you that it failed. Therefore, struct iosys_map should only be initialized by the code that creates the stored pointer. However, you won't need the offset'ed iosys_map because the memcpy_to/from helpers now have the offset parameter. + /** * iosys_map_set_vaddr_iomem - Sets a iosys mapping structure to an address in I/O memory * @map: The iosys_map structure @@ -220,7 +260,7 @@ static inline void iosys_map_clear(struct iosys_map *map) } /** - * iosys_map_memcpy_to_offset - Memcpy into offset of iosys_map + * iosys_map_memcpy_to - Memcpy into iosys_map That's the fix for the other patch. :) * @dst: The iosys_map structure * @dst_offset: The offset from which to copy * @src: The source buffer @@ -239,6 +279,26 @@ static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, memcpy(dst->vaddr + dst_offset, src, len); } +/** + *
Re: [PATCH v3 2/2] drm/i915/uapi: Add query for hwconfig table
On 2/4/2022 01:55, Daniel Vetter wrote: On Wed, Jan 19, 2022 at 9:35 PM wrote: From: Rodrigo Vivi GuC contains a consolidated table with a bunch of information about the current device. Previously, this information was spread and hardcoded to all the components including GuC, i915 and various UMDs. The goal here is to consolidate the data into GuC in a way that all interested components can grab the very latest and synchronized information using a simple query. As per most of the other queries, this one can be called twice. Once with item.length=0 to determine the exact buffer size, then allocate the user memory and call it again for to retrieve the table data. For example: struct drm_i915_query_item item = { .query_id = DRM_I915_QUERY_HWCONCFIG_TABLE; }; query.items_ptr = (int64_t) &item; query.num_items = 1; ioctl(fd, DRM_IOCTL_I915_QUERY, query, sizeof(query)); if (item.length <= 0) return -ENOENT; data = malloc(item.length); item.data_ptr = (int64_t) &data; ioctl(fd, DRM_IOCTL_I915_QUERY, query, sizeof(query)); // Parse the data as appropriate... The returned array is a simple and flexible KLV (Key/Length/Value) formatted table. For example, it could be just: enum device_attr { ATTR_SOME_VALUE = 0, ATTR_SOME_MASK = 1, }; static const u32 hwconfig[] = { ATTR_SOME_VALUE, 1, // Value Length in DWords 8, // Value ATTR_SOME_MASK, 3, 0x00, 0x, 0xFF00, }; The attribute ids are defined in a hardware spec. Cc: Tvrtko Ursulin Cc: Kenneth Graunke Cc: Michal Wajdeczko Cc: Slawomir Milczarek Signed-off-by: Rodrigo Vivi Signed-off-by: John Harrison Reviewed-by: Matthew Brost --- drivers/gpu/drm/i915/i915_query.c | 23 +++ include/uapi/drm/i915_drm.h | 1 + 2 files changed, 24 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index 2dfbc22857a3..609e64d5f395 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -479,12 +479,35 @@ static int query_memregion_info(struct drm_i915_private *i915, return total_length; } +static int query_hwconfig_table(struct drm_i915_private *i915, + struct drm_i915_query_item *query_item) +{ + struct intel_gt *gt = to_gt(i915); + struct intel_guc_hwconfig *hwconfig = >->uc.guc.hwconfig; + + if (!hwconfig->size || !hwconfig->ptr) + return -ENODEV; + + if (query_item->length == 0) + return hwconfig->size; + + if (query_item->length < hwconfig->size) + return -EINVAL; + + if (copy_to_user(u64_to_user_ptr(query_item->data_ptr), +hwconfig->ptr, hwconfig->size)) + return -EFAULT; + + return hwconfig->size; +} + static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv, struct drm_i915_query_item *query_item) = { query_topology_info, query_engine_info, query_perf_config, query_memregion_info, + query_hwconfig_table, }; int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 914ebd9290e5..132515199f27 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -2685,6 +2685,7 @@ struct drm_i915_query_item { #define DRM_I915_QUERY_ENGINE_INFO 2 #define DRM_I915_QUERY_PERF_CONFIG 3 #define DRM_I915_QUERY_MEMORY_REGIONS 4 +#define DRM_I915_QUERY_HWCONFIG_TABLE 5 /* Must be kept compact -- no holes and well documented */ New uapi needs kerneldoc in the uapi header, and please fill in any gaps you have (i.e. if the query uapi this is built on top of isn't fully documented yet). Also this holds across the board, so please keep in mind in patch review. -Daniel There is no extra documentation to add. The query interface itself is already documented. This new query does not have any kernel defined data structures associated with it. There is just 'struct drm_i915_query_item' with a length and a pointer, all of which are fully documented. John.
Re: [PATCH 02/19] iosys-map: Add offset to iosys_map_memcpy_to()
Hi Am 04.02.22 um 18:44 schrieb Lucas De Marchi: In certain situations it's useful to be able to write to an offset of the mapping. Add a dst_offset to iosys_map_memcpy_to(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/drm_cache.c | 2 +- drivers/gpu/drm/drm_fb_helper.c | 2 +- include/linux/iosys-map.h | 17 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index 66597e411764..c3e6e615bf09 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -218,7 +218,7 @@ static void memcpy_fallback(struct iosys_map *dst, if (!dst->is_iomem && !src->is_iomem) { memcpy(dst->vaddr, src->vaddr, len); } else if (!src->is_iomem) { - iosys_map_memcpy_to(dst, src->vaddr, len); + iosys_map_memcpy_to(dst, 0, src->vaddr, len); } else if (!dst->is_iomem) { memcpy_fromio(dst->vaddr, src->vaddr_iomem, len); } else { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 238f815cb2a0..bf5cc9a42e5a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -385,7 +385,7 @@ static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper, iosys_map_incr(dst, offset); /* go to first pixel within clip rect */ for (y = clip->y1; y < clip->y2; y++) { - iosys_map_memcpy_to(dst, src, len); + iosys_map_memcpy_to(dst, 0, src, len); iosys_map_incr(dst, fb->pitches[0]); src += fb->pitches[0]; } diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index f4186f91caa6..edd7fa3be9e9 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -220,22 +220,23 @@ static inline void iosys_map_clear(struct iosys_map *map) } /** - * iosys_map_memcpy_to - Memcpy into iosys mapping + * iosys_map_memcpy_to_offset - Memcpy into offset of iosys_map 'iosys_map_memcpy_to' With that fixed: Reviewed-by: Thomas Zimmermann Best regards Thomas * @dst: The iosys_map structure + * @dst_offset:The offset from which to copy * @src: The source buffer * @len: The number of byte in src * - * Copies data into a iosys mapping. The source buffer is in system - * memory. Depending on the buffer's location, the helper picks the correct - * method of accessing the memory. + * Copies data into a iosys_map with an offset. The source buffer is in + * system memory. Depending on the buffer's location, the helper picks the + * correct method of accessing the memory. */ -static inline void iosys_map_memcpy_to(struct iosys_map *dst, const void *src, - size_t len) +static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, + const void *src, size_t len) { if (dst->is_iomem) - memcpy_toio(dst->vaddr_iomem, src, len); + memcpy_toio(dst->vaddr_iomem + dst_offset, src, len); else - memcpy(dst->vaddr, src, len); + memcpy(dst->vaddr + dst_offset, src, len); } /** -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Ivo Totev OpenPGP_signature Description: OpenPGP digital signature
[PATCH v3 3/3] drm/msm/dp: enable widebus feature for display port
Widebus feature will transmit two pixel data per pixel clock to interface. This feature now is required to be enabled to easy migrant to higher resolution applications in future. However since some legacy chipsets does not support this feature, this feature is enabled base on chip's hardware revision. changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches -- enable widebus feature base on chip hardware revision Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 4 +++- drivers/gpu/drm/msm/dp/dp_catalog.c | 36 +++-- drivers/gpu/drm/msm/dp/dp_catalog.h | 3 ++- drivers/gpu/drm/msm/dp/dp_ctrl.c| 13 +++ drivers/gpu/drm/msm/dp/dp_ctrl.h| 1 + drivers/gpu/drm/msm/dp/dp_display.c | 30 drivers/gpu/drm/msm/dp/dp_display.h | 2 ++ drivers/gpu/drm/msm/dp/dp_panel.c | 4 ++-- drivers/gpu/drm/msm/dp/dp_panel.h | 2 +- drivers/gpu/drm/msm/msm_drv.h | 6 + 10 files changed, 90 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0c22839..b2d23c2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2167,8 +2167,10 @@ int dpu_encoder_setup(struct drm_device *dev, struct drm_encoder *enc, timer_setup(&dpu_enc->vsync_event_timer, dpu_encoder_vsync_event_handler, 0); - else if (disp_info->intf_type == DRM_MODE_ENCODER_TMDS) + else if (disp_info->intf_type == DRM_MODE_ENCODER_TMDS) { dpu_enc->dp = priv->dp[disp_info->h_tile_instance[0]]; + dpu_enc->wide_bus_en = msm_dp_wide_bus_enable(dpu_enc->dp); + } INIT_DELAYED_WORK(&dpu_enc->delayed_off_work, dpu_encoder_off_work); diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index 64f0b26..99d087e 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -483,6 +483,27 @@ int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, } /** + * dp_catalog_hw_revision() - retrieve DP hw revision + * + * @dp_catalog: DP catalog structure + * + * return: u32 + * + * This function return the DP controller hw revision + * + */ +u32 dp_catalog_hw_revision(struct dp_catalog *dp_catalog) +{ + u32 revision; + struct dp_catalog_private *catalog = container_of(dp_catalog, + struct dp_catalog_private, dp_catalog); + + revision = dp_read_ahb(catalog, REG_DP_HW_VERSION); + + return revision; +} + +/** * dp_catalog_ctrl_reset() - reset DP controller * * @dp_catalog: DP catalog structure @@ -739,10 +760,11 @@ u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog) } /* panel related catalog functions */ -int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog) +int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog, bool wide_bus_en) { struct dp_catalog_private *catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog); + u32 reg; dp_write_link(catalog, REG_DP_TOTAL_HOR_VER, dp_catalog->total); @@ -751,7 +773,17 @@ int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog) dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, dp_catalog->width_blanking); dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, dp_catalog->dp_active); - dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, 0); + + reg = dp_read_p0(catalog, MMSS_DP_INTF_CONFIG); + + if (wide_bus_en) + reg |= BIT(4); /* DATABUS_WIDEN */ + else + reg &= ~BIT(4); + + DRM_DEBUG_DP("wide_bus_en=%d reg=%x\n", wide_bus_en, reg); + + dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, reg); return 0; } diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 7dea101..a3a0129 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -95,6 +95,7 @@ void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb); void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate, u32 stream_rate_khz, bool fixed_nvid); int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, u32 pattern); +u32 dp_catalog_hw_revision(struct dp_catalog *dp_catalog); void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog); bool dp_catalog_ctrl_ma
[PATCH v3 2/3] drm/msm/dp: revise timing engine programming to support compression (DSC)
Divides horizontal width by 3 at timing engine of interface. There are major part of compression (DSC) programming have to be done at DSC controller which is not covered by this patch. Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 22 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | 3 +++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 35d4aaa..ee7ca34 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -128,7 +128,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, * video timing. It is recommended to enable it for all cases, except * if compression is enabled in 1 pixel per clock mode */ - if (p->wide_bus_en) + if (!p->compression_en || p->wide_bus_en) intf_cfg2 |= BIT(4); if (p->wide_bus_en) @@ -150,10 +150,16 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, */ data_width = p->width; - if (!dp_intf && p->wide_bus_en) + if (p->compression_en) { + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3); + + if (p->wide_bus_en) + data_width >>= 1; + } else if (!dp_intf && p->wide_bus_en) { data_width = p->width >> 1; - else + } else { data_width = p->width; + } hsync_data_start_x = hsync_start_x; hsync_data_end_x = hsync_start_x + data_width - 1; @@ -178,8 +184,16 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, active_hctl = (active_h_end << 16) | active_h_start; - if (dp_intf) + if (dp_intf) { display_hctl = active_hctl; + if (p->compression_en) { + active_data_hctl = (hsync_start_x + + p->extra_dto_cycles) << 16; + active_data_hctl += hsync_start_x; + + display_data_hctl = active_data_hctl; + } + } den_polarity = 0; if (ctx->cap->type == INTF_HDMI) { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h index e4a518a..8fc71ce 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h @@ -32,6 +32,9 @@ struct intf_timing_params { u32 hsync_skew; bool wide_bus_en; + bool compression_en; + u32 extra_dto_cycles; /* for DP only */ + u32 dce_bytes_per_line; }; struct intf_prog_fetch { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH v3 1/3] drm/msm/dp: revise timing engine programming to support widebus feature
Widebus feature will transmit two pixel data per pixel clock to interface. Timing engine provides driving force for this purpose. This patch base on HPG (Hardware Programming Guide) to revise timing engine register setting to accommodate both widebus and non widebus application. Also horizontal width parameters need to be reduced by half since two pixel data are clocked out per pixel clock when widebus feature enabled. In addition, revised timing engine function is an generic function and intend to be shared by all platforms to reduce maintenance efforts. Changes in v2: -- remove compression related code from timing -- remove op_info from struct msm_drm_private -- remove unnecessary wide_bus_en variables -- pass wide_bus_en into timing configuration by struct msm_dp Changes in v3: -- split patch into 3 patches Signed-off-by: Kuogee Hsieh --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c| 10 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h| 2 + .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 14 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c| 99 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h| 2 + 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0d315b4..0c22839 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -208,6 +208,8 @@ struct dpu_encoder_virt { u32 idle_timeout; + bool wide_bus_en; + struct msm_dp *dp; }; @@ -217,6 +219,14 @@ static u32 dither_matrix[DITHER_MATRIX_SZ] = { 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10 }; + +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); + + return dpu_enc->wide_bus_en; +} + static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc) { struct dpu_hw_dither_cfg dither_cfg = { 0 }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 99a5d73..893d74d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -168,4 +168,6 @@ int dpu_encoder_get_linecount(struct drm_encoder *drm_enc); */ int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc); +bool dpu_encoder_is_widebus_enabled(struct drm_encoder *drm_enc); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 185379b..3d6c914 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -110,6 +110,20 @@ static void drm_mode_to_intf_timing_params( timing->v_back_porch += timing->v_front_porch; timing->v_front_porch = 0; } + + timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent); + + /* +* for DP, divide the horizonal parameters by 2 when +* widebus is enabled +*/ + if (timing->wide_bus_en) { + timing->width = timing->width >> 1; + timing->xres = timing->xres >> 1; + timing->h_back_porch = timing->h_back_porch >> 1; + timing->h_front_porch = timing->h_front_porch >> 1; + timing->hsync_pulse_width = timing->hsync_pulse_width >> 1; + } } static u32 get_horizontal_total(const struct intf_timing_params *timing) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 116e2b5..35d4aaa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -33,6 +33,7 @@ #define INTF_TP_COLOR1 0x05C #define INTF_CONFIG20x060 #define INTF_DISPLAY_DATA_HCTL 0x064 +#define INTF_ACTIVE_DATA_HCTL 0x068 #define INTF_FRAME_LINE_COUNT_EN0x0A8 #define INTF_FRAME_COUNT0x0AC #define INTF_LINE_COUNT 0x0B0 @@ -90,68 +91,95 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, u32 hsync_period, vsync_period; u32 display_v_start, display_v_end; u32 hsync_start_x, hsync_end_x; + u32 hsync_data_start_x, hsync_data_end_x; u32 active_h_start, active_h_end; u32 active_v_start, active_v_end; u32 active_hctl, display_hctl, hsync_ctl; u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; u32 panel_format; - u32 intf_cfg, intf_cfg2 = 0, display_data_hctl = 0; + u32 intf_cfg, intf_cfg2 = 0; + u32 display_data_hctl = 0, active_data_hctl = 0; + u32 data_width; + bool dp_intf = false; /* read interface_cfg */ intf_cfg = DPU_REG_READ(c, INTF_CO
[PATCH v3 0/3] enable widebus feature base on chip hardware revision
split into 3 patches 1) widebus timing engine programming 2) dsc timing engine 3) enable widebus feature base on chip hardware revision Kuogee Hsieh (3): drm/msm/dp: revise timing engine programming to support widebus feature drm/msm/dp: revise timing engine programming to support compression (DSC) drm/msm/dp: enable widebus feature for display port drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c| 14 ++- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h| 2 + .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 14 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c| 107 +++-- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h| 5 + drivers/gpu/drm/msm/dp/dp_catalog.c| 36 ++- drivers/gpu/drm/msm/dp/dp_catalog.h| 3 +- drivers/gpu/drm/msm/dp/dp_ctrl.c | 13 ++- drivers/gpu/drm/msm/dp/dp_ctrl.h | 1 + drivers/gpu/drm/msm/dp/dp_display.c| 30 ++ drivers/gpu/drm/msm/dp/dp_display.h| 2 + drivers/gpu/drm/msm/dp/dp_panel.c | 4 +- drivers/gpu/drm/msm/dp/dp_panel.h | 2 +- drivers/gpu/drm/msm/msm_drv.h | 6 ++ 14 files changed, 197 insertions(+), 42 deletions(-) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH 02/19] iosys-map: Add offset to iosys_map_memcpy_to()
Am 04.02.22 um 18:44 schrieb Lucas De Marchi: In certain situations it's useful to be able to write to an offset of the mapping. Add a dst_offset to iosys_map_memcpy_to(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi Reviewed-by: Christian König --- drivers/gpu/drm/drm_cache.c | 2 +- drivers/gpu/drm/drm_fb_helper.c | 2 +- include/linux/iosys-map.h | 17 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index 66597e411764..c3e6e615bf09 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -218,7 +218,7 @@ static void memcpy_fallback(struct iosys_map *dst, if (!dst->is_iomem && !src->is_iomem) { memcpy(dst->vaddr, src->vaddr, len); } else if (!src->is_iomem) { - iosys_map_memcpy_to(dst, src->vaddr, len); + iosys_map_memcpy_to(dst, 0, src->vaddr, len); } else if (!dst->is_iomem) { memcpy_fromio(dst->vaddr, src->vaddr_iomem, len); } else { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 238f815cb2a0..bf5cc9a42e5a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -385,7 +385,7 @@ static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper, iosys_map_incr(dst, offset); /* go to first pixel within clip rect */ for (y = clip->y1; y < clip->y2; y++) { - iosys_map_memcpy_to(dst, src, len); + iosys_map_memcpy_to(dst, 0, src, len); iosys_map_incr(dst, fb->pitches[0]); src += fb->pitches[0]; } diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index f4186f91caa6..edd7fa3be9e9 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -220,22 +220,23 @@ static inline void iosys_map_clear(struct iosys_map *map) } /** - * iosys_map_memcpy_to - Memcpy into iosys mapping + * iosys_map_memcpy_to_offset - Memcpy into offset of iosys_map * @dst: The iosys_map structure + * @dst_offset:The offset from which to copy * @src: The source buffer * @len: The number of byte in src * - * Copies data into a iosys mapping. The source buffer is in system - * memory. Depending on the buffer's location, the helper picks the correct - * method of accessing the memory. + * Copies data into a iosys_map with an offset. The source buffer is in + * system memory. Depending on the buffer's location, the helper picks the + * correct method of accessing the memory. */ -static inline void iosys_map_memcpy_to(struct iosys_map *dst, const void *src, - size_t len) +static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, + const void *src, size_t len) { if (dst->is_iomem) - memcpy_toio(dst->vaddr_iomem, src, len); + memcpy_toio(dst->vaddr_iomem + dst_offset, src, len); else - memcpy(dst->vaddr, src, len); + memcpy(dst->vaddr + dst_offset, src, len); } /**
Re: [RFC PATCH 1/3] drm: Extract amdgpu_sa.c as a generic suballocation helper
Oh, that's on my TODO list for years! Am 04.02.22 um 18:48 schrieb Maarten Lankhorst: Suballocating a buffer object is something that is not driver generic, and is useful for other drivers as well. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/Makefile | 4 +- drivers/gpu/drm/drm_suballoc.c | 424 + include/drm/drm_suballoc.h | 78 ++ 3 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_suballoc.c create mode 100644 include/drm/drm_suballoc.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8675c2af7ae1..b848bcf8790c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -57,7 +57,9 @@ drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o \ drm_scdc_helper.o drm_gem_atomic_helper.o \ drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o drm_self_refresh_helper.o drm_rect.o + drm_format_helper.o drm_self_refresh_helper.o drm_rect.o \ + drm_suballoc.o + I think we should put that into a separate module like we now do with other helpers as well. drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_suballoc.c b/drivers/gpu/drm/drm_suballoc.c new file mode 100644 index ..e0bb35367b71 --- /dev/null +++ b/drivers/gpu/drm/drm_suballoc.c @@ -0,0 +1,424 @@ +/* + * Copyright 2011 Red Hat 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + *Jerome Glisse + */ That is hopelessly outdated. IIRC I completely rewrote that stuff in ~2012. +/* Algorithm: + * + * We store the last allocated bo in "hole", we always try to allocate + * after the last allocated bo. Principle is that in a linear GPU ring + * progression was is after last is the oldest bo we allocated and thus + * the first one that should no longer be in use by the GPU. + * + * If it's not the case we skip over the bo after last to the closest + * done bo if such one exist. If none exist and we are not asked to + * block we report failure to allocate. + * + * If we are asked to block we wait on all the oldest fence of all + * rings. We just wait for any of those fence to complete. + */ + +#include +#include +#include +#include +#include +#include + +static void drm_suballoc_remove_locked(struct drm_suballoc *sa); +static void drm_suballoc_try_free(struct drm_suballoc_manager *sa_manager); + +/** + * drm_suballoc_manager_init - Initialise the drm_suballoc_manager + * + * @sa_manager: pointer to the sa_manager + * @size: number of bytes we want to suballocate + * @align: alignment for each suballocated chunk + * + * Prepares the suballocation manager for suballocations. + */ +void drm_suballoc_manager_init(struct drm_suballoc_manager *sa_manager, + u32 size, u32 align) +{ + u32 i; + + if (!align) + align = 1; + + /* alignment must be a power of 2 */ + BUG_ON(align & (align - 1)); When we move that I think we should cleanup the code once more, e.g. use is_power_of_2() function here for example. There are also a bunch of places with extra {} and constructs like "if () return true; else return false;" which could certainly be simplified. Apart from that really great idea. Regards, Christian. + + init_waitqueue_head(&sa_manager->wq); + sa_manager->size = size; + sa_manager->align = align; + sa_manager->hole = &sa_manager->olist; + INIT_LIST_HEAD(&sa_manager->olist); + for (i = 0; i < DRM_SUBALLOC_MAX_QUEUES; ++i) + INIT_LIST_HEAD(&sa_manager->flist[i]); +} +EXPORT_SYMBOL(drm_
[PATCH] drm/nouveau/backlight: Fix LVDS backlight detection on some laptops
It seems that some laptops will report having both an eDP and LVDS connector, even though only the LVDS connector is actually hooked up. This can lead to issues with backlight registration if the eDP connector ends up getting registered before the LVDS connector, as the backlight device will then be registered to the eDP connector instead of the LVDS connector. So, fix this by only registering the backlight on connectors that are reported as being connected. Signed-off-by: Lyude Paul Fixes: 6eca310e8924 ("drm/nouveau/kms/nv50-: Add basic DPCD backlight support for nouveau") Bugzilla: https://gitlab.freedesktop.org/drm/nouveau/-/issues/137 Cc: # v5.15+ --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index ae2f2abc8f5a..6af12dc99d7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -294,7 +294,8 @@ nv50_backlight_init(struct nouveau_backlight *bl, struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); struct nvif_object *device = &drm->client.device.object; - if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1))) + if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)) || + nv_conn->base.status != connector_status_connected) return -ENODEV; if (nv_conn->type == DCB_CONNECTOR_eDP) { -- 2.34.1
[RFC PATCH 1/3] drm: Extract amdgpu_sa.c as a generic suballocation helper
Suballocating a buffer object is something that is not driver generic, and is useful for other drivers as well. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/Makefile | 4 +- drivers/gpu/drm/drm_suballoc.c | 424 + include/drm/drm_suballoc.h | 78 ++ 3 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_suballoc.c create mode 100644 include/drm/drm_suballoc.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8675c2af7ae1..b848bcf8790c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -57,7 +57,9 @@ drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o \ drm_scdc_helper.o drm_gem_atomic_helper.o \ drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o drm_self_refresh_helper.o drm_rect.o + drm_format_helper.o drm_self_refresh_helper.o drm_rect.o \ + drm_suballoc.o + drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_suballoc.c b/drivers/gpu/drm/drm_suballoc.c new file mode 100644 index ..e0bb35367b71 --- /dev/null +++ b/drivers/gpu/drm/drm_suballoc.c @@ -0,0 +1,424 @@ +/* + * Copyright 2011 Red Hat 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + *Jerome Glisse + */ +/* Algorithm: + * + * We store the last allocated bo in "hole", we always try to allocate + * after the last allocated bo. Principle is that in a linear GPU ring + * progression was is after last is the oldest bo we allocated and thus + * the first one that should no longer be in use by the GPU. + * + * If it's not the case we skip over the bo after last to the closest + * done bo if such one exist. If none exist and we are not asked to + * block we report failure to allocate. + * + * If we are asked to block we wait on all the oldest fence of all + * rings. We just wait for any of those fence to complete. + */ + +#include +#include +#include +#include +#include +#include + +static void drm_suballoc_remove_locked(struct drm_suballoc *sa); +static void drm_suballoc_try_free(struct drm_suballoc_manager *sa_manager); + +/** + * drm_suballoc_manager_init - Initialise the drm_suballoc_manager + * + * @sa_manager: pointer to the sa_manager + * @size: number of bytes we want to suballocate + * @align: alignment for each suballocated chunk + * + * Prepares the suballocation manager for suballocations. + */ +void drm_suballoc_manager_init(struct drm_suballoc_manager *sa_manager, + u32 size, u32 align) +{ + u32 i; + + if (!align) + align = 1; + + /* alignment must be a power of 2 */ + BUG_ON(align & (align - 1)); + + init_waitqueue_head(&sa_manager->wq); + sa_manager->size = size; + sa_manager->align = align; + sa_manager->hole = &sa_manager->olist; + INIT_LIST_HEAD(&sa_manager->olist); + for (i = 0; i < DRM_SUBALLOC_MAX_QUEUES; ++i) + INIT_LIST_HEAD(&sa_manager->flist[i]); +} +EXPORT_SYMBOL(drm_suballoc_manager_init); + +/** + * drm_suballoc_manager_fini - Destroy the drm_suballoc_manager + * + * @sa_manager: pointer to the sa_manager + * + * Cleans up the suballocation manager after use. All fences added + * with drm_suballoc_free() must be signaled, or we cannot clean up + * the entire manager. + */ +void drm_suballoc_manager_fini(struct drm_suballoc_manager *sa_manager) +{ + struct drm_suballoc *sa, *tmp; + + if (!sa_manager->size) + return; + + if (!list_empty(&sa_manager->olist)) { + sa_manager->hole = &sa_manager->olist, + drm
[RFC PATCH 2/3] drm/amd: Convert amdgpu to use suballocation helper.
Now that the suballocation helper is generic, we can use it in amdgpu. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/amd/amdgpu/amdgpu.h| 29 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | 5 +- drivers/gpu/drm/amd/amdgpu/amdgpu_object.h | 21 +- drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | 320 ++--- 4 files changed, 39 insertions(+), 336 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 9a53a4de2bb7..a8c7a7ef480c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -61,6 +61,7 @@ #include #include #include +#include #include #include "dm_pp_interface.h" @@ -417,29 +418,11 @@ struct amdgpu_clock { * alignment). */ -#define AMDGPU_SA_NUM_FENCE_LISTS 32 - struct amdgpu_sa_manager { - wait_queue_head_t wq; - struct amdgpu_bo*bo; - struct list_head*hole; - struct list_headflist[AMDGPU_SA_NUM_FENCE_LISTS]; - struct list_headolist; - unsignedsize; - uint64_tgpu_addr; - void*cpu_ptr; - uint32_tdomain; - uint32_talign; -}; - -/* sub-allocation buffer */ -struct amdgpu_sa_bo { - struct list_headolist; - struct list_headflist; - struct amdgpu_sa_manager*manager; - unsignedsoffset; - unsignedeoffset; - struct dma_fence*fence; + struct drm_suballoc_manager base; + struct amdgpu_bo*bo; + uint64_tgpu_addr; + void*cpu_ptr; }; int amdgpu_fence_slab_init(void); @@ -470,7 +453,7 @@ struct amdgpu_flip_work { */ struct amdgpu_ib { - struct amdgpu_sa_bo *sa_bo; + struct drm_suballoc *sa_bo; uint32_tlength_dw; uint64_tgpu_addr; uint32_t*ptr; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c index bc1297dcdf97..883828a4988c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c @@ -69,7 +69,7 @@ int amdgpu_ib_get(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (size) { r = amdgpu_sa_bo_new(&adev->ib_pools[pool_type], - &ib->sa_bo, size, 256); + &ib->sa_bo, size); if (r) { dev_err(adev->dev, "failed to get a new IB (%d)\n", r); return r; @@ -307,8 +307,7 @@ int amdgpu_ib_pool_init(struct amdgpu_device *adev) for (i = 0; i < AMDGPU_IB_POOL_MAX; i++) { r = amdgpu_sa_bo_manager_init(adev, &adev->ib_pools[i], - AMDGPU_IB_POOL_SIZE, - AMDGPU_GPU_PAGE_SIZE, + AMDGPU_IB_POOL_SIZE, 256, AMDGPU_GEM_DOMAIN_GTT); if (r) goto error; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index 4c9cbdc66995..7db4fe1bc1d6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -337,15 +337,20 @@ uint32_t amdgpu_bo_get_preferred_domain(struct amdgpu_device *adev, /* * sub allocation */ +static inline struct amdgpu_sa_manager * +to_amdgpu_sa_manager(struct drm_suballoc_manager *manager) +{ + return container_of(manager, struct amdgpu_sa_manager, base); +} -static inline uint64_t amdgpu_sa_bo_gpu_addr(struct amdgpu_sa_bo *sa_bo) +static inline uint64_t amdgpu_sa_bo_gpu_addr(struct drm_suballoc *sa_bo) { - return sa_bo->manager->gpu_addr + sa_bo->soffset; + return to_amdgpu_sa_manager(sa_bo->manager)->gpu_addr + sa_bo->soffset; } -static inline void * amdgpu_sa_bo_cpu_addr(struct amdgpu_sa_bo *sa_bo) +static inline void * amdgpu_sa_bo_cpu_addr(struct drm_suballoc *sa_bo) { - return sa_bo->manager->cpu_ptr + sa_bo->soffset; + return to_amdgpu_sa_manager(sa_bo->manager)->cpu_ptr + sa_bo->soffset; } int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, @@ -356,11 +361,11 @@ void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, struct amdgpu_sa_manager *sa_manager); int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, -struct amdgpu_sa_bo **sa_bo, -unsigned size, unsigned align); +struct drm_suballoc **sa_bo, +unsigned siz
[RFC PATCH 3/3] drm/radeon: Use the drm suballocation manager implementation.
Use the generic suballocation helper lifted from amdgpu. Note that the generic suballocator only allows a single alignment, so we may waste a few more bytes for radeon_semaphore, shouldn't be a big deal, could be re-added if needed. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/radeon/radeon.h | 55 +--- drivers/gpu/drm/radeon/radeon_ib.c| 10 +- drivers/gpu/drm/radeon/radeon_object.h| 23 +- drivers/gpu/drm/radeon/radeon_sa.c| 314 ++ drivers/gpu/drm/radeon/radeon_semaphore.c | 6 +- 5 files changed, 51 insertions(+), 357 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 895776c421d4..a6339c9e7c47 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -79,6 +79,7 @@ #include #include +#include #include "radeon_family.h" #include "radeon_mode.h" @@ -512,52 +513,12 @@ struct radeon_bo { }; #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, tbo.base) -/* sub-allocation manager, it has to be protected by another lock. - * By conception this is an helper for other part of the driver - * like the indirect buffer or semaphore, which both have their - * locking. - * - * Principe is simple, we keep a list of sub allocation in offset - * order (first entry has offset == 0, last entry has the highest - * offset). - * - * When allocating new object we first check if there is room at - * the end total_size - (last_object_offset + last_object_size) >= - * alloc_size. If so we allocate new object there. - * - * When there is not enough room at the end, we start waiting for - * each sub object until we reach object_offset+object_size >= - * alloc_size, this object then become the sub object we return. - * - * Alignment can't be bigger than page size. - * - * Hole are not considered for allocation to keep things simple. - * Assumption is that there won't be hole (all object on same - * alignment). - */ struct radeon_sa_manager { - wait_queue_head_t wq; - struct radeon_bo*bo; - struct list_head*hole; - struct list_headflist[RADEON_NUM_RINGS]; - struct list_headolist; - unsignedsize; - uint64_tgpu_addr; - void*cpu_ptr; - uint32_tdomain; - uint32_talign; -}; - -struct radeon_sa_bo; - -/* sub-allocation buffer */ -struct radeon_sa_bo { - struct list_headolist; - struct list_headflist; - struct radeon_sa_manager*manager; - unsignedsoffset; - unsignedeoffset; - struct radeon_fence *fence; + struct drm_suballoc_manager base; + struct radeon_bo*bo; + uint64_tgpu_addr; + void*cpu_ptr; + u32 domain; }; /* @@ -588,7 +549,7 @@ int radeon_mode_dumb_mmap(struct drm_file *filp, * Semaphores. */ struct radeon_semaphore { - struct radeon_sa_bo *sa_bo; + struct drm_suballoc *sa_bo; signed waiters; uint64_tgpu_addr; }; @@ -817,7 +778,7 @@ void radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask); */ struct radeon_ib { - struct radeon_sa_bo *sa_bo; + struct drm_suballoc *sa_bo; uint32_tlength_dw; uint64_tgpu_addr; uint32_t*ptr; diff --git a/drivers/gpu/drm/radeon/radeon_ib.c b/drivers/gpu/drm/radeon/radeon_ib.c index 62b116727b4f..bca2cbd27abf 100644 --- a/drivers/gpu/drm/radeon/radeon_ib.c +++ b/drivers/gpu/drm/radeon/radeon_ib.c @@ -61,7 +61,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring, { int r; - r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256); + r = radeon_sa_bo_new(&rdev->ring_tmp_bo, &ib->sa_bo, size); if (r) { dev_err(rdev->dev, "failed to get a new IB (%d)\n", r); return r; @@ -97,7 +97,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring, void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib) { radeon_sync_free(rdev, &ib->sync, ib->fence); - radeon_sa_bo_free(rdev, &ib->sa_bo, ib->fence); + radeon_sa_bo_free(&ib->sa_bo, ib->fence); radeon_fence_unref(&ib->fence); } @@ -201,8 +201,7 @@ int radeon_ib_pool_init(struct radeon_device *rdev) if (rdev->family >= CHIP_BONAIRE) { r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo, - RADEON_IB_POOL_SIZE*64*1024, - RADEON_GPU_PAGE_SIZE, + RADEON_IB_POOL_SIZE*6
[RFC PATCH 0/3] drm/helpers: Make the suballocation manager drm generic.
The suballocation manager itself is not dependent on any implementation detail, and can be made generic. I want to potentially use it inside i915, as it looks like a clean implementation to do so. :) Looking for feedback and some testing, as I don't have a amdgpu/radeon myself. Only compile tested so far, so some stupid bugs may remain. Maarten Lankhorst (3): drm: Extract amdgpu_sa.c as a generic suballocation helper drm/amd: Convert amdgpu to use suballocation helper. drm/radeon: Use the drm suballocation manager implementation. drivers/gpu/drm/Makefile | 4 +- drivers/gpu/drm/amd/amdgpu/amdgpu.h| 29 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | 5 +- drivers/gpu/drm/amd/amdgpu/amdgpu_object.h | 21 +- drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | 320 +--- drivers/gpu/drm/drm_suballoc.c | 424 + drivers/gpu/drm/radeon/radeon.h| 55 +-- drivers/gpu/drm/radeon/radeon_ib.c | 10 +- drivers/gpu/drm/radeon/radeon_object.h | 23 +- drivers/gpu/drm/radeon/radeon_sa.c | 314 ++- drivers/gpu/drm/radeon/radeon_semaphore.c | 6 +- include/drm/drm_suballoc.h | 78 12 files changed, 595 insertions(+), 694 deletions(-) create mode 100644 drivers/gpu/drm/drm_suballoc.c create mode 100644 include/drm/drm_suballoc.h -- 2.34.1
[PATCH 11/19] drm/i915/guc: Convert golden context prep to iosys_map
Use the saved ads_map to prepare the golden context. One difference from the init context is that this function can be called before there is a gem object (and thus the guc->ads_map) to calculare the size of the golden context that should be allocated for that object. So in this case the function needs to be prepared for not having the system_info with enabled engines filled out. To accomplish that an info_map is prepared on the side to point either to the gem object or the local variable on the stack. This allows making fill_engine_enable_masks() operate always with a iosys_map argument. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 52 +- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 973762ce2196..80fbab831536 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -67,6 +67,12 @@ struct __guc_ads_blob { iosys_map_wr_field(&(guc_)->ads_map, struct __guc_ads_blob, \ field_, val_) +#define info_map_write(map_, field_, val_) \ + iosys_map_wr_field(map_, struct guc_gt_system_info, field_, val_) + +#define info_map_read(map_, field_) \ + iosys_map_rd_field(map_, struct guc_gt_system_info, field_) + static u32 guc_ads_regset_size(struct intel_guc *guc) { GEM_BUG_ON(!guc->ads_regset_size); @@ -378,24 +384,24 @@ static void guc_mmio_reg_state_init(struct intel_guc *guc, } static void fill_engine_enable_masks(struct intel_gt *gt, -struct guc_gt_system_info *info) +struct iosys_map *info_map) { - info->engine_enabled_masks[GUC_RENDER_CLASS] = 1; - info->engine_enabled_masks[GUC_BLITTER_CLASS] = 1; - info->engine_enabled_masks[GUC_VIDEO_CLASS] = VDBOX_MASK(gt); - info->engine_enabled_masks[GUC_VIDEOENHANCE_CLASS] = VEBOX_MASK(gt); + info_map_write(info_map, engine_enabled_masks[GUC_RENDER_CLASS], 1); + info_map_write(info_map, engine_enabled_masks[GUC_BLITTER_CLASS], 1); + info_map_write(info_map, engine_enabled_masks[GUC_VIDEO_CLASS], VDBOX_MASK(gt)); + info_map_write(info_map, engine_enabled_masks[GUC_VIDEOENHANCE_CLASS], VEBOX_MASK(gt)); } #define LR_HW_CONTEXT_SIZE (80 * sizeof(u32)) #define LRC_SKIP_SIZE (LRC_PPHWSP_SZ * PAGE_SIZE + LR_HW_CONTEXT_SIZE) -static int guc_prep_golden_context(struct intel_guc *guc, - struct __guc_ads_blob *blob) +static int guc_prep_golden_context(struct intel_guc *guc) { struct intel_gt *gt = guc_to_gt(guc); u32 addr_ggtt, offset; u32 total_size = 0, alloc_size, real_size; u8 engine_class, guc_class; - struct guc_gt_system_info *info, local_info; + struct guc_gt_system_info local_info; + struct iosys_map info_map; /* * Reserve the memory for the golden contexts and point GuC at it but @@ -409,14 +415,15 @@ static int guc_prep_golden_context(struct intel_guc *guc, * GuC will also validate that the LRC base + size fall within the * allowed GGTT range. */ - if (blob) { + if (!iosys_map_is_null(&guc->ads_map)) { offset = guc_ads_golden_ctxt_offset(guc); addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset; - info = &blob->system_info; + info_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map, +offsetof(struct __guc_ads_blob, system_info)); } else { memset(&local_info, 0, sizeof(local_info)); - info = &local_info; - fill_engine_enable_masks(gt, info); + iosys_map_set_vaddr(&info_map, &local_info); + fill_engine_enable_masks(gt, &info_map); } for (engine_class = 0; engine_class <= MAX_ENGINE_CLASS; ++engine_class) { @@ -425,14 +432,14 @@ static int guc_prep_golden_context(struct intel_guc *guc, guc_class = engine_class_to_guc_class(engine_class); - if (!info->engine_enabled_masks[guc_class]) + if (!info_map_read(&info_map, engine_enabled_masks[guc_class])) continue; real_size = intel_engine_context_size(gt, engine_class); alloc_size = PAGE_ALIGN(real_size); total_size += alloc_size; - if (!blob) + if (iosys_map_is_null(&guc->ads_map)) continue; /* @@ -446,12 +453,15 @@ static int guc_prep_golden_context(struct intel_guc *guc, * what comes before it in the context image (which is identical
[PATCH 15/19] drm/i915/guc: Prepare for error propagation
Currently guc_mmio_reg_add() relies on having enough memory available in the array to add a new slot. It uses `GEM_BUG_ON(count >= regset->size);` to protect going above the threshold. In order to allow guc_mmio_reg_add() to handle the memory allocation by itself, it must return an error in case of failures. Adjust return code so this error can be propagated to the callers of guc_mmio_reg_add() and guc_mmio_regset_init(). No intended change in behavior. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 31 +- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 245b703568ff..3a4558948c31 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -244,8 +244,8 @@ static int guc_mmio_reg_cmp(const void *a, const void *b) return (int)ra->offset - (int)rb->offset; } -static void guc_mmio_reg_add(struct temp_regset *regset, -u32 offset, u32 flags) +static long __must_check guc_mmio_reg_add(struct temp_regset *regset, + u32 offset, u32 flags) { u32 count = regset->used; struct guc_mmio_reg reg = { @@ -264,7 +264,7 @@ static void guc_mmio_reg_add(struct temp_regset *regset, */ if (bsearch(®, regset->registers, count, sizeof(reg), guc_mmio_reg_cmp)) - return; + return 0; slot = ®set->registers[count]; regset->used++; @@ -277,6 +277,8 @@ static void guc_mmio_reg_add(struct temp_regset *regset, swap(slot[1], slot[0]); } + + return 0; } #define GUC_MMIO_REG_ADD(regset, reg, masked) \ @@ -284,32 +286,35 @@ static void guc_mmio_reg_add(struct temp_regset *regset, i915_mmio_reg_offset((reg)), \ (masked) ? GUC_REGSET_MASKED : 0) -static void guc_mmio_regset_init(struct temp_regset *regset, -struct intel_engine_cs *engine) +static int guc_mmio_regset_init(struct temp_regset *regset, + struct intel_engine_cs *engine) { const u32 base = engine->mmio_base; struct i915_wa_list *wal = &engine->wa_list; struct i915_wa *wa; unsigned int i; + int ret = 0; regset->used = 0; - GUC_MMIO_REG_ADD(regset, RING_MODE_GEN7(base), true); - GUC_MMIO_REG_ADD(regset, RING_HWS_PGA(base), false); - GUC_MMIO_REG_ADD(regset, RING_IMR(base), false); + ret |= GUC_MMIO_REG_ADD(regset, RING_MODE_GEN7(base), true); + ret |= GUC_MMIO_REG_ADD(regset, RING_HWS_PGA(base), false); + ret |= GUC_MMIO_REG_ADD(regset, RING_IMR(base), false); for (i = 0, wa = wal->list; i < wal->count; i++, wa++) - GUC_MMIO_REG_ADD(regset, wa->reg, wa->masked_reg); + ret |= GUC_MMIO_REG_ADD(regset, wa->reg, wa->masked_reg); /* Be extra paranoid and include all whitelist registers. */ for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) - GUC_MMIO_REG_ADD(regset, -RING_FORCE_TO_NONPRIV(base, i), -false); + ret |= GUC_MMIO_REG_ADD(regset, + RING_FORCE_TO_NONPRIV(base, i), + false); /* add in local MOCS registers */ for (i = 0; i < GEN9_LNCFCMOCS_REG_COUNT; i++) - GUC_MMIO_REG_ADD(regset, GEN9_LNCFCMOCS(i), false); + ret |= GUC_MMIO_REG_ADD(regset, GEN9_LNCFCMOCS(i), false); + + return ret ? -1 : 0; } static int guc_mmio_reg_state_query(struct intel_guc *guc) -- 2.35.1
[PATCH 18/19] drm/i915/guc: Convert __guc_ads_init to iosys_map
Now that all the called functions from __guc_ads_init() are converted to use ads_map, stop using ads_blob in __guc_ads_init(). Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 25 -- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index cf6fafa1024c..6262fd4e0d4a 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -613,7 +613,6 @@ static void __guc_ads_init(struct intel_guc *guc) { struct intel_gt *gt = guc_to_gt(guc); struct drm_i915_private *i915 = gt->i915; - struct __guc_ads_blob *blob = guc->ads_blob; struct iosys_map info_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map, offsetof(struct __guc_ads_blob, system_info)); u32 base; @@ -624,17 +623,18 @@ static void __guc_ads_init(struct intel_guc *guc) /* System info */ fill_engine_enable_masks(gt, &info_map); - blob->system_info.generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_SLICE_ENABLED] = - hweight8(gt->info.sseu.slice_mask); - blob->system_info.generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_VDBOX_SFC_SUPPORT_MASK] = - gt->info.vdbox_sfc_access; + ads_blob_write(guc, system_info.generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_SLICE_ENABLED], + hweight8(gt->info.sseu.slice_mask)); + ads_blob_write(guc, system_info.generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_VDBOX_SFC_SUPPORT_MASK], + gt->info.vdbox_sfc_access); if (GRAPHICS_VER(i915) >= 12 && !IS_DGFX(i915)) { u32 distdbreg = intel_uncore_read(gt->uncore, GEN12_DIST_DBS_POPULATED); - blob->system_info.generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_DOORBELL_COUNT_PER_SQIDI] = - ((distdbreg >> GEN12_DOORBELLS_PER_SQIDI_SHIFT) & -GEN12_DOORBELLS_PER_SQIDI) + 1; + ads_blob_write(guc, + system_info.generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_DOORBELL_COUNT_PER_SQIDI], + ((distdbreg >> GEN12_DOORBELLS_PER_SQIDI_SHIFT) + & GEN12_DOORBELLS_PER_SQIDI) + 1); } /* Golden contexts for re-initialising after a watchdog reset */ @@ -648,14 +648,17 @@ static void __guc_ads_init(struct intel_guc *guc) guc_capture_list_init(guc); /* ADS */ - blob->ads.scheduler_policies = base + ptr_offset(blob, policies); - blob->ads.gt_system_info = base + ptr_offset(blob, system_info); + ads_blob_write(guc, ads.scheduler_policies, base + + offsetof(struct __guc_ads_blob, policies)); + ads_blob_write(guc, ads.gt_system_info, base + + offsetof(struct __guc_ads_blob, system_info)); /* MMIO save/restore list */ guc_mmio_reg_state_init(guc); /* Private Data */ - blob->ads.private_data = base + guc_ads_private_data_offset(guc); + ads_blob_write(guc, ads.private_data, base + + guc_ads_private_data_offset(guc)); i915_gem_object_flush_map(guc->ads_vma->obj); } -- 2.35.1
[PATCH 14/19] drm/i915/guc: Convert capture list to iosys_map
Use iosys_map to write the fields ads.capture_*. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 10 +- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 9e96a44a6bbc..245b703568ff 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -544,7 +544,7 @@ static void guc_init_golden_context(struct intel_guc *guc) GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size); } -static void guc_capture_list_init(struct intel_guc *guc, struct __guc_ads_blob *blob) +static void guc_capture_list_init(struct intel_guc *guc) { int i, j; u32 addr_ggtt, offset; @@ -556,11 +556,11 @@ static void guc_capture_list_init(struct intel_guc *guc, struct __guc_ads_blob * for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) { for (j = 0; j < GUC_MAX_ENGINE_CLASSES; j++) { - blob->ads.capture_instance[i][j] = addr_ggtt; - blob->ads.capture_class[i][j] = addr_ggtt; + ads_blob_write(guc, ads.capture_instance[i][j], addr_ggtt); + ads_blob_write(guc, ads.capture_class[i][j], addr_ggtt); } - blob->ads.capture_global[i] = addr_ggtt; + ads_blob_write(guc, ads.capture_global[i], addr_ggtt); } } @@ -600,7 +600,7 @@ static void __guc_ads_init(struct intel_guc *guc) base = intel_guc_ggtt_offset(guc, guc->ads_vma); /* Capture list for hang debug */ - guc_capture_list_init(guc, blob); + guc_capture_list_init(guc); /* ADS */ blob->ads.scheduler_policies = base + ptr_offset(blob, policies); -- 2.35.1
[PATCH 17/19] drm/i915/guc: Convert guc_mmio_reg_state_init to iosys_map
Now that the regset list is prepared, convert guc_mmio_reg_state_init() to use iosys_map to copy the array to the final location and initialize additional fields in ads.reg_state_list. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 30 +- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index c040d8d8d7a4..cf6fafa1024c 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -383,40 +383,46 @@ static long guc_mmio_reg_state_create(struct intel_guc *guc) return ret; } -static void guc_mmio_reg_state_init(struct intel_guc *guc, - struct __guc_ads_blob *blob) +static void guc_mmio_reg_state_init(struct intel_guc *guc) { + struct iosys_map ads_regset_map; struct intel_gt *gt = guc_to_gt(guc); struct intel_engine_cs *engine; - struct guc_mmio_reg *ads_registers; enum intel_engine_id id; u32 addr_ggtt, offset; offset = guc_ads_regset_offset(guc); addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset; - ads_registers = (struct guc_mmio_reg *)(((u8 *)blob) + offset); + ads_regset_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map, offset); - memcpy(ads_registers, guc->ads_regset, guc->ads_regset_size); + iosys_map_memcpy_to(&ads_regset_map, 0, guc->ads_regset, + guc->ads_regset_size); for_each_engine(engine, gt, id) { u32 count = guc->ads_regset_count[id]; - struct guc_mmio_reg_set *ads_reg_set; u8 guc_class; /* Class index is checked in class converter */ GEM_BUG_ON(engine->instance >= GUC_MAX_INSTANCES_PER_CLASS); guc_class = engine_class_to_guc_class(engine->class); - ads_reg_set = &blob->ads.reg_state_list[guc_class][engine->instance]; if (!count) { - ads_reg_set->address = 0; - ads_reg_set->count = 0; + ads_blob_write(guc, + ads.reg_state_list[guc_class][engine->instance].address, + 0); + ads_blob_write(guc, + ads.reg_state_list[guc_class][engine->instance].count, + 0); continue; } - ads_reg_set->address = addr_ggtt; - ads_reg_set->count = count; + ads_blob_write(guc, + ads.reg_state_list[guc_class][engine->instance].address, + addr_ggtt); + ads_blob_write(guc, + ads.reg_state_list[guc_class][engine->instance].count, + count); addr_ggtt += count * sizeof(struct guc_mmio_reg); } @@ -646,7 +652,7 @@ static void __guc_ads_init(struct intel_guc *guc) blob->ads.gt_system_info = base + ptr_offset(blob, system_info); /* MMIO save/restore list */ - guc_mmio_reg_state_init(guc, blob); + guc_mmio_reg_state_init(guc); /* Private Data */ blob->ads.private_data = base + guc_ads_private_data_offset(guc); -- 2.35.1
[PATCH 10/19] drm/i915/guc: Convert guc_ads_private_data_reset to iosys_map
Use iosys_map_memset() to zero the private data as ADS may be either on system or IO memory. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 1d21a2d457e0..973762ce2196 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -674,8 +674,8 @@ static void guc_ads_private_data_reset(struct intel_guc *guc) if (!size) return; - memset((void *)guc->ads_blob + guc_ads_private_data_offset(guc), 0, - size); + iosys_map_memset(&guc->ads_map, guc_ads_private_data_offset(guc), +0, size); } /** -- 2.35.1
[PATCH 19/19] drm/i915/guc: Remove plain ads_blob pointer
Now we have the access to content of GuC ADS either using iosys_map API or using a temporary buffer. Remove guc->ads_blob as there shouldn't be updates using the bare pointer anymore. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc.h | 3 +-- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 8 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index f857e9190750..bf7079480d47 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -147,8 +147,7 @@ struct intel_guc { /** @ads_vma: object allocated to hold the GuC ADS */ struct i915_vma *ads_vma; - /** @ads_blob: contents of the GuC ADS */ - struct __guc_ads_blob *ads_blob; + /** @ads_map: contents of the GuC ADS */ struct iosys_map ads_map; /** @ads_regset_size: size of the save/restore regsets in the ADS */ u32 ads_regset_size; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 6262fd4e0d4a..7dd44b6d76da 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -672,6 +672,7 @@ static void __guc_ads_init(struct intel_guc *guc) */ int intel_guc_ads_create(struct intel_guc *guc) { + void *ads_blob; u32 size; int ret; @@ -696,14 +697,14 @@ int intel_guc_ads_create(struct intel_guc *guc) size = guc_ads_blob_size(guc); ret = intel_guc_allocate_and_map_vma(guc, size, &guc->ads_vma, -(void **)&guc->ads_blob); +&ads_blob); if (ret) return ret; if (i915_gem_object_is_lmem(guc->ads_vma->obj)) - iosys_map_set_vaddr_iomem(&guc->ads_map, (void __iomem *)guc->ads_blob); + iosys_map_set_vaddr_iomem(&guc->ads_map, (void __iomem *)ads_blob); else - iosys_map_set_vaddr(&guc->ads_map, guc->ads_blob); + iosys_map_set_vaddr(&guc->ads_map, ads_blob); __guc_ads_init(guc); @@ -725,7 +726,6 @@ void intel_guc_ads_init_late(struct intel_guc *guc) void intel_guc_ads_destroy(struct intel_guc *guc) { i915_vma_unpin_and_release(&guc->ads_vma, I915_VMA_RELEASE_MAP); - guc->ads_blob = NULL; iosys_map_clear(&guc->ads_map); kfree(guc->ads_regset); } -- 2.35.1
[PATCH 12/19] drm/i915/guc: Replace check for golden context size
In the other places in this function, guc->ads_map is being protected from access when it's not yet set. However the last check is actually about guc->ads_golden_ctxt_size been set before. These checks should always match as the size is initialized on the first call to guc_prep_golden_context(), but it's clearer if we have a single return and check for guc->ads_golden_ctxt_size. This is just a readability improvement, no change in behavior. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 80fbab831536..098a4756e8c5 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -461,10 +461,10 @@ static int guc_prep_golden_context(struct intel_guc *guc) addr_ggtt += alloc_size; } - if (iosys_map_is_null(&guc->ads_map)) - return total_size; + /* Make sure current size matches what we calculated previously */ + if (guc->ads_golden_ctxt_size) + GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size); - GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size); return total_size; } -- 2.35.1
[PATCH 09/19] drm/i915/guc: Convert engine record to iosys_map
Use iosys_map to read fields from the dma_blob so access to IO and system memory is abstracted away. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 14 ++ drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h | 3 ++- .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 17 ++--- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 6311b9da87e4..1d21a2d457e0 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -698,18 +698,16 @@ void intel_guc_ads_reset(struct intel_guc *guc) u32 intel_guc_engine_usage_offset(struct intel_guc *guc) { - struct __guc_ads_blob *blob = guc->ads_blob; - u32 base = intel_guc_ggtt_offset(guc, guc->ads_vma); - u32 offset = base + ptr_offset(blob, engine_usage); - - return offset; + return intel_guc_ggtt_offset(guc, guc->ads_vma) + + offsetof(struct __guc_ads_blob, engine_usage); } -struct guc_engine_usage_record *intel_guc_engine_usage(struct intel_engine_cs *engine) +struct iosys_map intel_guc_engine_usage_record_map(struct intel_engine_cs *engine) { struct intel_guc *guc = &engine->gt->uc.guc; - struct __guc_ads_blob *blob = guc->ads_blob; u8 guc_class = engine_class_to_guc_class(engine->class); + size_t offset = offsetof(struct __guc_ads_blob, + engine_usage.engines[guc_class][ilog2(engine->logical_mask)]); - return &blob->engine_usage.engines[guc_class][ilog2(engine->logical_mask)]; + return IOSYS_MAP_INIT_OFFSET(&guc->ads_map, offset); } diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h index e74c110facff..1c64f4d6ea21 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.h @@ -7,6 +7,7 @@ #define _INTEL_GUC_ADS_H_ #include +#include struct intel_guc; struct drm_printer; @@ -18,7 +19,7 @@ void intel_guc_ads_init_late(struct intel_guc *guc); void intel_guc_ads_reset(struct intel_guc *guc); void intel_guc_ads_print_policy_info(struct intel_guc *guc, struct drm_printer *p); -struct guc_engine_usage_record *intel_guc_engine_usage(struct intel_engine_cs *engine); +struct iosys_map intel_guc_engine_usage_record_map(struct intel_engine_cs *engine); u32 intel_guc_engine_usage_offset(struct intel_guc *guc); #endif diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c index b3a429a92c0d..6d34842f68b4 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c @@ -1139,6 +1139,9 @@ __extend_last_switch(struct intel_guc *guc, u64 *prev_start, u32 new_start) *prev_start = ((u64)gt_stamp_hi << 32) | new_start; } +#define record_read(map_, field_) \ + iosys_map_rd_field(map_, struct guc_engine_usage_record, field_) + /* * GuC updates shared memory and KMD reads it. Since this is not synchronized, * we run into a race where the value read is inconsistent. Sometimes the @@ -1153,17 +1156,17 @@ __extend_last_switch(struct intel_guc *guc, u64 *prev_start, u32 new_start) static void __get_engine_usage_record(struct intel_engine_cs *engine, u32 *last_in, u32 *id, u32 *total) { - struct guc_engine_usage_record *rec = intel_guc_engine_usage(engine); + struct iosys_map rec_map = intel_guc_engine_usage_record_map(engine); int i = 0; do { - *last_in = READ_ONCE(rec->last_switch_in_stamp); - *id = READ_ONCE(rec->current_context_index); - *total = READ_ONCE(rec->total_runtime); + *last_in = record_read(&rec_map, last_switch_in_stamp); + *id = record_read(&rec_map, current_context_index); + *total = record_read(&rec_map, total_runtime); - if (READ_ONCE(rec->last_switch_in_stamp) == *last_in && - READ_ONCE(rec->current_context_index) == *id && - READ_ONCE(rec->total_runtime) == *total) + if (record_read(&rec_map, last_switch_in_stamp) == *last_in && + record_read(&rec_map, current_context_index) == *id && + record_read(&rec_map, total_runtime) == *total) break; } while (++i < 6); } -- 2.35.1
[PATCH 16/19] drm/i915/guc: Use a single pass to calculate regset
The ADS initialitazion was using 2 passes to calculate the regset sent to GuC to initialize each engine: the first pass to just have the final object size and the second to set each register in place in the final gem object. However in order to maintain an ordered set of registers to pass to guc, each register needs to be added and moved in the final array. The second phase may actually happen in IO memory rather than system memory and accessing IO memory by simply dereferencing the pointer doesn't work on all architectures. Other places of the ADS initializaition were converted to use the iosys_map API, but here there may be a lot more accesses to IO memory. So, instead of following that same approach, convert the regset initialization to calculate the final array in 1 pass and in the second pass that array is just copied to its final location, updating the pointers for each engine written to the ADS blob. One important thing is that struct temp_regset now have different semantics: `registers` continues to track the registers of a single engine, however the other fields are updated together, according to the newly added `storage`, which tracks the memory allocated for all the registers. So rename some of these fields and add a __mmio_reg_add(): this function (possibly) allocates memory and operates on the storage pointer while guc_mmio_reg_add() continues to manage the registers pointer. On a Tiger Lake system using enable_guc=3, the following log message is now seen: [ 187.334310] i915 :00:02.0: [drm:intel_guc_ads_create [i915]] Used 4 KB for temporary ADS regset This change has also been tested on an ARM64 host with DG2 and other discrete graphics cards. v2 (Daniele): - Fix leaking tempset on error path - Add comments on struct temp_regset to document the meaning of each field Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc.h | 7 ++ drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 128 + 2 files changed, 90 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index 9b9ba79f7594..f857e9190750 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -152,6 +152,13 @@ struct intel_guc { struct iosys_map ads_map; /** @ads_regset_size: size of the save/restore regsets in the ADS */ u32 ads_regset_size; + /** +* @ads_regset_count: number of save/restore registers in the ADS for +* each engine +*/ + u32 ads_regset_count[I915_NUM_ENGINES]; + /** @ads_regset: save/restore regsets in the ADS */ + struct guc_mmio_reg *ads_regset; /** @ads_golden_ctxt_size: size of the golden contexts in the ADS */ u32 ads_golden_ctxt_size; /** @ads_engine_usage_size: size of engine usage in the ADS */ diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 3a4558948c31..c040d8d8d7a4 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -226,14 +226,18 @@ static void guc_mapping_table_init(struct intel_gt *gt, /* * The save/restore register list must be pre-calculated to a temporary - * buffer of driver defined size before it can be generated in place - * inside the ADS. + * buffer before it can be copied inside the ADS. */ -#define MAX_MMIO_REGS 128 /* Arbitrary size, increase as needed */ struct temp_regset { + /* +* ptr to the section of the storage for the engine currently being +* worked on +*/ struct guc_mmio_reg *registers; - u32 used; - u32 size; + /* ptr to the base of the allocated storage for all engines */ + struct guc_mmio_reg *storage; + u32 storage_used; + u32 storage_max; }; static int guc_mmio_reg_cmp(const void *a, const void *b) @@ -244,18 +248,44 @@ static int guc_mmio_reg_cmp(const void *a, const void *b) return (int)ra->offset - (int)rb->offset; } +static struct guc_mmio_reg * __must_check +__mmio_reg_add(struct temp_regset *regset, struct guc_mmio_reg *reg) +{ + u32 pos = regset->storage_used; + struct guc_mmio_reg *slot; + + if (pos >= regset->storage_max) { + size_t size = ALIGN((pos + 1) * sizeof(*slot), PAGE_SIZE); + struct guc_mmio_reg *r = krealloc(regset->storage, + size, GFP_KERNEL); + if (!r) { + WARN_ONCE(1, "Incomplete regset list: can't add register (%d)\n", + -ENOMEM); + return ERR_PTR(-ENOMEM); + } + + regset->registers = r + (regset->registers - regset->storage); +
[PATCH 13/19] drm/i915/guc: Convert mapping table to iosys_map
Use iosys_map to write the fields system_info.mapping_table[][]. Since we already have the info_map around where needed, just use it instead of going through guc->ads_map. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 12 ++-- 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 098a4756e8c5..9e96a44a6bbc 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -204,7 +204,7 @@ int intel_guc_global_policies_update(struct intel_guc *guc) } static void guc_mapping_table_init(struct intel_gt *gt, - struct guc_gt_system_info *system_info) + struct iosys_map *info_map) { unsigned int i, j; struct intel_engine_cs *engine; @@ -213,14 +213,14 @@ static void guc_mapping_table_init(struct intel_gt *gt, /* Table must be set to invalid values for entries not used */ for (i = 0; i < GUC_MAX_ENGINE_CLASSES; ++i) for (j = 0; j < GUC_MAX_INSTANCES_PER_CLASS; ++j) - system_info->mapping_table[i][j] = - GUC_MAX_INSTANCES_PER_CLASS; + info_map_write(info_map, mapping_table[i][j], + GUC_MAX_INSTANCES_PER_CLASS); for_each_engine(engine, gt, id) { u8 guc_class = engine_class_to_guc_class(engine->class); - system_info->mapping_table[guc_class][ilog2(engine->logical_mask)] = - engine->instance; + info_map_write(info_map, mapping_table[guc_class][ilog2(engine->logical_mask)], + engine->instance); } } @@ -595,7 +595,7 @@ static void __guc_ads_init(struct intel_guc *guc) /* Golden contexts for re-initialising after a watchdog reset */ guc_prep_golden_context(guc); - guc_mapping_table_init(guc_to_gt(guc), &blob->system_info); + guc_mapping_table_init(guc_to_gt(guc), &info_map); base = intel_guc_ggtt_offset(guc, guc->ads_vma); -- 2.35.1
[PATCH 07/19] drm/i915/guc: Convert golden context init to iosys_map
Now the map is saved during creation, so use it to initialize the golden context, reading from shmem and writing to either system or IO memory. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 25 +++--- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 3a0afce7564e..d32b407a2d25 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -473,18 +473,17 @@ static struct intel_engine_cs *find_engine_state(struct intel_gt *gt, u8 engine_ static void guc_init_golden_context(struct intel_guc *guc) { - struct __guc_ads_blob *blob = guc->ads_blob; struct intel_engine_cs *engine; struct intel_gt *gt = guc_to_gt(guc); + struct iosys_map golden_context_map; u32 addr_ggtt, offset; u32 total_size = 0, alloc_size, real_size; u8 engine_class, guc_class; - u8 *ptr; if (!intel_uc_uses_guc_submission(>->uc)) return; - GEM_BUG_ON(!blob); + GEM_BUG_ON(iosys_map_is_null(&guc->ads_map)); /* * Go back and fill in the golden context data now that it is @@ -492,15 +491,15 @@ static void guc_init_golden_context(struct intel_guc *guc) */ offset = guc_ads_golden_ctxt_offset(guc); addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset; - ptr = ((u8 *)blob) + offset; + + golden_context_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map, offset); for (engine_class = 0; engine_class <= MAX_ENGINE_CLASS; ++engine_class) { if (engine_class == OTHER_CLASS) continue; guc_class = engine_class_to_guc_class(engine_class); - - if (!blob->system_info.engine_enabled_masks[guc_class]) + if (!ads_blob_read(guc, system_info.engine_enabled_masks[guc_class])) continue; real_size = intel_engine_context_size(gt, engine_class); @@ -511,18 +510,20 @@ static void guc_init_golden_context(struct intel_guc *guc) if (!engine) { drm_err(>->i915->drm, "No engine state recorded for class %d!\n", engine_class); - blob->ads.eng_state_size[guc_class] = 0; - blob->ads.golden_context_lrca[guc_class] = 0; + ads_blob_write(guc, ads.eng_state_size[guc_class], 0); + ads_blob_write(guc, ads.golden_context_lrca[guc_class], 0); continue; } - GEM_BUG_ON(blob->ads.eng_state_size[guc_class] != + GEM_BUG_ON(ads_blob_read(guc, ads.eng_state_size[guc_class]) != real_size - LRC_SKIP_SIZE); - GEM_BUG_ON(blob->ads.golden_context_lrca[guc_class] != addr_ggtt); + GEM_BUG_ON(ads_blob_read(guc, ads.golden_context_lrca[guc_class]) != addr_ggtt); + addr_ggtt += alloc_size; - shmem_read(engine->default_state, 0, ptr, real_size); - ptr += alloc_size; + shmem_read_to_iosys_map(engine->default_state, 0, + &golden_context_map, real_size); + iosys_map_incr(&golden_context_map, alloc_size); } GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size); -- 2.35.1
[PATCH 08/19] drm/i915/guc: Convert policies update to iosys_map
Use iosys_map to write the policies update so access to IO and system memory is abstracted away. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 41 -- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index d32b407a2d25..6311b9da87e4 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -130,33 +130,37 @@ static u32 guc_ads_blob_size(struct intel_guc *guc) guc_ads_private_data_size(guc); } -static void guc_policies_init(struct intel_guc *guc, struct guc_policies *policies) +static void guc_policies_init(struct intel_guc *guc) { struct intel_gt *gt = guc_to_gt(guc); struct drm_i915_private *i915 = gt->i915; + u32 global_flags = 0; - policies->dpc_promote_time = GLOBAL_POLICY_DEFAULT_DPC_PROMOTE_TIME_US; - policies->max_num_work_items = GLOBAL_POLICY_MAX_NUM_WI; + ads_blob_write(guc, policies.dpc_promote_time, + GLOBAL_POLICY_DEFAULT_DPC_PROMOTE_TIME_US); + ads_blob_write(guc, policies.max_num_work_items, + GLOBAL_POLICY_MAX_NUM_WI); - policies->global_flags = 0; if (i915->params.reset < 2) - policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET; + global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET; - policies->is_valid = 1; + ads_blob_write(guc, policies.global_flags, global_flags); + ads_blob_write(guc, policies.is_valid, 1); } void intel_guc_ads_print_policy_info(struct intel_guc *guc, struct drm_printer *dp) { - struct __guc_ads_blob *blob = guc->ads_blob; - - if (unlikely(!blob)) + if (unlikely(iosys_map_is_null(&guc->ads_map))) return; drm_printf(dp, "Global scheduling policies:\n"); - drm_printf(dp, " DPC promote time = %u\n", blob->policies.dpc_promote_time); - drm_printf(dp, " Max num work items = %u\n", blob->policies.max_num_work_items); - drm_printf(dp, " Flags = %u\n", blob->policies.global_flags); + drm_printf(dp, " DPC promote time = %u\n", + ads_blob_read(guc, policies.dpc_promote_time)); + drm_printf(dp, " Max num work items = %u\n", + ads_blob_read(guc, policies.max_num_work_items)); + drm_printf(dp, " Flags = %u\n", + ads_blob_read(guc, policies.global_flags)); } static int guc_action_policies_update(struct intel_guc *guc, u32 policy_offset) @@ -171,23 +175,24 @@ static int guc_action_policies_update(struct intel_guc *guc, u32 policy_offset) int intel_guc_global_policies_update(struct intel_guc *guc) { - struct __guc_ads_blob *blob = guc->ads_blob; struct intel_gt *gt = guc_to_gt(guc); + u32 scheduler_policies; intel_wakeref_t wakeref; int ret; - if (!blob) + if (iosys_map_is_null(&guc->ads_map)) return -EOPNOTSUPP; - GEM_BUG_ON(!blob->ads.scheduler_policies); + scheduler_policies = ads_blob_read(guc, ads.scheduler_policies); + GEM_BUG_ON(!scheduler_policies); - guc_policies_init(guc, &blob->policies); + guc_policies_init(guc); if (!intel_guc_is_ready(guc)) return 0; with_intel_runtime_pm(>->i915->runtime_pm, wakeref) - ret = guc_action_policies_update(guc, blob->ads.scheduler_policies); + ret = guc_action_policies_update(guc, scheduler_policies); return ret; } @@ -557,7 +562,7 @@ static void __guc_ads_init(struct intel_guc *guc) u32 base; /* GuC scheduling policies */ - guc_policies_init(guc, &blob->policies); + guc_policies_init(guc); /* System info */ fill_engine_enable_masks(gt, &blob->system_info); -- 2.35.1
[PATCH 06/19] drm/i915/guc: Add read/write helpers for ADS blob
Add helpers on top of iosys_map_read_field() / iosys_map_write_field() functions so they always use the right arguments and make code easier to read. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index 13671b186908..3a0afce7564e 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -60,6 +60,13 @@ struct __guc_ads_blob { struct guc_mmio_reg regset[0]; } __packed; +#define ads_blob_read(guc_, field_)\ + iosys_map_rd_field(&(guc_)->ads_map, struct __guc_ads_blob, field_) + +#define ads_blob_write(guc_, field_, val_) \ + iosys_map_wr_field(&(guc_)->ads_map, struct __guc_ads_blob, \ + field_, val_) + static u32 guc_ads_regset_size(struct intel_guc *guc) { GEM_BUG_ON(!guc->ads_regset_size); -- 2.35.1
[PATCH 01/19] dma-buf-map: Rename to iosys-map
Rename struct dma_buf_map to struct iosys_map and corresponding APIs. Over time dma-buf-map grew up to more functionality than the one used by dma-buf: in fact it's just a shim layer to abstract system memory, that can be accessed via regular load and store, from IO memory that needs to be acessed via arch helpers. The idea is to extend this API so it can fulfill other needs, internal to a single driver. Example: in the i915 driver it's desired to share the implementation for integrated graphics, which uses mostly system memory, with discrete graphics, which may need to access IO memory. The conversion was mostly done with the following semantic patch: @r1@ @@ - struct dma_buf_map + struct iosys_map @r2@ @@ ( - DMA_BUF_MAP_INIT_VADDR + IOSYS_MAP_INIT_VADDR | - dma_buf_map_set_vaddr + iosys_map_set_vaddr | - dma_buf_map_set_vaddr_iomem + iosys_map_set_vaddr_iomem | - dma_buf_map_is_equal + iosys_map_is_equal | - dma_buf_map_is_null + iosys_map_is_null | - dma_buf_map_is_set + iosys_map_is_set | - dma_buf_map_clear + iosys_map_clear | - dma_buf_map_memcpy_to + iosys_map_memcpy_to | - dma_buf_map_incr + iosys_map_incr ) @@ @@ - #include + #include Then some files had their includes adjusted and some comments were update to remove mentions to dma-buf-map. Since this is not specific to dma-buf anymore, move the documentation to the "Bus-Independent Device Accesses" section. v2: - Squash patches v3: - Fix wrong removal of dma-buf.h from MAINTAINERS - Move documentation from dma-buf.rst to device-io.rst v4: - Change documentation tile and level Signed-off-by: Lucas De Marchi Acked-by: Christian König Acked-by: Sumit Semwal Acked-by: Thomas Zimmermann --- Documentation/driver-api/device-io.rst| 9 + Documentation/driver-api/dma-buf.rst | 9 - Documentation/gpu/todo.rst| 20 +- MAINTAINERS | 9 +- drivers/dma-buf/dma-buf.c | 22 +- drivers/dma-buf/heaps/cma_heap.c | 10 +- drivers/dma-buf/heaps/system_heap.c | 10 +- drivers/gpu/drm/ast/ast_drv.h | 2 +- drivers/gpu/drm/ast/ast_mode.c| 8 +- drivers/gpu/drm/drm_cache.c | 18 +- drivers/gpu/drm/drm_client.c | 9 +- drivers/gpu/drm/drm_fb_helper.c | 12 +- drivers/gpu/drm/drm_gem.c | 12 +- drivers/gpu/drm/drm_gem_cma_helper.c | 9 +- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 16 +- drivers/gpu/drm/drm_gem_shmem_helper.c| 15 +- drivers/gpu/drm/drm_gem_ttm_helper.c | 4 +- drivers/gpu/drm/drm_gem_vram_helper.c | 25 +- drivers/gpu/drm/drm_internal.h| 6 +- drivers/gpu/drm/drm_mipi_dbi.c| 8 +- drivers/gpu/drm/drm_prime.c | 4 +- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 8 +- drivers/gpu/drm/gud/gud_pipe.c| 4 +- drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 5 +- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c| 8 +- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 6 +- .../gpu/drm/i915/gem/selftests/mock_dmabuf.c | 6 +- drivers/gpu/drm/lima/lima_gem.c | 3 +- drivers/gpu/drm/lima/lima_sched.c | 4 +- drivers/gpu/drm/mediatek/mtk_drm_gem.c| 7 +- drivers/gpu/drm/mediatek/mtk_drm_gem.h| 5 +- drivers/gpu/drm/mgag200/mgag200_mode.c| 4 +- drivers/gpu/drm/msm/msm_drv.h | 4 +- drivers/gpu/drm/msm/msm_gem_prime.c | 6 +- drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 13 +- drivers/gpu/drm/qxl/qxl_display.c | 8 +- drivers/gpu/drm/qxl/qxl_draw.c| 6 +- drivers/gpu/drm/qxl/qxl_drv.h | 10 +- drivers/gpu/drm/qxl/qxl_object.c | 8 +- drivers/gpu/drm/qxl/qxl_object.h | 4 +- drivers/gpu/drm/qxl/qxl_prime.c | 4 +- drivers/gpu/drm/radeon/radeon_gem.c | 1 + drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 9 +- drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 5 +- drivers/gpu/drm/tegra/gem.c | 10 +- drivers/gpu/drm/tiny/cirrus.c | 8 +- drivers/gpu/drm/tiny/gm12u320.c | 7 +- drivers/gpu/drm/ttm/ttm_bo_util.c | 16 +- drivers/gpu/drm/ttm/ttm_resource.c| 42 +-- drivers/gpu/drm/ttm/ttm_tt.c | 8 +- drivers/gpu/drm/udl/udl_modeset.c | 3 +- drivers/gpu/drm/vboxvideo/vbox_mode.c | 4 +- drivers/gpu/drm/vkms/vkms_c
[PATCH 05/19] drm/i915/guc: Keep iosys_map of ads_blob around
Convert intel_guc_ads_create() and initialization to use iosys_map rather than plain pointer and save it in the guc struct. This will help with additional updates to the ads_blob after the creation/initialization by abstracting the IO vs system memory. Cc: Matt Roper Cc: Thomas Hellström Cc: Daniel Vetter Cc: John Harrison Cc: Matthew Brost Cc: Daniele Ceraolo Spurio Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/uc/intel_guc.h | 4 +++- drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c | 6 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index 697d9d66acef..9b9ba79f7594 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -6,8 +6,9 @@ #ifndef _INTEL_GUC_H_ #define _INTEL_GUC_H_ -#include #include +#include +#include #include "intel_uncore.h" #include "intel_guc_fw.h" @@ -148,6 +149,7 @@ struct intel_guc { struct i915_vma *ads_vma; /** @ads_blob: contents of the GuC ADS */ struct __guc_ads_blob *ads_blob; + struct iosys_map ads_map; /** @ads_regset_size: size of the save/restore regsets in the ADS */ u32 ads_regset_size; /** @ads_golden_ctxt_size: size of the golden contexts in the ADS */ diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c index e61150adcbe9..13671b186908 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c @@ -624,6 +624,11 @@ int intel_guc_ads_create(struct intel_guc *guc) if (ret) return ret; + if (i915_gem_object_is_lmem(guc->ads_vma->obj)) + iosys_map_set_vaddr_iomem(&guc->ads_map, (void __iomem *)guc->ads_blob); + else + iosys_map_set_vaddr(&guc->ads_map, guc->ads_blob); + __guc_ads_init(guc); return 0; @@ -645,6 +650,7 @@ void intel_guc_ads_destroy(struct intel_guc *guc) { i915_vma_unpin_and_release(&guc->ads_vma, I915_VMA_RELEASE_MAP); guc->ads_blob = NULL; + iosys_map_clear(&guc->ads_map); } static void guc_ads_private_data_reset(struct intel_guc *guc) -- 2.35.1
[PATCH 04/19] drm/i915/gt: Add helper for shmem copy to iosys_map
Add a variant of shmem_read() that takes a iosys_map pointer rather than a plain pointer as argument. It's mostly a copy __shmem_rw() but adapting the api and removing the write support since there's currently only need to use iosys_map as destination. Reworking __shmem_rw() to share the implementation was tempting, but finding a good balance between reuse and clarity pushed towards a little code duplication. Since the function is small, just add the similar function with a copy/paste/adapt approach. Cc: Matt Roper Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Cc: David Airlie Cc: Daniel Vetter Cc: Matthew Auld Cc: Thomas Hellström Cc: Maarten Lankhorst Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/i915/gt/shmem_utils.c | 33 +++ drivers/gpu/drm/i915/gt/shmem_utils.h | 3 +++ 2 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.c b/drivers/gpu/drm/i915/gt/shmem_utils.c index 0683b27a3890..764adefdb4be 100644 --- a/drivers/gpu/drm/i915/gt/shmem_utils.c +++ b/drivers/gpu/drm/i915/gt/shmem_utils.c @@ -3,6 +3,7 @@ * Copyright © 2020 Intel Corporation */ +#include #include #include #include @@ -123,6 +124,38 @@ static int __shmem_rw(struct file *file, loff_t off, return 0; } +int shmem_read_to_iosys_map(struct file *file, loff_t off, + struct iosys_map *map, size_t len) +{ + struct iosys_map map_iter = *map; + unsigned long pfn; + + for (pfn = off >> PAGE_SHIFT; len; pfn++) { + unsigned int this = + min_t(size_t, PAGE_SIZE - offset_in_page(off), len); + struct page *page; + void *vaddr; + + page = shmem_read_mapping_page_gfp(file->f_mapping, pfn, + GFP_KERNEL); + if (IS_ERR(page)) + return PTR_ERR(page); + + vaddr = kmap(page); + iosys_map_memcpy_to(&map_iter, 0, vaddr + offset_in_page(off), + this); + mark_page_accessed(page); + kunmap(page); + put_page(page); + + len -= this; + iosys_map_incr(&map_iter, this); + off = 0; + } + + return 0; +} + int shmem_read(struct file *file, loff_t off, void *dst, size_t len) { return __shmem_rw(file, off, dst, len, false); diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.h b/drivers/gpu/drm/i915/gt/shmem_utils.h index c1669170c351..e1784999faee 100644 --- a/drivers/gpu/drm/i915/gt/shmem_utils.h +++ b/drivers/gpu/drm/i915/gt/shmem_utils.h @@ -8,6 +8,7 @@ #include +struct iosys_map; struct drm_i915_gem_object; struct file; @@ -17,6 +18,8 @@ struct file *shmem_create_from_object(struct drm_i915_gem_object *obj); void *shmem_pin_map(struct file *file); void shmem_unpin_map(struct file *file, void *ptr); +int shmem_read_to_iosys_map(struct file *file, loff_t off, + struct iosys_map *map, size_t len); int shmem_read(struct file *file, loff_t off, void *dst, size_t len); int shmem_write(struct file *file, loff_t off, void *src, size_t len); -- 2.35.1
[PATCH 02/19] iosys-map: Add offset to iosys_map_memcpy_to()
In certain situations it's useful to be able to write to an offset of the mapping. Add a dst_offset to iosys_map_memcpy_to(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi --- drivers/gpu/drm/drm_cache.c | 2 +- drivers/gpu/drm/drm_fb_helper.c | 2 +- include/linux/iosys-map.h | 17 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index 66597e411764..c3e6e615bf09 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -218,7 +218,7 @@ static void memcpy_fallback(struct iosys_map *dst, if (!dst->is_iomem && !src->is_iomem) { memcpy(dst->vaddr, src->vaddr, len); } else if (!src->is_iomem) { - iosys_map_memcpy_to(dst, src->vaddr, len); + iosys_map_memcpy_to(dst, 0, src->vaddr, len); } else if (!dst->is_iomem) { memcpy_fromio(dst->vaddr, src->vaddr_iomem, len); } else { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 238f815cb2a0..bf5cc9a42e5a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -385,7 +385,7 @@ static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper, iosys_map_incr(dst, offset); /* go to first pixel within clip rect */ for (y = clip->y1; y < clip->y2; y++) { - iosys_map_memcpy_to(dst, src, len); + iosys_map_memcpy_to(dst, 0, src, len); iosys_map_incr(dst, fb->pitches[0]); src += fb->pitches[0]; } diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index f4186f91caa6..edd7fa3be9e9 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -220,22 +220,23 @@ static inline void iosys_map_clear(struct iosys_map *map) } /** - * iosys_map_memcpy_to - Memcpy into iosys mapping + * iosys_map_memcpy_to_offset - Memcpy into offset of iosys_map * @dst: The iosys_map structure + * @dst_offset:The offset from which to copy * @src: The source buffer * @len: The number of byte in src * - * Copies data into a iosys mapping. The source buffer is in system - * memory. Depending on the buffer's location, the helper picks the correct - * method of accessing the memory. + * Copies data into a iosys_map with an offset. The source buffer is in + * system memory. Depending on the buffer's location, the helper picks the + * correct method of accessing the memory. */ -static inline void iosys_map_memcpy_to(struct iosys_map *dst, const void *src, - size_t len) +static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, + const void *src, size_t len) { if (dst->is_iomem) - memcpy_toio(dst->vaddr_iomem, src, len); + memcpy_toio(dst->vaddr_iomem + dst_offset, src, len); else - memcpy(dst->vaddr, src, len); + memcpy(dst->vaddr + dst_offset, src, len); } /** -- 2.35.1
[PATCH 03/19] iosys-map: Add a few more helpers
First the simplest ones: - iosys_map_memset(): when abstracting system and I/O memory, just like the memcpy() use case, memset() also has dedicated functions to be called for using IO memory. - iosys_map_memcpy_from(): we may need to copy data from I/O memory, not only to. In certain situations it's useful to be able to read or write to an offset that is calculated by having the memory layout given by a struct declaration. Usually we are going to read/write a u8, u16, u32 or u64. As a pre-requisite for the implementation, add iosys_map_memcpy_from() to be the equivalent of iosys_map_memcpy_to(), but in the other direction. Then add 2 pairs of macros: - iosys_map_rd() / iosys_map_wr() - iosys_map_rd_field() / iosys_map_wr_field() The first pair takes the C-type and offset to read/write. The second pair uses a struct describing the layout of the mapping in order to calculate the offset and size being read/written. We could use readb, readw, readl, readq and the write* counterparts, however due to alignment issues this may not work on all architectures. If alignment needs to be checked to call the right function, it's not possible to decide at compile-time which function to call: so just leave the decision to the memcpy function that will do exactly that. Finally, in order to use the above macros with a map derived from another, add another initializer: IOSYS_MAP_INIT_OFFSET(). Cc: Sumit Semwal Cc: Christian König Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Cc: linux-ker...@vger.kernel.org Signed-off-by: Lucas De Marchi --- include/linux/iosys-map.h | 154 +- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index edd7fa3be9e9..96f8b61ac6fb 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -6,6 +6,7 @@ #ifndef __IOSYS_MAP_H__ #define __IOSYS_MAP_H__ +#include #include #include @@ -133,6 +134,45 @@ static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr) map->is_iomem = false; } +/** + * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map + * @map_: The dma-buf mapping structure to copy from + * @offset_: Offset to add to the other mapping + * + * Initializes a new iosys_map struct based on another passed as argument. It + * does a shallow copy of the struct so it's possible to update the back storage + * without changing where the original map points to. It is the equivalent of + * doing: + * + * .. code-block: c + * + * iosys_map map = other_map; + * iosys_map_incr(&map, &offset); + * + * Example usage: + * + * .. code-block: c + * + * void foo(struct device *dev, struct iosys_map *base_map) + * { + * ... + * struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET); + * ... + * } + * + * The advantage of using the initializer over just increasing the offset with + * ``iosys_map_incr()`` like above is that the new map will always point to the + * right place of the buffer during its scope. It reduces the risk of updating + * the wrong part of the buffer and having no compiler warning about that. If + * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn + * using a uninitialized variable. + */ +#define IOSYS_MAP_INIT_OFFSET(map_, offset_) (struct iosys_map) \ + { \ + .vaddr = (map_)->vaddr + (offset_), \ + .is_iomem = (map_)->is_iomem, \ + } + /** * iosys_map_set_vaddr_iomem - Sets a iosys mapping structure to an address in I/O memory * @map: The iosys_map structure @@ -220,7 +260,7 @@ static inline void iosys_map_clear(struct iosys_map *map) } /** - * iosys_map_memcpy_to_offset - Memcpy into offset of iosys_map + * iosys_map_memcpy_to - Memcpy into iosys_map * @dst: The iosys_map structure * @dst_offset:The offset from which to copy * @src: The source buffer @@ -239,6 +279,26 @@ static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, memcpy(dst->vaddr + dst_offset, src, len); } +/** + * iosys_map_memcpy_from - Memcpy from iosys_map into system memory + * @dst: Destination in system memory + * @src: The iosys_map structure + * @src_offset:The offset from which to copy + * @len: The number of byte in src + * + * Copies data from a iosys_map with an offset. The dest buffer is in + * system memory. Depending on the mapping location, the helper picks the + * correct method of accessing the memory. + */ +static inline void iosys_map_memcpy_from(void *dst, const struct iosys_map *src, +size_t src_offset, size_t len) +{ +
[PATCH 00/19] drm/i915/guc: Refactor ADS access to use iosys_map
2nd version of https://patchwork.freedesktop.org/series/99378/ As first patch I'm including the dma-buf-map rename to iosys-map for completeness and to allow the other patches to be reviewed. However the first patch was also sent by itself. I think all the feedback from v1 was incorporated in this version. All the new helpers in iosys-map were squashed in a single patch as requested. Original cover letter: While porting i915 to arm64 we noticed some issues accessing lmem. Some writes were getting corrupted and the final state of the buffer didn't have exactly what we wrote. This became evident when enabling GuC submission: depending on the number of engines the ADS struct was being corrupted and GuC would reject it, refusin to initialize. >From Documentation/core-api/bus-virt-phys-mapping.rst: This memory is called "PCI memory" or "shared memory" or "IO memory" or whatever, and there is only one way to access it: the readb/writeb and related functions. You should never take the address of such memory, because there is really nothing you can do with such an address: it's not conceptually in the same memory space as "real memory" at all, so you cannot just dereference a pointer. (Sadly, on x86 it **is** in the same memory space, so on x86 it actually works to just deference a pointer, but it's not portable). When reading or writing words directly to IO memory, in order to be portable the Linux kernel provides the abstraction detailed in section "Differences between I/O access functions" of Documentation/driver-api/device-io.rst. This limits our ability to simply overlay our structs on top a buffer and directly access it since that buffer may come from IO memory rather than system memory. Hence the approach taken in intel_guc_ads.c needs to be refactored. This is not the only place in i915 that neeed to be changed, but the one causing the most problems, with a real reproducer. This first set of patch focuses on fixing the gem object to pass the ADS After the addition of a few helpers in the dma_buf_map API, most of intel_guc_ads.c can be converted to use it. The exception is the regset initialization: we'd incur into a lot of extra indirection when reading/writting each register. So the regset is converted to use a temporary buffer allocated on probe, which is then copied to its final location when finishing the initialization or on gt reset. Testing on some discrete cards, after this change we can correctly pass the ADS struct to GuC and have it initialized correctly. thanks, Lucas De Marchi Lucas De Marchi (19): dma-buf-map: Rename to iosys-map iosys-map: Add offset to iosys_map_memcpy_to() iosys-map: Add a few more helpers drm/i915/gt: Add helper for shmem copy to iosys_map drm/i915/guc: Keep iosys_map of ads_blob around drm/i915/guc: Add read/write helpers for ADS blob drm/i915/guc: Convert golden context init to iosys_map drm/i915/guc: Convert policies update to iosys_map drm/i915/guc: Convert engine record to iosys_map drm/i915/guc: Convert guc_ads_private_data_reset to iosys_map drm/i915/guc: Convert golden context prep to iosys_map drm/i915/guc: Replace check for golden context size drm/i915/guc: Convert mapping table to iosys_map drm/i915/guc: Convert capture list to iosys_map drm/i915/guc: Prepare for error propagation drm/i915/guc: Use a single pass to calculate regset drm/i915/guc: Convert guc_mmio_reg_state_init to iosys_map drm/i915/guc: Convert __guc_ads_init to iosys_map drm/i915/guc: Remove plain ads_blob pointer Documentation/driver-api/device-io.rst| 9 + Documentation/driver-api/dma-buf.rst | 9 - Documentation/gpu/todo.rst| 20 +- MAINTAINERS | 9 +- drivers/dma-buf/dma-buf.c | 22 +- drivers/dma-buf/heaps/cma_heap.c | 10 +- drivers/dma-buf/heaps/system_heap.c | 10 +- drivers/gpu/drm/ast/ast_drv.h | 2 +- drivers/gpu/drm/ast/ast_mode.c| 8 +- drivers/gpu/drm/drm_cache.c | 18 +- drivers/gpu/drm/drm_client.c | 9 +- drivers/gpu/drm/drm_fb_helper.c | 12 +- drivers/gpu/drm/drm_gem.c | 12 +- drivers/gpu/drm/drm_gem_cma_helper.c | 9 +- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 16 +- drivers/gpu/drm/drm_gem_shmem_helper.c| 15 +- drivers/gpu/drm/drm_gem_ttm_helper.c | 4 +- drivers/gpu/drm/drm_gem_vram_helper.c | 25 +- drivers/gpu/drm/drm_internal.h| 6 +- drivers/gpu/drm/drm_mipi_dbi.c| 8 +- drivers/gpu/drm/drm_prime.c | 4 +- drivers/gpu/drm/etnaviv/etnaviv_drv.h | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 8 +- drivers/gpu/drm/gud/gud_pipe.c| 4 +- drivers/gpu/drm/hyperv/hyperv_drm_modeset.c |
[Important!] 2022 X.Org Foundation Membership deadline for voting in the election
The 2022 X.Org Foundation elections are rapidly approaching. We will be forwarding instructions on the nomination process to membership in the near future. Please note that only current members can vote in the upcoming election, and that the deadline for new memberships or renewals to vote in the upcoming election is March 17th 2022 at 23:59 UTC. If you are interested in joining the X.Org Foundation or in renewing your membership, please visit the membership system site at: https://members.x.org/ You can find the current election schedule here: https://www.x.org/wiki/BoardOfDirectors/Elections/2022/ Lyude Paul, On behalf of the X.Org elections committee