drm/i915/gvt: Prevent divided by zero when calculating refresh rate
authorColin Xu <colin.xu@intel.com>
Fri, 16 Apr 2021 08:33:55 +0000 (16:33 +0800)
committerZhenyu Wang <zhenyuw@linux.intel.com>
Thu, 29 Apr 2021 09:00:09 +0000 (17:00 +0800)
To get refresh rate as vblank timer period and keep the precision, the
calculation of rate is multiplied by 1000. However old logic was using:
rate = pixel clock / (h * v / 1000). When the h/v total is invalid, like
all 0, h * v / 1000 will be rounded to 0, which leads to a divided by 0
fault.

0 H/V are already checked above. Instead of divide after divide, refine
the calculation to divide after multiply: "pixel clock * 1000 / (h * v)"
Guest driver should guarantee the correctness of the timing regs' value.

Fixes: 6a4500c7b83f ("drm/i915/gvt: Get accurate vGPU virtual display refresh rate from vreg")
Reported-by: Zhenyu Wang <zhenyuw@linux.intel.com>
Signed-off-by: Colin Xu <colin.xu@intel.com>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20210416083355.159305-1-colin.xu@intel.com
Reviewed-by: Zhenyu Wang <zhenyuw@linux.intel.com>
drivers/gpu/drm/i915/gvt/handlers.c

index 477badf..dda3207 100644 (file)
@@ -669,8 +669,8 @@ static void vgpu_update_refresh_rate(struct intel_vgpu *vgpu)
        link_n = vgpu_vreg_t(vgpu, PIPE_LINK_N1(TRANSCODER_A));
 
        /* Get H/V total from transcoder timing */
-       htotal = (vgpu_vreg_t(vgpu, HTOTAL(TRANSCODER_A)) >> TRANS_HTOTAL_SHIFT) + 1;
-       vtotal = (vgpu_vreg_t(vgpu, VTOTAL(TRANSCODER_A)) >> TRANS_VTOTAL_SHIFT) + 1;
+       htotal = (vgpu_vreg_t(vgpu, HTOTAL(TRANSCODER_A)) >> TRANS_HTOTAL_SHIFT);
+       vtotal = (vgpu_vreg_t(vgpu, VTOTAL(TRANSCODER_A)) >> TRANS_VTOTAL_SHIFT);
 
        if (dp_br && link_n && htotal && vtotal) {
                u64 pixel_clk = 0;
@@ -682,7 +682,7 @@ static void vgpu_update_refresh_rate(struct intel_vgpu *vgpu)
                pixel_clk *= MSEC_PER_SEC;
 
                /* Calcuate refresh rate by (pixel_clk / (h_total * v_total)) */
-               new_rate = DIV64_U64_ROUND_CLOSEST(pixel_clk, div64_u64(mul_u32_u32(htotal, vtotal), MSEC_PER_SEC));
+               new_rate = DIV64_U64_ROUND_CLOSEST(mul_u64_u32_shr(pixel_clk, MSEC_PER_SEC, 0), mul_u32_u32(htotal + 1, vtotal + 1));
 
                if (*old_rate != new_rate)
                        *old_rate = new_rate;