Merge tag 'gvt-next-2021-03-16' of https://github.com/intel/gvt-linux into drm-intel-next
gvt-next-2021-03-16 - Parse accurate vGPU virtual display rate (Colin) - Convert vblank timer as per-vGPU based on current rate (Colin) - spelling fix (Bhaskar) Signed-off-by: Jani Nikula <jani.nikula@intel.com> From: Zhenyu Wang <zhenyuw@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20210316074330.GC1551@zhen-hp.sh.intel.com
This commit is contained in:
commit
2b25fb31a3
@ -516,11 +516,27 @@ static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num)
|
||||
port->dpcd = NULL;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart vblank_timer_fn(struct hrtimer *data)
|
||||
{
|
||||
struct intel_vgpu_vblank_timer *vblank_timer;
|
||||
struct intel_vgpu *vgpu;
|
||||
|
||||
vblank_timer = container_of(data, struct intel_vgpu_vblank_timer, timer);
|
||||
vgpu = container_of(vblank_timer, struct intel_vgpu, vblank_timer);
|
||||
|
||||
/* Set vblank emulation request per-vGPU bit */
|
||||
intel_gvt_request_service(vgpu->gvt,
|
||||
INTEL_GVT_REQUEST_EMULATE_VBLANK + vgpu->id);
|
||||
hrtimer_add_expires_ns(&vblank_timer->timer, vblank_timer->period);
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num,
|
||||
int type, unsigned int resolution)
|
||||
{
|
||||
struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
|
||||
struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
|
||||
struct intel_vgpu_vblank_timer *vblank_timer = &vgpu->vblank_timer;
|
||||
|
||||
if (drm_WARN_ON(&i915->drm, resolution >= GVT_EDID_NUM))
|
||||
return -EINVAL;
|
||||
@ -544,6 +560,14 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num,
|
||||
port->dpcd->data[DPCD_SINK_COUNT] = 0x1;
|
||||
port->type = type;
|
||||
port->id = resolution;
|
||||
port->vrefresh_k = GVT_DEFAULT_REFRESH_RATE * MSEC_PER_SEC;
|
||||
vgpu->display.port_num = port_num;
|
||||
|
||||
/* Init hrtimer based on default refresh rate */
|
||||
hrtimer_init(&vblank_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
vblank_timer->timer.function = vblank_timer_fn;
|
||||
vblank_timer->vrefresh_k = port->vrefresh_k;
|
||||
vblank_timer->period = DIV64_U64_ROUND_CLOSEST(NSEC_PER_SEC * MSEC_PER_SEC, vblank_timer->vrefresh_k);
|
||||
|
||||
emulate_monitor_status_change(vgpu);
|
||||
|
||||
@ -551,41 +575,44 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num,
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_gvt_check_vblank_emulation - check if vblank emulation timer should
|
||||
* be turned on/off when a virtual pipe is enabled/disabled.
|
||||
* @gvt: a GVT device
|
||||
* vgpu_update_vblank_emulation - Update per-vGPU vblank_timer
|
||||
* @vgpu: vGPU operated
|
||||
* @turnon: Turn ON/OFF vblank_timer
|
||||
*
|
||||
* This function is used to turn on/off vblank timer according to currently
|
||||
* enabled/disabled virtual pipes.
|
||||
* This function is used to turn on/off or update the per-vGPU vblank_timer
|
||||
* when PIPECONF is enabled or disabled. vblank_timer period is also updated
|
||||
* if guest changed the refresh rate.
|
||||
*
|
||||
*/
|
||||
void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt)
|
||||
void vgpu_update_vblank_emulation(struct intel_vgpu *vgpu, bool turnon)
|
||||
{
|
||||
struct intel_gvt_irq *irq = &gvt->irq;
|
||||
struct intel_vgpu *vgpu;
|
||||
int pipe, id;
|
||||
int found = false;
|
||||
struct intel_vgpu_vblank_timer *vblank_timer = &vgpu->vblank_timer;
|
||||
struct intel_vgpu_port *port =
|
||||
intel_vgpu_port(vgpu, vgpu->display.port_num);
|
||||
|
||||
mutex_lock(&gvt->lock);
|
||||
for_each_active_vgpu(gvt, vgpu, id) {
|
||||
for (pipe = 0; pipe < I915_MAX_PIPES; pipe++) {
|
||||
if (pipe_is_enabled(vgpu, pipe)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (turnon) {
|
||||
/*
|
||||
* Skip the re-enable if already active and vrefresh unchanged.
|
||||
* Otherwise, stop timer if already active and restart with new
|
||||
* period.
|
||||
*/
|
||||
if (vblank_timer->vrefresh_k != port->vrefresh_k ||
|
||||
!hrtimer_active(&vblank_timer->timer)) {
|
||||
/* Stop timer before start with new period if active */
|
||||
if (hrtimer_active(&vblank_timer->timer))
|
||||
hrtimer_cancel(&vblank_timer->timer);
|
||||
|
||||
/* Make sure new refresh rate updated to timer period */
|
||||
vblank_timer->vrefresh_k = port->vrefresh_k;
|
||||
vblank_timer->period = DIV64_U64_ROUND_CLOSEST(NSEC_PER_SEC * MSEC_PER_SEC, vblank_timer->vrefresh_k);
|
||||
hrtimer_start(&vblank_timer->timer,
|
||||
ktime_add_ns(ktime_get(), vblank_timer->period),
|
||||
HRTIMER_MODE_ABS);
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
} else {
|
||||
/* Caller request to stop vblank */
|
||||
hrtimer_cancel(&vblank_timer->timer);
|
||||
}
|
||||
|
||||
/* all the pipes are disabled */
|
||||
if (!found)
|
||||
hrtimer_cancel(&irq->vblank_timer.timer);
|
||||
else
|
||||
hrtimer_start(&irq->vblank_timer.timer,
|
||||
ktime_add_ns(ktime_get(), irq->vblank_timer.period),
|
||||
HRTIMER_MODE_ABS);
|
||||
mutex_unlock(&gvt->lock);
|
||||
}
|
||||
|
||||
static void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe)
|
||||
@ -617,7 +644,7 @@ static void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe)
|
||||
}
|
||||
}
|
||||
|
||||
static void emulate_vblank(struct intel_vgpu *vgpu)
|
||||
void intel_vgpu_emulate_vblank(struct intel_vgpu *vgpu)
|
||||
{
|
||||
int pipe;
|
||||
|
||||
@ -627,24 +654,6 @@ static void emulate_vblank(struct intel_vgpu *vgpu)
|
||||
mutex_unlock(&vgpu->vgpu_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_gvt_emulate_vblank - trigger vblank events for vGPUs on GVT device
|
||||
* @gvt: a GVT device
|
||||
*
|
||||
* This function is used to trigger vblank interrupts for vGPUs on GVT device
|
||||
*
|
||||
*/
|
||||
void intel_gvt_emulate_vblank(struct intel_gvt *gvt)
|
||||
{
|
||||
struct intel_vgpu *vgpu;
|
||||
int id;
|
||||
|
||||
mutex_lock(&gvt->lock);
|
||||
for_each_active_vgpu(gvt, vgpu, id)
|
||||
emulate_vblank(vgpu);
|
||||
mutex_unlock(&gvt->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_vgpu_emulate_hotplug - trigger hotplug event for vGPU
|
||||
* @vgpu: a vGPU
|
||||
@ -753,6 +762,8 @@ void intel_vgpu_clean_display(struct intel_vgpu *vgpu)
|
||||
clean_virtual_dp_monitor(vgpu, PORT_D);
|
||||
else
|
||||
clean_virtual_dp_monitor(vgpu, PORT_B);
|
||||
|
||||
vgpu_update_vblank_emulation(vgpu, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@
|
||||
#define _GVT_DISPLAY_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
||||
struct intel_gvt;
|
||||
struct intel_vgpu;
|
||||
@ -157,6 +158,7 @@ enum intel_vgpu_edid {
|
||||
GVT_EDID_NUM,
|
||||
};
|
||||
|
||||
#define GVT_DEFAULT_REFRESH_RATE 60
|
||||
struct intel_vgpu_port {
|
||||
/* per display EDID information */
|
||||
struct intel_vgpu_edid_data *edid;
|
||||
@ -164,6 +166,14 @@ struct intel_vgpu_port {
|
||||
struct intel_vgpu_dpcd_data *dpcd;
|
||||
int type;
|
||||
enum intel_vgpu_edid id;
|
||||
/* x1000 to get accurate 59.94, 24.976, 29.94, etc. in timing std. */
|
||||
u32 vrefresh_k;
|
||||
};
|
||||
|
||||
struct intel_vgpu_vblank_timer {
|
||||
struct hrtimer timer;
|
||||
u32 vrefresh_k;
|
||||
u64 period;
|
||||
};
|
||||
|
||||
static inline char *vgpu_edid_str(enum intel_vgpu_edid id)
|
||||
@ -202,8 +212,8 @@ static inline unsigned int vgpu_edid_yres(enum intel_vgpu_edid id)
|
||||
}
|
||||
}
|
||||
|
||||
void intel_gvt_emulate_vblank(struct intel_gvt *gvt);
|
||||
void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt);
|
||||
void intel_vgpu_emulate_vblank(struct intel_vgpu *vgpu);
|
||||
void vgpu_update_vblank_emulation(struct intel_vgpu *vgpu, bool turnon);
|
||||
|
||||
int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution);
|
||||
void intel_vgpu_reset_display(struct intel_vgpu *vgpu);
|
||||
|
@ -1159,8 +1159,8 @@ static inline void ppgtt_generate_shadow_entry(struct intel_gvt_gtt_entry *se,
|
||||
* @vgpu: target vgpu
|
||||
* @entry: target pfn's gtt entry
|
||||
*
|
||||
* Return 1 if 2MB huge gtt shadowing is possilbe, 0 if miscondition,
|
||||
* negtive if found err.
|
||||
* Return 1 if 2MB huge gtt shadowing is possible, 0 if miscondition,
|
||||
* negative if found err.
|
||||
*/
|
||||
static int is_2MB_gtt_possible(struct intel_vgpu *vgpu,
|
||||
struct intel_gvt_gtt_entry *entry)
|
||||
|
@ -203,6 +203,22 @@ static void init_device_info(struct intel_gvt *gvt)
|
||||
info->msi_cap_offset = pdev->msi_cap;
|
||||
}
|
||||
|
||||
static void intel_gvt_test_and_emulate_vblank(struct intel_gvt *gvt)
|
||||
{
|
||||
struct intel_vgpu *vgpu;
|
||||
int id;
|
||||
|
||||
mutex_lock(&gvt->lock);
|
||||
idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) {
|
||||
if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK + id,
|
||||
(void *)&gvt->service_request)) {
|
||||
if (vgpu->active)
|
||||
intel_vgpu_emulate_vblank(vgpu);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&gvt->lock);
|
||||
}
|
||||
|
||||
static int gvt_service_thread(void *data)
|
||||
{
|
||||
struct intel_gvt *gvt = (struct intel_gvt *)data;
|
||||
@ -220,9 +236,7 @@ static int gvt_service_thread(void *data)
|
||||
if (WARN_ONCE(ret, "service thread is waken up by signal.\n"))
|
||||
continue;
|
||||
|
||||
if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK,
|
||||
(void *)&gvt->service_request))
|
||||
intel_gvt_emulate_vblank(gvt);
|
||||
intel_gvt_test_and_emulate_vblank(gvt);
|
||||
|
||||
if (test_bit(INTEL_GVT_REQUEST_SCHED,
|
||||
(void *)&gvt->service_request) ||
|
||||
@ -278,7 +292,6 @@ void intel_gvt_clean_device(struct drm_i915_private *i915)
|
||||
intel_gvt_clean_sched_policy(gvt);
|
||||
intel_gvt_clean_workload_scheduler(gvt);
|
||||
intel_gvt_clean_gtt(gvt);
|
||||
intel_gvt_clean_irq(gvt);
|
||||
intel_gvt_free_firmware(gvt);
|
||||
intel_gvt_clean_mmio_info(gvt);
|
||||
idr_destroy(&gvt->vgpu_idr);
|
||||
@ -337,7 +350,7 @@ int intel_gvt_init_device(struct drm_i915_private *i915)
|
||||
|
||||
ret = intel_gvt_init_gtt(gvt);
|
||||
if (ret)
|
||||
goto out_clean_irq;
|
||||
goto out_free_firmware;
|
||||
|
||||
ret = intel_gvt_init_workload_scheduler(gvt);
|
||||
if (ret)
|
||||
@ -392,8 +405,6 @@ out_clean_workload_scheduler:
|
||||
intel_gvt_clean_workload_scheduler(gvt);
|
||||
out_clean_gtt:
|
||||
intel_gvt_clean_gtt(gvt);
|
||||
out_clean_irq:
|
||||
intel_gvt_clean_irq(gvt);
|
||||
out_free_firmware:
|
||||
intel_gvt_free_firmware(gvt);
|
||||
out_clean_mmio_info:
|
||||
|
@ -133,6 +133,7 @@ struct intel_vgpu_display {
|
||||
struct intel_vgpu_i2c_edid i2c_edid;
|
||||
struct intel_vgpu_port ports[I915_MAX_PORTS];
|
||||
struct intel_vgpu_sbi sbi;
|
||||
enum port port_num;
|
||||
};
|
||||
|
||||
struct vgpu_sched_ctl {
|
||||
@ -214,6 +215,7 @@ struct intel_vgpu {
|
||||
struct list_head dmabuf_obj_list_head;
|
||||
struct mutex dmabuf_lock;
|
||||
struct idr object_idr;
|
||||
struct intel_vgpu_vblank_timer vblank_timer;
|
||||
|
||||
u32 scan_nonprivbb;
|
||||
};
|
||||
@ -346,13 +348,16 @@ static inline struct intel_gvt *to_gvt(struct drm_i915_private *i915)
|
||||
}
|
||||
|
||||
enum {
|
||||
INTEL_GVT_REQUEST_EMULATE_VBLANK = 0,
|
||||
|
||||
/* Scheduling trigger by timer */
|
||||
INTEL_GVT_REQUEST_SCHED = 1,
|
||||
INTEL_GVT_REQUEST_SCHED = 0,
|
||||
|
||||
/* Scheduling trigger by event */
|
||||
INTEL_GVT_REQUEST_EVENT_SCHED = 2,
|
||||
INTEL_GVT_REQUEST_EVENT_SCHED = 1,
|
||||
|
||||
/* per-vGPU vblank emulation request */
|
||||
INTEL_GVT_REQUEST_EMULATE_VBLANK = 2,
|
||||
INTEL_GVT_REQUEST_EMULATE_VBLANK_MAX = INTEL_GVT_REQUEST_EMULATE_VBLANK
|
||||
+ GVT_MAX_VGPU,
|
||||
};
|
||||
|
||||
static inline void intel_gvt_request_service(struct intel_gvt *gvt,
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "i915_drv.h"
|
||||
#include "gvt.h"
|
||||
#include "i915_pvinfo.h"
|
||||
#include "display/intel_display_types.h"
|
||||
|
||||
/* XXX FIXME i915 has changed PP_XXX definition */
|
||||
#define PCH_PP_STATUS _MMIO(0xc7200)
|
||||
@ -443,6 +444,254 @@ static int dpy_reg_mmio_read(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only PIPE_A is enabled in current vGPU display and PIPE_A is tied to
|
||||
* TRANSCODER_A in HW. DDI/PORT could be PORT_x depends on
|
||||
* setup_virtual_dp_monitor().
|
||||
* emulate_monitor_status_change() set up PLL for PORT_x as the initial enabled
|
||||
* DPLL. Later guest driver may setup a different DPLLx when setting mode.
|
||||
* So the correct sequence to find DP stream clock is:
|
||||
* Check TRANS_DDI_FUNC_CTL on TRANSCODER_A to get PORT_x.
|
||||
* Check correct PLLx for PORT_x to get PLL frequency and DP bitrate.
|
||||
* Then Refresh rate then can be calculated based on follow equations:
|
||||
* Pixel clock = h_total * v_total * refresh_rate
|
||||
* stream clock = Pixel clock
|
||||
* ls_clk = DP bitrate
|
||||
* Link M/N = strm_clk / ls_clk
|
||||
*/
|
||||
|
||||
static u32 bdw_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port)
|
||||
{
|
||||
u32 dp_br = 0;
|
||||
u32 ddi_pll_sel = vgpu_vreg_t(vgpu, PORT_CLK_SEL(port));
|
||||
|
||||
switch (ddi_pll_sel) {
|
||||
case PORT_CLK_SEL_LCPLL_2700:
|
||||
dp_br = 270000 * 2;
|
||||
break;
|
||||
case PORT_CLK_SEL_LCPLL_1350:
|
||||
dp_br = 135000 * 2;
|
||||
break;
|
||||
case PORT_CLK_SEL_LCPLL_810:
|
||||
dp_br = 81000 * 2;
|
||||
break;
|
||||
case PORT_CLK_SEL_SPLL:
|
||||
{
|
||||
switch (vgpu_vreg_t(vgpu, SPLL_CTL) & SPLL_FREQ_MASK) {
|
||||
case SPLL_FREQ_810MHz:
|
||||
dp_br = 81000 * 2;
|
||||
break;
|
||||
case SPLL_FREQ_1350MHz:
|
||||
dp_br = 135000 * 2;
|
||||
break;
|
||||
case SPLL_FREQ_2700MHz:
|
||||
dp_br = 270000 * 2;
|
||||
break;
|
||||
default:
|
||||
gvt_dbg_dpy("vgpu-%d PORT_%c can't get freq from SPLL 0x%08x\n",
|
||||
vgpu->id, port_name(port), vgpu_vreg_t(vgpu, SPLL_CTL));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PORT_CLK_SEL_WRPLL1:
|
||||
case PORT_CLK_SEL_WRPLL2:
|
||||
{
|
||||
u32 wrpll_ctl;
|
||||
int refclk, n, p, r;
|
||||
|
||||
if (ddi_pll_sel == PORT_CLK_SEL_WRPLL1)
|
||||
wrpll_ctl = vgpu_vreg_t(vgpu, WRPLL_CTL(DPLL_ID_WRPLL1));
|
||||
else
|
||||
wrpll_ctl = vgpu_vreg_t(vgpu, WRPLL_CTL(DPLL_ID_WRPLL2));
|
||||
|
||||
switch (wrpll_ctl & WRPLL_REF_MASK) {
|
||||
case WRPLL_REF_PCH_SSC:
|
||||
refclk = vgpu->gvt->gt->i915->dpll.ref_clks.ssc;
|
||||
break;
|
||||
case WRPLL_REF_LCPLL:
|
||||
refclk = 2700000;
|
||||
break;
|
||||
default:
|
||||
gvt_dbg_dpy("vgpu-%d PORT_%c WRPLL can't get refclk 0x%08x\n",
|
||||
vgpu->id, port_name(port), wrpll_ctl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = wrpll_ctl & WRPLL_DIVIDER_REF_MASK;
|
||||
p = (wrpll_ctl & WRPLL_DIVIDER_POST_MASK) >> WRPLL_DIVIDER_POST_SHIFT;
|
||||
n = (wrpll_ctl & WRPLL_DIVIDER_FB_MASK) >> WRPLL_DIVIDER_FB_SHIFT;
|
||||
|
||||
dp_br = (refclk * n / 10) / (p * r) * 2;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gvt_dbg_dpy("vgpu-%d PORT_%c has invalid clock select 0x%08x\n",
|
||||
vgpu->id, port_name(port), vgpu_vreg_t(vgpu, PORT_CLK_SEL(port)));
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return dp_br;
|
||||
}
|
||||
|
||||
static u32 bxt_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port)
|
||||
{
|
||||
u32 dp_br = 0;
|
||||
int refclk = vgpu->gvt->gt->i915->dpll.ref_clks.nssc;
|
||||
enum dpio_phy phy = DPIO_PHY0;
|
||||
enum dpio_channel ch = DPIO_CH0;
|
||||
struct dpll clock = {0};
|
||||
u32 temp;
|
||||
|
||||
/* Port to PHY mapping is fixed, see bxt_ddi_phy_info{} */
|
||||
switch (port) {
|
||||
case PORT_A:
|
||||
phy = DPIO_PHY1;
|
||||
ch = DPIO_CH0;
|
||||
break;
|
||||
case PORT_B:
|
||||
phy = DPIO_PHY0;
|
||||
ch = DPIO_CH0;
|
||||
break;
|
||||
case PORT_C:
|
||||
phy = DPIO_PHY0;
|
||||
ch = DPIO_CH1;
|
||||
break;
|
||||
default:
|
||||
gvt_dbg_dpy("vgpu-%d no PHY for PORT_%c\n", vgpu->id, port_name(port));
|
||||
goto out;
|
||||
}
|
||||
|
||||
temp = vgpu_vreg_t(vgpu, BXT_PORT_PLL_ENABLE(port));
|
||||
if (!(temp & PORT_PLL_ENABLE) || !(temp & PORT_PLL_LOCK)) {
|
||||
gvt_dbg_dpy("vgpu-%d PORT_%c PLL_ENABLE 0x%08x isn't enabled or locked\n",
|
||||
vgpu->id, port_name(port), temp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
clock.m1 = 2;
|
||||
clock.m2 = (vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 0)) & PORT_PLL_M2_MASK) << 22;
|
||||
if (vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 3)) & PORT_PLL_M2_FRAC_ENABLE)
|
||||
clock.m2 |= vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 2)) & PORT_PLL_M2_FRAC_MASK;
|
||||
clock.n = (vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 1)) & PORT_PLL_N_MASK) >> PORT_PLL_N_SHIFT;
|
||||
clock.p1 = (vgpu_vreg_t(vgpu, BXT_PORT_PLL_EBB_0(phy, ch)) & PORT_PLL_P1_MASK) >> PORT_PLL_P1_SHIFT;
|
||||
clock.p2 = (vgpu_vreg_t(vgpu, BXT_PORT_PLL_EBB_0(phy, ch)) & PORT_PLL_P2_MASK) >> PORT_PLL_P2_SHIFT;
|
||||
clock.m = clock.m1 * clock.m2;
|
||||
clock.p = clock.p1 * clock.p2;
|
||||
|
||||
if (clock.n == 0 || clock.p == 0) {
|
||||
gvt_dbg_dpy("vgpu-%d PORT_%c PLL has invalid divider\n", vgpu->id, port_name(port));
|
||||
goto out;
|
||||
}
|
||||
|
||||
clock.vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, clock.m), clock.n << 22);
|
||||
clock.dot = DIV_ROUND_CLOSEST(clock.vco, clock.p);
|
||||
|
||||
dp_br = clock.dot / 5;
|
||||
|
||||
out:
|
||||
return dp_br;
|
||||
}
|
||||
|
||||
static u32 skl_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port)
|
||||
{
|
||||
u32 dp_br = 0;
|
||||
enum intel_dpll_id dpll_id = DPLL_ID_SKL_DPLL0;
|
||||
|
||||
/* Find the enabled DPLL for the DDI/PORT */
|
||||
if (!(vgpu_vreg_t(vgpu, DPLL_CTRL2) & DPLL_CTRL2_DDI_CLK_OFF(port)) &&
|
||||
(vgpu_vreg_t(vgpu, DPLL_CTRL2) & DPLL_CTRL2_DDI_SEL_OVERRIDE(port))) {
|
||||
dpll_id += (vgpu_vreg_t(vgpu, DPLL_CTRL2) &
|
||||
DPLL_CTRL2_DDI_CLK_SEL_MASK(port)) >>
|
||||
DPLL_CTRL2_DDI_CLK_SEL_SHIFT(port);
|
||||
} else {
|
||||
gvt_dbg_dpy("vgpu-%d DPLL for PORT_%c isn't turned on\n",
|
||||
vgpu->id, port_name(port));
|
||||
return dp_br;
|
||||
}
|
||||
|
||||
/* Find PLL output frequency from correct DPLL, and get bir rate */
|
||||
switch ((vgpu_vreg_t(vgpu, DPLL_CTRL1) &
|
||||
DPLL_CTRL1_LINK_RATE_MASK(dpll_id)) >>
|
||||
DPLL_CTRL1_LINK_RATE_SHIFT(dpll_id)) {
|
||||
case DPLL_CTRL1_LINK_RATE_810:
|
||||
dp_br = 81000 * 2;
|
||||
break;
|
||||
case DPLL_CTRL1_LINK_RATE_1080:
|
||||
dp_br = 108000 * 2;
|
||||
break;
|
||||
case DPLL_CTRL1_LINK_RATE_1350:
|
||||
dp_br = 135000 * 2;
|
||||
break;
|
||||
case DPLL_CTRL1_LINK_RATE_1620:
|
||||
dp_br = 162000 * 2;
|
||||
break;
|
||||
case DPLL_CTRL1_LINK_RATE_2160:
|
||||
dp_br = 216000 * 2;
|
||||
break;
|
||||
case DPLL_CTRL1_LINK_RATE_2700:
|
||||
dp_br = 270000 * 2;
|
||||
break;
|
||||
default:
|
||||
dp_br = 0;
|
||||
gvt_dbg_dpy("vgpu-%d PORT_%c fail to get DPLL-%d freq\n",
|
||||
vgpu->id, port_name(port), dpll_id);
|
||||
}
|
||||
|
||||
return dp_br;
|
||||
}
|
||||
|
||||
static void vgpu_update_refresh_rate(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = vgpu->gvt->gt->i915;
|
||||
enum port port;
|
||||
u32 dp_br, link_m, link_n, htotal, vtotal;
|
||||
|
||||
/* Find DDI/PORT assigned to TRANSCODER_A, expect B or D */
|
||||
port = (vgpu_vreg_t(vgpu, TRANS_DDI_FUNC_CTL(TRANSCODER_A)) &
|
||||
TRANS_DDI_PORT_MASK) >> TRANS_DDI_PORT_SHIFT;
|
||||
if (port != PORT_B && port != PORT_D) {
|
||||
gvt_dbg_dpy("vgpu-%d unsupported PORT_%c\n", vgpu->id, port_name(port));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate DP bitrate from PLL */
|
||||
if (IS_BROADWELL(dev_priv))
|
||||
dp_br = bdw_vgpu_get_dp_bitrate(vgpu, port);
|
||||
else if (IS_BROXTON(dev_priv))
|
||||
dp_br = bxt_vgpu_get_dp_bitrate(vgpu, port);
|
||||
else
|
||||
dp_br = skl_vgpu_get_dp_bitrate(vgpu, port);
|
||||
|
||||
/* Get DP link symbol clock M/N */
|
||||
link_m = vgpu_vreg_t(vgpu, PIPE_LINK_M1(TRANSCODER_A));
|
||||
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;
|
||||
|
||||
if (dp_br && link_n && htotal && vtotal) {
|
||||
u64 pixel_clk = 0;
|
||||
u32 new_rate = 0;
|
||||
u32 *old_rate = &(intel_vgpu_port(vgpu, vgpu->display.port_num)->vrefresh_k);
|
||||
|
||||
/* Calcuate pixel clock by (ls_clk * M / N) */
|
||||
pixel_clk = div_u64(mul_u32_u32(link_m, dp_br), link_n);
|
||||
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));
|
||||
|
||||
if (*old_rate != new_rate)
|
||||
*old_rate = new_rate;
|
||||
|
||||
gvt_dbg_dpy("vgpu-%d PIPE_%c refresh rate updated to %d\n",
|
||||
vgpu->id, pipe_name(PIPE_A), new_rate);
|
||||
}
|
||||
}
|
||||
|
||||
static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
void *p_data, unsigned int bytes)
|
||||
{
|
||||
@ -451,14 +700,14 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
write_vreg(vgpu, offset, p_data, bytes);
|
||||
data = vgpu_vreg(vgpu, offset);
|
||||
|
||||
if (data & PIPECONF_ENABLE)
|
||||
if (data & PIPECONF_ENABLE) {
|
||||
vgpu_vreg(vgpu, offset) |= I965_PIPECONF_ACTIVE;
|
||||
else
|
||||
vgpu_update_refresh_rate(vgpu);
|
||||
vgpu_update_vblank_emulation(vgpu, true);
|
||||
} else {
|
||||
vgpu_vreg(vgpu, offset) &= ~I965_PIPECONF_ACTIVE;
|
||||
/* vgpu_lock already hold by emulate mmio r/w */
|
||||
mutex_unlock(&vgpu->vgpu_lock);
|
||||
intel_gvt_check_vblank_emulation(vgpu->gvt);
|
||||
mutex_lock(&vgpu->vgpu_lock);
|
||||
vgpu_update_vblank_emulation(vgpu, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -647,38 +647,6 @@ static void init_events(
|
||||
}
|
||||
}
|
||||
|
||||
static enum hrtimer_restart vblank_timer_fn(struct hrtimer *data)
|
||||
{
|
||||
struct intel_gvt_vblank_timer *vblank_timer;
|
||||
struct intel_gvt_irq *irq;
|
||||
struct intel_gvt *gvt;
|
||||
|
||||
vblank_timer = container_of(data, struct intel_gvt_vblank_timer, timer);
|
||||
irq = container_of(vblank_timer, struct intel_gvt_irq, vblank_timer);
|
||||
gvt = container_of(irq, struct intel_gvt, irq);
|
||||
|
||||
intel_gvt_request_service(gvt, INTEL_GVT_REQUEST_EMULATE_VBLANK);
|
||||
hrtimer_add_expires_ns(&vblank_timer->timer, vblank_timer->period);
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_gvt_clean_irq - clean up GVT-g IRQ emulation subsystem
|
||||
* @gvt: a GVT device
|
||||
*
|
||||
* This function is called at driver unloading stage, to clean up GVT-g IRQ
|
||||
* emulation subsystem.
|
||||
*
|
||||
*/
|
||||
void intel_gvt_clean_irq(struct intel_gvt *gvt)
|
||||
{
|
||||
struct intel_gvt_irq *irq = &gvt->irq;
|
||||
|
||||
hrtimer_cancel(&irq->vblank_timer.timer);
|
||||
}
|
||||
|
||||
#define VBLANK_TIMER_PERIOD 16000000
|
||||
|
||||
/**
|
||||
* intel_gvt_init_irq - initialize GVT-g IRQ emulation subsystem
|
||||
* @gvt: a GVT device
|
||||
@ -692,7 +660,6 @@ void intel_gvt_clean_irq(struct intel_gvt *gvt)
|
||||
int intel_gvt_init_irq(struct intel_gvt *gvt)
|
||||
{
|
||||
struct intel_gvt_irq *irq = &gvt->irq;
|
||||
struct intel_gvt_vblank_timer *vblank_timer = &irq->vblank_timer;
|
||||
|
||||
gvt_dbg_core("init irq framework\n");
|
||||
|
||||
@ -707,9 +674,5 @@ int intel_gvt_init_irq(struct intel_gvt *gvt)
|
||||
|
||||
init_irq_map(irq);
|
||||
|
||||
hrtimer_init(&vblank_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
vblank_timer->timer.function = vblank_timer_fn;
|
||||
vblank_timer->period = VBLANK_TIMER_PERIOD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -201,11 +201,6 @@ struct intel_gvt_irq_map {
|
||||
u32 down_irq_bitmask;
|
||||
};
|
||||
|
||||
struct intel_gvt_vblank_timer {
|
||||
struct hrtimer timer;
|
||||
u64 period;
|
||||
};
|
||||
|
||||
/* structure containing device specific IRQ state */
|
||||
struct intel_gvt_irq {
|
||||
struct intel_gvt_irq_ops *ops;
|
||||
@ -214,11 +209,9 @@ struct intel_gvt_irq {
|
||||
struct intel_gvt_event_info events[INTEL_GVT_EVENT_MAX];
|
||||
DECLARE_BITMAP(pending_events, INTEL_GVT_EVENT_MAX);
|
||||
struct intel_gvt_irq_map *irq_map;
|
||||
struct intel_gvt_vblank_timer vblank_timer;
|
||||
};
|
||||
|
||||
int intel_gvt_init_irq(struct intel_gvt *gvt);
|
||||
void intel_gvt_clean_irq(struct intel_gvt *gvt);
|
||||
|
||||
void intel_vgpu_trigger_virtual_event(struct intel_vgpu *vgpu,
|
||||
enum intel_gvt_event_type event);
|
||||
|
@ -300,8 +300,6 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu)
|
||||
mutex_unlock(&vgpu->vgpu_lock);
|
||||
|
||||
mutex_lock(&gvt->lock);
|
||||
if (idr_is_empty(&gvt->vgpu_idr))
|
||||
intel_gvt_clean_irq(gvt);
|
||||
intel_gvt_update_vgpu_types(gvt);
|
||||
mutex_unlock(&gvt->lock);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user