Merge tag 'drm-intel-next-2017-05-29' of git://anongit.freedesktop.org/git/drm-intel into drm-next

More stuff for 4.13:

- skl+ wm fixes from Mahesh Kumar
- some refactor and tests for i915_sw_fence (Chris)
- tune execlist/scheduler code (Chris)
- g4x,g33 gpu reset improvements (Chris, Mika)
- guc code cleanup (Michal Wajdeczko, Michał Winiarski)
- dp aux backlight improvements (Puthikorn Voravootivat)
- buffer based guc/host communication (Michal Wajdeczko)

* tag 'drm-intel-next-2017-05-29' of git://anongit.freedesktop.org/git/drm-intel: (253 commits)
  drm/i915: Update DRIVER_DATE to 20170529
  drm/i915: Keep the forcewake timer alive for 1ms past the most recent use
  drm/i915/guc: capture GuC logs if FW fails to load
  drm/i915/guc: Introduce buffer based cmd transport
  drm/i915/guc: Disable send function on fini
  drm: Add definition for eDP backlight frequency
  drm/i915: Drop AUX backlight enable check for backlight control
  drm/i915: Consolidate #ifdef CONFIG_INTEL_IOMMU
  drm/i915: Only GGTT vma may be pinned and prevent shrinking
  drm/i915: Serialize GTT/Aperture accesses on BXT
  drm/i915: Convert i915_gem_object_ops->flags values to use BIT()
  drm/i915/selftests: Silence compiler warning in igt_ctx_exec
  drm/i915/guc: Skip port assign on first iteration of GuC dequeue
  drm/i915: Remove misleading comment in request_alloc
  drm/i915/g33: Improve reset reliability
  Revert "drm/i915: Restore lost "Initialized i915" welcome message"
  drm/i915/huc: Update GLK HuC version
  drm/i915: Check for allocation failure
  drm/i915/guc: Remove action status and statistics from debugfs
  drm/i915/g4x: Improve gpu reset reliability
  ...
This commit is contained in:
Dave Airlie 2017-05-30 15:25:28 +10:00
commit a82256bc02
98 changed files with 7958 additions and 3123 deletions

View File

@ -61,6 +61,18 @@ config DRM_I915_SW_FENCE_DEBUG_OBJECTS
If in doubt, say "N".
config DRM_I915_SW_FENCE_CHECK_DAG
bool "Enable additional driver debugging for detecting dependency cycles"
depends on DRM_I915
default n
help
Choose this option to turn on extra driver debugging that may affect
performance but will catch some internal issues.
Recommended for driver developers only.
If in doubt, say "N".
config DRM_I915_SELFTEST
bool "Enable selftests upon driver load"
depends on DRM_I915

View File

@ -16,6 +16,7 @@ i915-y := i915_drv.o \
i915_params.o \
i915_pci.o \
i915_suspend.o \
i915_syncmap.o \
i915_sw_fence.o \
i915_sysfs.o \
intel_csr.o \
@ -57,6 +58,7 @@ i915-y += i915_cmd_parser.o \
# general-purpose microcontroller (GuC) support
i915-y += intel_uc.o \
intel_guc_ct.o \
intel_guc_log.o \
intel_guc_loader.o \
intel_huc.o \

View File

@ -280,10 +280,10 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
(0 << CH7017_PHASE_DETECTOR_SHIFT);
} else {
outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
lvds_pll_feedback_div =
CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
(3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
lvds_pll_feedback_div = 35;
lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
(0 << CH7017_PHASE_DETECTOR_SHIFT);
if (1) { /* XXX: dual channel panel detection. Assume yes for now. */

View File

@ -69,8 +69,7 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload)
gvt_dbg_sched("ring id %d workload lrca %x", ring_id,
workload->ctx_desc.lrca);
context_page_num = intel_lr_context_size(
gvt->dev_priv->engine[ring_id]);
context_page_num = gvt->dev_priv->engine[ring_id]->context_size;
context_page_num = context_page_num >> PAGE_SHIFT;
@ -181,6 +180,7 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
struct intel_engine_cs *engine = dev_priv->engine[ring_id];
struct drm_i915_gem_request *rq;
struct intel_vgpu *vgpu = workload->vgpu;
struct intel_ring *ring;
int ret;
gvt_dbg_sched("ring id %d prepare to dispatch workload %p\n",
@ -199,8 +199,9 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
* shadow_ctx pages invalid. So gvt need to pin itself. After update
* the guest context, gvt can unpin the shadow_ctx safely.
*/
ret = engine->context_pin(engine, shadow_ctx);
if (ret) {
ring = engine->context_pin(engine, shadow_ctx);
if (IS_ERR(ring)) {
ret = PTR_ERR(ring);
gvt_vgpu_err("fail to pin shadow context\n");
workload->status = ret;
mutex_unlock(&dev_priv->drm.struct_mutex);
@ -330,8 +331,7 @@ static void update_guest_context(struct intel_vgpu_workload *workload)
gvt_dbg_sched("ring id %d workload lrca %x\n", ring_id,
workload->ctx_desc.lrca);
context_page_num = intel_lr_context_size(
gvt->dev_priv->engine[ring_id]);
context_page_num = gvt->dev_priv->engine[ring_id]->context_size;
context_page_num = context_page_num >> PAGE_SHIFT;

View File

@ -1166,8 +1166,8 @@ static bool check_cmd(const struct intel_engine_cs *engine,
find_reg(engine, is_master, reg_addr);
if (!reg) {
DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (exec_id=%d)\n",
reg_addr, *cmd, engine->exec_id);
DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (%s)\n",
reg_addr, *cmd, engine->name);
return false;
}
@ -1222,11 +1222,11 @@ static bool check_cmd(const struct intel_engine_cs *engine,
desc->bits[i].mask;
if (dword != desc->bits[i].expected) {
DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (exec_id=%d)\n",
DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (%s)\n",
*cmd,
desc->bits[i].mask,
desc->bits[i].expected,
dword, engine->exec_id);
dword, engine->name);
return false;
}
}
@ -1284,7 +1284,7 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
if (*cmd == MI_BATCH_BUFFER_END) {
if (needs_clflush_after) {
void *ptr = ptr_mask_bits(shadow_batch_obj->mm.mapping);
void *ptr = page_mask_bits(shadow_batch_obj->mm.mapping);
drm_clflush_virt_range(ptr,
(void *)(cmd + 1) - ptr);
}

View File

@ -2482,8 +2482,6 @@ static void i915_guc_client_info(struct seq_file *m,
client->wq_size, client->wq_offset, client->wq_tail);
seq_printf(m, "\tWork queue full: %u\n", client->no_wq_space);
seq_printf(m, "\tFailed doorbell: %u\n", client->b_fail);
seq_printf(m, "\tLast submission result: %d\n", client->retcode);
for_each_engine(engine, dev_priv, id) {
u64 submissions = client->submissions[id];
@ -2494,42 +2492,34 @@ static void i915_guc_client_info(struct seq_file *m,
seq_printf(m, "\tTotal: %llu\n", tot);
}
static int i915_guc_info(struct seq_file *m, void *data)
static bool check_guc_submission(struct seq_file *m)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
const struct intel_guc *guc = &dev_priv->guc;
struct intel_engine_cs *engine;
enum intel_engine_id id;
u64 total;
if (!guc->execbuf_client) {
seq_printf(m, "GuC submission %s\n",
HAS_GUC_SCHED(dev_priv) ?
"disabled" :
"not supported");
return 0;
return false;
}
return true;
}
static int i915_guc_info(struct seq_file *m, void *data)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
const struct intel_guc *guc = &dev_priv->guc;
if (!check_guc_submission(m))
return 0;
seq_printf(m, "Doorbell map:\n");
seq_printf(m, "\t%*pb\n", GUC_NUM_DOORBELLS, guc->doorbell_bitmap);
seq_printf(m, "Doorbell next cacheline: 0x%x\n\n", guc->db_cacheline);
seq_printf(m, "GuC total action count: %llu\n", guc->action_count);
seq_printf(m, "GuC action failure count: %u\n", guc->action_fail);
seq_printf(m, "GuC last action command: 0x%x\n", guc->action_cmd);
seq_printf(m, "GuC last action status: 0x%x\n", guc->action_status);
seq_printf(m, "GuC last action error code: %d\n", guc->action_err);
total = 0;
seq_printf(m, "\nGuC submissions:\n");
for_each_engine(engine, dev_priv, id) {
u64 submissions = guc->submissions[id];
total += submissions;
seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x\n",
engine->name, submissions, guc->last_seqno[id]);
}
seq_printf(m, "\t%s: %llu\n", "Total", total);
seq_printf(m, "\nGuC execbuf client @ %p:\n", guc->execbuf_client);
i915_guc_client_info(m, dev_priv, guc->execbuf_client);
@ -2540,36 +2530,99 @@ static int i915_guc_info(struct seq_file *m, void *data)
return 0;
}
static int i915_guc_log_dump(struct seq_file *m, void *data)
static int i915_guc_stage_pool(struct seq_file *m, void *data)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
struct drm_i915_gem_object *obj;
int i = 0, pg;
const struct intel_guc *guc = &dev_priv->guc;
struct guc_stage_desc *desc = guc->stage_desc_pool_vaddr;
struct i915_guc_client *client = guc->execbuf_client;
unsigned int tmp;
int index;
if (!dev_priv->guc.log.vma)
if (!check_guc_submission(m))
return 0;
obj = dev_priv->guc.log.vma->obj;
for (pg = 0; pg < obj->base.size / PAGE_SIZE; pg++) {
u32 *log = kmap_atomic(i915_gem_object_get_page(obj, pg));
for (index = 0; index < GUC_MAX_STAGE_DESCRIPTORS; index++, desc++) {
struct intel_engine_cs *engine;
for (i = 0; i < PAGE_SIZE / sizeof(u32); i += 4)
seq_printf(m, "0x%08x 0x%08x 0x%08x 0x%08x\n",
*(log + i), *(log + i + 1),
*(log + i + 2), *(log + i + 3));
if (!(desc->attribute & GUC_STAGE_DESC_ATTR_ACTIVE))
continue;
kunmap_atomic(log);
seq_printf(m, "GuC stage descriptor %u:\n", index);
seq_printf(m, "\tIndex: %u\n", desc->stage_id);
seq_printf(m, "\tAttribute: 0x%x\n", desc->attribute);
seq_printf(m, "\tPriority: %d\n", desc->priority);
seq_printf(m, "\tDoorbell id: %d\n", desc->db_id);
seq_printf(m, "\tEngines used: 0x%x\n",
desc->engines_used);
seq_printf(m, "\tDoorbell trigger phy: 0x%llx, cpu: 0x%llx, uK: 0x%x\n",
desc->db_trigger_phy,
desc->db_trigger_cpu,
desc->db_trigger_uk);
seq_printf(m, "\tProcess descriptor: 0x%x\n",
desc->process_desc);
seq_printf(m, "\tWorkqueue address: 0x%x, size: 0x%x\n",
desc->wq_addr, desc->wq_size);
seq_putc(m, '\n');
for_each_engine_masked(engine, dev_priv, client->engines, tmp) {
u32 guc_engine_id = engine->guc_id;
struct guc_execlist_context *lrc =
&desc->lrc[guc_engine_id];
seq_printf(m, "\t%s LRC:\n", engine->name);
seq_printf(m, "\t\tContext desc: 0x%x\n",
lrc->context_desc);
seq_printf(m, "\t\tContext id: 0x%x\n", lrc->context_id);
seq_printf(m, "\t\tLRCA: 0x%x\n", lrc->ring_lrca);
seq_printf(m, "\t\tRing begin: 0x%x\n", lrc->ring_begin);
seq_printf(m, "\t\tRing end: 0x%x\n", lrc->ring_end);
seq_putc(m, '\n');
}
}
return 0;
}
static int i915_guc_log_dump(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_i915_private *dev_priv = node_to_i915(node);
bool dump_load_err = !!node->info_ent->data;
struct drm_i915_gem_object *obj = NULL;
u32 *log;
int i = 0;
if (dump_load_err)
obj = dev_priv->guc.load_err_log;
else if (dev_priv->guc.log.vma)
obj = dev_priv->guc.log.vma->obj;
if (!obj)
return 0;
log = i915_gem_object_pin_map(obj, I915_MAP_WC);
if (IS_ERR(log)) {
DRM_DEBUG("Failed to pin object\n");
seq_puts(m, "(log data unaccessible)\n");
return PTR_ERR(log);
}
for (i = 0; i < obj->base.size / sizeof(u32); i += 4)
seq_printf(m, "0x%08x 0x%08x 0x%08x 0x%08x\n",
*(log + i), *(log + i + 1),
*(log + i + 2), *(log + i + 3));
seq_putc(m, '\n');
i915_gem_object_unpin_map(obj);
return 0;
}
static int i915_guc_log_control_get(void *data, u64 *val)
{
struct drm_device *dev = data;
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_i915_private *dev_priv = data;
if (!dev_priv->guc.log.vma)
return -EINVAL;
@ -2581,14 +2634,13 @@ static int i915_guc_log_control_get(void *data, u64 *val)
static int i915_guc_log_control_set(void *data, u64 val)
{
struct drm_device *dev = data;
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_i915_private *dev_priv = data;
int ret;
if (!dev_priv->guc.log.vma)
return -EINVAL;
ret = mutex_lock_interruptible(&dev->struct_mutex);
ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
if (ret)
return ret;
@ -2596,7 +2648,7 @@ static int i915_guc_log_control_set(void *data, u64 val)
ret = i915_guc_log_control(dev_priv, val);
intel_runtime_pm_put(dev_priv);
mutex_unlock(&dev->struct_mutex);
mutex_unlock(&dev_priv->drm.struct_mutex);
return ret;
}
@ -2855,7 +2907,8 @@ static int i915_dmc_info(struct seq_file *m, void *unused)
seq_printf(m, "version: %d.%d\n", CSR_VERSION_MAJOR(csr->version),
CSR_VERSION_MINOR(csr->version));
if (IS_SKYLAKE(dev_priv) && csr->version >= CSR_VERSION(1, 6)) {
if (IS_KABYLAKE(dev_priv) ||
(IS_SKYLAKE(dev_priv) && csr->version >= CSR_VERSION(1, 6))) {
seq_printf(m, "DC3 -> DC5 count: %d\n",
I915_READ(SKL_CSR_DC3_DC5_COUNT));
seq_printf(m, "DC5 -> DC6 count: %d\n",
@ -3043,36 +3096,6 @@ static void intel_connector_info(struct seq_file *m,
intel_seq_print_mode(m, 2, mode);
}
static bool cursor_active(struct drm_i915_private *dev_priv, int pipe)
{
u32 state;
if (IS_I845G(dev_priv) || IS_I865G(dev_priv))
state = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE;
else
state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
return state;
}
static bool cursor_position(struct drm_i915_private *dev_priv,
int pipe, int *x, int *y)
{
u32 pos;
pos = I915_READ(CURPOS(pipe));
*x = (pos >> CURSOR_X_SHIFT) & CURSOR_POS_MASK;
if (pos & (CURSOR_POS_SIGN << CURSOR_X_SHIFT))
*x = -*x;
*y = (pos >> CURSOR_Y_SHIFT) & CURSOR_POS_MASK;
if (pos & (CURSOR_POS_SIGN << CURSOR_Y_SHIFT))
*y = -*y;
return cursor_active(dev_priv, pipe);
}
static const char *plane_type(enum drm_plane_type type)
{
switch (type) {
@ -3194,9 +3217,7 @@ static int i915_display_info(struct seq_file *m, void *unused)
seq_printf(m, "CRTC info\n");
seq_printf(m, "---------\n");
for_each_intel_crtc(dev, crtc) {
bool active;
struct intel_crtc_state *pipe_config;
int x, y;
drm_modeset_lock(&crtc->base.mutex, NULL);
pipe_config = to_intel_crtc_state(crtc->base.state);
@ -3208,14 +3229,18 @@ static int i915_display_info(struct seq_file *m, void *unused)
yesno(pipe_config->dither), pipe_config->pipe_bpp);
if (pipe_config->base.active) {
struct intel_plane *cursor =
to_intel_plane(crtc->base.cursor);
intel_crtc_info(m, crtc);
active = cursor_position(dev_priv, crtc->pipe, &x, &y);
seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n",
yesno(crtc->cursor_base),
x, y, crtc->base.cursor->state->crtc_w,
crtc->base.cursor->state->crtc_h,
crtc->cursor_addr, yesno(active));
seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x\n",
yesno(cursor->base.state->visible),
cursor->base.state->crtc_x,
cursor->base.state->crtc_y,
cursor->base.state->crtc_w,
cursor->base.state->crtc_h,
cursor->cursor.base);
intel_scaler_info(m, crtc);
intel_plane_info(m, crtc);
}
@ -3316,7 +3341,7 @@ static int i915_engine_info(struct seq_file *m, void *unused)
if (i915.enable_execlists) {
u32 ptr, read, write;
struct rb_node *rb;
unsigned int idx;
seq_printf(m, "\tExeclist status: 0x%08x %08x\n",
I915_READ(RING_EXECLIST_STATUS_LO(engine)),
@ -3334,8 +3359,7 @@ static int i915_engine_info(struct seq_file *m, void *unused)
if (read > write)
write += GEN8_CSB_ENTRIES;
while (read < write) {
unsigned int idx = ++read % GEN8_CSB_ENTRIES;
idx = ++read % GEN8_CSB_ENTRIES;
seq_printf(m, "\tExeclist CSB[%d]: 0x%08x, context: %d\n",
idx,
I915_READ(RING_CONTEXT_STATUS_BUF_LO(engine, idx)),
@ -3343,28 +3367,30 @@ static int i915_engine_info(struct seq_file *m, void *unused)
}
rcu_read_lock();
rq = READ_ONCE(engine->execlist_port[0].request);
if (rq) {
seq_printf(m, "\t\tELSP[0] count=%d, ",
engine->execlist_port[0].count);
print_request(m, rq, "rq: ");
} else {
seq_printf(m, "\t\tELSP[0] idle\n");
}
rq = READ_ONCE(engine->execlist_port[1].request);
if (rq) {
seq_printf(m, "\t\tELSP[1] count=%d, ",
engine->execlist_port[1].count);
print_request(m, rq, "rq: ");
} else {
seq_printf(m, "\t\tELSP[1] idle\n");
for (idx = 0; idx < ARRAY_SIZE(engine->execlist_port); idx++) {
unsigned int count;
rq = port_unpack(&engine->execlist_port[idx],
&count);
if (rq) {
seq_printf(m, "\t\tELSP[%d] count=%d, ",
idx, count);
print_request(m, rq, "rq: ");
} else {
seq_printf(m, "\t\tELSP[%d] idle\n",
idx);
}
}
rcu_read_unlock();
spin_lock_irq(&engine->timeline->lock);
for (rb = engine->execlist_first; rb; rb = rb_next(rb)) {
rq = rb_entry(rb, typeof(*rq), priotree.node);
print_request(m, rq, "\t\tQ ");
for (rb = engine->execlist_first; rb; rb = rb_next(rb)){
struct i915_priolist *p =
rb_entry(rb, typeof(*p), node);
list_for_each_entry(rq, &p->requests,
priotree.link)
print_request(m, rq, "\t\tQ ");
}
spin_unlock_irq(&engine->timeline->lock);
} else if (INTEL_GEN(dev_priv) > 6) {
@ -3704,16 +3730,10 @@ static ssize_t i915_displayport_test_active_write(struct file *file,
if (len == 0)
return 0;
input_buffer = kmalloc(len + 1, GFP_KERNEL);
if (!input_buffer)
return -ENOMEM;
input_buffer = memdup_user_nul(ubuf, len);
if (IS_ERR(input_buffer))
return PTR_ERR(input_buffer);
if (copy_from_user(input_buffer, ubuf, len)) {
status = -EFAULT;
goto out;
}
input_buffer[len] = '\0';
DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len);
drm_connector_list_iter_begin(dev, &conn_iter);
@ -3739,7 +3759,6 @@ static ssize_t i915_displayport_test_active_write(struct file *file,
}
}
drm_connector_list_iter_end(&conn_iter);
out:
kfree(input_buffer);
if (status < 0)
return status;
@ -3900,6 +3919,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
num_levels = 3;
else if (IS_VALLEYVIEW(dev_priv))
num_levels = 1;
else if (IS_G4X(dev_priv))
num_levels = 3;
else
num_levels = ilk_wm_max_level(dev_priv) + 1;
@ -3912,8 +3933,10 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
* - WM1+ latency values in 0.5us units
* - latencies are in us on gen9/vlv/chv
*/
if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
IS_CHERRYVIEW(dev_priv))
if (INTEL_GEN(dev_priv) >= 9 ||
IS_VALLEYVIEW(dev_priv) ||
IS_CHERRYVIEW(dev_priv) ||
IS_G4X(dev_priv))
latency *= 10;
else if (level > 0)
latency *= 5;
@ -3974,7 +3997,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file)
{
struct drm_i915_private *dev_priv = inode->i_private;
if (INTEL_GEN(dev_priv) < 5)
if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
return -ENODEV;
return single_open(file, pri_wm_latency_show, dev_priv);
@ -4016,6 +4039,8 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
num_levels = 3;
else if (IS_VALLEYVIEW(dev_priv))
num_levels = 1;
else if (IS_G4X(dev_priv))
num_levels = 3;
else
num_levels = ilk_wm_max_level(dev_priv) + 1;
@ -4776,6 +4801,8 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_guc_info", i915_guc_info, 0},
{"i915_guc_load_status", i915_guc_load_status_info, 0},
{"i915_guc_log_dump", i915_guc_log_dump, 0},
{"i915_guc_load_err_log_dump", i915_guc_log_dump, 0, (void *)1},
{"i915_guc_stage_pool", i915_guc_stage_pool, 0},
{"i915_huc_load_status", i915_huc_load_status_info, 0},
{"i915_frequency_info", i915_frequency_info, 0},
{"i915_hangcheck_info", i915_hangcheck_info, 0},

View File

@ -350,6 +350,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_EXEC_SOFTPIN:
case I915_PARAM_HAS_EXEC_ASYNC:
case I915_PARAM_HAS_EXEC_FENCE:
case I915_PARAM_HAS_EXEC_CAPTURE:
/* For the time being all of these are always true;
* if some supported hardware does not have one of these
* features this value needs to be provided from
@ -834,10 +835,6 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
intel_uc_init_early(dev_priv);
i915_memcpy_init_early(dev_priv);
ret = intel_engines_init_early(dev_priv);
if (ret)
return ret;
ret = i915_workqueues_init(dev_priv);
if (ret < 0)
goto err_engines;
@ -855,7 +852,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
intel_init_audio_hooks(dev_priv);
ret = i915_gem_load_init(dev_priv);
if (ret < 0)
goto err_workqueues;
goto err_irq;
intel_display_crc_init(dev_priv);
@ -867,7 +864,8 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
return 0;
err_workqueues:
err_irq:
intel_irq_fini(dev_priv);
i915_workqueues_cleanup(dev_priv);
err_engines:
i915_engines_cleanup(dev_priv);
@ -882,6 +880,7 @@ static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv)
{
i915_perf_fini(dev_priv);
i915_gem_load_cleanup(dev_priv);
intel_irq_fini(dev_priv);
i915_workqueues_cleanup(dev_priv);
i915_engines_cleanup(dev_priv);
}
@ -947,14 +946,21 @@ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv)
ret = i915_mmio_setup(dev_priv);
if (ret < 0)
goto put_bridge;
goto err_bridge;
intel_uncore_init(dev_priv);
ret = intel_engines_init_mmio(dev_priv);
if (ret)
goto err_uncore;
i915_gem_init_mmio(dev_priv);
return 0;
put_bridge:
err_uncore:
intel_uncore_fini(dev_priv);
err_bridge:
pci_dev_put(dev_priv->bridge_dev);
return ret;
@ -1213,9 +1219,8 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
struct drm_i915_private *dev_priv;
int ret;
/* Enable nuclear pageflip on ILK+, except vlv/chv */
if (!i915.nuclear_pageflip &&
(match_info->gen < 5 || match_info->has_gmch_display))
/* Enable nuclear pageflip on ILK+ */
if (!i915.nuclear_pageflip && match_info->gen < 5)
driver.driver_features &= ~DRIVER_ATOMIC;
ret = -ENOMEM;
@ -1272,10 +1277,6 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_priv->ipc_enabled = false;
/* Everything is in place, we can now relax! */
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver.name, driver.major, driver.minor, driver.patchlevel,
driver.date, pci_name(pdev), dev_priv->drm.primary->index);
if (IS_ENABLED(CONFIG_DRM_I915_DEBUG))
DRM_INFO("DRM_I915_DEBUG enabled\n");
if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))

View File

@ -55,6 +55,7 @@
#include "i915_reg.h"
#include "i915_utils.h"
#include "intel_uncore.h"
#include "intel_bios.h"
#include "intel_dpll_mgr.h"
#include "intel_uc.h"
@ -79,8 +80,8 @@
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
#define DRIVER_DATE "20170403"
#define DRIVER_TIMESTAMP 1491198738
#define DRIVER_DATE "20170529"
#define DRIVER_TIMESTAMP 1496041258
/* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
* WARN_ON()) for hw state sanity checks to check for unexpected conditions
@ -114,6 +115,13 @@ typedef struct {
fp; \
})
static inline bool is_fixed16_zero(uint_fixed_16_16_t val)
{
if (val.val == 0)
return true;
return false;
}
static inline uint_fixed_16_16_t u32_to_fixed_16_16(uint32_t val)
{
uint_fixed_16_16_t fp;
@ -152,8 +160,39 @@ static inline uint_fixed_16_16_t max_fixed_16_16(uint_fixed_16_16_t max1,
return max;
}
static inline uint_fixed_16_16_t fixed_16_16_div_round_up(uint32_t val,
uint32_t d)
static inline uint32_t div_round_up_fixed16(uint_fixed_16_16_t val,
uint_fixed_16_16_t d)
{
return DIV_ROUND_UP(val.val, d.val);
}
static inline uint32_t mul_round_up_u32_fixed16(uint32_t val,
uint_fixed_16_16_t mul)
{
uint64_t intermediate_val;
uint32_t result;
intermediate_val = (uint64_t) val * mul.val;
intermediate_val = DIV_ROUND_UP_ULL(intermediate_val, 1 << 16);
WARN_ON(intermediate_val >> 32);
result = clamp_t(uint32_t, intermediate_val, 0, ~0);
return result;
}
static inline uint_fixed_16_16_t mul_fixed16(uint_fixed_16_16_t val,
uint_fixed_16_16_t mul)
{
uint64_t intermediate_val;
uint_fixed_16_16_t fp;
intermediate_val = (uint64_t) val.val * mul.val;
intermediate_val = intermediate_val >> 16;
WARN_ON(intermediate_val >> 32);
fp.val = clamp_t(uint32_t, intermediate_val, 0, ~0);
return fp;
}
static inline uint_fixed_16_16_t fixed_16_16_div(uint32_t val, uint32_t d)
{
uint_fixed_16_16_t fp, res;
@ -162,8 +201,7 @@ static inline uint_fixed_16_16_t fixed_16_16_div_round_up(uint32_t val,
return res;
}
static inline uint_fixed_16_16_t fixed_16_16_div_round_up_u64(uint32_t val,
uint32_t d)
static inline uint_fixed_16_16_t fixed_16_16_div_u64(uint32_t val, uint32_t d)
{
uint_fixed_16_16_t res;
uint64_t interm_val;
@ -176,6 +214,17 @@ static inline uint_fixed_16_16_t fixed_16_16_div_round_up_u64(uint32_t val,
return res;
}
static inline uint32_t div_round_up_u32_fixed16(uint32_t val,
uint_fixed_16_16_t d)
{
uint64_t interm_val;
interm_val = (uint64_t)val << 16;
interm_val = DIV_ROUND_UP_ULL(interm_val, d.val);
WARN_ON(interm_val >> 32);
return clamp_t(uint32_t, interm_val, 0, ~0);
}
static inline uint_fixed_16_16_t mul_u32_fixed_16_16(uint32_t val,
uint_fixed_16_16_t mul)
{
@ -676,116 +725,6 @@ struct drm_i915_display_funcs {
void (*load_luts)(struct drm_crtc_state *crtc_state);
};
enum forcewake_domain_id {
FW_DOMAIN_ID_RENDER = 0,
FW_DOMAIN_ID_BLITTER,
FW_DOMAIN_ID_MEDIA,
FW_DOMAIN_ID_COUNT
};
enum forcewake_domains {
FORCEWAKE_RENDER = BIT(FW_DOMAIN_ID_RENDER),
FORCEWAKE_BLITTER = BIT(FW_DOMAIN_ID_BLITTER),
FORCEWAKE_MEDIA = BIT(FW_DOMAIN_ID_MEDIA),
FORCEWAKE_ALL = (FORCEWAKE_RENDER |
FORCEWAKE_BLITTER |
FORCEWAKE_MEDIA)
};
#define FW_REG_READ (1)
#define FW_REG_WRITE (2)
enum decoupled_power_domain {
GEN9_DECOUPLED_PD_BLITTER = 0,
GEN9_DECOUPLED_PD_RENDER,
GEN9_DECOUPLED_PD_MEDIA,
GEN9_DECOUPLED_PD_ALL
};
enum decoupled_ops {
GEN9_DECOUPLED_OP_WRITE = 0,
GEN9_DECOUPLED_OP_READ
};
enum forcewake_domains
intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
i915_reg_t reg, unsigned int op);
struct intel_uncore_funcs {
void (*force_wake_get)(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void (*force_wake_put)(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
uint8_t (*mmio_readb)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
uint16_t (*mmio_readw)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
uint32_t (*mmio_readl)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
uint64_t (*mmio_readq)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
void (*mmio_writeb)(struct drm_i915_private *dev_priv,
i915_reg_t r, uint8_t val, bool trace);
void (*mmio_writew)(struct drm_i915_private *dev_priv,
i915_reg_t r, uint16_t val, bool trace);
void (*mmio_writel)(struct drm_i915_private *dev_priv,
i915_reg_t r, uint32_t val, bool trace);
};
struct intel_forcewake_range {
u32 start;
u32 end;
enum forcewake_domains domains;
};
struct intel_uncore {
spinlock_t lock; /** lock is also taken in irq contexts. */
const struct intel_forcewake_range *fw_domains_table;
unsigned int fw_domains_table_entries;
struct notifier_block pmic_bus_access_nb;
struct intel_uncore_funcs funcs;
unsigned fifo_count;
enum forcewake_domains fw_domains;
enum forcewake_domains fw_domains_active;
u32 fw_set;
u32 fw_clear;
u32 fw_reset;
struct intel_uncore_forcewake_domain {
enum forcewake_domain_id id;
enum forcewake_domains mask;
unsigned wake_count;
struct hrtimer timer;
i915_reg_t reg_set;
i915_reg_t reg_ack;
} fw_domain[FW_DOMAIN_ID_COUNT];
int unclaimed_mmio_check;
};
#define __mask_next_bit(mask) ({ \
int __idx = ffs(mask) - 1; \
mask &= ~BIT(__idx); \
__idx; \
})
/* Iterate over initialised fw domains */
#define for_each_fw_domain_masked(domain__, mask__, dev_priv__, tmp__) \
for (tmp__ = (mask__); \
tmp__ ? (domain__ = &(dev_priv__)->uncore.fw_domain[__mask_next_bit(tmp__)]), 1 : 0;)
#define for_each_fw_domain(domain__, dev_priv__, tmp__) \
for_each_fw_domain_masked(domain__, (dev_priv__)->uncore.fw_domains, dev_priv__, tmp__)
#define CSR_VERSION(major, minor) ((major) << 16 | (minor))
#define CSR_VERSION_MAJOR(version) ((version) >> 16)
#define CSR_VERSION_MINOR(version) ((version) & 0xffff)
@ -821,8 +760,8 @@ struct intel_csr {
func(has_gmbus_irq); \
func(has_gmch_display); \
func(has_guc); \
func(has_guc_ct); \
func(has_hotplug); \
func(has_hw_contexts); \
func(has_l3_dpf); \
func(has_llc); \
func(has_logical_ring_contexts); \
@ -1025,6 +964,9 @@ struct i915_gpu_state {
u32 *pages[0];
} *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
struct drm_i915_error_object **user_bo;
long user_bo_count;
struct drm_i915_error_object *wa_ctx;
struct drm_i915_error_request {
@ -1511,11 +1453,7 @@ struct i915_gem_mm {
/** LRU list of objects with fence regs on them. */
struct list_head fence_list;
/**
* Are we in a non-interruptible section of code like
* modesetting?
*/
bool interruptible;
u64 unordered_timeline;
/* the indicator for dispatch video commands on two BSD rings */
atomic_t bsd_engine_dispatch_index;
@ -1566,7 +1504,7 @@ struct i915_gpu_error {
*
* This is a counter which gets incremented when reset is triggered,
*
* Before the reset commences, the I915_RESET_IN_PROGRESS bit is set
* Before the reset commences, the I915_RESET_BACKOFF bit is set
* meaning that any waiters holding onto the struct_mutex should
* relinquish the lock immediately in order for the reset to start.
*
@ -1763,13 +1701,15 @@ struct ilk_wm_values {
enum intel_ddb_partitioning partitioning;
};
struct vlv_pipe_wm {
struct g4x_pipe_wm {
uint16_t plane[I915_MAX_PLANES];
uint16_t fbc;
};
struct vlv_sr_wm {
struct g4x_sr_wm {
uint16_t plane;
uint16_t cursor;
uint16_t fbc;
};
struct vlv_wm_ddl_values {
@ -1777,13 +1717,22 @@ struct vlv_wm_ddl_values {
};
struct vlv_wm_values {
struct vlv_pipe_wm pipe[3];
struct vlv_sr_wm sr;
struct g4x_pipe_wm pipe[3];
struct g4x_sr_wm sr;
struct vlv_wm_ddl_values ddl[3];
uint8_t level;
bool cxsr;
};
struct g4x_wm_values {
struct g4x_pipe_wm pipe[2];
struct g4x_sr_wm sr;
struct g4x_sr_wm hpll;
bool cxsr;
bool hpll_en;
bool fbc_en;
};
struct skl_ddb_entry {
uint16_t start, end; /* in number of blocks, 'end' is exclusive */
};
@ -2100,7 +2049,7 @@ struct i915_oa_ops {
size_t *offset);
/**
* @oa_buffer_is_empty: Check if OA buffer empty (false positives OK)
* @oa_buffer_check: Check for OA buffer data + update tail
*
* This is either called via fops or the poll check hrtimer (atomic
* ctx) without any locks taken.
@ -2113,7 +2062,7 @@ struct i915_oa_ops {
* here, which will be handled gracefully - likely resulting in an
* %EAGAIN error for userspace.
*/
bool (*oa_buffer_is_empty)(struct drm_i915_private *dev_priv);
bool (*oa_buffer_check)(struct drm_i915_private *dev_priv);
};
struct intel_cdclk_state {
@ -2127,6 +2076,7 @@ struct drm_i915_private {
struct kmem_cache *vmas;
struct kmem_cache *requests;
struct kmem_cache *dependencies;
struct kmem_cache *priorities;
const struct intel_device_info info;
@ -2362,7 +2312,6 @@ struct drm_i915_private {
*/
struct mutex av_mutex;
uint32_t hw_context_size;
struct list_head context_list;
u32 fdi_rx_config;
@ -2413,6 +2362,7 @@ struct drm_i915_private {
struct ilk_wm_values hw;
struct skl_wm_values skl_hw;
struct vlv_wm_values vlv;
struct g4x_wm_values g4x;
};
uint8_t max_level;
@ -2454,11 +2404,14 @@ struct drm_i915_private {
wait_queue_head_t poll_wq;
bool pollin;
/**
* For rate limiting any notifications of spurious
* invalid OA reports
*/
struct ratelimit_state spurious_report_rs;
bool periodic;
int period_exponent;
int timestamp_frequency;
int tail_margin;
int metrics_set;
@ -2472,6 +2425,70 @@ struct drm_i915_private {
u8 *vaddr;
int format;
int format_size;
/**
* Locks reads and writes to all head/tail state
*
* Consider: the head and tail pointer state
* needs to be read consistently from a hrtimer
* callback (atomic context) and read() fop
* (user context) with tail pointer updates
* happening in atomic context and head updates
* in user context and the (unlikely)
* possibility of read() errors needing to
* reset all head/tail state.
*
* Note: Contention or performance aren't
* currently a significant concern here
* considering the relatively low frequency of
* hrtimer callbacks (5ms period) and that
* reads typically only happen in response to a
* hrtimer event and likely complete before the
* next callback.
*
* Note: This lock is not held *while* reading
* and copying data to userspace so the value
* of head observed in htrimer callbacks won't
* represent any partial consumption of data.
*/
spinlock_t ptr_lock;
/**
* One 'aging' tail pointer and one 'aged'
* tail pointer ready to used for reading.
*
* Initial values of 0xffffffff are invalid
* and imply that an update is required
* (and should be ignored by an attempted
* read)
*/
struct {
u32 offset;
} tails[2];
/**
* Index for the aged tail ready to read()
* data up to.
*/
unsigned int aged_tail_idx;
/**
* A monotonic timestamp for when the current
* aging tail pointer was read; used to
* determine when it is old enough to trust.
*/
u64 aging_timestamp;
/**
* Although we can always read back the head
* pointer register, we prefer to avoid
* trusting the HW state, just to avoid any
* risk that some hardware condition could
* somehow bump the head pointer unpredictably
* and cause us to forward the wrong OA buffer
* data to userspace.
*/
u32 head;
} oa_buffer;
u32 gen7_latched_oastatus1;
@ -2870,7 +2887,6 @@ intel_info(const struct drm_i915_private *dev_priv)
#define HWS_NEEDS_PHYSICAL(dev_priv) ((dev_priv)->info.hws_needs_physical)
#define HAS_HW_CONTEXTS(dev_priv) ((dev_priv)->info.has_hw_contexts)
#define HAS_LOGICAL_RING_CONTEXTS(dev_priv) \
((dev_priv)->info.has_logical_ring_contexts)
#define USES_PPGTT(dev_priv) (i915.enable_ppgtt)
@ -2909,6 +2925,7 @@ intel_info(const struct drm_i915_private *dev_priv)
#define HAS_FW_BLC(dev_priv) (INTEL_GEN(dev_priv) > 2)
#define HAS_PIPE_CXSR(dev_priv) ((dev_priv)->info.has_pipe_cxsr)
#define HAS_FBC(dev_priv) ((dev_priv)->info.has_fbc)
#define HAS_CUR_FBC(dev_priv) (!HAS_GMCH_DISPLAY(dev_priv) && INTEL_INFO(dev_priv)->gen >= 7)
#define HAS_IPS(dev_priv) (IS_HSW_ULT(dev_priv) || IS_BROADWELL(dev_priv))
@ -2931,6 +2948,7 @@ intel_info(const struct drm_i915_private *dev_priv)
* properties, so we have separate macros to test them.
*/
#define HAS_GUC(dev_priv) ((dev_priv)->info.has_guc)
#define HAS_GUC_CT(dev_priv) ((dev_priv)->info.has_guc_ct)
#define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv))
#define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv))
#define HAS_HUC_UCODE(dev_priv) (HAS_GUC(dev_priv))
@ -2981,15 +2999,26 @@ intel_info(const struct drm_i915_private *dev_priv)
#include "i915_trace.h"
static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv)
static inline bool intel_vtd_active(void)
{
#ifdef CONFIG_INTEL_IOMMU
if (INTEL_GEN(dev_priv) >= 6 && intel_iommu_gfx_mapped)
if (intel_iommu_gfx_mapped)
return true;
#endif
return false;
}
static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv)
{
return INTEL_GEN(dev_priv) >= 6 && intel_vtd_active();
}
static inline bool
intel_ggtt_update_needs_vtd_wa(struct drm_i915_private *dev_priv)
{
return IS_BROXTON(dev_priv) && intel_vtd_active();
}
int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
int enable_ppgtt);
@ -3026,7 +3055,7 @@ extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on);
int intel_engines_init_early(struct drm_i915_private *dev_priv);
int intel_engines_init_mmio(struct drm_i915_private *dev_priv);
int intel_engines_init(struct drm_i915_private *dev_priv);
/* intel_hotplug.c */
@ -3063,43 +3092,10 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
const char *fmt, ...);
extern void intel_irq_init(struct drm_i915_private *dev_priv);
extern void intel_irq_fini(struct drm_i915_private *dev_priv);
int intel_irq_install(struct drm_i915_private *dev_priv);
void intel_irq_uninstall(struct drm_i915_private *dev_priv);
extern void intel_uncore_sanitize(struct drm_i915_private *dev_priv);
extern void intel_uncore_init(struct drm_i915_private *dev_priv);
extern bool intel_uncore_unclaimed_mmio(struct drm_i915_private *dev_priv);
extern bool intel_uncore_arm_unclaimed_mmio_detection(struct drm_i915_private *dev_priv);
extern void intel_uncore_fini(struct drm_i915_private *dev_priv);
extern void intel_uncore_suspend(struct drm_i915_private *dev_priv);
extern void intel_uncore_resume_early(struct drm_i915_private *dev_priv);
const char *intel_uncore_forcewake_domain_to_str(const enum forcewake_domain_id id);
void intel_uncore_forcewake_get(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void intel_uncore_forcewake_put(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
/* Like above but the caller must manage the uncore.lock itself.
* Must be used with I915_READ_FW and friends.
*/
void intel_uncore_forcewake_get__locked(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
u64 intel_uncore_edram_size(struct drm_i915_private *dev_priv);
void assert_forcewakes_inactive(struct drm_i915_private *dev_priv);
int intel_wait_for_register(struct drm_i915_private *dev_priv,
i915_reg_t reg,
const u32 mask,
const u32 value,
const unsigned long timeout_ms);
int intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
i915_reg_t reg,
const u32 mask,
const u32 value,
const unsigned long timeout_ms);
static inline bool intel_gvt_active(struct drm_i915_private *dev_priv)
{
return dev_priv->gvt;
@ -3447,8 +3443,9 @@ int i915_gem_object_wait_priority(struct drm_i915_gem_object *obj,
#define I915_PRIORITY_DISPLAY I915_PRIORITY_MAX
int __must_check
i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj,
bool write);
i915_gem_object_set_to_wc_domain(struct drm_i915_gem_object *obj, bool write);
int __must_check
i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write);
int __must_check
i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write);
struct i915_vma * __must_check
@ -3711,8 +3708,8 @@ int intel_lpe_audio_init(struct drm_i915_private *dev_priv);
void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv);
void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv);
void intel_lpe_audio_notify(struct drm_i915_private *dev_priv,
void *eld, int port, int pipe, int tmds_clk_speed,
bool dp_output, int link_rate);
enum pipe pipe, enum port port,
const void *eld, int ls_clock, bool dp_output);
/* intel_i2c.c */
extern int intel_setup_gmbus(struct drm_i915_private *dev_priv);

View File

@ -46,8 +46,6 @@
#include <linux/dma-buf.h>
static void i915_gem_flush_free_objects(struct drm_i915_private *i915);
static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
static bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj)
{
@ -705,6 +703,61 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
args->size, &args->handle);
}
static inline enum fb_op_origin
fb_write_origin(struct drm_i915_gem_object *obj, unsigned int domain)
{
return (domain == I915_GEM_DOMAIN_GTT ?
obj->frontbuffer_ggtt_origin : ORIGIN_CPU);
}
static void
flush_write_domain(struct drm_i915_gem_object *obj, unsigned int flush_domains)
{
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
if (!(obj->base.write_domain & flush_domains))
return;
/* No actual flushing is required for the GTT write domain. Writes
* to it "immediately" go to main memory as far as we know, so there's
* no chipset flush. It also doesn't land in render cache.
*
* However, we do have to enforce the order so that all writes through
* the GTT land before any writes to the device, such as updates to
* the GATT itself.
*
* We also have to wait a bit for the writes to land from the GTT.
* An uncached read (i.e. mmio) seems to be ideal for the round-trip
* timing. This issue has only been observed when switching quickly
* between GTT writes and CPU reads from inside the kernel on recent hw,
* and it appears to only affect discrete GTT blocks (i.e. on LLC
* system agents we cannot reproduce this behaviour).
*/
wmb();
switch (obj->base.write_domain) {
case I915_GEM_DOMAIN_GTT:
if (INTEL_GEN(dev_priv) >= 6 && !HAS_LLC(dev_priv)) {
if (intel_runtime_pm_get_if_in_use(dev_priv)) {
spin_lock_irq(&dev_priv->uncore.lock);
POSTING_READ_FW(RING_ACTHD(dev_priv->engine[RCS]->mmio_base));
spin_unlock_irq(&dev_priv->uncore.lock);
intel_runtime_pm_put(dev_priv);
}
}
intel_fb_obj_flush(obj,
fb_write_origin(obj, I915_GEM_DOMAIN_GTT));
break;
case I915_GEM_DOMAIN_CPU:
i915_gem_clflush_object(obj, I915_CLFLUSH_SYNC);
break;
}
obj->base.write_domain = 0;
}
static inline int
__copy_to_user_swizzled(char __user *cpu_vaddr,
const char *gpu_vaddr, int gpu_offset,
@ -794,7 +847,7 @@ int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
goto out;
}
i915_gem_object_flush_gtt_write_domain(obj);
flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
/* If we're not in the cpu read domain, set ourself into the gtt
* read domain and manually flush cachelines (if required). This
@ -846,7 +899,7 @@ int i915_gem_obj_prepare_shmem_write(struct drm_i915_gem_object *obj,
goto out;
}
i915_gem_object_flush_gtt_write_domain(obj);
flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
/* If we're not in the cpu write domain, set ourself into the
* gtt write domain and manually flush cachelines (as required).
@ -1501,13 +1554,6 @@ err:
return ret;
}
static inline enum fb_op_origin
write_origin(struct drm_i915_gem_object *obj, unsigned domain)
{
return (domain == I915_GEM_DOMAIN_GTT ?
obj->frontbuffer_ggtt_origin : ORIGIN_CPU);
}
static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *i915;
@ -1591,10 +1637,12 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
if (err)
goto out_unpin;
if (read_domains & I915_GEM_DOMAIN_GTT)
err = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
if (read_domains & I915_GEM_DOMAIN_WC)
err = i915_gem_object_set_to_wc_domain(obj, write_domain);
else if (read_domains & I915_GEM_DOMAIN_GTT)
err = i915_gem_object_set_to_gtt_domain(obj, write_domain);
else
err = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0);
err = i915_gem_object_set_to_cpu_domain(obj, write_domain);
/* And bump the LRU for this access */
i915_gem_object_bump_inactive_ggtt(obj);
@ -1602,7 +1650,8 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&dev->struct_mutex);
if (write_domain != 0)
intel_fb_obj_invalidate(obj, write_origin(obj, write_domain));
intel_fb_obj_invalidate(obj,
fb_write_origin(obj, write_domain));
out_unpin:
i915_gem_object_unpin_pages(obj);
@ -1737,6 +1786,9 @@ static unsigned int tile_row_pages(struct drm_i915_gem_object *obj)
* into userspace. (This view is aligned and sized appropriately for
* fenced access.)
*
* 2 - Recognise WC as a separate cache domain so that we can flush the
* delayed writes via GTT before performing direct access via WC.
*
* Restrictions:
*
* * snoopable objects cannot be accessed via the GTT. It can cause machine
@ -1764,7 +1816,7 @@ static unsigned int tile_row_pages(struct drm_i915_gem_object *obj)
*/
int i915_gem_mmap_gtt_version(void)
{
return 1;
return 2;
}
static inline struct i915_ggtt_view
@ -2228,7 +2280,7 @@ void __i915_gem_object_put_pages(struct drm_i915_gem_object *obj,
if (obj->mm.mapping) {
void *ptr;
ptr = ptr_mask_bits(obj->mm.mapping);
ptr = page_mask_bits(obj->mm.mapping);
if (is_vmalloc_addr(ptr))
vunmap(ptr);
else
@ -2560,7 +2612,7 @@ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
}
GEM_BUG_ON(!obj->mm.pages);
ptr = ptr_unpack_bits(obj->mm.mapping, has_type);
ptr = page_unpack_bits(obj->mm.mapping, &has_type);
if (ptr && has_type != type) {
if (pinned) {
ret = -EBUSY;
@ -2582,7 +2634,7 @@ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
goto err_unpin;
}
obj->mm.mapping = ptr_pack_bits(ptr, type);
obj->mm.mapping = page_pack_bits(ptr, type);
}
out_unlock:
@ -2967,12 +3019,14 @@ static void engine_set_wedged(struct intel_engine_cs *engine)
*/
if (i915.enable_execlists) {
struct execlist_port *port = engine->execlist_port;
unsigned long flags;
unsigned int n;
spin_lock_irqsave(&engine->timeline->lock, flags);
i915_gem_request_put(engine->execlist_port[0].request);
i915_gem_request_put(engine->execlist_port[1].request);
for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++)
i915_gem_request_put(port_request(&port[n]));
memset(engine->execlist_port, 0, sizeof(engine->execlist_port));
engine->execlist_queue = RB_ROOT;
engine->execlist_first = NULL;
@ -3101,8 +3155,6 @@ i915_gem_idle_work_handler(struct work_struct *work)
struct drm_i915_private *dev_priv =
container_of(work, typeof(*dev_priv), gt.idle_work.work);
struct drm_device *dev = &dev_priv->drm;
struct intel_engine_cs *engine;
enum intel_engine_id id;
bool rearm_hangcheck;
if (!READ_ONCE(dev_priv->gt.awake))
@ -3140,10 +3192,8 @@ i915_gem_idle_work_handler(struct work_struct *work)
if (wait_for(intel_engines_are_idle(dev_priv), 10))
DRM_ERROR("Timeout waiting for engines to idle\n");
for_each_engine(engine, dev_priv, id) {
intel_engine_disarm_breadcrumbs(engine);
i915_gem_batch_pool_fini(&engine->batch_pool);
}
intel_engines_mark_idle(dev_priv);
i915_gem_timelines_mark_idle(dev_priv);
GEM_BUG_ON(!dev_priv->gt.awake);
dev_priv->gt.awake = false;
@ -3320,56 +3370,6 @@ int i915_gem_wait_for_idle(struct drm_i915_private *i915, unsigned int flags)
return ret;
}
/** Flushes the GTT write domain for the object if it's dirty. */
static void
i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
if (obj->base.write_domain != I915_GEM_DOMAIN_GTT)
return;
/* No actual flushing is required for the GTT write domain. Writes
* to it "immediately" go to main memory as far as we know, so there's
* no chipset flush. It also doesn't land in render cache.
*
* However, we do have to enforce the order so that all writes through
* the GTT land before any writes to the device, such as updates to
* the GATT itself.
*
* We also have to wait a bit for the writes to land from the GTT.
* An uncached read (i.e. mmio) seems to be ideal for the round-trip
* timing. This issue has only been observed when switching quickly
* between GTT writes and CPU reads from inside the kernel on recent hw,
* and it appears to only affect discrete GTT blocks (i.e. on LLC
* system agents we cannot reproduce this behaviour).
*/
wmb();
if (INTEL_GEN(dev_priv) >= 6 && !HAS_LLC(dev_priv)) {
if (intel_runtime_pm_get_if_in_use(dev_priv)) {
spin_lock_irq(&dev_priv->uncore.lock);
POSTING_READ_FW(RING_ACTHD(dev_priv->engine[RCS]->mmio_base));
spin_unlock_irq(&dev_priv->uncore.lock);
intel_runtime_pm_put(dev_priv);
}
}
intel_fb_obj_flush(obj, write_origin(obj, I915_GEM_DOMAIN_GTT));
obj->base.write_domain = 0;
}
/** Flushes the CPU write domain for the object if it's dirty. */
static void
i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj)
{
if (obj->base.write_domain != I915_GEM_DOMAIN_CPU)
return;
i915_gem_clflush_object(obj, I915_CLFLUSH_SYNC);
obj->base.write_domain = 0;
}
static void __i915_gem_object_flush_for_display(struct drm_i915_gem_object *obj)
{
if (obj->base.write_domain != I915_GEM_DOMAIN_CPU && !obj->cache_dirty)
@ -3389,6 +3389,69 @@ void i915_gem_object_flush_if_display(struct drm_i915_gem_object *obj)
mutex_unlock(&obj->base.dev->struct_mutex);
}
/**
* Moves a single object to the WC read, and possibly write domain.
* @obj: object to act on
* @write: ask for write access or read only
*
* This function returns when the move is complete, including waiting on
* flushes to occur.
*/
int
i915_gem_object_set_to_wc_domain(struct drm_i915_gem_object *obj, bool write)
{
int ret;
lockdep_assert_held(&obj->base.dev->struct_mutex);
ret = i915_gem_object_wait(obj,
I915_WAIT_INTERRUPTIBLE |
I915_WAIT_LOCKED |
(write ? I915_WAIT_ALL : 0),
MAX_SCHEDULE_TIMEOUT,
NULL);
if (ret)
return ret;
if (obj->base.write_domain == I915_GEM_DOMAIN_WC)
return 0;
/* Flush and acquire obj->pages so that we are coherent through
* direct access in memory with previous cached writes through
* shmemfs and that our cache domain tracking remains valid.
* For example, if the obj->filp was moved to swap without us
* being notified and releasing the pages, we would mistakenly
* continue to assume that the obj remained out of the CPU cached
* domain.
*/
ret = i915_gem_object_pin_pages(obj);
if (ret)
return ret;
flush_write_domain(obj, ~I915_GEM_DOMAIN_WC);
/* Serialise direct access to this object with the barriers for
* coherent writes from the GPU, by effectively invalidating the
* WC domain upon first access.
*/
if ((obj->base.read_domains & I915_GEM_DOMAIN_WC) == 0)
mb();
/* It should now be out of any other write domains, and we can update
* the domain values for our changes.
*/
GEM_BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_WC) != 0);
obj->base.read_domains |= I915_GEM_DOMAIN_WC;
if (write) {
obj->base.read_domains = I915_GEM_DOMAIN_WC;
obj->base.write_domain = I915_GEM_DOMAIN_WC;
obj->mm.dirty = true;
}
i915_gem_object_unpin_pages(obj);
return 0;
}
/**
* Moves a single object to the GTT read, and possibly write domain.
* @obj: object to act on
@ -3428,7 +3491,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
if (ret)
return ret;
i915_gem_object_flush_cpu_write_domain(obj);
flush_write_domain(obj, ~I915_GEM_DOMAIN_GTT);
/* Serialise direct access to this object with the barriers for
* coherent writes from the GPU, by effectively invalidating the
@ -3802,7 +3865,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
if (obj->base.write_domain == I915_GEM_DOMAIN_CPU)
return 0;
i915_gem_object_flush_gtt_write_domain(obj);
flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
/* Flush the CPU cache if it's still invalid. */
if ((obj->base.read_domains & I915_GEM_DOMAIN_CPU) == 0) {
@ -3996,7 +4059,7 @@ __busy_set_if_active(const struct dma_fence *fence,
if (i915_gem_request_completed(rq))
return 0;
return flag(rq->engine->exec_id);
return flag(rq->engine->uabi_id);
}
static __always_inline unsigned int
@ -4195,7 +4258,7 @@ i915_gem_object_create(struct drm_i915_private *dev_priv, u64 size)
* catch if we ever need to fix it. In the meantime, if you do spot
* such a local variable, please consider fixing!
*/
if (WARN_ON(size >> PAGE_SHIFT > INT_MAX))
if (size >> PAGE_SHIFT > INT_MAX)
return ERR_PTR(-E2BIG);
if (overflows_type(size, obj->base.size))
@ -4302,6 +4365,8 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
intel_runtime_pm_put(i915);
mutex_unlock(&i915->drm.struct_mutex);
cond_resched();
llist_for_each_entry_safe(obj, on, freed, freed) {
GEM_BUG_ON(obj->bind_count);
GEM_BUG_ON(atomic_read(&obj->frontbuffer_bits));
@ -4349,8 +4414,11 @@ static void __i915_gem_free_work(struct work_struct *work)
* unbound now.
*/
while ((freed = llist_del_all(&i915->mm.free_list)))
while ((freed = llist_del_all(&i915->mm.free_list))) {
__i915_gem_free_objects(i915, freed);
if (need_resched())
break;
}
}
static void __i915_gem_free_object_rcu(struct rcu_head *head)
@ -4415,10 +4483,9 @@ void i915_gem_sanitize(struct drm_i915_private *i915)
* try to take over. The only way to remove the earlier state
* is by resetting. However, resetting on earlier gen is tricky as
* it may impact the display and we are uncertain about the stability
* of the reset, so we only reset recent machines with logical
* context support (that must be reset to remove any stray contexts).
* of the reset, so this could be applied to even earlier gen.
*/
if (HAS_HW_CONTEXTS(i915)) {
if (INTEL_GEN(i915) >= 5) {
int reset = intel_gpu_reset(i915, ALL_ENGINES);
WARN_ON(reset && reset != -ENODEV);
}
@ -4661,11 +4728,9 @@ bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value)
if (value >= 0)
return value;
#ifdef CONFIG_INTEL_IOMMU
/* Enable semaphores on SNB when IO remapping is off */
if (INTEL_INFO(dev_priv)->gen == 6 && intel_iommu_gfx_mapped)
if (IS_GEN6(dev_priv) && intel_vtd_active())
return false;
#endif
return true;
}
@ -4676,7 +4741,7 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->drm.struct_mutex);
i915_gem_clflush_init(dev_priv);
dev_priv->mm.unordered_timeline = dma_fence_context_alloc(1);
if (!i915.enable_execlists) {
dev_priv->gt.resume = intel_legacy_submission_resume;
@ -4799,12 +4864,16 @@ i915_gem_load_init(struct drm_i915_private *dev_priv)
if (!dev_priv->dependencies)
goto err_requests;
dev_priv->priorities = KMEM_CACHE(i915_priolist, SLAB_HWCACHE_ALIGN);
if (!dev_priv->priorities)
goto err_dependencies;
mutex_lock(&dev_priv->drm.struct_mutex);
INIT_LIST_HEAD(&dev_priv->gt.timelines);
err = i915_gem_timeline_init__global(dev_priv);
mutex_unlock(&dev_priv->drm.struct_mutex);
if (err)
goto err_dependencies;
goto err_priorities;
INIT_LIST_HEAD(&dev_priv->context_list);
INIT_WORK(&dev_priv->mm.free_work, __i915_gem_free_work);
@ -4822,14 +4891,14 @@ i915_gem_load_init(struct drm_i915_private *dev_priv)
init_waitqueue_head(&dev_priv->pending_flip_queue);
dev_priv->mm.interruptible = true;
atomic_set(&dev_priv->mm.bsd_engine_dispatch_index, 0);
spin_lock_init(&dev_priv->fb_tracking.lock);
return 0;
err_priorities:
kmem_cache_destroy(dev_priv->priorities);
err_dependencies:
kmem_cache_destroy(dev_priv->dependencies);
err_requests:
@ -4853,6 +4922,7 @@ void i915_gem_load_cleanup(struct drm_i915_private *dev_priv)
WARN_ON(!list_empty(&dev_priv->gt.timelines));
mutex_unlock(&dev_priv->drm.struct_mutex);
kmem_cache_destroy(dev_priv->priorities);
kmem_cache_destroy(dev_priv->dependencies);
kmem_cache_destroy(dev_priv->requests);
kmem_cache_destroy(dev_priv->vmas);
@ -4864,9 +4934,10 @@ void i915_gem_load_cleanup(struct drm_i915_private *dev_priv)
int i915_gem_freeze(struct drm_i915_private *dev_priv)
{
mutex_lock(&dev_priv->drm.struct_mutex);
/* Discard all purgeable objects, let userspace recover those as
* required after resuming.
*/
i915_gem_shrink_all(dev_priv);
mutex_unlock(&dev_priv->drm.struct_mutex);
return 0;
}
@ -4891,12 +4962,13 @@ int i915_gem_freeze_late(struct drm_i915_private *dev_priv)
* we update that state just before writing out the image.
*
* To try and reduce the hibernation image, we manually shrink
* the objects as well.
* the objects as well, see i915_gem_freeze()
*/
mutex_lock(&dev_priv->drm.struct_mutex);
i915_gem_shrink(dev_priv, -1UL, I915_SHRINK_UNBOUND);
i915_gem_drain_freed_objects(dev_priv);
mutex_lock(&dev_priv->drm.struct_mutex);
for (p = phases; *p; p++) {
list_for_each_entry(obj, *p, global_link) {
obj->base.read_domains = I915_GEM_DOMAIN_CPU;

View File

@ -25,6 +25,8 @@
#ifndef __I915_GEM_H__
#define __I915_GEM_H__
#include <linux/bug.h>
#ifdef CONFIG_DRM_I915_DEBUG_GEM
#define GEM_BUG_ON(expr) BUG_ON(expr)
#define GEM_WARN_ON(expr) WARN_ON(expr)

View File

@ -27,7 +27,6 @@
#include "i915_gem_clflush.h"
static DEFINE_SPINLOCK(clflush_lock);
static u64 clflush_context;
struct clflush {
struct dma_fence dma; /* Must be first for dma_fence_free() */
@ -157,7 +156,7 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj,
dma_fence_init(&clflush->dma,
&i915_clflush_ops,
&clflush_lock,
clflush_context,
to_i915(obj->base.dev)->mm.unordered_timeline,
0);
i915_sw_fence_init(&clflush->wait, i915_clflush_notify);
@ -182,8 +181,3 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj,
GEM_BUG_ON(obj->base.write_domain != I915_GEM_DOMAIN_CPU);
}
}
void i915_gem_clflush_init(struct drm_i915_private *i915)
{
clflush_context = dma_fence_context_alloc(1);
}

View File

@ -28,7 +28,6 @@
struct drm_i915_private;
struct drm_i915_gem_object;
void i915_gem_clflush_init(struct drm_i915_private *i915);
void i915_gem_clflush_object(struct drm_i915_gem_object *obj,
unsigned int flags);
#define I915_CLFLUSH_FORCE BIT(0)

View File

@ -92,33 +92,6 @@
#define ALL_L3_SLICES(dev) (1 << NUM_L3_SLICES(dev)) - 1
static int get_context_size(struct drm_i915_private *dev_priv)
{
int ret;
u32 reg;
switch (INTEL_GEN(dev_priv)) {
case 6:
reg = I915_READ(CXT_SIZE);
ret = GEN6_CXT_TOTAL_SIZE(reg) * 64;
break;
case 7:
reg = I915_READ(GEN7_CXT_SIZE);
if (IS_HASWELL(dev_priv))
ret = HSW_CXT_TOTAL_SIZE;
else
ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
break;
case 8:
ret = GEN8_CXT_TOTAL_SIZE;
break;
default:
BUG();
}
return ret;
}
void i915_gem_context_free(struct kref *ctx_ref)
{
struct i915_gem_context *ctx = container_of(ctx_ref, typeof(*ctx), ref);
@ -151,45 +124,6 @@ void i915_gem_context_free(struct kref *ctx_ref)
kfree(ctx);
}
static struct drm_i915_gem_object *
alloc_context_obj(struct drm_i915_private *dev_priv, u64 size)
{
struct drm_i915_gem_object *obj;
int ret;
lockdep_assert_held(&dev_priv->drm.struct_mutex);
obj = i915_gem_object_create(dev_priv, size);
if (IS_ERR(obj))
return obj;
/*
* Try to make the context utilize L3 as well as LLC.
*
* On VLV we don't have L3 controls in the PTEs so we
* shouldn't touch the cache level, especially as that
* would make the object snooped which might have a
* negative performance impact.
*
* Snooping is required on non-llc platforms in execlist
* mode, but since all GGTT accesses use PAT entry 0 we
* get snooping anyway regardless of cache_level.
*
* This is only applicable for Ivy Bridge devices since
* later platforms don't have L3 control bits in the PTE.
*/
if (IS_IVYBRIDGE(dev_priv)) {
ret = i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC);
/* Failure shouldn't ever happen this early */
if (WARN_ON(ret)) {
i915_gem_object_put(obj);
return ERR_PTR(ret);
}
}
return obj;
}
static void context_close(struct i915_gem_context *ctx)
{
i915_gem_context_set_closed(ctx);
@ -265,26 +199,7 @@ __create_hw_context(struct drm_i915_private *dev_priv,
kref_init(&ctx->ref);
list_add_tail(&ctx->link, &dev_priv->context_list);
ctx->i915 = dev_priv;
if (dev_priv->hw_context_size) {
struct drm_i915_gem_object *obj;
struct i915_vma *vma;
obj = alloc_context_obj(dev_priv, dev_priv->hw_context_size);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
goto err_out;
}
vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL);
if (IS_ERR(vma)) {
i915_gem_object_put(obj);
ret = PTR_ERR(vma);
goto err_out;
}
ctx->engine[RCS].state = vma;
}
ctx->priority = I915_PRIORITY_NORMAL;
/* Default context will never have a file_priv */
ret = DEFAULT_CONTEXT_HANDLE;
@ -443,21 +358,6 @@ int i915_gem_context_init(struct drm_i915_private *dev_priv)
BUILD_BUG_ON(MAX_CONTEXT_HW_ID > INT_MAX);
ida_init(&dev_priv->context_hw_ida);
if (i915.enable_execlists) {
/* NB: intentionally left blank. We will allocate our own
* backing objects as we need them, thank you very much */
dev_priv->hw_context_size = 0;
} else if (HAS_HW_CONTEXTS(dev_priv)) {
dev_priv->hw_context_size =
round_up(get_context_size(dev_priv),
I915_GTT_PAGE_SIZE);
if (dev_priv->hw_context_size > (1<<20)) {
DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n",
dev_priv->hw_context_size);
dev_priv->hw_context_size = 0;
}
}
ctx = i915_gem_create_context(dev_priv, NULL);
if (IS_ERR(ctx)) {
DRM_ERROR("Failed to create default global context (error %ld)\n",
@ -477,8 +377,8 @@ int i915_gem_context_init(struct drm_i915_private *dev_priv)
GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
DRM_DEBUG_DRIVER("%s context support initialized\n",
i915.enable_execlists ? "LR" :
dev_priv->hw_context_size ? "HW" : "fake");
dev_priv->engine[RCS]->context_size ? "logical" :
"fake");
return 0;
}
@ -941,11 +841,6 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv)
return 0;
}
static bool contexts_enabled(struct drm_device *dev)
{
return i915.enable_execlists || to_i915(dev)->hw_context_size;
}
static bool client_is_banned(struct drm_i915_file_private *file_priv)
{
return file_priv->context_bans > I915_MAX_CLIENT_CONTEXT_BANS;
@ -954,12 +849,13 @@ static bool client_is_banned(struct drm_i915_file_private *file_priv)
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_i915_gem_context_create *args = data;
struct drm_i915_file_private *file_priv = file->driver_priv;
struct i915_gem_context *ctx;
int ret;
if (!contexts_enabled(dev))
if (!dev_priv->engine[RCS]->context_size)
return -ENODEV;
if (args->pad != 0)
@ -977,7 +873,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
if (ret)
return ret;
ctx = i915_gem_create_context(to_i915(dev), file_priv);
ctx = i915_gem_create_context(dev_priv, file_priv);
mutex_unlock(&dev->struct_mutex);
if (IS_ERR(ctx))
return PTR_ERR(ctx);

View File

@ -122,12 +122,36 @@ static void i915_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, unsigned long
}
static void *i915_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num)
{
struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf);
struct page *page;
if (page_num >= obj->base.size >> PAGE_SHIFT)
return NULL;
if (!i915_gem_object_has_struct_page(obj))
return NULL;
if (i915_gem_object_pin_pages(obj))
return NULL;
/* Synchronisation is left to the caller (via .begin_cpu_access()) */
page = i915_gem_object_get_page(obj, page_num);
if (IS_ERR(page))
goto err_unpin;
return kmap(page);
err_unpin:
i915_gem_object_unpin_pages(obj);
return NULL;
}
static void i915_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
{
struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf);
kunmap(virt_to_page(addr));
i915_gem_object_unpin_pages(obj);
}
static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)

View File

@ -1114,6 +1114,18 @@ i915_gem_execbuffer_move_to_gpu(struct drm_i915_gem_request *req,
list_for_each_entry(vma, vmas, exec_list) {
struct drm_i915_gem_object *obj = vma->obj;
if (vma->exec_entry->flags & EXEC_OBJECT_CAPTURE) {
struct i915_gem_capture_list *capture;
capture = kmalloc(sizeof(*capture), GFP_KERNEL);
if (unlikely(!capture))
return -ENOMEM;
capture->next = req->capture_list;
capture->vma = vma;
req->capture_list = capture;
}
if (vma->exec_entry->flags & EXEC_OBJECT_ASYNC)
continue;

View File

@ -168,13 +168,11 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
if (enable_ppgtt == 3 && has_full_48bit_ppgtt)
return 3;
#ifdef CONFIG_INTEL_IOMMU
/* Disable ppgtt on SNB if VT-d is on. */
if (IS_GEN6(dev_priv) && intel_iommu_gfx_mapped) {
if (IS_GEN6(dev_priv) && intel_vtd_active()) {
DRM_INFO("Disabling PPGTT because VT-d is on\n");
return 0;
}
#endif
/* Early VLV doesn't have this */
if (IS_VALLEYVIEW(dev_priv) && dev_priv->drm.pdev->revision < 0xb) {
@ -195,9 +193,12 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
u32 pte_flags;
int ret;
ret = vma->vm->allocate_va_range(vma->vm, vma->node.start, vma->size);
if (ret)
return ret;
if (!(vma->flags & I915_VMA_LOCAL_BIND)) {
ret = vma->vm->allocate_va_range(vma->vm, vma->node.start,
vma->size);
if (ret)
return ret;
}
vma->pages = vma->obj->mm.pages;
@ -1989,14 +1990,10 @@ void i915_ppgtt_release(struct kref *kref)
*/
static bool needs_idle_maps(struct drm_i915_private *dev_priv)
{
#ifdef CONFIG_INTEL_IOMMU
/* Query intel_iommu to see if we need the workaround. Presumably that
* was loaded first.
*/
if (IS_GEN5(dev_priv) && IS_MOBILE(dev_priv) && intel_iommu_gfx_mapped)
return true;
#endif
return false;
return IS_GEN5(dev_priv) && IS_MOBILE(dev_priv) && intel_vtd_active();
}
void i915_check_and_clear_faults(struct drm_i915_private *dev_priv)
@ -2188,6 +2185,101 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm,
gen8_set_pte(&gtt_base[i], scratch_pte);
}
static void bxt_vtd_ggtt_wa(struct i915_address_space *vm)
{
struct drm_i915_private *dev_priv = vm->i915;
/*
* Make sure the internal GAM fifo has been cleared of all GTT
* writes before exiting stop_machine(). This guarantees that
* any aperture accesses waiting to start in another process
* cannot back up behind the GTT writes causing a hang.
* The register can be any arbitrary GAM register.
*/
POSTING_READ(GFX_FLSH_CNTL_GEN6);
}
struct insert_page {
struct i915_address_space *vm;
dma_addr_t addr;
u64 offset;
enum i915_cache_level level;
};
static int bxt_vtd_ggtt_insert_page__cb(void *_arg)
{
struct insert_page *arg = _arg;
gen8_ggtt_insert_page(arg->vm, arg->addr, arg->offset, arg->level, 0);
bxt_vtd_ggtt_wa(arg->vm);
return 0;
}
static void bxt_vtd_ggtt_insert_page__BKL(struct i915_address_space *vm,
dma_addr_t addr,
u64 offset,
enum i915_cache_level level,
u32 unused)
{
struct insert_page arg = { vm, addr, offset, level };
stop_machine(bxt_vtd_ggtt_insert_page__cb, &arg, NULL);
}
struct insert_entries {
struct i915_address_space *vm;
struct sg_table *st;
u64 start;
enum i915_cache_level level;
};
static int bxt_vtd_ggtt_insert_entries__cb(void *_arg)
{
struct insert_entries *arg = _arg;
gen8_ggtt_insert_entries(arg->vm, arg->st, arg->start, arg->level, 0);
bxt_vtd_ggtt_wa(arg->vm);
return 0;
}
static void bxt_vtd_ggtt_insert_entries__BKL(struct i915_address_space *vm,
struct sg_table *st,
u64 start,
enum i915_cache_level level,
u32 unused)
{
struct insert_entries arg = { vm, st, start, level };
stop_machine(bxt_vtd_ggtt_insert_entries__cb, &arg, NULL);
}
struct clear_range {
struct i915_address_space *vm;
u64 start;
u64 length;
};
static int bxt_vtd_ggtt_clear_range__cb(void *_arg)
{
struct clear_range *arg = _arg;
gen8_ggtt_clear_range(arg->vm, arg->start, arg->length);
bxt_vtd_ggtt_wa(arg->vm);
return 0;
}
static void bxt_vtd_ggtt_clear_range__BKL(struct i915_address_space *vm,
u64 start,
u64 length)
{
struct clear_range arg = { vm, start, length };
stop_machine(bxt_vtd_ggtt_clear_range__cb, &arg, NULL);
}
static void gen6_ggtt_clear_range(struct i915_address_space *vm,
u64 start, u64 length)
{
@ -2306,10 +2398,11 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
if (flags & I915_VMA_LOCAL_BIND) {
struct i915_hw_ppgtt *appgtt = i915->mm.aliasing_ppgtt;
if (appgtt->base.allocate_va_range) {
if (!(vma->flags & I915_VMA_LOCAL_BIND) &&
appgtt->base.allocate_va_range) {
ret = appgtt->base.allocate_va_range(&appgtt->base,
vma->node.start,
vma->node.size);
vma->size);
if (ret)
goto err_pages;
}
@ -2579,14 +2672,14 @@ static size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
{
snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
snb_gmch_ctl &= SNB_GMCH_GMS_MASK;
return snb_gmch_ctl << 25; /* 32 MB units */
return (size_t)snb_gmch_ctl << 25; /* 32 MB units */
}
static size_t gen8_get_stolen_size(u16 bdw_gmch_ctl)
{
bdw_gmch_ctl >>= BDW_GMCH_GMS_SHIFT;
bdw_gmch_ctl &= BDW_GMCH_GMS_MASK;
return bdw_gmch_ctl << 25; /* 32 MB units */
return (size_t)bdw_gmch_ctl << 25; /* 32 MB units */
}
static size_t chv_get_stolen_size(u16 gmch_ctrl)
@ -2600,11 +2693,11 @@ static size_t chv_get_stolen_size(u16 gmch_ctrl)
* 0x17 to 0x1d: 4MB increments start at 36MB
*/
if (gmch_ctrl < 0x11)
return gmch_ctrl << 25;
return (size_t)gmch_ctrl << 25;
else if (gmch_ctrl < 0x17)
return (gmch_ctrl - 0x11 + 2) << 22;
return (size_t)(gmch_ctrl - 0x11 + 2) << 22;
else
return (gmch_ctrl - 0x17 + 9) << 22;
return (size_t)(gmch_ctrl - 0x17 + 9) << 22;
}
static size_t gen9_get_stolen_size(u16 gen9_gmch_ctl)
@ -2613,10 +2706,10 @@ static size_t gen9_get_stolen_size(u16 gen9_gmch_ctl)
gen9_gmch_ctl &= BDW_GMCH_GMS_MASK;
if (gen9_gmch_ctl < 0xf0)
return gen9_gmch_ctl << 25; /* 32 MB units */
return (size_t)gen9_gmch_ctl << 25; /* 32 MB units */
else
/* 4MB increments starting at 0xf0 for 4MB */
return (gen9_gmch_ctl - 0xf0 + 1) << 22;
return (size_t)(gen9_gmch_ctl - 0xf0 + 1) << 22;
}
static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size)
@ -2743,13 +2836,17 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
struct pci_dev *pdev = dev_priv->drm.pdev;
unsigned int size;
u16 snb_gmch_ctl;
int err;
/* TODO: We're not aware of mappable constraints on gen8 yet */
ggtt->mappable_base = pci_resource_start(pdev, 2);
ggtt->mappable_end = pci_resource_len(pdev, 2);
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(39)))
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(39));
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(39));
if (!err)
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(39));
if (err)
DRM_ERROR("Can't set DMA mask/consistent mask (%d)\n", err);
pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
@ -2781,6 +2878,14 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
ggtt->base.insert_entries = gen8_ggtt_insert_entries;
/* Serialize GTT updates with aperture access on BXT if VT-d is on. */
if (intel_ggtt_update_needs_vtd_wa(dev_priv)) {
ggtt->base.insert_entries = bxt_vtd_ggtt_insert_entries__BKL;
ggtt->base.insert_page = bxt_vtd_ggtt_insert_page__BKL;
if (ggtt->base.clear_range != nop_clear_range)
ggtt->base.clear_range = bxt_vtd_ggtt_clear_range__BKL;
}
ggtt->invalidate = gen6_ggtt_invalidate;
return ggtt_probe_common(ggtt, size);
@ -2792,6 +2897,7 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt)
struct pci_dev *pdev = dev_priv->drm.pdev;
unsigned int size;
u16 snb_gmch_ctl;
int err;
ggtt->mappable_base = pci_resource_start(pdev, 2);
ggtt->mappable_end = pci_resource_len(pdev, 2);
@ -2804,8 +2910,11 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt)
return -ENXIO;
}
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(40)))
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40));
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(40));
if (!err)
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40));
if (err)
DRM_ERROR("Can't set DMA mask/consistent mask (%d)\n", err);
pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
ggtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl);
@ -2924,10 +3033,8 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv)
ggtt->base.total >> 20);
DRM_DEBUG_DRIVER("GMADR size = %lldM\n", ggtt->mappable_end >> 20);
DRM_DEBUG_DRIVER("GTT stolen size = %uM\n", ggtt->stolen_size >> 20);
#ifdef CONFIG_INTEL_IOMMU
if (intel_iommu_gfx_mapped)
if (intel_vtd_active())
DRM_INFO("VT-d active for gfx access\n");
#endif
return 0;
}

View File

@ -37,8 +37,8 @@
struct drm_i915_gem_object_ops {
unsigned int flags;
#define I915_GEM_OBJECT_HAS_STRUCT_PAGE 0x1
#define I915_GEM_OBJECT_IS_SHRINKABLE 0x2
#define I915_GEM_OBJECT_HAS_STRUCT_PAGE BIT(0)
#define I915_GEM_OBJECT_IS_SHRINKABLE BIT(1)
/* Interface between the GEM object and its backing storage.
* get_pages() is called once prior to the use of the associated set

View File

@ -61,7 +61,7 @@ static bool i915_fence_enable_signaling(struct dma_fence *fence)
if (i915_fence_signaled(fence))
return false;
intel_engine_enable_signaling(to_request(fence));
intel_engine_enable_signaling(to_request(fence), true);
return true;
}
@ -159,7 +159,7 @@ i915_priotree_fini(struct drm_i915_private *i915, struct i915_priotree *pt)
{
struct i915_dependency *dep, *next;
GEM_BUG_ON(!RB_EMPTY_NODE(&pt->node));
GEM_BUG_ON(!list_empty(&pt->link));
/* Everyone we depended upon (the fences we wait to be signaled)
* should retire before us and remove themselves from our list.
@ -185,7 +185,7 @@ i915_priotree_init(struct i915_priotree *pt)
{
INIT_LIST_HEAD(&pt->signalers_list);
INIT_LIST_HEAD(&pt->waiters_list);
RB_CLEAR_NODE(&pt->node);
INIT_LIST_HEAD(&pt->link);
pt->priority = INT_MIN;
}
@ -214,12 +214,12 @@ static int reset_all_global_seqno(struct drm_i915_private *i915, u32 seqno)
}
/* Finally reset hw state */
tl->seqno = seqno;
intel_engine_init_global_seqno(engine, seqno);
tl->seqno = seqno;
list_for_each_entry(timeline, &i915->gt.timelines, link)
memset(timeline->engine[id].sync_seqno, 0,
sizeof(timeline->engine[id].sync_seqno));
memset(timeline->engine[id].global_sync, 0,
sizeof(timeline->engine[id].global_sync));
}
return 0;
@ -271,6 +271,48 @@ void i915_gem_retire_noop(struct i915_gem_active *active,
/* Space left intentionally blank */
}
static void advance_ring(struct drm_i915_gem_request *request)
{
unsigned int tail;
/* We know the GPU must have read the request to have
* sent us the seqno + interrupt, so use the position
* of tail of the request to update the last known position
* of the GPU head.
*
* Note this requires that we are always called in request
* completion order.
*/
if (list_is_last(&request->ring_link, &request->ring->request_list)) {
/* We may race here with execlists resubmitting this request
* as we retire it. The resubmission will move the ring->tail
* forwards (to request->wa_tail). We either read the
* current value that was written to hw, or the value that
* is just about to be. Either works, if we miss the last two
* noops - they are safe to be replayed on a reset.
*/
tail = READ_ONCE(request->ring->tail);
} else {
tail = request->postfix;
}
list_del(&request->ring_link);
request->ring->head = tail;
}
static void free_capture_list(struct drm_i915_gem_request *request)
{
struct i915_gem_capture_list *capture;
capture = request->capture_list;
while (capture) {
struct i915_gem_capture_list *next = capture->next;
kfree(capture);
capture = next;
}
}
static void i915_gem_request_retire(struct drm_i915_gem_request *request)
{
struct intel_engine_cs *engine = request->engine;
@ -287,16 +329,6 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
list_del_init(&request->link);
spin_unlock_irq(&engine->timeline->lock);
/* We know the GPU must have read the request to have
* sent us the seqno + interrupt, so use the position
* of tail of the request to update the last known position
* of the GPU head.
*
* Note this requires that we are always called in request
* completion order.
*/
list_del(&request->ring_link);
request->ring->head = request->postfix;
if (!--request->i915->gt.active_requests) {
GEM_BUG_ON(!request->i915->gt.awake);
mod_delayed_work(request->i915->wq,
@ -304,6 +336,9 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
msecs_to_jiffies(100));
}
unreserve_seqno(request->engine);
advance_ring(request);
free_capture_list(request);
/* Walk through the active list, calling retire on each. This allows
* objects to track their GPU activity and mark themselves as idle
@ -402,7 +437,7 @@ void __i915_gem_request_submit(struct drm_i915_gem_request *request)
spin_lock_nested(&request->lock, SINGLE_DEPTH_NESTING);
request->global_seqno = seqno;
if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &request->fence.flags))
intel_engine_enable_signaling(request);
intel_engine_enable_signaling(request, false);
spin_unlock(&request->lock);
engine->emit_breadcrumb(request,
@ -503,9 +538,6 @@ submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
*
* @engine: engine that we wish to issue the request on.
* @ctx: context that the request will be associated with.
* This can be NULL if the request is not directly related to
* any specific user context, in which case this function will
* choose an appropriate context to use.
*
* Returns a pointer to the allocated request if successful,
* or an error code if not.
@ -516,6 +548,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
{
struct drm_i915_private *dev_priv = engine->i915;
struct drm_i915_gem_request *req;
struct intel_ring *ring;
int ret;
lockdep_assert_held(&dev_priv->drm.struct_mutex);
@ -530,9 +563,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
* GGTT space, so do this first before we reserve a seqno for
* ourselves.
*/
ret = engine->context_pin(engine, ctx);
if (ret)
return ERR_PTR(ret);
ring = engine->context_pin(engine, ctx);
if (IS_ERR(ring))
return ERR_CAST(ring);
GEM_BUG_ON(!ring);
ret = reserve_seqno(engine);
if (ret)
@ -598,11 +632,13 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
req->i915 = dev_priv;
req->engine = engine;
req->ctx = ctx;
req->ring = ring;
/* No zalloc, must clear what we need by hand */
req->global_seqno = 0;
req->file_priv = NULL;
req->batch = NULL;
req->capture_list = NULL;
/*
* Reserve space in the ring buffer for all the commands required to
@ -623,7 +659,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
* GPU processing the request, we never over-estimate the
* position of the head.
*/
req->head = req->ring->tail;
req->head = req->ring->emit;
/* Check that we didn't interrupt ourselves with a new request */
GEM_BUG_ON(req->timeline->seqno != req->fence.seqno);
@ -651,6 +687,7 @@ i915_gem_request_await_request(struct drm_i915_gem_request *to,
int ret;
GEM_BUG_ON(to == from);
GEM_BUG_ON(to->timeline == from->timeline);
if (i915_gem_request_completed(from))
return 0;
@ -663,9 +700,6 @@ i915_gem_request_await_request(struct drm_i915_gem_request *to,
return ret;
}
if (to->timeline == from->timeline)
return 0;
if (to->engine == from->engine) {
ret = i915_sw_fence_await_sw_fence_gfp(&to->submit,
&from->submit,
@ -674,55 +708,45 @@ i915_gem_request_await_request(struct drm_i915_gem_request *to,
}
seqno = i915_gem_request_global_seqno(from);
if (!seqno) {
ret = i915_sw_fence_await_dma_fence(&to->submit,
&from->fence, 0,
GFP_KERNEL);
return ret < 0 ? ret : 0;
}
if (!seqno)
goto await_dma_fence;
if (seqno <= to->timeline->sync_seqno[from->engine->id])
return 0;
if (!to->engine->semaphore.sync_to) {
if (!__i915_gem_request_started(from, seqno))
goto await_dma_fence;
trace_i915_gem_ring_sync_to(to, from);
if (!i915.semaphores) {
if (!i915_spin_request(from, TASK_INTERRUPTIBLE, 2)) {
ret = i915_sw_fence_await_dma_fence(&to->submit,
&from->fence, 0,
GFP_KERNEL);
if (ret < 0)
return ret;
}
if (!__i915_spin_request(from, seqno, TASK_INTERRUPTIBLE, 2))
goto await_dma_fence;
} else {
GEM_BUG_ON(!from->engine->semaphore.signal);
if (seqno <= to->timeline->global_sync[from->engine->id])
return 0;
trace_i915_gem_ring_sync_to(to, from);
ret = to->engine->semaphore.sync_to(to, from);
if (ret)
return ret;
to->timeline->global_sync[from->engine->id] = seqno;
}
to->timeline->sync_seqno[from->engine->id] = seqno;
return 0;
await_dma_fence:
ret = i915_sw_fence_await_dma_fence(&to->submit,
&from->fence, 0,
GFP_KERNEL);
return ret < 0 ? ret : 0;
}
int
i915_gem_request_await_dma_fence(struct drm_i915_gem_request *req,
struct dma_fence *fence)
{
struct dma_fence_array *array;
struct dma_fence **child = &fence;
unsigned int nchild = 1;
int ret;
int i;
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
return 0;
if (dma_fence_is_i915(fence))
return i915_gem_request_await_request(req, to_request(fence));
if (!dma_fence_is_array(fence)) {
ret = i915_sw_fence_await_dma_fence(&req->submit,
fence, I915_FENCE_TIMEOUT,
GFP_KERNEL);
return ret < 0 ? ret : 0;
}
/* Note that if the fence-array was created in signal-on-any mode,
* we should *not* decompose it into its individual fences. However,
@ -731,21 +755,46 @@ i915_gem_request_await_dma_fence(struct drm_i915_gem_request *req,
* amdgpu and we should not see any incoming fence-array from
* sync-file being in signal-on-any mode.
*/
if (dma_fence_is_array(fence)) {
struct dma_fence_array *array = to_dma_fence_array(fence);
array = to_dma_fence_array(fence);
for (i = 0; i < array->num_fences; i++) {
struct dma_fence *child = array->fences[i];
child = array->fences;
nchild = array->num_fences;
GEM_BUG_ON(!nchild);
}
if (dma_fence_is_i915(child))
do {
fence = *child++;
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
continue;
/*
* Requests on the same timeline are explicitly ordered, along
* with their dependencies, by i915_add_request() which ensures
* that requests are submitted in-order through each ring.
*/
if (fence->context == req->fence.context)
continue;
/* Squash repeated waits to the same timelines */
if (fence->context != req->i915->mm.unordered_timeline &&
intel_timeline_sync_is_later(req->timeline, fence))
continue;
if (dma_fence_is_i915(fence))
ret = i915_gem_request_await_request(req,
to_request(child));
to_request(fence));
else
ret = i915_sw_fence_await_dma_fence(&req->submit,
child, I915_FENCE_TIMEOUT,
ret = i915_sw_fence_await_dma_fence(&req->submit, fence,
I915_FENCE_TIMEOUT,
GFP_KERNEL);
if (ret < 0)
return ret;
}
/* Record the latest fence used against each timeline */
if (fence->context != req->i915->mm.unordered_timeline)
intel_timeline_sync_set(req->timeline, fence);
} while (--nchild);
return 0;
}

View File

@ -67,12 +67,18 @@ struct i915_dependency {
struct i915_priotree {
struct list_head signalers_list; /* those before us, we depend upon */
struct list_head waiters_list; /* those after us, they depend upon us */
struct rb_node node;
struct list_head link;
int priority;
#define I915_PRIORITY_MAX 1024
#define I915_PRIORITY_NORMAL 0
#define I915_PRIORITY_MIN (-I915_PRIORITY_MAX)
};
struct i915_gem_capture_list {
struct i915_gem_capture_list *next;
struct i915_vma *vma;
};
/**
* Request queue structure.
*
@ -167,6 +173,12 @@ struct drm_i915_gem_request {
* error state dump only).
*/
struct i915_vma *batch;
/** Additional buffers requested by userspace to be captured upon
* a GPU hang. The vma/obj on this list are protected by their
* active reference - all objects on this list must also be
* on the active_list (of their final request).
*/
struct i915_gem_capture_list *capture_list;
struct list_head active_list;
/** Time at which this request was emitted, in jiffies. */

View File

@ -35,9 +35,9 @@
#include "i915_drv.h"
#include "i915_trace.h"
static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
static bool shrinker_lock(struct drm_i915_private *dev_priv, bool *unlock)
{
switch (mutex_trylock_recursive(&dev->struct_mutex)) {
switch (mutex_trylock_recursive(&dev_priv->drm.struct_mutex)) {
case MUTEX_TRYLOCK_FAILED:
return false;
@ -53,24 +53,29 @@ static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
BUG();
}
static void i915_gem_shrinker_unlock(struct drm_device *dev, bool unlock)
static void shrinker_unlock(struct drm_i915_private *dev_priv, bool unlock)
{
if (!unlock)
return;
mutex_unlock(&dev->struct_mutex);
/* expedite the RCU grace period to free some request slabs */
synchronize_rcu_expedited();
mutex_unlock(&dev_priv->drm.struct_mutex);
}
static bool any_vma_pinned(struct drm_i915_gem_object *obj)
{
struct i915_vma *vma;
list_for_each_entry(vma, &obj->vma_list, obj_link)
list_for_each_entry(vma, &obj->vma_list, obj_link) {
/* Only GGTT vma may be permanently pinned, and are always
* at the start of the list. We can stop hunting as soon
* as we see a ppGTT vma.
*/
if (!i915_vma_is_ggtt(vma))
break;
if (i915_vma_is_pinned(vma))
return true;
}
return false;
}
@ -156,7 +161,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv,
unsigned long count = 0;
bool unlock;
if (!i915_gem_shrinker_lock(&dev_priv->drm, &unlock))
if (!shrinker_lock(dev_priv, &unlock))
return 0;
trace_i915_gem_shrink(dev_priv, target, flags);
@ -244,7 +249,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv,
i915_gem_retire_requests(dev_priv);
i915_gem_shrinker_unlock(&dev_priv->drm, unlock);
shrinker_unlock(dev_priv, unlock);
return count;
}
@ -274,8 +279,6 @@ unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv)
I915_SHRINK_ACTIVE);
intel_runtime_pm_put(dev_priv);
synchronize_rcu(); /* wait for our earlier RCU delayed slab frees */
return freed;
}
@ -284,12 +287,11 @@ i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
{
struct drm_i915_private *dev_priv =
container_of(shrinker, struct drm_i915_private, mm.shrinker);
struct drm_device *dev = &dev_priv->drm;
struct drm_i915_gem_object *obj;
unsigned long count;
bool unlock;
if (!i915_gem_shrinker_lock(dev, &unlock))
if (!shrinker_lock(dev_priv, &unlock))
return 0;
i915_gem_retire_requests(dev_priv);
@ -304,7 +306,7 @@ i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
count += obj->base.size >> PAGE_SHIFT;
}
i915_gem_shrinker_unlock(dev, unlock);
shrinker_unlock(dev_priv, unlock);
return count;
}
@ -314,11 +316,10 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
{
struct drm_i915_private *dev_priv =
container_of(shrinker, struct drm_i915_private, mm.shrinker);
struct drm_device *dev = &dev_priv->drm;
unsigned long freed;
bool unlock;
if (!i915_gem_shrinker_lock(dev, &unlock))
if (!shrinker_lock(dev_priv, &unlock))
return SHRINK_STOP;
freed = i915_gem_shrink(dev_priv,
@ -332,26 +333,20 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
I915_SHRINK_BOUND |
I915_SHRINK_UNBOUND);
i915_gem_shrinker_unlock(dev, unlock);
shrinker_unlock(dev_priv, unlock);
return freed;
}
struct shrinker_lock_uninterruptible {
bool was_interruptible;
bool unlock;
};
static bool
i915_gem_shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv,
struct shrinker_lock_uninterruptible *slu,
int timeout_ms)
shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv, bool *unlock,
int timeout_ms)
{
unsigned long timeout = jiffies + msecs_to_jiffies_timeout(timeout_ms);
do {
if (i915_gem_wait_for_idle(dev_priv, 0) == 0 &&
i915_gem_shrinker_lock(&dev_priv->drm, &slu->unlock))
shrinker_lock(dev_priv, unlock))
break;
schedule_timeout_killable(1);
@ -364,29 +359,19 @@ i915_gem_shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv,
}
} while (1);
slu->was_interruptible = dev_priv->mm.interruptible;
dev_priv->mm.interruptible = false;
return true;
}
static void
i915_gem_shrinker_unlock_uninterruptible(struct drm_i915_private *dev_priv,
struct shrinker_lock_uninterruptible *slu)
{
dev_priv->mm.interruptible = slu->was_interruptible;
i915_gem_shrinker_unlock(&dev_priv->drm, slu->unlock);
}
static int
i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
{
struct drm_i915_private *dev_priv =
container_of(nb, struct drm_i915_private, mm.oom_notifier);
struct shrinker_lock_uninterruptible slu;
struct drm_i915_gem_object *obj;
unsigned long unevictable, bound, unbound, freed_pages;
bool unlock;
if (!i915_gem_shrinker_lock_uninterruptible(dev_priv, &slu, 5000))
if (!shrinker_lock_uninterruptible(dev_priv, &unlock, 5000))
return NOTIFY_DONE;
freed_pages = i915_gem_shrink_all(dev_priv);
@ -415,7 +400,7 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
bound += obj->base.size >> PAGE_SHIFT;
}
i915_gem_shrinker_unlock_uninterruptible(dev_priv, &slu);
shrinker_unlock(dev_priv, unlock);
if (freed_pages || unbound || bound)
pr_info("Purging GPU memory, %lu pages freed, "
@ -435,12 +420,12 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
{
struct drm_i915_private *dev_priv =
container_of(nb, struct drm_i915_private, mm.vmap_notifier);
struct shrinker_lock_uninterruptible slu;
struct i915_vma *vma, *next;
unsigned long freed_pages = 0;
bool unlock;
int ret;
if (!i915_gem_shrinker_lock_uninterruptible(dev_priv, &slu, 5000))
if (!shrinker_lock_uninterruptible(dev_priv, &unlock, 5000))
return NOTIFY_DONE;
/* Force everything onto the inactive lists */
@ -465,7 +450,7 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
}
out:
i915_gem_shrinker_unlock_uninterruptible(dev_priv, &slu);
shrinker_unlock(dev_priv, unlock);
*(unsigned long *)ptr += freed_pages;
return NOTIFY_DONE;

View File

@ -414,12 +414,10 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
return 0;
}
#ifdef CONFIG_INTEL_IOMMU
if (intel_iommu_gfx_mapped && INTEL_GEN(dev_priv) < 8) {
if (intel_vtd_active() && INTEL_GEN(dev_priv) < 8) {
DRM_INFO("DMAR active, disabling use of stolen memory\n");
return 0;
}
#endif
if (ggtt->stolen_size == 0)
return 0;

View File

@ -23,6 +23,32 @@
*/
#include "i915_drv.h"
#include "i915_syncmap.h"
static void __intel_timeline_init(struct intel_timeline *tl,
struct i915_gem_timeline *parent,
u64 context,
struct lock_class_key *lockclass,
const char *lockname)
{
tl->fence_context = context;
tl->common = parent;
#ifdef CONFIG_DEBUG_SPINLOCK
__raw_spin_lock_init(&tl->lock.rlock, lockname, lockclass);
#else
spin_lock_init(&tl->lock);
#endif
init_request_active(&tl->last_request, NULL);
INIT_LIST_HEAD(&tl->requests);
i915_syncmap_init(&tl->sync);
}
static void __intel_timeline_fini(struct intel_timeline *tl)
{
GEM_BUG_ON(!list_empty(&tl->requests));
i915_syncmap_free(&tl->sync);
}
static int __i915_gem_timeline_init(struct drm_i915_private *i915,
struct i915_gem_timeline *timeline,
@ -35,6 +61,14 @@ static int __i915_gem_timeline_init(struct drm_i915_private *i915,
lockdep_assert_held(&i915->drm.struct_mutex);
/*
* Ideally we want a set of engines on a single leaf as we expect
* to mostly be tracking synchronisation between engines. It is not
* a huge issue if this is not the case, but we may want to mitigate
* any page crossing penalties if they become an issue.
*/
BUILD_BUG_ON(KSYNCMAP < I915_NUM_ENGINES);
timeline->i915 = i915;
timeline->name = kstrdup(name ?: "[kernel]", GFP_KERNEL);
if (!timeline->name)
@ -44,19 +78,10 @@ static int __i915_gem_timeline_init(struct drm_i915_private *i915,
/* Called during early_init before we know how many engines there are */
fences = dma_fence_context_alloc(ARRAY_SIZE(timeline->engine));
for (i = 0; i < ARRAY_SIZE(timeline->engine); i++) {
struct intel_timeline *tl = &timeline->engine[i];
tl->fence_context = fences++;
tl->common = timeline;
#ifdef CONFIG_DEBUG_SPINLOCK
__raw_spin_lock_init(&tl->lock.rlock, lockname, lockclass);
#else
spin_lock_init(&tl->lock);
#endif
init_request_active(&tl->last_request, NULL);
INIT_LIST_HEAD(&tl->requests);
}
for (i = 0; i < ARRAY_SIZE(timeline->engine); i++)
__intel_timeline_init(&timeline->engine[i],
timeline, fences++,
lockclass, lockname);
return 0;
}
@ -81,18 +106,52 @@ int i915_gem_timeline_init__global(struct drm_i915_private *i915)
&class, "&global_timeline->lock");
}
/**
* i915_gem_timelines_mark_idle -- called when the driver idles
* @i915 - the drm_i915_private device
*
* When the driver is completely idle, we know that all of our sync points
* have been signaled and our tracking is then entirely redundant. Any request
* to wait upon an older sync point will be completed instantly as we know
* the fence is signaled and therefore we will not even look them up in the
* sync point map.
*/
void i915_gem_timelines_mark_idle(struct drm_i915_private *i915)
{
struct i915_gem_timeline *timeline;
int i;
lockdep_assert_held(&i915->drm.struct_mutex);
list_for_each_entry(timeline, &i915->gt.timelines, link) {
for (i = 0; i < ARRAY_SIZE(timeline->engine); i++) {
struct intel_timeline *tl = &timeline->engine[i];
/*
* All known fences are completed so we can scrap
* the current sync point tracking and start afresh,
* any attempt to wait upon a previous sync point
* will be skipped as the fence was signaled.
*/
i915_syncmap_free(&tl->sync);
}
}
}
void i915_gem_timeline_fini(struct i915_gem_timeline *timeline)
{
int i;
lockdep_assert_held(&timeline->i915->drm.struct_mutex);
for (i = 0; i < ARRAY_SIZE(timeline->engine); i++) {
struct intel_timeline *tl = &timeline->engine[i];
GEM_BUG_ON(!list_empty(&tl->requests));
}
for (i = 0; i < ARRAY_SIZE(timeline->engine); i++)
__intel_timeline_fini(&timeline->engine[i]);
list_del(&timeline->link);
kfree(timeline->name);
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/mock_timeline.c"
#include "selftests/i915_gem_timeline.c"
#endif

View File

@ -27,7 +27,9 @@
#include <linux/list.h>
#include "i915_utils.h"
#include "i915_gem_request.h"
#include "i915_syncmap.h"
struct i915_gem_timeline;
@ -55,7 +57,25 @@ struct intel_timeline {
* struct_mutex.
*/
struct i915_gem_active last_request;
u32 sync_seqno[I915_NUM_ENGINES];
/**
* We track the most recent seqno that we wait on in every context so
* that we only have to emit a new await and dependency on a more
* recent sync point. As the contexts may be executed out-of-order, we
* have to track each individually and can not rely on an absolute
* global_seqno. When we know that all tracked fences are completed
* (i.e. when the driver is idle), we know that the syncmap is
* redundant and we can discard it without loss of generality.
*/
struct i915_syncmap *sync;
/**
* Separately to the inter-context seqno map above, we track the last
* barrier (e.g. semaphore wait) to the global engine timelines. Note
* that this tracks global_seqno rather than the context.seqno, and
* so it is subject to the limitations of hw wraparound and that we
* may need to revoke global_seqno (on pre-emption).
*/
u32 global_sync[I915_NUM_ENGINES];
struct i915_gem_timeline *common;
};
@ -73,6 +93,31 @@ int i915_gem_timeline_init(struct drm_i915_private *i915,
struct i915_gem_timeline *tl,
const char *name);
int i915_gem_timeline_init__global(struct drm_i915_private *i915);
void i915_gem_timelines_mark_idle(struct drm_i915_private *i915);
void i915_gem_timeline_fini(struct i915_gem_timeline *tl);
static inline int __intel_timeline_sync_set(struct intel_timeline *tl,
u64 context, u32 seqno)
{
return i915_syncmap_set(&tl->sync, context, seqno);
}
static inline int intel_timeline_sync_set(struct intel_timeline *tl,
const struct dma_fence *fence)
{
return __intel_timeline_sync_set(tl, fence->context, fence->seqno);
}
static inline bool __intel_timeline_sync_is_later(struct intel_timeline *tl,
u64 context, u32 seqno)
{
return i915_syncmap_is_later(&tl->sync, context, seqno);
}
static inline bool intel_timeline_sync_is_later(struct intel_timeline *tl,
const struct dma_fence *fence)
{
return __intel_timeline_sync_is_later(tl, fence->context, fence->seqno);
}
#endif

View File

@ -712,6 +712,10 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
print_error_obj(m, dev_priv->engine[i], NULL, obj);
}
for (j = 0; j < ee->user_bo_count; j++)
print_error_obj(m, dev_priv->engine[i],
"user", ee->user_bo[j]);
if (ee->num_requests) {
err_printf(m, "%s --- %d requests\n",
dev_priv->engine[i]->name,
@ -825,11 +829,15 @@ void __i915_gpu_state_free(struct kref *error_ref)
{
struct i915_gpu_state *error =
container_of(error_ref, typeof(*error), ref);
int i;
long i, j;
for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
struct drm_i915_error_engine *ee = &error->engine[i];
for (j = 0; j < ee->user_bo_count; j++)
i915_error_object_free(ee->user_bo[j]);
kfree(ee->user_bo);
i915_error_object_free(ee->batchbuffer);
i915_error_object_free(ee->wa_batchbuffer);
i915_error_object_free(ee->ringbuffer);
@ -1316,12 +1324,17 @@ static void engine_record_requests(struct intel_engine_cs *engine,
static void error_record_engine_execlists(struct intel_engine_cs *engine,
struct drm_i915_error_engine *ee)
{
const struct execlist_port *port = engine->execlist_port;
unsigned int n;
for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++)
if (engine->execlist_port[n].request)
record_request(engine->execlist_port[n].request,
&ee->execlist[n]);
for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++) {
struct drm_i915_gem_request *rq = port_request(&port[n]);
if (!rq)
break;
record_request(rq, &ee->execlist[n]);
}
}
static void record_context(struct drm_i915_error_context *e,
@ -1346,6 +1359,35 @@ static void record_context(struct drm_i915_error_context *e,
e->active = ctx->active_count;
}
static void request_record_user_bo(struct drm_i915_gem_request *request,
struct drm_i915_error_engine *ee)
{
struct i915_gem_capture_list *c;
struct drm_i915_error_object **bo;
long count;
count = 0;
for (c = request->capture_list; c; c = c->next)
count++;
bo = NULL;
if (count)
bo = kcalloc(count, sizeof(*bo), GFP_ATOMIC);
if (!bo)
return;
count = 0;
for (c = request->capture_list; c; c = c->next) {
bo[count] = i915_error_object_create(request->i915, c->vma);
if (!bo[count])
break;
count++;
}
ee->user_bo = bo;
ee->user_bo_count = count;
}
static void i915_gem_record_rings(struct drm_i915_private *dev_priv,
struct i915_gpu_state *error)
{
@ -1392,6 +1434,7 @@ static void i915_gem_record_rings(struct drm_i915_private *dev_priv,
ee->wa_batchbuffer =
i915_error_object_create(dev_priv,
engine->scratch);
request_record_user_bo(request, ee);
ee->ctx =
i915_error_object_create(dev_priv,
@ -1560,6 +1603,9 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
error->done_reg = I915_READ(DONE_REG);
}
if (INTEL_GEN(dev_priv) >= 5)
error->ccid = I915_READ(CCID);
/* 3: Feature specific registers */
if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) {
error->gam_ecochk = I915_READ(GAM_ECOCHK);
@ -1567,9 +1613,6 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
}
/* 4: Everything else */
if (HAS_HW_CONTEXTS(dev_priv))
error->ccid = I915_READ(CCID);
if (INTEL_GEN(dev_priv) >= 8) {
error->ier = I915_READ(GEN8_DE_MISC_IER);
for (i = 0; i < 4; i++)

View File

@ -480,9 +480,7 @@ static void guc_wq_item_append(struct i915_guc_client *client,
GEM_BUG_ON(freespace < wqi_size);
/* The GuC firmware wants the tail index in QWords, not bytes */
tail = rq->tail;
assert_ring_tail_valid(rq->ring, rq->tail);
tail >>= 3;
tail = intel_ring_set_tail(rq->ring, rq->tail) >> 3;
GEM_BUG_ON(tail > WQ_RING_TAIL_MAX);
/* For now workqueue item is 4 DWs; workqueue buffer is 2 pages. So we
@ -616,12 +614,6 @@ static void __i915_guc_submit(struct drm_i915_gem_request *rq)
b_ret = guc_ring_doorbell(client);
client->submissions[engine_id] += 1;
client->retcode = b_ret;
if (b_ret)
client->b_fail += 1;
guc->submissions[engine_id] += 1;
guc->last_seqno[engine_id] = rq->global_seqno;
spin_unlock_irqrestore(&client->wq_lock, flags);
}
@ -651,47 +643,68 @@ static void nested_enable_signaling(struct drm_i915_gem_request *rq)
trace_dma_fence_enable_signal(&rq->fence);
spin_lock_nested(&rq->lock, SINGLE_DEPTH_NESTING);
intel_engine_enable_signaling(rq);
intel_engine_enable_signaling(rq, true);
spin_unlock(&rq->lock);
}
static void port_assign(struct execlist_port *port,
struct drm_i915_gem_request *rq)
{
GEM_BUG_ON(rq == port_request(port));
if (port_isset(port))
i915_gem_request_put(port_request(port));
port_set(port, i915_gem_request_get(rq));
nested_enable_signaling(rq);
}
static bool i915_guc_dequeue(struct intel_engine_cs *engine)
{
struct execlist_port *port = engine->execlist_port;
struct drm_i915_gem_request *last = port[0].request;
struct drm_i915_gem_request *last = port_request(port);
struct rb_node *rb;
bool submit = false;
spin_lock_irq(&engine->timeline->lock);
rb = engine->execlist_first;
GEM_BUG_ON(rb_first(&engine->execlist_queue) != rb);
while (rb) {
struct drm_i915_gem_request *rq =
rb_entry(rb, typeof(*rq), priotree.node);
struct i915_priolist *p = rb_entry(rb, typeof(*p), node);
struct drm_i915_gem_request *rq, *rn;
if (last && rq->ctx != last->ctx) {
if (port != engine->execlist_port)
break;
list_for_each_entry_safe(rq, rn, &p->requests, priotree.link) {
if (last && rq->ctx != last->ctx) {
if (port != engine->execlist_port) {
__list_del_many(&p->requests,
&rq->priotree.link);
goto done;
}
i915_gem_request_assign(&port->request, last);
nested_enable_signaling(last);
port++;
if (submit)
port_assign(port, last);
port++;
}
INIT_LIST_HEAD(&rq->priotree.link);
rq->priotree.priority = INT_MAX;
i915_guc_submit(rq);
trace_i915_gem_request_in(rq, port_index(port, engine));
last = rq;
submit = true;
}
rb = rb_next(rb);
rb_erase(&rq->priotree.node, &engine->execlist_queue);
RB_CLEAR_NODE(&rq->priotree.node);
rq->priotree.priority = INT_MAX;
i915_guc_submit(rq);
trace_i915_gem_request_in(rq, port - engine->execlist_port);
last = rq;
submit = true;
}
if (submit) {
i915_gem_request_assign(&port->request, last);
nested_enable_signaling(last);
engine->execlist_first = rb;
rb_erase(&p->node, &engine->execlist_queue);
INIT_LIST_HEAD(&p->requests);
if (p->priority != I915_PRIORITY_NORMAL)
kmem_cache_free(engine->i915->priorities, p);
}
done:
engine->execlist_first = rb;
if (submit)
port_assign(port, last);
spin_unlock_irq(&engine->timeline->lock);
return submit;
@ -705,17 +718,19 @@ static void i915_guc_irq_handler(unsigned long data)
bool submit;
do {
rq = port[0].request;
rq = port_request(&port[0]);
while (rq && i915_gem_request_completed(rq)) {
trace_i915_gem_request_out(rq);
i915_gem_request_put(rq);
port[0].request = port[1].request;
port[1].request = NULL;
rq = port[0].request;
port[0] = port[1];
memset(&port[1], 0, sizeof(port[1]));
rq = port_request(&port[0]);
}
submit = false;
if (!port[1].request)
if (!port_count(&port[1]))
submit = i915_guc_dequeue(engine);
} while (submit);
}
@ -1053,8 +1068,7 @@ static int guc_ads_create(struct intel_guc *guc)
dev_priv->engine[RCS]->status_page.ggtt_offset;
for_each_engine(engine, dev_priv, id)
blob->ads.eng_state_size[engine->guc_id] =
intel_lr_context_size(engine);
blob->ads.eng_state_size[engine->guc_id] = engine->context_size;
base = guc_ggtt_offset(vma);
blob->ads.scheduler_policies = base + ptr_offset(blob, policies);

View File

@ -1200,7 +1200,7 @@ out:
static void ivybridge_parity_work(struct work_struct *work)
{
struct drm_i915_private *dev_priv =
container_of(work, struct drm_i915_private, l3_parity.error_work);
container_of(work, typeof(*dev_priv), l3_parity.error_work);
u32 error_status, row, bank, subbank;
char *parity_event[6];
uint32_t misccpctl;
@ -1317,14 +1317,16 @@ static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
ivybridge_parity_error_irq_handler(dev_priv, gt_iir);
}
static __always_inline void
static void
gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
{
bool tasklet = false;
if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
tasklet = true;
if (port_count(&engine->execlist_port[0])) {
__set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
tasklet = true;
}
}
if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
@ -2917,7 +2919,6 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
u32 pipestat_mask;
u32 enable_mask;
enum pipe pipe;
u32 val;
pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
PIPE_CRC_DONE_INTERRUPT_STATUS;
@ -2928,18 +2929,16 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
enable_mask = I915_DISPLAY_PORT_INTERRUPT |
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
I915_LPE_PIPE_A_INTERRUPT |
I915_LPE_PIPE_B_INTERRUPT;
if (IS_CHERRYVIEW(dev_priv))
enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
I915_LPE_PIPE_C_INTERRUPT;
WARN_ON(dev_priv->irq_mask != ~0);
val = (I915_LPE_PIPE_A_INTERRUPT |
I915_LPE_PIPE_B_INTERRUPT |
I915_LPE_PIPE_C_INTERRUPT);
enable_mask |= val;
dev_priv->irq_mask = ~enable_mask;
GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask);
@ -4197,11 +4196,15 @@ static void i965_irq_uninstall(struct drm_device * dev)
void intel_irq_init(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = &dev_priv->drm;
int i;
intel_hpd_init_work(dev_priv);
INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
for (i = 0; i < MAX_L3_SLICES; ++i)
dev_priv->l3_parity.remap_info[i] = NULL;
if (HAS_GUC_SCHED(dev_priv))
dev_priv->pm_guc_events = GEN9_GUC_TO_HOST_INT_EVENT;
@ -4326,6 +4329,20 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
}
}
/**
* intel_irq_fini - deinitializes IRQ support
* @i915: i915 device instance
*
* This function deinitializes all the IRQ support.
*/
void intel_irq_fini(struct drm_i915_private *i915)
{
int i;
for (i = 0; i < MAX_L3_SLICES; ++i)
kfree(i915->l3_parity.remap_info[i]);
}
/**
* intel_irq_install - enables the hardware interrupt
* @dev_priv: i915 device instance

View File

@ -220,7 +220,6 @@ static const struct intel_device_info intel_ironlake_m_info = {
.has_rc6 = 1, \
.has_rc6p = 1, \
.has_gmbus_irq = 1, \
.has_hw_contexts = 1, \
.has_aliasing_ppgtt = 1, \
GEN_DEFAULT_PIPEOFFSETS, \
CURSOR_OFFSETS
@ -245,7 +244,6 @@ static const struct intel_device_info intel_sandybridge_m_info = {
.has_rc6 = 1, \
.has_rc6p = 1, \
.has_gmbus_irq = 1, \
.has_hw_contexts = 1, \
.has_aliasing_ppgtt = 1, \
.has_full_ppgtt = 1, \
GEN_DEFAULT_PIPEOFFSETS, \
@ -280,7 +278,6 @@ static const struct intel_device_info intel_valleyview_info = {
.has_runtime_pm = 1,
.has_rc6 = 1,
.has_gmbus_irq = 1,
.has_hw_contexts = 1,
.has_gmch_display = 1,
.has_hotplug = 1,
.has_aliasing_ppgtt = 1,
@ -340,7 +337,6 @@ static const struct intel_device_info intel_cherryview_info = {
.has_resource_streamer = 1,
.has_rc6 = 1,
.has_gmbus_irq = 1,
.has_hw_contexts = 1,
.has_logical_ring_contexts = 1,
.has_gmch_display = 1,
.has_aliasing_ppgtt = 1,
@ -387,7 +383,6 @@ static const struct intel_device_info intel_skylake_gt3_info = {
.has_rc6 = 1, \
.has_dp_mst = 1, \
.has_gmbus_irq = 1, \
.has_hw_contexts = 1, \
.has_logical_ring_contexts = 1, \
.has_guc = 1, \
.has_decoupled_mmio = 1, \

View File

@ -205,25 +205,49 @@
#define OA_TAKEN(tail, head) ((tail - head) & (OA_BUFFER_SIZE - 1))
/* There's a HW race condition between OA unit tail pointer register updates and
/**
* DOC: OA Tail Pointer Race
*
* There's a HW race condition between OA unit tail pointer register updates and
* writes to memory whereby the tail pointer can sometimes get ahead of what's
* been written out to the OA buffer so far.
* been written out to the OA buffer so far (in terms of what's visible to the
* CPU).
*
* Although this can be observed explicitly by checking for a zeroed report-id
* field in tail reports, it seems preferable to account for this earlier e.g.
* as part of the _oa_buffer_is_empty checks to minimize -EAGAIN polling cycles
* in this situation.
* Although this can be observed explicitly while copying reports to userspace
* by checking for a zeroed report-id field in tail reports, we want to account
* for this earlier, as part of the _oa_buffer_check to avoid lots of redundant
* read() attempts.
*
* To give time for the most recent reports to land before they may be copied to
* userspace, the driver operates as if the tail pointer effectively lags behind
* the HW tail pointer by 'tail_margin' bytes. The margin in bytes is calculated
* based on this constant in nanoseconds, the current OA sampling exponent
* and current report size.
* In effect we define a tail pointer for reading that lags the real tail
* pointer by at least %OA_TAIL_MARGIN_NSEC nanoseconds, which gives enough
* time for the corresponding reports to become visible to the CPU.
*
* There is also a fallback check while reading to simply skip over reports with
* a zeroed report-id.
* To manage this we actually track two tail pointers:
* 1) An 'aging' tail with an associated timestamp that is tracked until we
* can trust the corresponding data is visible to the CPU; at which point
* it is considered 'aged'.
* 2) An 'aged' tail that can be used for read()ing.
*
* The two separate pointers let us decouple read()s from tail pointer aging.
*
* The tail pointers are checked and updated at a limited rate within a hrtimer
* callback (the same callback that is used for delivering POLLIN events)
*
* Initially the tails are marked invalid with %INVALID_TAIL_PTR which
* indicates that an updated tail pointer is needed.
*
* Most of the implementation details for this workaround are in
* gen7_oa_buffer_check_unlocked() and gen7_appand_oa_reports()
*
* Note for posterity: previously the driver used to define an effective tail
* pointer that lagged the real pointer by a 'tail margin' measured in bytes
* derived from %OA_TAIL_MARGIN_NSEC and the configured sampling frequency.
* This was flawed considering that the OA unit may also automatically generate
* non-periodic reports (such as on context switch) or the OA unit may be
* enabled without any periodic sampling.
*/
#define OA_TAIL_MARGIN_NSEC 100000ULL
#define INVALID_TAIL_PTR 0xffffffff
/* frequency for checking whether the OA unit has written new reports to the
* circular OA buffer...
@ -308,27 +332,121 @@ struct perf_open_properties {
int oa_period_exponent;
};
/* NB: This is either called via fops or the poll check hrtimer (atomic ctx)
/**
* gen7_oa_buffer_check_unlocked - check for data and update tail ptr state
* @dev_priv: i915 device instance
*
* It's safe to read OA config state here unlocked, assuming that this is only
* called while the stream is enabled, while the global OA configuration can't
* be modified.
* This is either called via fops (for blocking reads in user ctx) or the poll
* check hrtimer (atomic ctx) to check the OA buffer tail pointer and check
* if there is data available for userspace to read.
*
* Note: we don't lock around the head/tail reads even though there's the slim
* possibility of read() fop errors forcing a re-init of the OA buffer
* pointers. A race here could result in a false positive !empty status which
* is acceptable.
* This function is central to providing a workaround for the OA unit tail
* pointer having a race with respect to what data is visible to the CPU.
* It is responsible for reading tail pointers from the hardware and giving
* the pointers time to 'age' before they are made available for reading.
* (See description of OA_TAIL_MARGIN_NSEC above for further details.)
*
* Besides returning true when there is data available to read() this function
* also has the side effect of updating the oa_buffer.tails[], .aging_timestamp
* and .aged_tail_idx state used for reading.
*
* Note: It's safe to read OA config state here unlocked, assuming that this is
* only called while the stream is enabled, while the global OA configuration
* can't be modified.
*
* Returns: %true if the OA buffer contains data, else %false
*/
static bool gen7_oa_buffer_is_empty_fop_unlocked(struct drm_i915_private *dev_priv)
static bool gen7_oa_buffer_check_unlocked(struct drm_i915_private *dev_priv)
{
int report_size = dev_priv->perf.oa.oa_buffer.format_size;
u32 oastatus2 = I915_READ(GEN7_OASTATUS2);
u32 oastatus1 = I915_READ(GEN7_OASTATUS1);
u32 head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK;
u32 tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
unsigned long flags;
unsigned int aged_idx;
u32 oastatus1;
u32 head, hw_tail, aged_tail, aging_tail;
u64 now;
return OA_TAKEN(tail, head) <
dev_priv->perf.oa.tail_margin + report_size;
/* We have to consider the (unlikely) possibility that read() errors
* could result in an OA buffer reset which might reset the head,
* tails[] and aged_tail state.
*/
spin_lock_irqsave(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
/* NB: The head we observe here might effectively be a little out of
* date (between head and tails[aged_idx].offset if there is currently
* a read() in progress.
*/
head = dev_priv->perf.oa.oa_buffer.head;
aged_idx = dev_priv->perf.oa.oa_buffer.aged_tail_idx;
aged_tail = dev_priv->perf.oa.oa_buffer.tails[aged_idx].offset;
aging_tail = dev_priv->perf.oa.oa_buffer.tails[!aged_idx].offset;
oastatus1 = I915_READ(GEN7_OASTATUS1);
hw_tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
/* The tail pointer increases in 64 byte increments,
* not in report_size steps...
*/
hw_tail &= ~(report_size - 1);
now = ktime_get_mono_fast_ns();
/* Update the aged tail
*
* Flip the tail pointer available for read()s once the aging tail is
* old enough to trust that the corresponding data will be visible to
* the CPU...
*
* Do this before updating the aging pointer in case we may be able to
* immediately start aging a new pointer too (if new data has become
* available) without needing to wait for a later hrtimer callback.
*/
if (aging_tail != INVALID_TAIL_PTR &&
((now - dev_priv->perf.oa.oa_buffer.aging_timestamp) >
OA_TAIL_MARGIN_NSEC)) {
aged_idx ^= 1;
dev_priv->perf.oa.oa_buffer.aged_tail_idx = aged_idx;
aged_tail = aging_tail;
/* Mark that we need a new pointer to start aging... */
dev_priv->perf.oa.oa_buffer.tails[!aged_idx].offset = INVALID_TAIL_PTR;
aging_tail = INVALID_TAIL_PTR;
}
/* Update the aging tail
*
* We throttle aging tail updates until we have a new tail that
* represents >= one report more data than is already available for
* reading. This ensures there will be enough data for a successful
* read once this new pointer has aged and ensures we will give the new
* pointer time to age.
*/
if (aging_tail == INVALID_TAIL_PTR &&
(aged_tail == INVALID_TAIL_PTR ||
OA_TAKEN(hw_tail, aged_tail) >= report_size)) {
struct i915_vma *vma = dev_priv->perf.oa.oa_buffer.vma;
u32 gtt_offset = i915_ggtt_offset(vma);
/* Be paranoid and do a bounds check on the pointer read back
* from hardware, just in case some spurious hardware condition
* could put the tail out of bounds...
*/
if (hw_tail >= gtt_offset &&
hw_tail < (gtt_offset + OA_BUFFER_SIZE)) {
dev_priv->perf.oa.oa_buffer.tails[!aged_idx].offset =
aging_tail = hw_tail;
dev_priv->perf.oa.oa_buffer.aging_timestamp = now;
} else {
DRM_ERROR("Ignoring spurious out of range OA buffer tail pointer = %u\n",
hw_tail);
}
}
spin_unlock_irqrestore(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
return aged_tail == INVALID_TAIL_PTR ?
false : OA_TAKEN(aged_tail, head) >= report_size;
}
/**
@ -421,8 +539,6 @@ static int append_oa_sample(struct i915_perf_stream *stream,
* @buf: destination buffer given by userspace
* @count: the number of bytes userspace wants to read
* @offset: (inout): the current position for writing into @buf
* @head_ptr: (inout): the current oa buffer cpu read position
* @tail: the current oa buffer gpu write position
*
* Notably any error condition resulting in a short read (-%ENOSPC or
* -%EFAULT) will be returned even though one or more records may
@ -431,7 +547,7 @@ static int append_oa_sample(struct i915_perf_stream *stream,
* userspace.
*
* Note: reports are consumed from the head, and appended to the
* tail, so the head chases the tail?... If you think that's mad
* tail, so the tail chases the head?... If you think that's mad
* and back-to-front you're not alone, but this follows the
* Gen PRM naming convention.
*
@ -440,57 +556,55 @@ static int append_oa_sample(struct i915_perf_stream *stream,
static int gen7_append_oa_reports(struct i915_perf_stream *stream,
char __user *buf,
size_t count,
size_t *offset,
u32 *head_ptr,
u32 tail)
size_t *offset)
{
struct drm_i915_private *dev_priv = stream->dev_priv;
int report_size = dev_priv->perf.oa.oa_buffer.format_size;
u8 *oa_buf_base = dev_priv->perf.oa.oa_buffer.vaddr;
int tail_margin = dev_priv->perf.oa.tail_margin;
u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma);
u32 mask = (OA_BUFFER_SIZE - 1);
u32 head;
size_t start_offset = *offset;
unsigned long flags;
unsigned int aged_tail_idx;
u32 head, tail;
u32 taken;
int ret = 0;
if (WARN_ON(!stream->enabled))
return -EIO;
head = *head_ptr - gtt_offset;
tail -= gtt_offset;
spin_lock_irqsave(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
/* The OA unit is expected to wrap the tail pointer according to the OA
* buffer size and since we should never write a misaligned head
* pointer we don't expect to read one back either...
head = dev_priv->perf.oa.oa_buffer.head;
aged_tail_idx = dev_priv->perf.oa.oa_buffer.aged_tail_idx;
tail = dev_priv->perf.oa.oa_buffer.tails[aged_tail_idx].offset;
spin_unlock_irqrestore(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
/* An invalid tail pointer here means we're still waiting for the poll
* hrtimer callback to give us a pointer
*/
if (tail > OA_BUFFER_SIZE || head > OA_BUFFER_SIZE ||
head % report_size) {
DRM_ERROR("Inconsistent OA buffer pointer (head = %u, tail = %u): force restart\n",
head, tail);
dev_priv->perf.oa.ops.oa_disable(dev_priv);
dev_priv->perf.oa.ops.oa_enable(dev_priv);
*head_ptr = I915_READ(GEN7_OASTATUS2) &
GEN7_OASTATUS2_HEAD_MASK;
return -EIO;
}
/* The tail pointer increases in 64 byte increments, not in report_size
* steps...
*/
tail &= ~(report_size - 1);
/* Move the tail pointer back by the current tail_margin to account for
* the possibility that the latest reports may not have really landed
* in memory yet...
*/
if (OA_TAKEN(tail, head) < report_size + tail_margin)
if (tail == INVALID_TAIL_PTR)
return -EAGAIN;
tail -= tail_margin;
tail &= mask;
/* NB: oa_buffer.head/tail include the gtt_offset which we don't want
* while indexing relative to oa_buf_base.
*/
head -= gtt_offset;
tail -= gtt_offset;
/* An out of bounds or misaligned head or tail pointer implies a driver
* bug since we validate + align the tail pointers we read from the
* hardware and we are in full control of the head pointer which should
* only be incremented by multiples of the report size (notably also
* all a power of two).
*/
if (WARN_ONCE(head > OA_BUFFER_SIZE || head % report_size ||
tail > OA_BUFFER_SIZE || tail % report_size,
"Inconsistent OA buffer pointers: head = %u, tail = %u\n",
head, tail))
return -EIO;
for (/* none */;
(taken = OA_TAKEN(tail, head));
@ -518,7 +632,8 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
* copying it to userspace...
*/
if (report32[0] == 0) {
DRM_NOTE("Skipping spurious, invalid OA report\n");
if (__ratelimit(&dev_priv->perf.oa.spurious_report_rs))
DRM_NOTE("Skipping spurious, invalid OA report\n");
continue;
}
@ -535,7 +650,21 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
report32[0] = 0;
}
*head_ptr = gtt_offset + head;
if (start_offset != *offset) {
spin_lock_irqsave(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
/* We removed the gtt_offset for the copy loop above, indexing
* relative to oa_buf_base so put back here...
*/
head += gtt_offset;
I915_WRITE(GEN7_OASTATUS2,
((head & GEN7_OASTATUS2_HEAD_MASK) |
OA_MEM_SELECT_GGTT));
dev_priv->perf.oa.oa_buffer.head = head;
spin_unlock_irqrestore(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
}
return ret;
}
@ -562,22 +691,14 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
size_t *offset)
{
struct drm_i915_private *dev_priv = stream->dev_priv;
int report_size = dev_priv->perf.oa.oa_buffer.format_size;
u32 oastatus2;
u32 oastatus1;
u32 head;
u32 tail;
int ret;
if (WARN_ON(!dev_priv->perf.oa.oa_buffer.vaddr))
return -EIO;
oastatus2 = I915_READ(GEN7_OASTATUS2);
oastatus1 = I915_READ(GEN7_OASTATUS1);
head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK;
tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
/* XXX: On Haswell we don't have a safe way to clear oastatus1
* bits while the OA unit is enabled (while the tail pointer
* may be updated asynchronously) so we ignore status bits
@ -616,11 +737,7 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
dev_priv->perf.oa.ops.oa_disable(dev_priv);
dev_priv->perf.oa.ops.oa_enable(dev_priv);
oastatus2 = I915_READ(GEN7_OASTATUS2);
oastatus1 = I915_READ(GEN7_OASTATUS1);
head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK;
tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
}
if (unlikely(oastatus1 & GEN7_OASTATUS1_REPORT_LOST)) {
@ -632,29 +749,7 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
GEN7_OASTATUS1_REPORT_LOST;
}
ret = gen7_append_oa_reports(stream, buf, count, offset,
&head, tail);
/* All the report sizes are a power of two and the
* head should always be incremented by some multiple
* of the report size.
*
* A warning here, but notably if we later read back a
* misaligned pointer we will treat that as a bug since
* it could lead to a buffer overrun.
*/
WARN_ONCE(head & (report_size - 1),
"i915: Writing misaligned OA head pointer");
/* Note: we update the head pointer here even if an error
* was returned since the error may represent a short read
* where some some reports were successfully copied.
*/
I915_WRITE(GEN7_OASTATUS2,
((head & GEN7_OASTATUS2_HEAD_MASK) |
OA_MEM_SELECT_GGTT));
return ret;
return gen7_append_oa_reports(stream, buf, count, offset);
}
/**
@ -679,14 +774,8 @@ static int i915_oa_wait_unlocked(struct i915_perf_stream *stream)
if (!dev_priv->perf.oa.periodic)
return -EIO;
/* Note: the oa_buffer_is_empty() condition is ok to run unlocked as it
* just performs mmio reads of the OA buffer head + tail pointers and
* it's assumed we're handling some operation that implies the stream
* can't be destroyed until completion (such as a read()) that ensures
* the device + OA buffer can't disappear
*/
return wait_event_interruptible(dev_priv->perf.oa.poll_wq,
!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv));
dev_priv->perf.oa.ops.oa_buffer_check(dev_priv));
}
/**
@ -744,6 +833,7 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
{
struct drm_i915_private *dev_priv = stream->dev_priv;
struct intel_engine_cs *engine = dev_priv->engine[RCS];
struct intel_ring *ring;
int ret;
ret = i915_mutex_lock_interruptible(&dev_priv->drm);
@ -755,9 +845,10 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
*
* NB: implied RCS engine...
*/
ret = engine->context_pin(engine, stream->ctx);
if (ret)
goto unlock;
ring = engine->context_pin(engine, stream->ctx);
mutex_unlock(&dev_priv->drm.struct_mutex);
if (IS_ERR(ring))
return PTR_ERR(ring);
/* Explicitly track the ID (instead of calling i915_ggtt_offset()
* on the fly) considering the difference with gen8+ and
@ -766,10 +857,7 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
dev_priv->perf.oa.specific_ctx_id =
i915_ggtt_offset(stream->ctx->engine[engine->id].state);
unlock:
mutex_unlock(&dev_priv->drm.struct_mutex);
return ret;
return 0;
}
/**
@ -824,19 +912,36 @@ static void i915_oa_stream_destroy(struct i915_perf_stream *stream)
oa_put_render_ctx_id(stream);
dev_priv->perf.oa.exclusive_stream = NULL;
if (dev_priv->perf.oa.spurious_report_rs.missed) {
DRM_NOTE("%d spurious OA report notices suppressed due to ratelimiting\n",
dev_priv->perf.oa.spurious_report_rs.missed);
}
}
static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
{
u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma);
unsigned long flags;
spin_lock_irqsave(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
/* Pre-DevBDW: OABUFFER must be set with counters off,
* before OASTATUS1, but after OASTATUS2
*/
I915_WRITE(GEN7_OASTATUS2, gtt_offset | OA_MEM_SELECT_GGTT); /* head */
dev_priv->perf.oa.oa_buffer.head = gtt_offset;
I915_WRITE(GEN7_OABUFFER, gtt_offset);
I915_WRITE(GEN7_OASTATUS1, gtt_offset | OABUFFER_SIZE_16M); /* tail */
/* Mark that we need updated tail pointers to read from... */
dev_priv->perf.oa.oa_buffer.tails[0].offset = INVALID_TAIL_PTR;
dev_priv->perf.oa.oa_buffer.tails[1].offset = INVALID_TAIL_PTR;
spin_unlock_irqrestore(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
/* On Haswell we have to track which OASTATUS1 flags we've
* already seen since they can't be cleared while periodic
* sampling is enabled.
@ -1094,12 +1199,6 @@ static void i915_oa_stream_disable(struct i915_perf_stream *stream)
hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer);
}
static u64 oa_exponent_to_ns(struct drm_i915_private *dev_priv, int exponent)
{
return div_u64(1000000000ULL * (2ULL << exponent),
dev_priv->perf.oa.timestamp_frequency);
}
static const struct i915_perf_stream_ops i915_oa_stream_ops = {
.destroy = i915_oa_stream_destroy,
.enable = i915_oa_stream_enable,
@ -1173,6 +1272,26 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
return -EINVAL;
}
/* We set up some ratelimit state to potentially throttle any _NOTES
* about spurious, invalid OA reports which we don't forward to
* userspace.
*
* The initialization is associated with opening the stream (not driver
* init) considering we print a _NOTE about any throttling when closing
* the stream instead of waiting until driver _fini which no one would
* ever see.
*
* Using the same limiting factors as printk_ratelimit()
*/
ratelimit_state_init(&dev_priv->perf.oa.spurious_report_rs,
5 * HZ, 10);
/* Since we use a DRM_NOTE for spurious reports it would be
* inconsistent to let __ratelimit() automatically print a warning for
* throttling.
*/
ratelimit_set_flags(&dev_priv->perf.oa.spurious_report_rs,
RATELIMIT_MSG_ON_RELEASE);
stream->sample_size = sizeof(struct drm_i915_perf_record_header);
format_size = dev_priv->perf.oa.oa_formats[props->oa_format].size;
@ -1190,20 +1309,9 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
dev_priv->perf.oa.metrics_set = props->metrics_set;
dev_priv->perf.oa.periodic = props->oa_periodic;
if (dev_priv->perf.oa.periodic) {
u32 tail;
if (dev_priv->perf.oa.periodic)
dev_priv->perf.oa.period_exponent = props->oa_period_exponent;
/* See comment for OA_TAIL_MARGIN_NSEC for details
* about this tail_margin...
*/
tail = div64_u64(OA_TAIL_MARGIN_NSEC,
oa_exponent_to_ns(dev_priv,
props->oa_period_exponent));
dev_priv->perf.oa.tail_margin = (tail + 1) * format_size;
}
if (stream->ctx) {
ret = oa_get_render_ctx_id(stream);
if (ret)
@ -1352,7 +1460,15 @@ static ssize_t i915_perf_read(struct file *file,
mutex_unlock(&dev_priv->perf.lock);
}
if (ret >= 0) {
/* We allow the poll checking to sometimes report false positive POLLIN
* events where we might actually report EAGAIN on read() if there's
* not really any data available. In this situation though we don't
* want to enter a busy loop between poll() reporting a POLLIN event
* and read() returning -EAGAIN. Clearing the oa.pollin state here
* effectively ensures we back off until the next hrtimer callback
* before reporting another POLLIN event.
*/
if (ret >= 0 || ret == -EAGAIN) {
/* Maybe make ->pollin per-stream state if we support multiple
* concurrent streams in the future.
*/
@ -1368,7 +1484,7 @@ static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer)
container_of(hrtimer, typeof(*dev_priv),
perf.oa.poll_check_timer);
if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)) {
if (dev_priv->perf.oa.ops.oa_buffer_check(dev_priv)) {
dev_priv->perf.oa.pollin = true;
wake_up(&dev_priv->perf.oa.poll_wq);
}
@ -1817,11 +1933,13 @@ static int read_properties_unlocked(struct drm_i915_private *dev_priv,
break;
case DRM_I915_PERF_PROP_OA_FORMAT:
if (value == 0 || value >= I915_OA_FORMAT_MAX) {
DRM_DEBUG("Invalid OA report format\n");
DRM_DEBUG("Out-of-range OA report format %llu\n",
value);
return -EINVAL;
}
if (!dev_priv->perf.oa.oa_formats[value].size) {
DRM_DEBUG("Invalid OA report format\n");
DRM_DEBUG("Unsupported OA report format %llu\n",
value);
return -EINVAL;
}
props->oa_format = value;
@ -2063,6 +2181,7 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
INIT_LIST_HEAD(&dev_priv->perf.streams);
mutex_init(&dev_priv->perf.lock);
spin_lock_init(&dev_priv->perf.hook_lock);
spin_lock_init(&dev_priv->perf.oa.oa_buffer.ptr_lock);
dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer;
dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set;
@ -2070,10 +2189,8 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
dev_priv->perf.oa.ops.oa_enable = gen7_oa_enable;
dev_priv->perf.oa.ops.oa_disable = gen7_oa_disable;
dev_priv->perf.oa.ops.read = gen7_oa_read;
dev_priv->perf.oa.ops.oa_buffer_is_empty =
gen7_oa_buffer_is_empty_fop_unlocked;
dev_priv->perf.oa.timestamp_frequency = 12500000;
dev_priv->perf.oa.ops.oa_buffer_check =
gen7_oa_buffer_check_unlocked;
dev_priv->perf.oa.oa_formats = hsw_oa_formats;

View File

@ -85,6 +85,14 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define VECS_HW 3
#define VCS2_HW 4
/* Engine class */
#define RENDER_CLASS 0
#define VIDEO_DECODE_CLASS 1
#define VIDEO_ENHANCEMENT_CLASS 2
#define COPY_ENGINE_CLASS 3
#define OTHER_CLASS 4
/* PCI config space */
#define MCHBAR_I915 0x44
@ -3051,10 +3059,14 @@ enum skl_disp_power_wells {
#define CLKCFG_FSB_667 (3 << 0) /* hrawclk 166 */
#define CLKCFG_FSB_800 (2 << 0) /* hrawclk 200 */
#define CLKCFG_FSB_1067 (6 << 0) /* hrawclk 266 */
#define CLKCFG_FSB_1067_ALT (0 << 0) /* hrawclk 266 */
#define CLKCFG_FSB_1333 (7 << 0) /* hrawclk 333 */
/* Note, below two are guess */
#define CLKCFG_FSB_1600 (4 << 0) /* hrawclk 400 */
#define CLKCFG_FSB_1600_ALT (0 << 0) /* hrawclk 400 */
/*
* Note that on at least on ELK the below value is reported for both
* 333 and 400 MHz BIOS FSB setting, but given that the gmch datasheet
* lists only 200/266/333 MHz FSB as supported let's decode it as 333 MHz.
*/
#define CLKCFG_FSB_1333_ALT (4 << 0) /* hrawclk 333 */
#define CLKCFG_FSB_MASK (7 << 0)
#define CLKCFG_MEM_533 (1 << 4)
#define CLKCFG_MEM_667 (2 << 4)
@ -3362,16 +3374,6 @@ enum skl_disp_power_wells {
#define GEN7_CXT_VFSTATE_SIZE(ctx_reg) (((ctx_reg) >> 0) & 0x3f)
#define GEN7_CXT_TOTAL_SIZE(ctx_reg) (GEN7_CXT_EXTENDED_SIZE(ctx_reg) + \
GEN7_CXT_VFSTATE_SIZE(ctx_reg))
/* Haswell does have the CXT_SIZE register however it does not appear to be
* valid. Now, docs explain in dwords what is in the context object. The full
* size is 70720 bytes, however, the power context and execlist context will
* never be saved (power context is stored elsewhere, and execlists don't work
* on HSW) - so the final size, including the extra state required for the
* Resource Streamer, is 66944 bytes, which rounds to 17 pages.
*/
#define HSW_CXT_TOTAL_SIZE (17 * PAGE_SIZE)
/* Same as Haswell, but 72064 bytes now. */
#define GEN8_CXT_TOTAL_SIZE (18 * PAGE_SIZE)
enum {
INTEL_ADVANCED_CONTEXT = 0,
@ -5437,9 +5439,7 @@ enum {
#define CURSOR_MODE_128_ARGB_AX ((1 << 5) | CURSOR_MODE_128_32B_AX)
#define CURSOR_MODE_256_ARGB_AX ((1 << 5) | CURSOR_MODE_256_32B_AX)
#define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX)
#define MCURSOR_PIPE_SELECT (1 << 28)
#define MCURSOR_PIPE_A 0x00
#define MCURSOR_PIPE_B (1 << 28)
#define MCURSOR_PIPE_SELECT(pipe) ((pipe) << 28)
#define MCURSOR_GAMMA_ENABLE (1 << 26)
#define CURSOR_ROTATE_180 (1<<15)
#define CURSOR_TRICKLE_FEED_DISABLE (1 << 14)
@ -5449,7 +5449,9 @@ enum {
#define CURSOR_POS_SIGN 0x8000
#define CURSOR_X_SHIFT 0
#define CURSOR_Y_SHIFT 16
#define CURSIZE _MMIO(0x700a0)
#define CURSIZE _MMIO(0x700a0) /* 845/865 */
#define _CUR_FBC_CTL_A 0x700a0 /* ivb+ */
#define CUR_FBC_CTL_EN (1 << 31)
#define _CURBCNTR 0x700c0
#define _CURBBASE 0x700c4
#define _CURBPOS 0x700c8
@ -5465,6 +5467,7 @@ enum {
#define CURCNTR(pipe) _CURSOR2(pipe, _CURACNTR)
#define CURBASE(pipe) _CURSOR2(pipe, _CURABASE)
#define CURPOS(pipe) _CURSOR2(pipe, _CURAPOS)
#define CUR_FBC_CTL(pipe) _CURSOR2(pipe, _CUR_FBC_CTL_A)
#define CURSOR_A_OFFSET 0x70080
#define CURSOR_B_OFFSET 0x700c0
@ -5497,8 +5500,7 @@ enum {
#define DISPPLANE_PIPE_CSC_ENABLE (1<<24)
#define DISPPLANE_SEL_PIPE_SHIFT 24
#define DISPPLANE_SEL_PIPE_MASK (3<<DISPPLANE_SEL_PIPE_SHIFT)
#define DISPPLANE_SEL_PIPE_A 0
#define DISPPLANE_SEL_PIPE_B (1<<DISPPLANE_SEL_PIPE_SHIFT)
#define DISPPLANE_SEL_PIPE(pipe) ((pipe)<<DISPPLANE_SEL_PIPE_SHIFT)
#define DISPPLANE_SRC_KEY_ENABLE (1<<22)
#define DISPPLANE_SRC_KEY_DISABLE 0
#define DISPPLANE_LINE_DOUBLE (1<<20)
@ -8276,7 +8278,7 @@ enum {
/* MIPI DSI registers */
#define _MIPI_PORT(port, a, c) ((port) ? c : a) /* ports A and C only */
#define _MIPI_PORT(port, a, c) (((port) == PORT_A) ? a : c) /* ports A and C only */
#define _MMIO_MIPI(port, a, c) _MMIO(_MIPI_PORT(port, a, c))
#define MIPIO_TXESC_CLK_DIV1 _MMIO(0x160004)

View File

@ -12,6 +12,7 @@
#include <linux/reservation.h>
#include "i915_sw_fence.h"
#include "i915_selftest.h"
#define I915_SW_FENCE_FLAG_ALLOC BIT(3) /* after WQ_FLAG_* for safety */
@ -120,34 +121,6 @@ void i915_sw_fence_fini(struct i915_sw_fence *fence)
}
#endif
static void i915_sw_fence_release(struct kref *kref)
{
struct i915_sw_fence *fence = container_of(kref, typeof(*fence), kref);
WARN_ON(atomic_read(&fence->pending) > 0);
debug_fence_destroy(fence);
if (fence->flags & I915_SW_FENCE_MASK) {
__i915_sw_fence_notify(fence, FENCE_FREE);
} else {
i915_sw_fence_fini(fence);
kfree(fence);
}
}
static void i915_sw_fence_put(struct i915_sw_fence *fence)
{
debug_fence_assert(fence);
kref_put(&fence->kref, i915_sw_fence_release);
}
static struct i915_sw_fence *i915_sw_fence_get(struct i915_sw_fence *fence)
{
debug_fence_assert(fence);
kref_get(&fence->kref);
return fence;
}
static void __i915_sw_fence_wake_up_all(struct i915_sw_fence *fence,
struct list_head *continuation)
{
@ -202,13 +175,15 @@ static void __i915_sw_fence_complete(struct i915_sw_fence *fence,
debug_fence_set_state(fence, DEBUG_FENCE_IDLE, DEBUG_FENCE_NOTIFY);
if (fence->flags & I915_SW_FENCE_MASK &&
__i915_sw_fence_notify(fence, FENCE_COMPLETE) != NOTIFY_DONE)
if (__i915_sw_fence_notify(fence, FENCE_COMPLETE) != NOTIFY_DONE)
return;
debug_fence_set_state(fence, DEBUG_FENCE_NOTIFY, DEBUG_FENCE_IDLE);
__i915_sw_fence_wake_up_all(fence, continuation);
debug_fence_destroy(fence);
__i915_sw_fence_notify(fence, FENCE_FREE);
}
static void i915_sw_fence_complete(struct i915_sw_fence *fence)
@ -232,33 +207,26 @@ void __i915_sw_fence_init(struct i915_sw_fence *fence,
const char *name,
struct lock_class_key *key)
{
BUG_ON((unsigned long)fn & ~I915_SW_FENCE_MASK);
BUG_ON(!fn || (unsigned long)fn & ~I915_SW_FENCE_MASK);
debug_fence_init(fence);
__init_waitqueue_head(&fence->wait, name, key);
kref_init(&fence->kref);
atomic_set(&fence->pending, 1);
fence->flags = (unsigned long)fn;
}
static void __i915_sw_fence_commit(struct i915_sw_fence *fence)
{
i915_sw_fence_complete(fence);
i915_sw_fence_put(fence);
}
void i915_sw_fence_commit(struct i915_sw_fence *fence)
{
debug_fence_activate(fence);
__i915_sw_fence_commit(fence);
i915_sw_fence_complete(fence);
}
static int i915_sw_fence_wake(wait_queue_t *wq, unsigned mode, int flags, void *key)
{
list_del(&wq->task_list);
__i915_sw_fence_complete(wq->private, key);
i915_sw_fence_put(wq->private);
if (wq->flags & I915_SW_FENCE_FLAG_ALLOC)
kfree(wq);
return 0;
@ -307,7 +275,7 @@ static bool i915_sw_fence_check_if_after(struct i915_sw_fence *fence,
unsigned long flags;
bool err;
if (!IS_ENABLED(CONFIG_I915_SW_FENCE_CHECK_DAG))
if (!IS_ENABLED(CONFIG_DRM_I915_SW_FENCE_CHECK_DAG))
return false;
spin_lock_irqsave(&i915_sw_fence_lock, flags);
@ -353,7 +321,7 @@ static int __i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence,
INIT_LIST_HEAD(&wq->task_list);
wq->flags = pending;
wq->func = i915_sw_fence_wake;
wq->private = i915_sw_fence_get(fence);
wq->private = fence;
i915_sw_fence_await(fence);
@ -402,7 +370,7 @@ static void timer_i915_sw_fence_wake(unsigned long data)
dma_fence_put(cb->dma);
cb->dma = NULL;
__i915_sw_fence_commit(cb->fence);
i915_sw_fence_complete(cb->fence);
cb->timer.function = NULL;
}
@ -413,7 +381,7 @@ static void dma_i915_sw_fence_wake(struct dma_fence *dma,
del_timer_sync(&cb->timer);
if (cb->timer.function)
__i915_sw_fence_commit(cb->fence);
i915_sw_fence_complete(cb->fence);
dma_fence_put(cb->dma);
kfree(cb);
@ -440,7 +408,7 @@ int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence,
return dma_fence_wait(dma, false);
}
cb->fence = i915_sw_fence_get(fence);
cb->fence = fence;
i915_sw_fence_await(fence);
cb->dma = NULL;
@ -523,3 +491,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
return ret;
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/i915_sw_fence.c"
#endif

View File

@ -23,7 +23,6 @@ struct reservation_object;
struct i915_sw_fence {
wait_queue_head_t wait;
unsigned long flags;
struct kref kref;
atomic_t pending;
};

View File

@ -0,0 +1,412 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/slab.h>
#include "i915_syncmap.h"
#include "i915_gem.h" /* GEM_BUG_ON() */
#include "i915_selftest.h"
#define SHIFT ilog2(KSYNCMAP)
#define MASK (KSYNCMAP - 1)
/*
* struct i915_syncmap is a layer of a radixtree that maps a u64 fence
* context id to the last u32 fence seqno waited upon from that context.
* Unlike lib/radixtree it uses a parent pointer that allows traversal back to
* the root. This allows us to access the whole tree via a single pointer
* to the most recently used layer. We expect fence contexts to be dense
* and most reuse to be on the same i915_gem_context but on neighbouring
* engines (i.e. on adjacent contexts) and reuse the same leaf, a very
* effective lookup cache. If the new lookup is not on the same leaf, we
* expect it to be on the neighbouring branch.
*
* A leaf holds an array of u32 seqno, and has height 0. The bitmap field
* allows us to store whether a particular seqno is valid (i.e. allows us
* to distinguish unset from 0).
*
* A branch holds an array of layer pointers, and has height > 0, and always
* has at least 2 layers (either branches or leaves) below it.
*
* For example,
* for x in
* 0 1 2 0x10 0x11 0x200 0x201
* 0x500000 0x500001 0x503000 0x503001
* 0xE<<60:
* i915_syncmap_set(&sync, x, lower_32_bits(x));
* will build a tree like:
* 0xXXXXXXXXXXXXXXXX
* 0-> 0x0000000000XXXXXX
* | 0-> 0x0000000000000XXX
* | | 0-> 0x00000000000000XX
* | | | 0-> 0x000000000000000X 0:0, 1:1, 2:2
* | | | 1-> 0x000000000000001X 0:10, 1:11
* | | 2-> 0x000000000000020X 0:200, 1:201
* | 5-> 0x000000000050XXXX
* | 0-> 0x000000000050000X 0:500000, 1:500001
* | 3-> 0x000000000050300X 0:503000, 1:503001
* e-> 0xe00000000000000X e:e
*/
struct i915_syncmap {
u64 prefix;
unsigned int height;
unsigned int bitmap;
struct i915_syncmap *parent;
/*
* Following this header is an array of either seqno or child pointers:
* union {
* u32 seqno[KSYNCMAP];
* struct i915_syncmap *child[KSYNCMAP];
* };
*/
};
/**
* i915_syncmap_init -- initialise the #i915_syncmap
* @root - pointer to the #i915_syncmap
*/
void i915_syncmap_init(struct i915_syncmap **root)
{
BUILD_BUG_ON_NOT_POWER_OF_2(KSYNCMAP);
BUILD_BUG_ON_NOT_POWER_OF_2(SHIFT);
BUILD_BUG_ON(KSYNCMAP > BITS_PER_BYTE * sizeof((*root)->bitmap));
*root = NULL;
}
static inline u32 *__sync_seqno(struct i915_syncmap *p)
{
GEM_BUG_ON(p->height);
return (u32 *)(p + 1);
}
static inline struct i915_syncmap **__sync_child(struct i915_syncmap *p)
{
GEM_BUG_ON(!p->height);
return (struct i915_syncmap **)(p + 1);
}
static inline unsigned int
__sync_branch_idx(const struct i915_syncmap *p, u64 id)
{
return (id >> p->height) & MASK;
}
static inline unsigned int
__sync_leaf_idx(const struct i915_syncmap *p, u64 id)
{
GEM_BUG_ON(p->height);
return id & MASK;
}
static inline u64 __sync_branch_prefix(const struct i915_syncmap *p, u64 id)
{
return id >> p->height >> SHIFT;
}
static inline u64 __sync_leaf_prefix(const struct i915_syncmap *p, u64 id)
{
GEM_BUG_ON(p->height);
return id >> SHIFT;
}
static inline bool seqno_later(u32 a, u32 b)
{
return (s32)(a - b) >= 0;
}
/**
* i915_syncmap_is_later -- compare against the last know sync point
* @root - pointer to the #i915_syncmap
* @id - the context id (other timeline) we are synchronising to
* @seqno - the sequence number along the other timeline
*
* If we have already synchronised this @root timeline with another (@id) then
* we can omit any repeated or earlier synchronisation requests. If the two
* timelines are already coupled, we can also omit the dependency between the
* two as that is already known via the timeline.
*
* Returns true if the two timelines are already synchronised wrt to @seqno,
* false if not and the synchronisation must be emitted.
*/
bool i915_syncmap_is_later(struct i915_syncmap **root, u64 id, u32 seqno)
{
struct i915_syncmap *p;
unsigned int idx;
p = *root;
if (!p)
return false;
if (likely(__sync_leaf_prefix(p, id) == p->prefix))
goto found;
/* First climb the tree back to a parent branch */
do {
p = p->parent;
if (!p)
return false;
if (__sync_branch_prefix(p, id) == p->prefix)
break;
} while (1);
/* And then descend again until we find our leaf */
do {
if (!p->height)
break;
p = __sync_child(p)[__sync_branch_idx(p, id)];
if (!p)
return false;
if (__sync_branch_prefix(p, id) != p->prefix)
return false;
} while (1);
*root = p;
found:
idx = __sync_leaf_idx(p, id);
if (!(p->bitmap & BIT(idx)))
return false;
return seqno_later(__sync_seqno(p)[idx], seqno);
}
static struct i915_syncmap *
__sync_alloc_leaf(struct i915_syncmap *parent, u64 id)
{
struct i915_syncmap *p;
p = kmalloc(sizeof(*p) + KSYNCMAP * sizeof(u32), GFP_KERNEL);
if (unlikely(!p))
return NULL;
p->parent = parent;
p->height = 0;
p->bitmap = 0;
p->prefix = __sync_leaf_prefix(p, id);
return p;
}
static inline void __sync_set_seqno(struct i915_syncmap *p, u64 id, u32 seqno)
{
unsigned int idx = __sync_leaf_idx(p, id);
p->bitmap |= BIT(idx);
__sync_seqno(p)[idx] = seqno;
}
static inline void __sync_set_child(struct i915_syncmap *p,
unsigned int idx,
struct i915_syncmap *child)
{
p->bitmap |= BIT(idx);
__sync_child(p)[idx] = child;
}
static noinline int __sync_set(struct i915_syncmap **root, u64 id, u32 seqno)
{
struct i915_syncmap *p = *root;
unsigned int idx;
if (!p) {
p = __sync_alloc_leaf(NULL, id);
if (unlikely(!p))
return -ENOMEM;
goto found;
}
/* Caller handled the likely cached case */
GEM_BUG_ON(__sync_leaf_prefix(p, id) == p->prefix);
/* Climb back up the tree until we find a common prefix */
do {
if (!p->parent)
break;
p = p->parent;
if (__sync_branch_prefix(p, id) == p->prefix)
break;
} while (1);
/*
* No shortcut, we have to descend the tree to find the right layer
* containing this fence.
*
* Each layer in the tree holds 16 (KSYNCMAP) pointers, either fences
* or lower layers. Leaf nodes (height = 0) contain the fences, all
* other nodes (height > 0) are internal layers that point to a lower
* node. Each internal layer has at least 2 descendents.
*
* Starting at the top, we check whether the current prefix matches. If
* it doesn't, we have gone past our target and need to insert a join
* into the tree, and a new leaf node for the target as a descendent
* of the join, as well as the original layer.
*
* The matching prefix means we are still following the right branch
* of the tree. If it has height 0, we have found our leaf and just
* need to replace the fence slot with ourselves. If the height is
* not zero, our slot contains the next layer in the tree (unless
* it is empty, in which case we can add ourselves as a new leaf).
* As descend the tree the prefix grows (and height decreases).
*/
do {
struct i915_syncmap *next;
if (__sync_branch_prefix(p, id) != p->prefix) {
unsigned int above;
/* Insert a join above the current layer */
next = kzalloc(sizeof(*next) + KSYNCMAP * sizeof(next),
GFP_KERNEL);
if (unlikely(!next))
return -ENOMEM;
/* Compute the height at which these two diverge */
above = fls64(__sync_branch_prefix(p, id) ^ p->prefix);
above = round_up(above, SHIFT);
next->height = above + p->height;
next->prefix = __sync_branch_prefix(next, id);
/* Insert the join into the parent */
if (p->parent) {
idx = __sync_branch_idx(p->parent, id);
__sync_child(p->parent)[idx] = next;
GEM_BUG_ON(!(p->parent->bitmap & BIT(idx)));
}
next->parent = p->parent;
/* Compute the idx of the other branch, not our id! */
idx = p->prefix >> (above - SHIFT) & MASK;
__sync_set_child(next, idx, p);
p->parent = next;
/* Ascend to the join */
p = next;
} else {
if (!p->height)
break;
}
/* Descend into the next layer */
GEM_BUG_ON(!p->height);
idx = __sync_branch_idx(p, id);
next = __sync_child(p)[idx];
if (!next) {
next = __sync_alloc_leaf(p, id);
if (unlikely(!next))
return -ENOMEM;
__sync_set_child(p, idx, next);
p = next;
break;
}
p = next;
} while (1);
found:
GEM_BUG_ON(p->prefix != __sync_leaf_prefix(p, id));
__sync_set_seqno(p, id, seqno);
*root = p;
return 0;
}
/**
* i915_syncmap_set -- mark the most recent syncpoint between contexts
* @root - pointer to the #i915_syncmap
* @id - the context id (other timeline) we have synchronised to
* @seqno - the sequence number along the other timeline
*
* When we synchronise this @root timeline with another (@id), we also know
* that we have synchronized with all previous seqno along that timeline. If
* we then have a request to synchronise with the same seqno or older, we can
* omit it, see i915_syncmap_is_later()
*
* Returns 0 on success, or a negative error code.
*/
int i915_syncmap_set(struct i915_syncmap **root, u64 id, u32 seqno)
{
struct i915_syncmap *p = *root;
/*
* We expect to be called in sequence following is_later(id), which
* should have preloaded the root for us.
*/
if (likely(p && __sync_leaf_prefix(p, id) == p->prefix)) {
__sync_set_seqno(p, id, seqno);
return 0;
}
return __sync_set(root, id, seqno);
}
static void __sync_free(struct i915_syncmap *p)
{
if (p->height) {
unsigned int i;
while ((i = ffs(p->bitmap))) {
p->bitmap &= ~0u << i;
__sync_free(__sync_child(p)[i - 1]);
}
}
kfree(p);
}
/**
* i915_syncmap_free -- free all memory associated with the syncmap
* @root - pointer to the #i915_syncmap
*
* Either when the timeline is to be freed and we no longer need the sync
* point tracking, or when the fences are all known to be signaled and the
* sync point tracking is redundant, we can free the #i915_syncmap to recover
* its allocations.
*
* Will reinitialise the @root pointer so that the #i915_syncmap is ready for
* reuse.
*/
void i915_syncmap_free(struct i915_syncmap **root)
{
struct i915_syncmap *p;
p = *root;
if (!p)
return;
while (p->parent)
p = p->parent;
__sync_free(p);
*root = NULL;
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/i915_syncmap.c"
#endif

View File

@ -0,0 +1,38 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __I915_SYNCMAP_H__
#define __I915_SYNCMAP_H__
#include <linux/types.h>
struct i915_syncmap;
#define KSYNCMAP 16 /* radix of the tree, how many slots in each layer */
void i915_syncmap_init(struct i915_syncmap **root);
int i915_syncmap_set(struct i915_syncmap **root, u64 id, u32 seqno);
bool i915_syncmap_is_later(struct i915_syncmap **root, u64 id, u32 seqno);
void i915_syncmap_free(struct i915_syncmap **root);
#endif /* __I915_SYNCMAP_H__ */

View File

@ -181,13 +181,10 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
struct drm_device *dev = &dev_priv->drm;
struct i915_gem_context *ctx;
u32 *temp = NULL; /* Just here to make handling failures easy */
int slice = (int)(uintptr_t)attr->private;
u32 **remap_info;
int ret;
if (!HAS_HW_CONTEXTS(dev_priv))
return -ENXIO;
ret = l3_access_valid(dev_priv, offset);
if (ret)
return ret;
@ -196,11 +193,12 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
if (ret)
return ret;
if (!dev_priv->l3_parity.remap_info[slice]) {
temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
if (!temp) {
mutex_unlock(&dev->struct_mutex);
return -ENOMEM;
remap_info = &dev_priv->l3_parity.remap_info[slice];
if (!*remap_info) {
*remap_info = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
if (!*remap_info) {
ret = -ENOMEM;
goto out;
}
}
@ -208,18 +206,18 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
* aren't propagated. Since I cannot find a stable way to reset the GPU
* at this point it is left as a TODO.
*/
if (temp)
dev_priv->l3_parity.remap_info[slice] = temp;
memcpy(dev_priv->l3_parity.remap_info[slice] + (offset/4), buf, count);
memcpy(*remap_info + (offset/4), buf, count);
/* NB: We defer the remapping until we switch to the context */
list_for_each_entry(ctx, &dev_priv->context_list, link)
ctx->remap_slice |= (1<<slice);
ret = count;
out:
mutex_unlock(&dev->struct_mutex);
return count;
return ret;
}
static struct bin_attribute dpf_attrs = {

View File

@ -89,6 +89,55 @@ TRACE_EVENT(intel_memory_cxsr,
__entry->frame[PIPE_C], __entry->scanline[PIPE_C])
);
TRACE_EVENT(g4x_wm,
TP_PROTO(struct intel_crtc *crtc, const struct g4x_wm_values *wm),
TP_ARGS(crtc, wm),
TP_STRUCT__entry(
__field(enum pipe, pipe)
__field(u32, frame)
__field(u32, scanline)
__field(u16, primary)
__field(u16, sprite)
__field(u16, cursor)
__field(u16, sr_plane)
__field(u16, sr_cursor)
__field(u16, sr_fbc)
__field(u16, hpll_plane)
__field(u16, hpll_cursor)
__field(u16, hpll_fbc)
__field(bool, cxsr)
__field(bool, hpll)
__field(bool, fbc)
),
TP_fast_assign(
__entry->pipe = crtc->pipe;
__entry->frame = crtc->base.dev->driver->get_vblank_counter(crtc->base.dev,
crtc->pipe);
__entry->scanline = intel_get_crtc_scanline(crtc);
__entry->primary = wm->pipe[crtc->pipe].plane[PLANE_PRIMARY];
__entry->sprite = wm->pipe[crtc->pipe].plane[PLANE_SPRITE0];
__entry->cursor = wm->pipe[crtc->pipe].plane[PLANE_CURSOR];
__entry->sr_plane = wm->sr.plane;
__entry->sr_cursor = wm->sr.cursor;
__entry->sr_fbc = wm->sr.fbc;
__entry->hpll_plane = wm->hpll.plane;
__entry->hpll_cursor = wm->hpll.cursor;
__entry->hpll_fbc = wm->hpll.fbc;
__entry->cxsr = wm->cxsr;
__entry->hpll = wm->hpll_en;
__entry->fbc = wm->fbc_en;
),
TP_printk("pipe %c, frame=%u, scanline=%u, wm %d/%d/%d, sr %s/%d/%d/%d, hpll %s/%d/%d/%d, fbc %s",
pipe_name(__entry->pipe), __entry->frame, __entry->scanline,
__entry->primary, __entry->sprite, __entry->cursor,
yesno(__entry->cxsr), __entry->sr_plane, __entry->sr_cursor, __entry->sr_fbc,
yesno(__entry->hpll), __entry->hpll_plane, __entry->hpll_cursor, __entry->hpll_fbc,
yesno(__entry->fbc))
);
TRACE_EVENT(vlv_wm,
TP_PROTO(struct intel_crtc *crtc, const struct vlv_wm_values *wm),
TP_ARGS(crtc, wm),

View File

@ -70,20 +70,27 @@
#define overflows_type(x, T) \
(sizeof(x) > sizeof(T) && (x) >> (sizeof(T) * BITS_PER_BYTE))
#define ptr_mask_bits(ptr) ({ \
#define ptr_mask_bits(ptr, n) ({ \
unsigned long __v = (unsigned long)(ptr); \
(typeof(ptr))(__v & PAGE_MASK); \
(typeof(ptr))(__v & -BIT(n)); \
})
#define ptr_unpack_bits(ptr, bits) ({ \
#define ptr_unmask_bits(ptr, n) ((unsigned long)(ptr) & (BIT(n) - 1))
#define ptr_unpack_bits(ptr, bits, n) ({ \
unsigned long __v = (unsigned long)(ptr); \
(bits) = __v & ~PAGE_MASK; \
(typeof(ptr))(__v & PAGE_MASK); \
*(bits) = __v & (BIT(n) - 1); \
(typeof(ptr))(__v & -BIT(n)); \
})
#define ptr_pack_bits(ptr, bits) \
#define ptr_pack_bits(ptr, bits, n) \
((typeof(ptr))((unsigned long)(ptr) | (bits)))
#define page_mask_bits(ptr) ptr_mask_bits(ptr, PAGE_SHIFT)
#define page_unmask_bits(ptr) ptr_unmask_bits(ptr, PAGE_SHIFT)
#define page_pack_bits(ptr, bits) ptr_pack_bits(ptr, bits, PAGE_SHIFT)
#define page_unpack_bits(ptr, bits) ptr_unpack_bits(ptr, bits, PAGE_SHIFT)
#define ptr_offset(ptr, member) offsetof(typeof(*(ptr)), member)
#define fetch_and_zero(ptr) ({ \
@ -92,4 +99,19 @@
__T; \
})
#define __mask_next_bit(mask) ({ \
int __idx = ffs(mask) - 1; \
mask &= ~BIT(__idx); \
__idx; \
})
#include <linux/list.h>
static inline void __list_del_many(struct list_head *head,
struct list_head *first)
{
first->prev = head;
WRITE_ONCE(head->next, first);
}
#endif /* !__I915_UTILS_H */

View File

@ -102,23 +102,7 @@ void
intel_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct i915_vma *vma;
vma = fetch_and_zero(&to_intel_plane_state(state)->vma);
/*
* FIXME: Normally intel_cleanup_plane_fb handles destruction of vma.
* We currently don't clear all planes during driver unload, so we have
* to be able to unpin vma here for now.
*
* Normally this can only happen during unload when kmscon is disabled
* and userspace doesn't attempt to set a framebuffer at all.
*/
if (vma) {
mutex_lock(&plane->dev->struct_mutex);
intel_unpin_fb_vma(vma);
mutex_unlock(&plane->dev->struct_mutex);
}
WARN_ON(to_intel_plane_state(state)->vma);
drm_atomic_helper_plane_destroy_state(plane, state);
}
@ -185,7 +169,7 @@ int intel_plane_atomic_check_with_state(struct intel_crtc_state *crtc_state,
}
intel_state->base.visible = false;
ret = intel_plane->check_plane(plane, crtc_state, intel_state);
ret = intel_plane->check_plane(intel_plane, crtc_state, intel_state);
if (ret)
return ret;
@ -235,14 +219,14 @@ static void intel_plane_atomic_update(struct drm_plane *plane,
trace_intel_update_plane(plane,
to_intel_crtc(crtc));
intel_plane->update_plane(plane,
intel_plane->update_plane(intel_plane,
to_intel_crtc_state(crtc->state),
intel_state);
} else {
trace_intel_disable_plane(plane,
to_intel_crtc(crtc));
intel_plane->disable_plane(plane, crtc);
intel_plane->disable_plane(intel_plane, to_intel_crtc(crtc));
}
}

View File

@ -632,20 +632,9 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder,
(int) port, (int) pipe);
}
switch (intel_encoder->type) {
case INTEL_OUTPUT_HDMI:
intel_lpe_audio_notify(dev_priv, connector->eld, port, pipe,
crtc_state->port_clock,
false, 0);
break;
case INTEL_OUTPUT_DP:
intel_lpe_audio_notify(dev_priv, connector->eld, port, pipe,
adjusted_mode->crtc_clock,
true, crtc_state->port_clock);
break;
default:
break;
}
intel_lpe_audio_notify(dev_priv, pipe, port, connector->eld,
crtc_state->port_clock,
intel_encoder->type == INTEL_OUTPUT_DP);
}
/**
@ -680,7 +669,7 @@ void intel_audio_codec_disable(struct intel_encoder *intel_encoder)
(int) port, (int) pipe);
}
intel_lpe_audio_notify(dev_priv, NULL, port, pipe, 0, false, 0);
intel_lpe_audio_notify(dev_priv, pipe, port, NULL, 0, false);
}
/**

View File

@ -64,10 +64,12 @@ static unsigned long wait_timeout(void)
static noinline void missed_breadcrumb(struct intel_engine_cs *engine)
{
DRM_DEBUG_DRIVER("%s missed breadcrumb at %pF, irq posted? %s\n",
DRM_DEBUG_DRIVER("%s missed breadcrumb at %pF, irq posted? %s, current seqno=%x, last=%x\n",
engine->name, __builtin_return_address(0),
yesno(test_bit(ENGINE_IRQ_BREADCRUMB,
&engine->irq_posted)));
&engine->irq_posted)),
intel_engine_get_seqno(engine),
intel_engine_last_submit(engine));
set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings);
}
@ -665,12 +667,13 @@ static int intel_breadcrumbs_signaler(void *arg)
return 0;
}
void intel_engine_enable_signaling(struct drm_i915_gem_request *request)
void intel_engine_enable_signaling(struct drm_i915_gem_request *request,
bool wakeup)
{
struct intel_engine_cs *engine = request->engine;
struct intel_breadcrumbs *b = &engine->breadcrumbs;
struct rb_node *parent, **p;
bool first, wakeup;
bool first;
u32 seqno;
/* Note that we may be called from an interrupt handler on another
@ -703,7 +706,7 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request)
* If we are the oldest waiter, enable the irq (after which we
* must double check that the seqno did not complete).
*/
wakeup = __intel_engine_add_wait(engine, &request->signaling.wait);
wakeup &= __intel_engine_add_wait(engine, &request->signaling.wait);
/* Now insert ourselves into the retirement ordered list of signals
* on this engine. We track the oldest seqno as that will be the

View File

@ -1071,9 +1071,15 @@ static int bxt_calc_cdclk(int max_pixclk)
static int glk_calc_cdclk(int max_pixclk)
{
if (max_pixclk > 2 * 158400)
/*
* FIXME: Avoid using a pixel clock that is more than 99% of the cdclk
* as a temporary workaround. Use a higher cdclk instead. (Note that
* intel_compute_max_dotclk() limits the max pixel clock to 99% of max
* cdclk.)
*/
if (max_pixclk > DIV_ROUND_UP(2 * 158400 * 99, 100))
return 316800;
else if (max_pixclk > 2 * 79200)
else if (max_pixclk > DIV_ROUND_UP(2 * 79200 * 99, 100))
return 158400;
else
return 79200;
@ -1664,7 +1670,11 @@ static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv)
int max_cdclk_freq = dev_priv->max_cdclk_freq;
if (IS_GEMINILAKE(dev_priv))
return 2 * max_cdclk_freq;
/*
* FIXME: Limiting to 99% as a temporary workaround. See
* glk_calc_cdclk() for details.
*/
return 2 * max_cdclk_freq * 99 / 100;
else if (INTEL_INFO(dev_priv)->gen >= 9 ||
IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
return max_cdclk_freq;
@ -1798,13 +1808,11 @@ static int g4x_hrawclk(struct drm_i915_private *dev_priv)
case CLKCFG_FSB_800:
return 200000;
case CLKCFG_FSB_1067:
case CLKCFG_FSB_1067_ALT:
return 266667;
case CLKCFG_FSB_1333:
case CLKCFG_FSB_1333_ALT:
return 333333;
/* these two are just a guess; one of them might be right */
case CLKCFG_FSB_1600:
case CLKCFG_FSB_1600_ALT:
return 400000;
default:
return 133333;
}

View File

@ -777,13 +777,6 @@ out:
return ret;
}
static int intel_crt_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
return 0;
}
void intel_crt_reset(struct drm_encoder *encoder)
{
struct drm_i915_private *dev_priv = to_i915(encoder->dev);
@ -814,10 +807,9 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
.destroy = intel_crt_destroy,
.set_property = intel_crt_set_property,
.set_property = drm_atomic_helper_connector_set_property,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_get_property = intel_connector_atomic_get_property,
};
static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {

View File

@ -337,7 +337,7 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv)
} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
for_each_pipe(dev_priv, pipe)
info->num_sprites[pipe] = 2;
} else if (INTEL_GEN(dev_priv) >= 5) {
} else if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) {
for_each_pipe(dev_priv, pipe)
info->num_sprites[pipe] = 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -133,36 +133,55 @@ static void vlv_steal_power_sequencer(struct drm_device *dev,
enum pipe pipe);
static void intel_dp_unset_edid(struct intel_dp *intel_dp);
static int
intel_dp_max_link_bw(struct intel_dp *intel_dp)
static int intel_dp_num_rates(u8 link_bw_code)
{
int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
switch (max_link_bw) {
case DP_LINK_BW_1_62:
case DP_LINK_BW_2_7:
case DP_LINK_BW_5_4:
break;
switch (link_bw_code) {
default:
WARN(1, "invalid max DP link bw val %x, using 1.62Gbps\n",
max_link_bw);
max_link_bw = DP_LINK_BW_1_62;
break;
link_bw_code);
case DP_LINK_BW_1_62:
return 1;
case DP_LINK_BW_2_7:
return 2;
case DP_LINK_BW_5_4:
return 3;
}
return max_link_bw;
}
static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp)
/* update sink rates from dpcd */
static void intel_dp_set_sink_rates(struct intel_dp *intel_dp)
{
int i, num_rates;
num_rates = intel_dp_num_rates(intel_dp->dpcd[DP_MAX_LINK_RATE]);
for (i = 0; i < num_rates; i++)
intel_dp->sink_rates[i] = default_rates[i];
intel_dp->num_sink_rates = num_rates;
}
/* Theoretical max between source and sink */
static int intel_dp_max_common_rate(struct intel_dp *intel_dp)
{
return intel_dp->common_rates[intel_dp->num_common_rates - 1];
}
/* Theoretical max between source and sink */
static int intel_dp_max_common_lane_count(struct intel_dp *intel_dp)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
u8 source_max, sink_max;
source_max = intel_dig_port->max_lanes;
sink_max = intel_dp->max_sink_lane_count;
int source_max = intel_dig_port->max_lanes;
int sink_max = drm_dp_max_lane_count(intel_dp->dpcd);
return min(source_max, sink_max);
}
int intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
return intel_dp->max_link_lane_count;
}
int
intel_dp_link_required(int pixel_clock, int bpp)
{
@ -205,34 +224,25 @@ intel_dp_downstream_max_dotclock(struct intel_dp *intel_dp)
return max_dotclk;
}
static int
intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
{
if (intel_dp->num_sink_rates) {
*sink_rates = intel_dp->sink_rates;
return intel_dp->num_sink_rates;
}
*sink_rates = default_rates;
return (intel_dp->max_sink_link_bw >> 3) + 1;
}
static int
intel_dp_source_rates(struct intel_dp *intel_dp, const int **source_rates)
static void
intel_dp_set_source_rates(struct intel_dp *intel_dp)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
const int *source_rates;
int size;
/* This should only be done once */
WARN_ON(intel_dp->source_rates || intel_dp->num_source_rates);
if (IS_GEN9_LP(dev_priv)) {
*source_rates = bxt_rates;
source_rates = bxt_rates;
size = ARRAY_SIZE(bxt_rates);
} else if (IS_GEN9_BC(dev_priv)) {
*source_rates = skl_rates;
source_rates = skl_rates;
size = ARRAY_SIZE(skl_rates);
} else {
*source_rates = default_rates;
source_rates = default_rates;
size = ARRAY_SIZE(default_rates);
}
@ -240,7 +250,8 @@ intel_dp_source_rates(struct intel_dp *intel_dp, const int **source_rates)
if (!intel_dp_source_supports_hbr2(intel_dp))
size--;
return size;
intel_dp->source_rates = source_rates;
intel_dp->num_source_rates = size;
}
static int intersect_rates(const int *source_rates, int source_len,
@ -266,50 +277,83 @@ static int intersect_rates(const int *source_rates, int source_len,
return k;
}
static int intel_dp_common_rates(struct intel_dp *intel_dp,
int *common_rates)
/* return index of rate in rates array, or -1 if not found */
static int intel_dp_rate_index(const int *rates, int len, int rate)
{
const int *source_rates, *sink_rates;
int source_len, sink_len;
int i;
sink_len = intel_dp_sink_rates(intel_dp, &sink_rates);
source_len = intel_dp_source_rates(intel_dp, &source_rates);
return intersect_rates(source_rates, source_len,
sink_rates, sink_len,
common_rates);
}
static int intel_dp_link_rate_index(struct intel_dp *intel_dp,
int *common_rates, int link_rate)
{
int common_len;
int index;
common_len = intel_dp_common_rates(intel_dp, common_rates);
for (index = 0; index < common_len; index++) {
if (link_rate == common_rates[common_len - index - 1])
return common_len - index - 1;
}
for (i = 0; i < len; i++)
if (rate == rates[i])
return i;
return -1;
}
static void intel_dp_set_common_rates(struct intel_dp *intel_dp)
{
WARN_ON(!intel_dp->num_source_rates || !intel_dp->num_sink_rates);
intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates,
intel_dp->num_source_rates,
intel_dp->sink_rates,
intel_dp->num_sink_rates,
intel_dp->common_rates);
/* Paranoia, there should always be something in common. */
if (WARN_ON(intel_dp->num_common_rates == 0)) {
intel_dp->common_rates[0] = default_rates[0];
intel_dp->num_common_rates = 1;
}
}
/* get length of common rates potentially limited by max_rate */
static int intel_dp_common_len_rate_limit(struct intel_dp *intel_dp,
int max_rate)
{
const int *common_rates = intel_dp->common_rates;
int i, common_len = intel_dp->num_common_rates;
/* Limit results by potentially reduced max rate */
for (i = 0; i < common_len; i++) {
if (common_rates[common_len - i - 1] <= max_rate)
return common_len - i;
}
return 0;
}
static bool intel_dp_link_params_valid(struct intel_dp *intel_dp)
{
/*
* FIXME: we need to synchronize the current link parameters with
* hardware readout. Currently fast link training doesn't work on
* boot-up.
*/
if (intel_dp->link_rate == 0 ||
intel_dp->link_rate > intel_dp->max_link_rate)
return false;
if (intel_dp->lane_count == 0 ||
intel_dp->lane_count > intel_dp_max_lane_count(intel_dp))
return false;
return true;
}
int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
int link_rate, uint8_t lane_count)
{
int common_rates[DP_MAX_SUPPORTED_RATES];
int link_rate_index;
int index;
link_rate_index = intel_dp_link_rate_index(intel_dp,
common_rates,
link_rate);
if (link_rate_index > 0) {
intel_dp->max_sink_link_bw = drm_dp_link_rate_to_bw_code(common_rates[link_rate_index - 1]);
intel_dp->max_sink_lane_count = lane_count;
index = intel_dp_rate_index(intel_dp->common_rates,
intel_dp->num_common_rates,
link_rate);
if (index > 0) {
intel_dp->max_link_rate = intel_dp->common_rates[index - 1];
intel_dp->max_link_lane_count = lane_count;
} else if (lane_count > 1) {
intel_dp->max_sink_link_bw = intel_dp_max_link_bw(intel_dp);
intel_dp->max_sink_lane_count = lane_count >> 1;
intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
intel_dp->max_link_lane_count = lane_count >> 1;
} else {
DRM_ERROR("Link Training Unsuccessful\n");
return -1;
@ -1486,24 +1530,21 @@ static void snprintf_int_array(char *str, size_t len,
static void intel_dp_print_rates(struct intel_dp *intel_dp)
{
const int *source_rates, *sink_rates;
int source_len, sink_len, common_len;
int common_rates[DP_MAX_SUPPORTED_RATES];
char str[128]; /* FIXME: too big for stack? */
if ((drm_debug & DRM_UT_KMS) == 0)
return;
source_len = intel_dp_source_rates(intel_dp, &source_rates);
snprintf_int_array(str, sizeof(str), source_rates, source_len);
snprintf_int_array(str, sizeof(str),
intel_dp->source_rates, intel_dp->num_source_rates);
DRM_DEBUG_KMS("source rates: %s\n", str);
sink_len = intel_dp_sink_rates(intel_dp, &sink_rates);
snprintf_int_array(str, sizeof(str), sink_rates, sink_len);
snprintf_int_array(str, sizeof(str),
intel_dp->sink_rates, intel_dp->num_sink_rates);
DRM_DEBUG_KMS("sink rates: %s\n", str);
common_len = intel_dp_common_rates(intel_dp, common_rates);
snprintf_int_array(str, sizeof(str), common_rates, common_len);
snprintf_int_array(str, sizeof(str),
intel_dp->common_rates, intel_dp->num_common_rates);
DRM_DEBUG_KMS("common rates: %s\n", str);
}
@ -1538,39 +1579,34 @@ bool intel_dp_read_desc(struct intel_dp *intel_dp)
return true;
}
static int rate_to_index(int find, const int *rates)
{
int i = 0;
for (i = 0; i < DP_MAX_SUPPORTED_RATES; ++i)
if (find == rates[i])
break;
return i;
}
int
intel_dp_max_link_rate(struct intel_dp *intel_dp)
{
int rates[DP_MAX_SUPPORTED_RATES] = {};
int len;
len = intel_dp_common_rates(intel_dp, rates);
len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->max_link_rate);
if (WARN_ON(len <= 0))
return 162000;
return rates[len - 1];
return intel_dp->common_rates[len - 1];
}
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
{
return rate_to_index(rate, intel_dp->sink_rates);
int i = intel_dp_rate_index(intel_dp->sink_rates,
intel_dp->num_sink_rates, rate);
if (WARN_ON(i < 0))
i = 0;
return i;
}
void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
uint8_t *link_bw, uint8_t *rate_select)
{
if (intel_dp->num_sink_rates) {
/* eDP 1.4 rate select method. */
if (intel_dp->use_rate_select) {
*link_bw = 0;
*rate_select =
intel_dp_rate_select(intel_dp, port_clock);
@ -1618,14 +1654,13 @@ intel_dp_compute_config(struct intel_encoder *encoder,
/* Conveniently, the link BW constants become indices with a shift...*/
int min_clock = 0;
int max_clock;
int link_rate_index;
int bpp, mode_rate;
int link_avail, link_clock;
int common_rates[DP_MAX_SUPPORTED_RATES] = {};
int common_len;
uint8_t link_bw, rate_select;
common_len = intel_dp_common_rates(intel_dp, common_rates);
common_len = intel_dp_common_len_rate_limit(intel_dp,
intel_dp->max_link_rate);
/* No common link rates between source and sink */
WARN_ON(common_len <= 0);
@ -1662,16 +1697,18 @@ intel_dp_compute_config(struct intel_encoder *encoder,
/* Use values requested by Compliance Test Request */
if (intel_dp->compliance.test_type == DP_TEST_LINK_TRAINING) {
link_rate_index = intel_dp_link_rate_index(intel_dp,
common_rates,
intel_dp->compliance.test_link_rate);
if (link_rate_index >= 0)
min_clock = max_clock = link_rate_index;
int index;
index = intel_dp_rate_index(intel_dp->common_rates,
intel_dp->num_common_rates,
intel_dp->compliance.test_link_rate);
if (index >= 0)
min_clock = max_clock = index;
min_lane_count = max_lane_count = intel_dp->compliance.test_lane_count;
}
DRM_DEBUG_KMS("DP link computation with max lane count %i "
"max bw %d pixel clock %iKHz\n",
max_lane_count, common_rates[max_clock],
max_lane_count, intel_dp->common_rates[max_clock],
adjusted_mode->crtc_clock);
/* Walk through all bpp values. Luckily they're all nicely spaced with 2
@ -1707,7 +1744,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
lane_count <= max_lane_count;
lane_count <<= 1) {
link_clock = common_rates[clock];
link_clock = intel_dp->common_rates[clock];
link_avail = intel_dp_max_data_rate(link_clock,
lane_count);
@ -1739,7 +1776,7 @@ found:
pipe_config->lane_count = lane_count;
pipe_config->pipe_bpp = bpp;
pipe_config->port_clock = common_rates[clock];
pipe_config->port_clock = intel_dp->common_rates[clock];
intel_dp_compute_rate(intel_dp, pipe_config->port_clock,
&link_bw, &rate_select);
@ -3051,7 +3088,8 @@ static bool intel_dp_get_y_cord_status(struct intel_dp *intel_dp)
{
uint8_t psr_caps = 0;
drm_dp_dpcd_readb(&intel_dp->aux, DP_PSR_CAPS, &psr_caps);
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_PSR_CAPS, &psr_caps) != 1)
return false;
return psr_caps & DP_PSR2_SU_Y_COORDINATE_REQUIRED;
}
@ -3059,9 +3097,9 @@ static bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp)
{
uint8_t dprx = 0;
drm_dp_dpcd_readb(&intel_dp->aux,
DP_DPRX_FEATURE_ENUMERATION_LIST,
&dprx);
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST,
&dprx) != 1)
return false;
return dprx & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED;
}
@ -3069,7 +3107,9 @@ static bool intel_dp_get_alpm_status(struct intel_dp *intel_dp)
{
uint8_t alpm_caps = 0;
drm_dp_dpcd_readb(&intel_dp->aux, DP_RECEIVER_ALPM_CAP, &alpm_caps);
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_RECEIVER_ALPM_CAP,
&alpm_caps) != 1)
return false;
return alpm_caps & DP_ALPM_CAP;
}
@ -3642,9 +3682,10 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
uint8_t frame_sync_cap;
dev_priv->psr.sink_support = true;
drm_dp_dpcd_read(&intel_dp->aux,
DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
&frame_sync_cap, 1);
if (drm_dp_dpcd_readb(&intel_dp->aux,
DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
&frame_sync_cap) != 1)
frame_sync_cap = 0;
dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : false;
/* PSR2 needs frame sync as well */
dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync;
@ -3695,6 +3736,13 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
intel_dp->num_sink_rates = i;
}
if (intel_dp->num_sink_rates)
intel_dp->use_rate_select = true;
else
intel_dp_set_sink_rates(intel_dp);
intel_dp_set_common_rates(intel_dp);
return true;
}
@ -3702,11 +3750,18 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
static bool
intel_dp_get_dpcd(struct intel_dp *intel_dp)
{
u8 sink_count;
if (!intel_dp_read_dpcd(intel_dp))
return false;
if (drm_dp_dpcd_read(&intel_dp->aux, DP_SINK_COUNT,
&intel_dp->sink_count, 1) < 0)
/* Don't clobber cached eDP rates. */
if (!is_edp(intel_dp)) {
intel_dp_set_sink_rates(intel_dp);
intel_dp_set_common_rates(intel_dp);
}
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &sink_count) <= 0)
return false;
/*
@ -3714,7 +3769,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
* a member variable in intel_dp will track any changes
* between short pulse interrupts.
*/
intel_dp->sink_count = DP_GET_SINK_COUNT(intel_dp->sink_count);
intel_dp->sink_count = DP_GET_SINK_COUNT(sink_count);
/*
* SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that
@ -3743,7 +3798,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
static bool
intel_dp_can_mst(struct intel_dp *intel_dp)
{
u8 buf[1];
u8 mstm_cap;
if (!i915.enable_dp_mst)
return false;
@ -3754,10 +3809,10 @@ intel_dp_can_mst(struct intel_dp *intel_dp)
if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
return false;
if (drm_dp_dpcd_read(&intel_dp->aux, DP_MSTM_CAP, buf, 1) != 1)
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_MSTM_CAP, &mstm_cap) != 1)
return false;
return buf[0] & DP_MST_CAP;
return mstm_cap & DP_MST_CAP;
}
static void
@ -3903,9 +3958,8 @@ stop:
static bool
intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
{
return drm_dp_dpcd_read(&intel_dp->aux,
DP_DEVICE_SERVICE_IRQ_VECTOR,
sink_irq_vector, 1) == 1;
return drm_dp_dpcd_readb(&intel_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
sink_irq_vector) == 1;
}
static bool
@ -3926,7 +3980,6 @@ static uint8_t intel_dp_autotest_link_training(struct intel_dp *intel_dp)
{
int status = 0;
int min_lane_count = 1;
int common_rates[DP_MAX_SUPPORTED_RATES] = {};
int link_rate_index, test_link_rate;
uint8_t test_lane_count, test_link_bw;
/* (DP CTS 1.2)
@ -3943,7 +3996,7 @@ static uint8_t intel_dp_autotest_link_training(struct intel_dp *intel_dp)
test_lane_count &= DP_MAX_LANE_COUNT_MASK;
/* Validate the requested lane count */
if (test_lane_count < min_lane_count ||
test_lane_count > intel_dp->max_sink_lane_count)
test_lane_count > intel_dp->max_link_lane_count)
return DP_TEST_NAK;
status = drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_LINK_RATE,
@ -3954,9 +4007,9 @@ static uint8_t intel_dp_autotest_link_training(struct intel_dp *intel_dp)
}
/* Validate the requested link rate */
test_link_rate = drm_dp_bw_code_to_link_rate(test_link_bw);
link_rate_index = intel_dp_link_rate_index(intel_dp,
common_rates,
test_link_rate);
link_rate_index = intel_dp_rate_index(intel_dp->common_rates,
intel_dp->num_common_rates,
test_link_rate);
if (link_rate_index < 0)
return DP_TEST_NAK;
@ -3969,13 +4022,13 @@ static uint8_t intel_dp_autotest_link_training(struct intel_dp *intel_dp)
static uint8_t intel_dp_autotest_video_pattern(struct intel_dp *intel_dp)
{
uint8_t test_pattern;
uint16_t test_misc;
uint8_t test_misc;
__be16 h_width, v_height;
int status = 0;
/* Read the TEST_PATTERN (DP CTS 3.1.5) */
status = drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_PATTERN,
&test_pattern, 1);
status = drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_PATTERN,
&test_pattern);
if (status <= 0) {
DRM_DEBUG_KMS("Test pattern read failed\n");
return DP_TEST_NAK;
@ -3997,8 +4050,8 @@ static uint8_t intel_dp_autotest_video_pattern(struct intel_dp *intel_dp)
return DP_TEST_NAK;
}
status = drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_MISC0,
&test_misc, 1);
status = drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_MISC0,
&test_misc);
if (status <= 0) {
DRM_DEBUG_KMS("TEST MISC read failed\n");
return DP_TEST_NAK;
@ -4057,10 +4110,8 @@ static uint8_t intel_dp_autotest_edid(struct intel_dp *intel_dp)
*/
block += intel_connector->detect_edid->extensions;
if (!drm_dp_dpcd_write(&intel_dp->aux,
DP_TEST_EDID_CHECKSUM,
&block->checksum,
1))
if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_EDID_CHECKSUM,
block->checksum) <= 0)
DRM_DEBUG_KMS("Failed to write EDID checksum\n");
test_result = DP_TEST_ACK | DP_TEST_EDID_CHECKSUM_WRITE;
@ -4224,9 +4275,11 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
if (!to_intel_crtc(intel_encoder->base.crtc)->active)
return;
/* FIXME: we need to synchronize this sort of stuff with hardware
* readout. Currently fast link training doesn't work on boot-up. */
if (!intel_dp->lane_count)
/*
* Validate the cached values of intel_dp->link_rate and
* intel_dp->lane_count before attempting to retrain.
*/
if (!intel_dp_link_params_valid(intel_dp))
return;
/* Retrain if Channel EQ or CR not ok */
@ -4613,11 +4666,11 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
yesno(drm_dp_tps3_supported(intel_dp->dpcd)));
if (intel_dp->reset_link_params) {
/* Set the max lane count for sink */
intel_dp->max_sink_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
/* Initial max link lane count */
intel_dp->max_link_lane_count = intel_dp_max_common_lane_count(intel_dp);
/* Set the max link BW for sink */
intel_dp->max_sink_link_bw = intel_dp_max_link_bw(intel_dp);
/* Initial max link rate */
intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
intel_dp->reset_link_params = false;
}
@ -5127,7 +5180,7 @@ bool intel_dp_is_edp(struct drm_i915_private *dev_priv, enum port port)
return intel_bios_is_port_edp(dev_priv, port);
}
void
static void
intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
{
struct intel_connector *intel_connector = to_intel_connector(connector);
@ -5932,6 +5985,29 @@ intel_dp_init_connector_port_info(struct intel_digital_port *intel_dig_port)
}
}
static void intel_dp_modeset_retry_work_fn(struct work_struct *work)
{
struct intel_connector *intel_connector;
struct drm_connector *connector;
intel_connector = container_of(work, typeof(*intel_connector),
modeset_retry_work);
connector = &intel_connector->base;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
connector->name);
/* Grab the locks before changing connector property*/
mutex_lock(&connector->dev->mode_config.mutex);
/* Set connector link status to BAD and send a Uevent to notify
* userspace to do a modeset.
*/
drm_mode_connector_set_link_status_property(connector,
DRM_MODE_LINK_STATUS_BAD);
mutex_unlock(&connector->dev->mode_config.mutex);
/* Send Hotplug uevent so userspace can reprobe */
drm_kms_helper_hotplug_event(connector->dev);
}
bool
intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
struct intel_connector *intel_connector)
@ -5944,11 +6020,17 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
enum port port = intel_dig_port->port;
int type;
/* Initialize the work for modeset in case of link train failure */
INIT_WORK(&intel_connector->modeset_retry_work,
intel_dp_modeset_retry_work_fn);
if (WARN(intel_dig_port->max_lanes < 1,
"Not enough lanes (%d) for DP on port %c\n",
intel_dig_port->max_lanes, port_name(port)))
return false;
intel_dp_set_source_rates(intel_dp);
intel_dp->reset_link_params = true;
intel_dp->pps_pipe = INVALID_PIPE;
intel_dp->active_pipe = INVALID_PIPE;

View File

@ -28,6 +28,10 @@ static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable)
{
uint8_t reg_val = 0;
/* Early return when display use other mechanism to enable backlight. */
if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP))
return;
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
&reg_val) < 0) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
@ -97,15 +101,37 @@ static void intel_dp_aux_enable_backlight(struct intel_connector *connector)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
uint8_t dpcd_buf = 0;
uint8_t edp_backlight_mode = 0;
if (drm_dp_dpcd_readb(&intel_dp->aux,
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
return;
}
edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
switch (edp_backlight_mode) {
case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM:
case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET:
case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
if (drm_dp_dpcd_writeb(&intel_dp->aux,
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, dpcd_buf) < 0) {
DRM_DEBUG_KMS("Failed to write aux backlight mode\n");
}
break;
/* Do nothing when it is already DPCD mode */
case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD:
default:
break;
}
set_aux_backlight_enable(intel_dp, true);
if ((drm_dp_dpcd_readb(&intel_dp->aux,
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) == 1) &&
((dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK) ==
DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET))
drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
(dpcd_buf | DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD));
intel_dp_aux_set_backlight(connector, connector->panel.backlight.level);
}
static void intel_dp_aux_disable_backlight(struct intel_connector *connector)
@ -143,9 +169,8 @@ intel_dp_aux_display_control_capable(struct intel_connector *connector)
* the panel can support backlight control over the aux channel
*/
if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&
!((intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_PIN_ENABLE_CAP) ||
(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP))) {
(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP) &&
!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
DRM_DEBUG_KMS("AUX Backlight Control Supported!\n");
return true;
}

View File

@ -146,7 +146,8 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
if (intel_dp->num_sink_rates)
/* eDP 1.4 rate select method. */
if (!link_bw)
drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
&rate_select, 1);
@ -313,6 +314,24 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp)
void
intel_dp_start_link_train(struct intel_dp *intel_dp)
{
intel_dp_link_training_clock_recovery(intel_dp);
intel_dp_link_training_channel_equalization(intel_dp);
struct intel_connector *intel_connector = intel_dp->attached_connector;
if (!intel_dp_link_training_clock_recovery(intel_dp))
goto failure_handling;
if (!intel_dp_link_training_channel_equalization(intel_dp))
goto failure_handling;
DRM_DEBUG_KMS("Link Training Passed at Link Rate = %d, Lane count = %d",
intel_dp->link_rate, intel_dp->lane_count);
return;
failure_handling:
DRM_DEBUG_KMS("Link Training failed at link rate = %d, lane count = %d",
intel_dp->link_rate, intel_dp->lane_count);
if (!intel_dp_get_link_train_fallback_values(intel_dp,
intel_dp->link_rate,
intel_dp->lane_count))
/* Schedule a Hotplug Uevent to userspace to start modeset */
schedule_work(&intel_connector->modeset_retry_work);
return;
}

View File

@ -56,7 +56,8 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
* for MST we always configure max link bw - the spec doesn't
* seem to suggest we should do otherwise.
*/
lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
lane_count = intel_dp_max_lane_count(intel_dp);
pipe_config->lane_count = lane_count;
pipe_config->pipe_bpp = bpp;
@ -329,14 +330,6 @@ intel_dp_mst_detect(struct drm_connector *connector, bool force)
return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr, intel_connector->port);
}
static int
intel_dp_mst_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
return 0;
}
static void
intel_dp_mst_connector_destroy(struct drm_connector *connector)
{
@ -353,8 +346,7 @@ static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = intel_dp_mst_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dp_mst_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
.set_property = drm_atomic_helper_connector_set_property,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
.destroy = intel_dp_mst_connector_destroy,
@ -378,7 +370,7 @@ intel_dp_mst_mode_valid(struct drm_connector *connector,
int max_rate, mode_rate, max_lanes, max_link_clock;
max_link_clock = intel_dp_max_link_rate(intel_dp);
max_lanes = drm_dp_max_lane_count(intel_dp->dpcd);
max_lanes = intel_dp_max_lane_count(intel_dp);
max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes);
mode_rate = intel_dp_link_required(mode->clock, bpp);
@ -495,7 +487,6 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo
drm_mode_connector_attach_encoder(&intel_connector->base,
&intel_dp->mst_encoders[i]->base.base);
}
intel_dp_add_properties(intel_dp, connector);
drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0);

View File

@ -88,7 +88,6 @@
int cpu, ret, timeout = (US) * 1000; \
u64 base; \
_WAIT_FOR_ATOMIC_CHECK(ATOMIC); \
BUILD_BUG_ON((US) > 50000); \
if (!(ATOMIC)) { \
preempt_disable(); \
cpu = smp_processor_id(); \
@ -130,8 +129,14 @@
ret__; \
})
#define wait_for_atomic(COND, MS) _wait_for_atomic((COND), (MS) * 1000, 1)
#define wait_for_atomic_us(COND, US) _wait_for_atomic((COND), (US), 1)
#define wait_for_atomic_us(COND, US) \
({ \
BUILD_BUG_ON(!__builtin_constant_p(US)); \
BUILD_BUG_ON((US) > 50000); \
_wait_for_atomic((COND), (US), 1); \
})
#define wait_for_atomic(COND, MS) wait_for_atomic_us((COND), (MS) * 1000)
#define KHz(x) (1000 * (x))
#define MHz(x) KHz(1000 * (x))
@ -321,6 +326,9 @@ struct intel_connector {
void *port; /* store this opaque as its illegal to dereference it */
struct intel_dp *mst_port;
/* Work struct to schedule a uevent on link train failure */
struct work_struct modeset_retry_work;
};
struct dpll {
@ -504,8 +512,8 @@ enum vlv_wm_level {
};
struct vlv_wm_state {
struct vlv_pipe_wm wm[NUM_VLV_WM_LEVELS];
struct vlv_sr_wm sr[NUM_VLV_WM_LEVELS];
struct g4x_pipe_wm wm[NUM_VLV_WM_LEVELS];
struct g4x_sr_wm sr[NUM_VLV_WM_LEVELS];
uint8_t num_levels;
bool cxsr;
};
@ -514,6 +522,22 @@ struct vlv_fifo_state {
u16 plane[I915_MAX_PLANES];
};
enum g4x_wm_level {
G4X_WM_LEVEL_NORMAL,
G4X_WM_LEVEL_SR,
G4X_WM_LEVEL_HPLL,
NUM_G4X_WM_LEVELS,
};
struct g4x_wm_state {
struct g4x_pipe_wm wm;
struct g4x_sr_wm sr;
struct g4x_sr_wm hpll;
bool cxsr;
bool hpll_en;
bool fbc_en;
};
struct intel_crtc_wm_state {
union {
struct {
@ -541,7 +565,7 @@ struct intel_crtc_wm_state {
struct {
/* "raw" watermarks (not inverted) */
struct vlv_pipe_wm raw[NUM_VLV_WM_LEVELS];
struct g4x_pipe_wm raw[NUM_VLV_WM_LEVELS];
/* intermediate watermarks (inverted) */
struct vlv_wm_state intermediate;
/* optimal watermarks (inverted) */
@ -549,6 +573,15 @@ struct intel_crtc_wm_state {
/* display FIFO split */
struct vlv_fifo_state fifo_state;
} vlv;
struct {
/* "raw" watermarks */
struct g4x_pipe_wm raw[NUM_G4X_WM_LEVELS];
/* intermediate watermarks */
struct g4x_wm_state intermediate;
/* optimal watermarks */
struct g4x_wm_state optimal;
} g4x;
};
/*
@ -766,11 +799,6 @@ struct intel_crtc {
int adjusted_x;
int adjusted_y;
uint32_t cursor_addr;
uint32_t cursor_cntl;
uint32_t cursor_size;
uint32_t cursor_base;
struct intel_crtc_state *config;
/* global reset count when the last flip was submitted */
@ -786,6 +814,7 @@ struct intel_crtc {
union {
struct intel_pipe_wm ilk;
struct vlv_wm_state vlv;
struct g4x_wm_state g4x;
} active;
} wm;
@ -811,18 +840,22 @@ struct intel_plane {
int max_downscale;
uint32_t frontbuffer_bit;
struct {
u32 base, cntl, size;
} cursor;
/*
* NOTE: Do not place new plane state fields here (e.g., when adding
* new plane properties). New runtime state should now be placed in
* the intel_plane_state structure and accessed via plane_state.
*/
void (*update_plane)(struct drm_plane *plane,
void (*update_plane)(struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
void (*disable_plane)(struct drm_plane *plane,
struct drm_crtc *crtc);
int (*check_plane)(struct drm_plane *plane,
void (*disable_plane)(struct intel_plane *plane,
struct intel_crtc *crtc);
int (*check_plane)(struct intel_plane *plane,
struct intel_crtc_state *crtc_state,
struct intel_plane_state *state);
};
@ -948,13 +981,20 @@ struct intel_dp {
uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
uint8_t edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
/* sink rates as reported by DP_SUPPORTED_LINK_RATES */
uint8_t num_sink_rates;
/* source rates */
int num_source_rates;
const int *source_rates;
/* sink rates as reported by DP_MAX_LINK_RATE/DP_SUPPORTED_LINK_RATES */
int num_sink_rates;
int sink_rates[DP_MAX_SUPPORTED_RATES];
/* Max lane count for the sink as per DPCD registers */
uint8_t max_sink_lane_count;
/* Max link BW for the sink as per DPCD registers */
int max_sink_link_bw;
bool use_rate_select;
/* intersection of source and sink rates */
int num_common_rates;
int common_rates[DP_MAX_SUPPORTED_RATES];
/* Max lane count for the current link */
int max_link_lane_count;
/* Max rate for the current link */
int max_link_rate;
/* sink or branch descriptor */
struct intel_dp_desc desc;
struct drm_dp_aux aux;
@ -1491,10 +1531,10 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp);
void intel_edp_panel_vdd_on(struct intel_dp *intel_dp);
void intel_edp_panel_on(struct intel_dp *intel_dp);
void intel_edp_panel_off(struct intel_dp *intel_dp);
void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector);
void intel_dp_mst_suspend(struct drm_device *dev);
void intel_dp_mst_resume(struct drm_device *dev);
int intel_dp_max_link_rate(struct intel_dp *intel_dp);
int intel_dp_max_lane_count(struct intel_dp *intel_dp);
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate);
void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
void intel_power_sequencer_reset(struct drm_i915_private *dev_priv);
@ -1825,6 +1865,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
struct intel_rps_client *rps,
unsigned long submitted);
void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req);
void g4x_wm_get_hw_state(struct drm_device *dev);
void vlv_wm_get_hw_state(struct drm_device *dev);
void ilk_wm_get_hw_state(struct drm_device *dev);
void skl_wm_get_hw_state(struct drm_device *dev);
@ -1832,6 +1873,7 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
struct skl_ddb_allocation *ddb /* out */);
void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
struct skl_pipe_wm *out);
void g4x_wm_sanitize(struct drm_i915_private *dev_priv);
void vlv_wm_sanitize(struct drm_i915_private *dev_priv);
bool intel_can_enable_sagv(struct drm_atomic_state *state);
int intel_enable_sagv(struct drm_i915_private *dev_priv);

View File

@ -410,11 +410,10 @@ static void glk_dsi_device_ready(struct intel_encoder *encoder)
val |= (ULPS_STATE_ENTER | DEVICE_READY);
I915_WRITE(MIPI_DEVICE_READY(port), val);
/* Wait for ULPS Not active */
/* Wait for ULPS active */
if (intel_wait_for_register(dev_priv,
MIPI_CTRL(port), GLK_ULPS_NOT_ACTIVE,
GLK_ULPS_NOT_ACTIVE, 20))
DRM_ERROR("ULPS is still active\n");
MIPI_CTRL(port), GLK_ULPS_NOT_ACTIVE, 0, 20))
DRM_ERROR("ULPS not active\n");
/* Exit ULPS */
val = I915_READ(MIPI_DEVICE_READY(port));

View File

@ -694,8 +694,8 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id)
clk_zero_cnt << 8 | prepare_cnt;
/*
* LP to HS switch count = 4TLPX + PREP_COUNT * 2 + EXIT_ZERO_COUNT * 2
* + 10UI + Extra Byte Count
* LP to HS switch count = 4TLPX + PREP_COUNT * mul + EXIT_ZERO_COUNT *
* mul + 10UI + Extra Byte Count
*
* HS to LP switch count = THS-TRAIL + 2TLPX + Extra Byte Count
* Extra Byte Count is calculated according to number of lanes.
@ -708,8 +708,8 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id)
/* B044 */
/* FIXME:
* The comment above does not match with the code */
lp_to_hs_switch = DIV_ROUND_UP(4 * tlpx_ui + prepare_cnt * 2 +
exit_zero_cnt * 2 + 10, 8);
lp_to_hs_switch = DIV_ROUND_UP(4 * tlpx_ui + prepare_cnt * mul +
exit_zero_cnt * mul + 10, 8);
hs_to_lp_switch = DIV_ROUND_UP(mipi_config->ths_trail + 2 * tlpx_ui, 8);

View File

@ -350,7 +350,7 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.early_unregister = intel_connector_unregister,
.destroy = intel_dvo_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_connector_atomic_get_property,
.set_property = drm_atomic_helper_connector_set_property,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
};

View File

@ -26,69 +26,177 @@
#include "intel_ringbuffer.h"
#include "intel_lrc.h"
static const struct engine_info {
/* Haswell does have the CXT_SIZE register however it does not appear to be
* valid. Now, docs explain in dwords what is in the context object. The full
* size is 70720 bytes, however, the power context and execlist context will
* never be saved (power context is stored elsewhere, and execlists don't work
* on HSW) - so the final size, including the extra state required for the
* Resource Streamer, is 66944 bytes, which rounds to 17 pages.
*/
#define HSW_CXT_TOTAL_SIZE (17 * PAGE_SIZE)
/* Same as Haswell, but 72064 bytes now. */
#define GEN8_CXT_TOTAL_SIZE (18 * PAGE_SIZE)
#define GEN8_LR_CONTEXT_RENDER_SIZE (20 * PAGE_SIZE)
#define GEN9_LR_CONTEXT_RENDER_SIZE (22 * PAGE_SIZE)
#define GEN8_LR_CONTEXT_OTHER_SIZE ( 2 * PAGE_SIZE)
struct engine_class_info {
const char *name;
unsigned int exec_id;
unsigned int hw_id;
u32 mmio_base;
unsigned irq_shift;
int (*init_legacy)(struct intel_engine_cs *engine);
int (*init_execlists)(struct intel_engine_cs *engine);
} intel_engines[] = {
[RCS] = {
};
static const struct engine_class_info intel_engine_classes[] = {
[RENDER_CLASS] = {
.name = "rcs",
.hw_id = RCS_HW,
.exec_id = I915_EXEC_RENDER,
.mmio_base = RENDER_RING_BASE,
.irq_shift = GEN8_RCS_IRQ_SHIFT,
.init_execlists = logical_render_ring_init,
.init_legacy = intel_init_render_ring_buffer,
},
[BCS] = {
[COPY_ENGINE_CLASS] = {
.name = "bcs",
.hw_id = BCS_HW,
.exec_id = I915_EXEC_BLT,
.mmio_base = BLT_RING_BASE,
.irq_shift = GEN8_BCS_IRQ_SHIFT,
.init_execlists = logical_xcs_ring_init,
.init_legacy = intel_init_blt_ring_buffer,
},
[VCS] = {
[VIDEO_DECODE_CLASS] = {
.name = "vcs",
.hw_id = VCS_HW,
.exec_id = I915_EXEC_BSD,
.mmio_base = GEN6_BSD_RING_BASE,
.irq_shift = GEN8_VCS1_IRQ_SHIFT,
.init_execlists = logical_xcs_ring_init,
.init_legacy = intel_init_bsd_ring_buffer,
},
[VCS2] = {
.name = "vcs2",
.hw_id = VCS2_HW,
.exec_id = I915_EXEC_BSD,
.mmio_base = GEN8_BSD2_RING_BASE,
.irq_shift = GEN8_VCS2_IRQ_SHIFT,
.init_execlists = logical_xcs_ring_init,
.init_legacy = intel_init_bsd2_ring_buffer,
},
[VECS] = {
[VIDEO_ENHANCEMENT_CLASS] = {
.name = "vecs",
.hw_id = VECS_HW,
.exec_id = I915_EXEC_VEBOX,
.mmio_base = VEBOX_RING_BASE,
.irq_shift = GEN8_VECS_IRQ_SHIFT,
.init_execlists = logical_xcs_ring_init,
.init_legacy = intel_init_vebox_ring_buffer,
},
};
struct engine_info {
unsigned int hw_id;
unsigned int uabi_id;
u8 class;
u8 instance;
u32 mmio_base;
unsigned irq_shift;
};
static const struct engine_info intel_engines[] = {
[RCS] = {
.hw_id = RCS_HW,
.uabi_id = I915_EXEC_RENDER,
.class = RENDER_CLASS,
.instance = 0,
.mmio_base = RENDER_RING_BASE,
.irq_shift = GEN8_RCS_IRQ_SHIFT,
},
[BCS] = {
.hw_id = BCS_HW,
.uabi_id = I915_EXEC_BLT,
.class = COPY_ENGINE_CLASS,
.instance = 0,
.mmio_base = BLT_RING_BASE,
.irq_shift = GEN8_BCS_IRQ_SHIFT,
},
[VCS] = {
.hw_id = VCS_HW,
.uabi_id = I915_EXEC_BSD,
.class = VIDEO_DECODE_CLASS,
.instance = 0,
.mmio_base = GEN6_BSD_RING_BASE,
.irq_shift = GEN8_VCS1_IRQ_SHIFT,
},
[VCS2] = {
.hw_id = VCS2_HW,
.uabi_id = I915_EXEC_BSD,
.class = VIDEO_DECODE_CLASS,
.instance = 1,
.mmio_base = GEN8_BSD2_RING_BASE,
.irq_shift = GEN8_VCS2_IRQ_SHIFT,
},
[VECS] = {
.hw_id = VECS_HW,
.uabi_id = I915_EXEC_VEBOX,
.class = VIDEO_ENHANCEMENT_CLASS,
.instance = 0,
.mmio_base = VEBOX_RING_BASE,
.irq_shift = GEN8_VECS_IRQ_SHIFT,
},
};
/**
* ___intel_engine_context_size() - return the size of the context for an engine
* @dev_priv: i915 device private
* @class: engine class
*
* Each engine class may require a different amount of space for a context
* image.
*
* Return: size (in bytes) of an engine class specific context image
*
* Note: this size includes the HWSP, which is part of the context image
* in LRC mode, but does not include the "shared data page" used with
* GuC submission. The caller should account for this if using the GuC.
*/
static u32
__intel_engine_context_size(struct drm_i915_private *dev_priv, u8 class)
{
u32 cxt_size;
BUILD_BUG_ON(I915_GTT_PAGE_SIZE != PAGE_SIZE);
switch (class) {
case RENDER_CLASS:
switch (INTEL_GEN(dev_priv)) {
default:
MISSING_CASE(INTEL_GEN(dev_priv));
case 9:
return GEN9_LR_CONTEXT_RENDER_SIZE;
case 8:
return i915.enable_execlists ?
GEN8_LR_CONTEXT_RENDER_SIZE :
GEN8_CXT_TOTAL_SIZE;
case 7:
if (IS_HASWELL(dev_priv))
return HSW_CXT_TOTAL_SIZE;
cxt_size = I915_READ(GEN7_CXT_SIZE);
return round_up(GEN7_CXT_TOTAL_SIZE(cxt_size) * 64,
PAGE_SIZE);
case 6:
cxt_size = I915_READ(CXT_SIZE);
return round_up(GEN6_CXT_TOTAL_SIZE(cxt_size) * 64,
PAGE_SIZE);
case 5:
case 4:
case 3:
case 2:
/* For the special day when i810 gets merged. */
case 1:
return 0;
}
break;
default:
MISSING_CASE(class);
case VIDEO_DECODE_CLASS:
case VIDEO_ENHANCEMENT_CLASS:
case COPY_ENGINE_CLASS:
if (INTEL_GEN(dev_priv) < 8)
return 0;
return GEN8_LR_CONTEXT_OTHER_SIZE;
}
}
static int
intel_engine_setup(struct drm_i915_private *dev_priv,
enum intel_engine_id id)
{
const struct engine_info *info = &intel_engines[id];
const struct engine_class_info *class_info;
struct intel_engine_cs *engine;
GEM_BUG_ON(info->class >= ARRAY_SIZE(intel_engine_classes));
class_info = &intel_engine_classes[info->class];
GEM_BUG_ON(dev_priv->engine[id]);
engine = kzalloc(sizeof(*engine), GFP_KERNEL);
if (!engine)
@ -96,11 +204,20 @@ intel_engine_setup(struct drm_i915_private *dev_priv,
engine->id = id;
engine->i915 = dev_priv;
engine->name = info->name;
engine->exec_id = info->exec_id;
WARN_ON(snprintf(engine->name, sizeof(engine->name), "%s%u",
class_info->name, info->instance) >=
sizeof(engine->name));
engine->uabi_id = info->uabi_id;
engine->hw_id = engine->guc_id = info->hw_id;
engine->mmio_base = info->mmio_base;
engine->irq_shift = info->irq_shift;
engine->class = info->class;
engine->instance = info->instance;
engine->context_size = __intel_engine_context_size(dev_priv,
engine->class);
if (WARN_ON(engine->context_size > BIT(20)))
engine->context_size = 0;
/* Nothing to do here, execute in order of dependencies */
engine->schedule = NULL;
@ -112,18 +229,18 @@ intel_engine_setup(struct drm_i915_private *dev_priv,
}
/**
* intel_engines_init_early() - allocate the Engine Command Streamers
* intel_engines_init_mmio() - allocate and prepare the Engine Command Streamers
* @dev_priv: i915 device private
*
* Return: non-zero if the initialization failed.
*/
int intel_engines_init_early(struct drm_i915_private *dev_priv)
int intel_engines_init_mmio(struct drm_i915_private *dev_priv)
{
struct intel_device_info *device_info = mkwrite_device_info(dev_priv);
unsigned int ring_mask = INTEL_INFO(dev_priv)->ring_mask;
unsigned int mask = 0;
const unsigned int ring_mask = INTEL_INFO(dev_priv)->ring_mask;
struct intel_engine_cs *engine;
enum intel_engine_id id;
unsigned int mask = 0;
unsigned int i;
int err;
@ -150,6 +267,12 @@ int intel_engines_init_early(struct drm_i915_private *dev_priv)
if (WARN_ON(mask != ring_mask))
device_info->ring_mask = mask;
/* We always presume we have at least RCS available for later probing */
if (WARN_ON(!HAS_ENGINE(dev_priv, RCS))) {
err = -ENODEV;
goto cleanup;
}
device_info->num_rings = hweight32(mask);
return 0;
@ -161,7 +284,7 @@ cleanup:
}
/**
* intel_engines_init() - allocate, populate and init the Engine Command Streamers
* intel_engines_init() - init the Engine Command Streamers
* @dev_priv: i915 device private
*
* Return: non-zero if the initialization failed.
@ -175,12 +298,14 @@ int intel_engines_init(struct drm_i915_private *dev_priv)
int err = 0;
for_each_engine(engine, dev_priv, id) {
const struct engine_class_info *class_info =
&intel_engine_classes[engine->class];
int (*init)(struct intel_engine_cs *engine);
if (i915.enable_execlists)
init = intel_engines[id].init_execlists;
init = class_info->init_execlists;
else
init = intel_engines[id].init_legacy;
init = class_info->init_legacy;
if (!init) {
kfree(engine);
dev_priv->engine[id] = NULL;
@ -223,6 +348,9 @@ void intel_engine_init_global_seqno(struct intel_engine_cs *engine, u32 seqno)
{
struct drm_i915_private *dev_priv = engine->i915;
GEM_BUG_ON(!intel_engine_is_idle(engine));
GEM_BUG_ON(i915_gem_active_isset(&engine->timeline->last_request));
/* Our semaphore implementation is strictly monotonic (i.e. we proceed
* so long as the semaphore value in the register/page is greater
* than the sync value), so whenever we reset the seqno,
@ -253,13 +381,12 @@ void intel_engine_init_global_seqno(struct intel_engine_cs *engine, u32 seqno)
intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
clear_bit(ENGINE_IRQ_BREADCRUMB, &engine->irq_posted);
GEM_BUG_ON(i915_gem_active_isset(&engine->timeline->last_request));
engine->hangcheck.seqno = seqno;
/* After manually advancing the seqno, fake the interrupt in case
* there are any waiters for that seqno.
*/
intel_engine_wakeup(engine);
GEM_BUG_ON(intel_engine_get_seqno(engine) != seqno);
}
static void intel_engine_init_timeline(struct intel_engine_cs *engine)
@ -342,6 +469,7 @@ static void intel_engine_cleanup_scratch(struct intel_engine_cs *engine)
*/
int intel_engine_init_common(struct intel_engine_cs *engine)
{
struct intel_ring *ring;
int ret;
engine->set_default_submission(engine);
@ -353,9 +481,9 @@ int intel_engine_init_common(struct intel_engine_cs *engine)
* be available. To avoid this we always pin the default
* context.
*/
ret = engine->context_pin(engine, engine->i915->kernel_context);
if (ret)
return ret;
ring = engine->context_pin(engine, engine->i915->kernel_context);
if (IS_ERR(ring))
return PTR_ERR(ring);
ret = intel_engine_init_breadcrumbs(engine);
if (ret)
@ -723,8 +851,10 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
*/
}
/* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl,glk */
/* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl */
WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
GEN9_ENABLE_YV12_BUGFIX |
GEN9_ENABLE_GPGPU_PREEMPTION);
/* Wa4x4STCOptimizationDisable:skl,bxt,kbl,glk */
@ -1086,17 +1216,24 @@ bool intel_engine_is_idle(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
/* More white lies, if wedged, hw state is inconsistent */
if (i915_terminally_wedged(&dev_priv->gpu_error))
return true;
/* Any inflight/incomplete requests? */
if (!i915_seqno_passed(intel_engine_get_seqno(engine),
intel_engine_last_submit(engine)))
return false;
if (I915_SELFTEST_ONLY(engine->breadcrumbs.mock))
return true;
/* Interrupt/tasklet pending? */
if (test_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted))
return false;
/* Both ports drained, no more ELSP submission? */
if (engine->execlist_port[0].request)
if (port_request(&engine->execlist_port[0]))
return false;
/* Ring stopped? */
@ -1137,6 +1274,18 @@ void intel_engines_reset_default_submission(struct drm_i915_private *i915)
engine->set_default_submission(engine);
}
void intel_engines_mark_idle(struct drm_i915_private *i915)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
for_each_engine(engine, i915, id) {
intel_engine_disarm_breadcrumbs(engine);
i915_gem_batch_pool_fini(&engine->batch_pool);
engine->no_priolist = false;
}
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/mock_engine.c"
#endif

View File

@ -1312,14 +1312,12 @@ static int intel_sanitize_fbc_option(struct drm_i915_private *dev_priv)
static bool need_fbc_vtd_wa(struct drm_i915_private *dev_priv)
{
#ifdef CONFIG_INTEL_IOMMU
/* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */
if (intel_iommu_gfx_mapped &&
if (intel_vtd_active() &&
(IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv))) {
DRM_INFO("Disabling framebuffer compression (FBC) to prevent screen flicker with VT-d enabled\n");
return true;
}
#endif
return false;
}

View File

@ -0,0 +1,461 @@
/*
* Copyright © 2016-2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "i915_drv.h"
#include "intel_guc_ct.h"
enum { CTB_SEND = 0, CTB_RECV = 1 };
enum { CTB_OWNER_HOST = 0 };
void intel_guc_ct_init_early(struct intel_guc_ct *ct)
{
/* we're using static channel owners */
ct->host_channel.owner = CTB_OWNER_HOST;
}
static inline const char *guc_ct_buffer_type_to_str(u32 type)
{
switch (type) {
case INTEL_GUC_CT_BUFFER_TYPE_SEND:
return "SEND";
case INTEL_GUC_CT_BUFFER_TYPE_RECV:
return "RECV";
default:
return "<invalid>";
}
}
static void guc_ct_buffer_desc_init(struct guc_ct_buffer_desc *desc,
u32 cmds_addr, u32 size, u32 owner)
{
DRM_DEBUG_DRIVER("CT: desc %p init addr=%#x size=%u owner=%u\n",
desc, cmds_addr, size, owner);
memset(desc, 0, sizeof(*desc));
desc->addr = cmds_addr;
desc->size = size;
desc->owner = owner;
}
static void guc_ct_buffer_desc_reset(struct guc_ct_buffer_desc *desc)
{
DRM_DEBUG_DRIVER("CT: desc %p reset head=%u tail=%u\n",
desc, desc->head, desc->tail);
desc->head = 0;
desc->tail = 0;
desc->is_in_error = 0;
}
static int guc_action_register_ct_buffer(struct intel_guc *guc,
u32 desc_addr,
u32 type)
{
u32 action[] = {
INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER,
desc_addr,
sizeof(struct guc_ct_buffer_desc),
type
};
int err;
/* Can't use generic send(), CT registration must go over MMIO */
err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action));
if (err)
DRM_ERROR("CT: register %s buffer failed; err=%d\n",
guc_ct_buffer_type_to_str(type), err);
return err;
}
static int guc_action_deregister_ct_buffer(struct intel_guc *guc,
u32 owner,
u32 type)
{
u32 action[] = {
INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER,
owner,
type
};
int err;
/* Can't use generic send(), CT deregistration must go over MMIO */
err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action));
if (err)
DRM_ERROR("CT: deregister %s buffer failed; owner=%d err=%d\n",
guc_ct_buffer_type_to_str(type), owner, err);
return err;
}
static bool ctch_is_open(struct intel_guc_ct_channel *ctch)
{
return ctch->vma != NULL;
}
static int ctch_init(struct intel_guc *guc,
struct intel_guc_ct_channel *ctch)
{
struct i915_vma *vma;
void *blob;
int err;
int i;
GEM_BUG_ON(ctch->vma);
/* We allocate 1 page to hold both descriptors and both buffers.
* ___________.....................
* |desc (SEND)| :
* |___________| PAGE/4
* :___________....................:
* |desc (RECV)| :
* |___________| PAGE/4
* :_______________________________:
* |cmds (SEND) |
* | PAGE/4
* |_______________________________|
* |cmds (RECV) |
* | PAGE/4
* |_______________________________|
*
* Each message can use a maximum of 32 dwords and we don't expect to
* have more than 1 in flight at any time, so we have enough space.
* Some logic further ahead will rely on the fact that there is only 1
* page and that it is always mapped, so if the size is changed the
* other code will need updating as well.
*/
/* allocate vma */
vma = intel_guc_allocate_vma(guc, PAGE_SIZE);
if (IS_ERR(vma)) {
err = PTR_ERR(vma);
goto err_out;
}
ctch->vma = vma;
/* map first page */
blob = i915_gem_object_pin_map(vma->obj, I915_MAP_WB);
if (IS_ERR(blob)) {
err = PTR_ERR(blob);
goto err_vma;
}
DRM_DEBUG_DRIVER("CT: vma base=%#x\n", guc_ggtt_offset(ctch->vma));
/* store pointers to desc and cmds */
for (i = 0; i < ARRAY_SIZE(ctch->ctbs); i++) {
GEM_BUG_ON((i != CTB_SEND) && (i != CTB_RECV));
ctch->ctbs[i].desc = blob + PAGE_SIZE/4 * i;
ctch->ctbs[i].cmds = blob + PAGE_SIZE/4 * i + PAGE_SIZE/2;
}
return 0;
err_vma:
i915_vma_unpin_and_release(&ctch->vma);
err_out:
DRM_DEBUG_DRIVER("CT: channel %d initialization failed; err=%d\n",
ctch->owner, err);
return err;
}
static void ctch_fini(struct intel_guc *guc,
struct intel_guc_ct_channel *ctch)
{
GEM_BUG_ON(!ctch->vma);
i915_gem_object_unpin_map(ctch->vma->obj);
i915_vma_unpin_and_release(&ctch->vma);
}
static int ctch_open(struct intel_guc *guc,
struct intel_guc_ct_channel *ctch)
{
u32 base;
int err;
int i;
DRM_DEBUG_DRIVER("CT: channel %d reopen=%s\n",
ctch->owner, yesno(ctch_is_open(ctch)));
if (!ctch->vma) {
err = ctch_init(guc, ctch);
if (unlikely(err))
goto err_out;
}
/* vma should be already allocated and map'ed */
base = guc_ggtt_offset(ctch->vma);
/* (re)initialize descriptors
* cmds buffers are in the second half of the blob page
*/
for (i = 0; i < ARRAY_SIZE(ctch->ctbs); i++) {
GEM_BUG_ON((i != CTB_SEND) && (i != CTB_RECV));
guc_ct_buffer_desc_init(ctch->ctbs[i].desc,
base + PAGE_SIZE/4 * i + PAGE_SIZE/2,
PAGE_SIZE/4,
ctch->owner);
}
/* register buffers, starting wirh RECV buffer
* descriptors are in first half of the blob
*/
err = guc_action_register_ct_buffer(guc,
base + PAGE_SIZE/4 * CTB_RECV,
INTEL_GUC_CT_BUFFER_TYPE_RECV);
if (unlikely(err))
goto err_fini;
err = guc_action_register_ct_buffer(guc,
base + PAGE_SIZE/4 * CTB_SEND,
INTEL_GUC_CT_BUFFER_TYPE_SEND);
if (unlikely(err))
goto err_deregister;
return 0;
err_deregister:
guc_action_deregister_ct_buffer(guc,
ctch->owner,
INTEL_GUC_CT_BUFFER_TYPE_RECV);
err_fini:
ctch_fini(guc, ctch);
err_out:
DRM_ERROR("CT: can't open channel %d; err=%d\n", ctch->owner, err);
return err;
}
static void ctch_close(struct intel_guc *guc,
struct intel_guc_ct_channel *ctch)
{
GEM_BUG_ON(!ctch_is_open(ctch));
guc_action_deregister_ct_buffer(guc,
ctch->owner,
INTEL_GUC_CT_BUFFER_TYPE_SEND);
guc_action_deregister_ct_buffer(guc,
ctch->owner,
INTEL_GUC_CT_BUFFER_TYPE_RECV);
ctch_fini(guc, ctch);
}
static u32 ctch_get_next_fence(struct intel_guc_ct_channel *ctch)
{
/* For now it's trivial */
return ++ctch->next_fence;
}
static int ctb_write(struct intel_guc_ct_buffer *ctb,
const u32 *action,
u32 len /* in dwords */,
u32 fence)
{
struct guc_ct_buffer_desc *desc = ctb->desc;
u32 head = desc->head / 4; /* in dwords */
u32 tail = desc->tail / 4; /* in dwords */
u32 size = desc->size / 4; /* in dwords */
u32 used; /* in dwords */
u32 header;
u32 *cmds = ctb->cmds;
unsigned int i;
GEM_BUG_ON(desc->size % 4);
GEM_BUG_ON(desc->head % 4);
GEM_BUG_ON(desc->tail % 4);
GEM_BUG_ON(tail >= size);
/*
* tail == head condition indicates empty. GuC FW does not support
* using up the entire buffer to get tail == head meaning full.
*/
if (tail < head)
used = (size - head) + tail;
else
used = tail - head;
/* make sure there is a space including extra dw for the fence */
if (unlikely(used + len + 1 >= size))
return -ENOSPC;
/* Write the message. The format is the following:
* DW0: header (including action code)
* DW1: fence
* DW2+: action data
*/
header = (len << GUC_CT_MSG_LEN_SHIFT) |
(GUC_CT_MSG_WRITE_FENCE_TO_DESC) |
(action[0] << GUC_CT_MSG_ACTION_SHIFT);
cmds[tail] = header;
tail = (tail + 1) % size;
cmds[tail] = fence;
tail = (tail + 1) % size;
for (i = 1; i < len; i++) {
cmds[tail] = action[i];
tail = (tail + 1) % size;
}
/* now update desc tail (back in bytes) */
desc->tail = tail * 4;
GEM_BUG_ON(desc->tail > desc->size);
return 0;
}
/* Wait for the response from the GuC.
* @fence: response fence
* @status: placeholder for status
* return: 0 response received (status is valid)
* -ETIMEDOUT no response within hardcoded timeout
* -EPROTO no response, ct buffer was in error
*/
static int wait_for_response(struct guc_ct_buffer_desc *desc,
u32 fence,
u32 *status)
{
int err;
/*
* Fast commands should complete in less than 10us, so sample quickly
* up to that length of time, then switch to a slower sleep-wait loop.
* No GuC command should ever take longer than 10ms.
*/
#define done (READ_ONCE(desc->fence) == fence)
err = wait_for_us(done, 10);
if (err)
err = wait_for(done, 10);
#undef done
if (unlikely(err)) {
DRM_ERROR("CT: fence %u failed; reported fence=%u\n",
fence, desc->fence);
if (WARN_ON(desc->is_in_error)) {
/* Something went wrong with the messaging, try to reset
* the buffer and hope for the best
*/
guc_ct_buffer_desc_reset(desc);
err = -EPROTO;
}
}
*status = desc->status;
return err;
}
static int ctch_send(struct intel_guc *guc,
struct intel_guc_ct_channel *ctch,
const u32 *action,
u32 len,
u32 *status)
{
struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_SEND];
struct guc_ct_buffer_desc *desc = ctb->desc;
u32 fence;
int err;
GEM_BUG_ON(!ctch_is_open(ctch));
GEM_BUG_ON(!len);
GEM_BUG_ON(len & ~GUC_CT_MSG_LEN_MASK);
fence = ctch_get_next_fence(ctch);
err = ctb_write(ctb, action, len, fence);
if (unlikely(err))
return err;
intel_guc_notify(guc);
err = wait_for_response(desc, fence, status);
if (unlikely(err))
return err;
if (*status != INTEL_GUC_STATUS_SUCCESS)
return -EIO;
return 0;
}
/*
* Command Transport (CT) buffer based GuC send function.
*/
static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len)
{
struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
u32 status = ~0; /* undefined */
int err;
mutex_lock(&guc->send_mutex);
err = ctch_send(guc, ctch, action, len, &status);
if (unlikely(err)) {
DRM_ERROR("CT: send action %#X failed; err=%d status=%#X\n",
action[0], err, status);
}
mutex_unlock(&guc->send_mutex);
return err;
}
/**
* Enable buffer based command transport
* Shall only be called for platforms with HAS_GUC_CT.
* @guc: the guc
* return: 0 on success
* non-zero on failure
*/
int intel_guc_enable_ct(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
int err;
GEM_BUG_ON(!HAS_GUC_CT(dev_priv));
err = ctch_open(guc, ctch);
if (unlikely(err))
return err;
/* Switch into cmd transport buffer based send() */
guc->send = intel_guc_send_ct;
DRM_INFO("CT: %s\n", enableddisabled(true));
return 0;
}
/**
* Disable buffer based command transport.
* Shall only be called for platforms with HAS_GUC_CT.
* @guc: the guc
*/
void intel_guc_disable_ct(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
GEM_BUG_ON(!HAS_GUC_CT(dev_priv));
if (!ctch_is_open(ctch))
return;
ctch_close(guc, ctch);
/* Disable send */
guc->send = intel_guc_send_nop;
DRM_INFO("CT: %s\n", enableddisabled(false));
}

View File

@ -0,0 +1,86 @@
/*
* Copyright © 2016-2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef _INTEL_GUC_CT_H_
#define _INTEL_GUC_CT_H_
struct intel_guc;
struct i915_vma;
#include "intel_guc_fwif.h"
/**
* DOC: Command Transport (CT).
*
* Buffer based command transport is a replacement for MMIO based mechanism.
* It can be used to perform both host-2-guc and guc-to-host communication.
*/
/** Represents single command transport buffer.
*
* A single command transport buffer consists of two parts, the header
* record (command transport buffer descriptor) and the actual buffer which
* holds the commands.
*
* @desc: pointer to the buffer descriptor
* @cmds: pointer to the commands buffer
*/
struct intel_guc_ct_buffer {
struct guc_ct_buffer_desc *desc;
u32 *cmds;
};
/** Represents pair of command transport buffers.
*
* Buffers go in pairs to allow bi-directional communication.
* To simplify the code we place both of them in the same vma.
* Buffers from the same pair must share unique owner id.
*
* @vma: pointer to the vma with pair of CT buffers
* @ctbs: buffers for sending(0) and receiving(1) commands
* @owner: unique identifier
* @next_fence: fence to be used with next send command
*/
struct intel_guc_ct_channel {
struct i915_vma *vma;
struct intel_guc_ct_buffer ctbs[2];
u32 owner;
u32 next_fence;
};
/** Holds all command transport channels.
*
* @host_channel: main channel used by the host
*/
struct intel_guc_ct {
struct intel_guc_ct_channel host_channel;
/* other channels are tbd */
};
void intel_guc_ct_init_early(struct intel_guc_ct *ct);
/* XXX: move to intel_uc.h ? don't fit there either */
int intel_guc_enable_ct(struct intel_guc *guc);
void intel_guc_disable_ct(struct intel_guc *guc);
#endif /* _INTEL_GUC_CT_H_ */

View File

@ -23,8 +23,8 @@
#ifndef _INTEL_GUC_FWIF_H
#define _INTEL_GUC_FWIF_H
#define GFXCORE_FAMILY_GEN9 12
#define GFXCORE_FAMILY_UNKNOWN 0x7fffffff
#define GUC_CORE_FAMILY_GEN9 12
#define GUC_CORE_FAMILY_UNKNOWN 0x7fffffff
#define GUC_CLIENT_PRIORITY_KMD_HIGH 0
#define GUC_CLIENT_PRIORITY_HIGH 1
@ -331,6 +331,47 @@ struct guc_stage_desc {
u64 desc_private;
} __packed;
/*
* Describes single command transport buffer.
* Used by both guc-master and clients.
*/
struct guc_ct_buffer_desc {
u32 addr; /* gfx address */
u64 host_private; /* host private data */
u32 size; /* size in bytes */
u32 head; /* offset updated by GuC*/
u32 tail; /* offset updated by owner */
u32 is_in_error; /* error indicator */
u32 fence; /* fence updated by GuC */
u32 status; /* status updated by GuC */
u32 owner; /* id of the channel owner */
u32 owner_sub_id; /* owner-defined field for extra tracking */
u32 reserved[5];
} __packed;
/* Type of command transport buffer */
#define INTEL_GUC_CT_BUFFER_TYPE_SEND 0x0u
#define INTEL_GUC_CT_BUFFER_TYPE_RECV 0x1u
/*
* Definition of the command transport message header (DW0)
*
* bit[4..0] message len (in dwords)
* bit[7..5] reserved
* bit[8] write fence to desc
* bit[9] write status to H2G buff
* bit[10] send status (via G2H)
* bit[15..11] reserved
* bit[31..16] action code
*/
#define GUC_CT_MSG_LEN_SHIFT 0
#define GUC_CT_MSG_LEN_MASK 0x1F
#define GUC_CT_MSG_WRITE_FENCE_TO_DESC (1 << 8)
#define GUC_CT_MSG_WRITE_STATUS_TO_BUFF (1 << 9)
#define GUC_CT_MSG_SEND_STATUS (1 << 10)
#define GUC_CT_MSG_ACTION_SHIFT 16
#define GUC_CT_MSG_ACTION_MASK 0xFFFF
#define GUC_FORCEWAKE_RENDER (1 << 0)
#define GUC_FORCEWAKE_MEDIA (1 << 1)
@ -515,6 +556,8 @@ enum intel_guc_action {
INTEL_GUC_ACTION_EXIT_S_STATE = 0x502,
INTEL_GUC_ACTION_SLPC_REQUEST = 0x3003,
INTEL_GUC_ACTION_AUTHENTICATE_HUC = 0x4000,
INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER = 0x4505,
INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER = 0x4506,
INTEL_GUC_ACTION_UK_LOG_ENABLE_LOGGING = 0x0E000,
INTEL_GUC_ACTION_LIMIT
};

View File

@ -61,6 +61,9 @@
#define KBL_FW_MAJOR 9
#define KBL_FW_MINOR 14
#define GLK_FW_MAJOR 10
#define GLK_FW_MINOR 56
#define GUC_FW_PATH(platform, major, minor) \
"i915/" __stringify(platform) "_guc_ver" __stringify(major) "_" __stringify(minor) ".bin"
@ -73,6 +76,8 @@ MODULE_FIRMWARE(I915_BXT_GUC_UCODE);
#define I915_KBL_GUC_UCODE GUC_FW_PATH(kbl, KBL_FW_MAJOR, KBL_FW_MINOR)
MODULE_FIRMWARE(I915_KBL_GUC_UCODE);
#define I915_GLK_GUC_UCODE GUC_FW_PATH(glk, GLK_FW_MAJOR, GLK_FW_MINOR)
static u32 get_gttype(struct drm_i915_private *dev_priv)
{
@ -86,11 +91,11 @@ static u32 get_core_family(struct drm_i915_private *dev_priv)
switch (gen) {
case 9:
return GFXCORE_FAMILY_GEN9;
return GUC_CORE_FAMILY_GEN9;
default:
WARN(1, "GEN%d does not support GuC operation!\n", gen);
return GFXCORE_FAMILY_UNKNOWN;
MISSING_CASE(gen);
return GUC_CORE_FAMILY_UNKNOWN;
}
}
@ -280,10 +285,6 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
/* init WOPCM */
I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE);
/* Enable MIA caching. GuC clock gating is disabled. */
I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE);
@ -405,6 +406,10 @@ int intel_guc_select_fw(struct intel_guc *guc)
guc->fw.path = I915_KBL_GUC_UCODE;
guc->fw.major_ver_wanted = KBL_FW_MAJOR;
guc->fw.minor_ver_wanted = KBL_FW_MINOR;
} else if (IS_GEMINILAKE(dev_priv)) {
guc->fw.path = I915_GLK_GUC_UCODE;
guc->fw.major_ver_wanted = GLK_FW_MAJOR;
guc->fw.minor_ver_wanted = GLK_FW_MINOR;
} else {
DRM_ERROR("No GuC firmware known for platform with GuC!\n");
return -ENOENT;

View File

@ -359,12 +359,16 @@ static int guc_log_runtime_create(struct intel_guc *guc)
void *vaddr;
struct rchan *guc_log_relay_chan;
size_t n_subbufs, subbuf_size;
int ret = 0;
int ret;
lockdep_assert_held(&dev_priv->drm.struct_mutex);
GEM_BUG_ON(guc_log_has_runtime(guc));
ret = i915_gem_object_set_to_wc_domain(guc->log.vma->obj, true);
if (ret)
return ret;
/* Create a WC (Uncached for read) vmalloc mapping of log
* buffer pages, so that we can directly get the data
* (up-to-date) from memory.

View File

@ -407,7 +407,7 @@ static void hangcheck_declare_hang(struct drm_i915_private *i915,
"%s, ", engine->name);
msg[len-2] = '\0';
return i915_handle_error(i915, hung, msg);
return i915_handle_error(i915, hung, "%s", msg);
}
/*

View File

@ -1327,6 +1327,11 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)
return false;
}
/* Display Wa #1139 */
if (IS_GLK_REVID(dev_priv, 0, GLK_REVID_A1) &&
crtc_state->base.adjusted_mode.htotal > 5460)
return false;
return true;
}
@ -1392,7 +1397,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
}
if (!pipe_config->bw_constrained) {
DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp);
DRM_DEBUG_KMS("forcing pipe bpp to %i for HDMI\n", desired_bpp);
pipe_config->pipe_bpp = desired_bpp;
}

View File

@ -52,6 +52,10 @@
#define KBL_HUC_FW_MINOR 00
#define KBL_BLD_NUM 1810
#define GLK_HUC_FW_MAJOR 02
#define GLK_HUC_FW_MINOR 00
#define GLK_BLD_NUM 1748
#define HUC_FW_PATH(platform, major, minor, bld_num) \
"i915/" __stringify(platform) "_huc_ver" __stringify(major) "_" \
__stringify(minor) "_" __stringify(bld_num) ".bin"
@ -68,6 +72,9 @@ MODULE_FIRMWARE(I915_BXT_HUC_UCODE);
KBL_HUC_FW_MINOR, KBL_BLD_NUM)
MODULE_FIRMWARE(I915_KBL_HUC_UCODE);
#define I915_GLK_HUC_UCODE HUC_FW_PATH(glk, GLK_HUC_FW_MAJOR, \
GLK_HUC_FW_MINOR, GLK_BLD_NUM)
/**
* huc_ucode_xfer() - DMA's the firmware
* @dev_priv: the drm_i915_private device
@ -99,11 +106,6 @@ static int huc_ucode_xfer(struct drm_i915_private *dev_priv)
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
/* init WOPCM */
I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE |
HUC_LOADING_AGENT_GUC);
/* Set the source address for the uCode */
offset = guc_ggtt_offset(vma) + huc_fw->header_offset;
I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
@ -169,6 +171,10 @@ void intel_huc_select_fw(struct intel_huc *huc)
huc->fw.path = I915_KBL_HUC_UCODE;
huc->fw.major_ver_wanted = KBL_HUC_FW_MAJOR;
huc->fw.minor_ver_wanted = KBL_HUC_FW_MINOR;
} else if (IS_GEMINILAKE(dev_priv)) {
huc->fw.path = I915_GLK_HUC_UCODE;
huc->fw.major_ver_wanted = GLK_HUC_FW_MAJOR;
huc->fw.minor_ver_wanted = GLK_HUC_FW_MINOR;
} else {
DRM_ERROR("No HuC firmware known for platform with HuC!\n");
return;
@ -186,68 +192,36 @@ void intel_huc_select_fw(struct intel_huc *huc)
* earlier call to intel_huc_init(), so here we need only check that
* is succeeded, and then transfer the image to the h/w.
*
* Return: non-zero code on error
*/
int intel_huc_init_hw(struct intel_huc *huc)
void intel_huc_init_hw(struct intel_huc *huc)
{
struct drm_i915_private *dev_priv = huc_to_i915(huc);
int err;
if (huc->fw.fetch_status == INTEL_UC_FIRMWARE_NONE)
return 0;
DRM_DEBUG_DRIVER("%s fw status: fetch %s, load %s\n",
huc->fw.path,
intel_uc_fw_status_repr(huc->fw.fetch_status),
intel_uc_fw_status_repr(huc->fw.load_status));
if (huc->fw.fetch_status == INTEL_UC_FIRMWARE_SUCCESS &&
huc->fw.load_status == INTEL_UC_FIRMWARE_FAIL)
return -ENOEXEC;
if (huc->fw.fetch_status != INTEL_UC_FIRMWARE_SUCCESS)
return;
huc->fw.load_status = INTEL_UC_FIRMWARE_PENDING;
switch (huc->fw.fetch_status) {
case INTEL_UC_FIRMWARE_FAIL:
/* something went wrong :( */
err = -EIO;
goto fail;
case INTEL_UC_FIRMWARE_NONE:
case INTEL_UC_FIRMWARE_PENDING:
default:
/* "can't happen" */
WARN_ONCE(1, "HuC fw %s invalid fetch_status %s [%d]\n",
huc->fw.path,
intel_uc_fw_status_repr(huc->fw.fetch_status),
huc->fw.fetch_status);
err = -ENXIO;
goto fail;
case INTEL_UC_FIRMWARE_SUCCESS:
break;
}
err = huc_ucode_xfer(dev_priv);
if (err)
goto fail;
huc->fw.load_status = INTEL_UC_FIRMWARE_SUCCESS;
huc->fw.load_status = err ?
INTEL_UC_FIRMWARE_FAIL : INTEL_UC_FIRMWARE_SUCCESS;
DRM_DEBUG_DRIVER("%s fw status: fetch %s, load %s\n",
huc->fw.path,
intel_uc_fw_status_repr(huc->fw.fetch_status),
intel_uc_fw_status_repr(huc->fw.load_status));
return 0;
if (huc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS)
DRM_ERROR("Failed to complete HuC uCode load with ret %d\n", err);
fail:
if (huc->fw.load_status == INTEL_UC_FIRMWARE_PENDING)
huc->fw.load_status = INTEL_UC_FIRMWARE_FAIL;
DRM_ERROR("Failed to complete HuC uCode load with ret %d\n", err);
return err;
return;
}
/**

View File

@ -63,6 +63,7 @@
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include "i915_drv.h"
#include <linux/delay.h>
@ -110,6 +111,11 @@ lpe_audio_platdev_create(struct drm_i915_private *dev_priv)
pinfo.size_data = sizeof(*pdata);
pinfo.dma_mask = DMA_BIT_MASK(32);
pdata->num_pipes = INTEL_INFO(dev_priv)->num_pipes;
pdata->num_ports = IS_CHERRYVIEW(dev_priv) ? 3 : 2; /* B,C,D or B,C */
pdata->port[0].pipe = -1;
pdata->port[1].pipe = -1;
pdata->port[2].pipe = -1;
spin_lock_init(&pdata->lpe_audio_slock);
platdev = platform_device_register_full(&pinfo);
@ -121,6 +127,10 @@ lpe_audio_platdev_create(struct drm_i915_private *dev_priv)
kfree(rsc);
pm_runtime_forbid(&platdev->dev);
pm_runtime_set_active(&platdev->dev);
pm_runtime_enable(&platdev->dev);
return platdev;
err:
@ -144,44 +154,10 @@ static void lpe_audio_platdev_destroy(struct drm_i915_private *dev_priv)
static void lpe_audio_irq_unmask(struct irq_data *d)
{
struct drm_i915_private *dev_priv = d->chip_data;
unsigned long irqflags;
u32 val = (I915_LPE_PIPE_A_INTERRUPT |
I915_LPE_PIPE_B_INTERRUPT);
if (IS_CHERRYVIEW(dev_priv))
val |= I915_LPE_PIPE_C_INTERRUPT;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
dev_priv->irq_mask &= ~val;
I915_WRITE(VLV_IIR, val);
I915_WRITE(VLV_IIR, val);
I915_WRITE(VLV_IMR, dev_priv->irq_mask);
POSTING_READ(VLV_IMR);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
static void lpe_audio_irq_mask(struct irq_data *d)
{
struct drm_i915_private *dev_priv = d->chip_data;
unsigned long irqflags;
u32 val = (I915_LPE_PIPE_A_INTERRUPT |
I915_LPE_PIPE_B_INTERRUPT);
if (IS_CHERRYVIEW(dev_priv))
val |= I915_LPE_PIPE_C_INTERRUPT;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
dev_priv->irq_mask |= val;
I915_WRITE(VLV_IMR, dev_priv->irq_mask);
I915_WRITE(VLV_IIR, val);
I915_WRITE(VLV_IIR, val);
POSTING_READ(VLV_IIR);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
static struct irq_chip lpe_audio_irqchip = {
@ -325,8 +301,6 @@ void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv)
desc = irq_to_desc(dev_priv->lpe_audio.irq);
lpe_audio_irq_mask(&desc->irq_data);
lpe_audio_platdev_destroy(dev_priv);
irq_free_desc(dev_priv->lpe_audio.irq);
@ -337,53 +311,47 @@ void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv)
* intel_lpe_audio_notify() - notify lpe audio event
* audio driver and i915
* @dev_priv: the i915 drm device private data
* @pipe: pipe
* @port: port
* @eld : ELD data
* @pipe: pipe id
* @port: port id
* @tmds_clk_speed: tmds clock frequency in Hz
* @ls_clock: Link symbol clock in kHz
* @dp_output: Driving a DP output?
*
* Notify lpe audio driver of eld change.
*/
void intel_lpe_audio_notify(struct drm_i915_private *dev_priv,
void *eld, int port, int pipe, int tmds_clk_speed,
bool dp_output, int link_rate)
enum pipe pipe, enum port port,
const void *eld, int ls_clock, bool dp_output)
{
unsigned long irq_flags;
struct intel_hdmi_lpe_audio_pdata *pdata = NULL;
unsigned long irqflags;
struct intel_hdmi_lpe_audio_pdata *pdata;
struct intel_hdmi_lpe_audio_port_pdata *ppdata;
u32 audio_enable;
if (!HAS_LPE_AUDIO(dev_priv))
return;
pdata = dev_get_platdata(
&(dev_priv->lpe_audio.platdev->dev));
pdata = dev_get_platdata(&dev_priv->lpe_audio.platdev->dev);
ppdata = &pdata->port[port - PORT_B];
spin_lock_irqsave(&pdata->lpe_audio_slock, irq_flags);
spin_lock_irqsave(&pdata->lpe_audio_slock, irqflags);
audio_enable = I915_READ(VLV_AUD_PORT_EN_DBG(port));
if (eld != NULL) {
memcpy(pdata->eld.eld_data, eld,
HDMI_MAX_ELD_BYTES);
pdata->eld.port_id = port;
pdata->eld.pipe_id = pipe;
pdata->hdmi_connected = true;
pdata->dp_output = dp_output;
if (tmds_clk_speed)
pdata->tmds_clock_speed = tmds_clk_speed;
if (link_rate)
pdata->link_rate = link_rate;
memcpy(ppdata->eld, eld, HDMI_MAX_ELD_BYTES);
ppdata->pipe = pipe;
ppdata->ls_clock = ls_clock;
ppdata->dp_output = dp_output;
/* Unmute the amp for both DP and HDMI */
I915_WRITE(VLV_AUD_PORT_EN_DBG(port),
audio_enable & ~VLV_AMP_MUTE);
} else {
memset(pdata->eld.eld_data, 0,
HDMI_MAX_ELD_BYTES);
pdata->hdmi_connected = false;
pdata->dp_output = false;
memset(ppdata->eld, 0, HDMI_MAX_ELD_BYTES);
ppdata->pipe = -1;
ppdata->ls_clock = 0;
ppdata->dp_output = false;
/* Mute the amp for both DP and HDMI */
I915_WRITE(VLV_AUD_PORT_EN_DBG(port),
@ -391,10 +359,7 @@ void intel_lpe_audio_notify(struct drm_i915_private *dev_priv,
}
if (pdata->notify_audio_lpe)
pdata->notify_audio_lpe(dev_priv->lpe_audio.platdev);
else
pdata->notify_pending = true;
pdata->notify_audio_lpe(dev_priv->lpe_audio.platdev, port - PORT_B);
spin_unlock_irqrestore(&pdata->lpe_audio_slock,
irq_flags);
spin_unlock_irqrestore(&pdata->lpe_audio_slock, irqflags);
}

View File

@ -138,10 +138,6 @@
#include "i915_drv.h"
#include "intel_mocs.h"
#define GEN9_LR_CONTEXT_RENDER_SIZE (22 * PAGE_SIZE)
#define GEN8_LR_CONTEXT_RENDER_SIZE (20 * PAGE_SIZE)
#define GEN8_LR_CONTEXT_OTHER_SIZE (2 * PAGE_SIZE)
#define RING_EXECLIST_QFULL (1 << 0x2)
#define RING_EXECLIST1_VALID (1 << 0x3)
#define RING_EXECLIST0_VALID (1 << 0x4)
@ -326,8 +322,7 @@ static u64 execlists_update_context(struct drm_i915_gem_request *rq)
rq->ctx->ppgtt ?: rq->i915->mm.aliasing_ppgtt;
u32 *reg_state = ce->lrc_reg_state;
assert_ring_tail_valid(rq->ring, rq->tail);
reg_state[CTX_RING_TAIL+1] = rq->tail;
reg_state[CTX_RING_TAIL+1] = intel_ring_set_tail(rq->ring, rq->tail);
/* True 32b PPGTT with dynamic page allocation: update PDP
* registers and point the unallocated PDPs to scratch page.
@ -342,39 +337,32 @@ static u64 execlists_update_context(struct drm_i915_gem_request *rq)
static void execlists_submit_ports(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
struct execlist_port *port = engine->execlist_port;
u32 __iomem *elsp =
dev_priv->regs + i915_mmio_reg_offset(RING_ELSP(engine));
u64 desc[2];
engine->i915->regs + i915_mmio_reg_offset(RING_ELSP(engine));
unsigned int n;
GEM_BUG_ON(port[0].count > 1);
if (!port[0].count)
execlists_context_status_change(port[0].request,
INTEL_CONTEXT_SCHEDULE_IN);
desc[0] = execlists_update_context(port[0].request);
GEM_DEBUG_EXEC(port[0].context_id = upper_32_bits(desc[0]));
port[0].count++;
for (n = ARRAY_SIZE(engine->execlist_port); n--; ) {
struct drm_i915_gem_request *rq;
unsigned int count;
u64 desc;
if (port[1].request) {
GEM_BUG_ON(port[1].count);
execlists_context_status_change(port[1].request,
INTEL_CONTEXT_SCHEDULE_IN);
desc[1] = execlists_update_context(port[1].request);
GEM_DEBUG_EXEC(port[1].context_id = upper_32_bits(desc[1]));
port[1].count = 1;
} else {
desc[1] = 0;
rq = port_unpack(&port[n], &count);
if (rq) {
GEM_BUG_ON(count > !n);
if (!count++)
execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN);
port_set(&port[n], port_pack(rq, count));
desc = execlists_update_context(rq);
GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc));
} else {
GEM_BUG_ON(!n);
desc = 0;
}
writel(upper_32_bits(desc), elsp);
writel(lower_32_bits(desc), elsp);
}
GEM_BUG_ON(desc[0] == desc[1]);
/* You must always write both descriptors in the order below. */
writel(upper_32_bits(desc[1]), elsp);
writel(lower_32_bits(desc[1]), elsp);
writel(upper_32_bits(desc[0]), elsp);
/* The context is automatically loaded after the following */
writel(lower_32_bits(desc[0]), elsp);
}
static bool ctx_single_port_submission(const struct i915_gem_context *ctx)
@ -395,6 +383,17 @@ static bool can_merge_ctx(const struct i915_gem_context *prev,
return true;
}
static void port_assign(struct execlist_port *port,
struct drm_i915_gem_request *rq)
{
GEM_BUG_ON(rq == port_request(port));
if (port_isset(port))
i915_gem_request_put(port_request(port));
port_set(port, port_pack(i915_gem_request_get(rq), port_count(port)));
}
static void execlists_dequeue(struct intel_engine_cs *engine)
{
struct drm_i915_gem_request *last;
@ -402,7 +401,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
struct rb_node *rb;
bool submit = false;
last = port->request;
last = port_request(port);
if (last)
/* WaIdleLiteRestore:bdw,skl
* Apply the wa NOOPs to prevent ring:HEAD == req:TAIL
@ -412,7 +411,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
*/
last->tail = last->wa_tail;
GEM_BUG_ON(port[1].request);
GEM_BUG_ON(port_isset(&port[1]));
/* Hardware submission is through 2 ports. Conceptually each port
* has a (RING_START, RING_HEAD, RING_TAIL) tuple. RING_START is
@ -437,72 +436,86 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
spin_lock_irq(&engine->timeline->lock);
rb = engine->execlist_first;
GEM_BUG_ON(rb_first(&engine->execlist_queue) != rb);
while (rb) {
struct drm_i915_gem_request *cursor =
rb_entry(rb, typeof(*cursor), priotree.node);
struct i915_priolist *p = rb_entry(rb, typeof(*p), node);
struct drm_i915_gem_request *rq, *rn;
/* Can we combine this request with the current port? It has to
* be the same context/ringbuffer and not have any exceptions
* (e.g. GVT saying never to combine contexts).
*
* If we can combine the requests, we can execute both by
* updating the RING_TAIL to point to the end of the second
* request, and so we never need to tell the hardware about
* the first.
*/
if (last && !can_merge_ctx(cursor->ctx, last->ctx)) {
/* If we are on the second port and cannot combine
* this request with the last, then we are done.
list_for_each_entry_safe(rq, rn, &p->requests, priotree.link) {
/*
* Can we combine this request with the current port?
* It has to be the same context/ringbuffer and not
* have any exceptions (e.g. GVT saying never to
* combine contexts).
*
* If we can combine the requests, we can execute both
* by updating the RING_TAIL to point to the end of the
* second request, and so we never need to tell the
* hardware about the first.
*/
if (port != engine->execlist_port)
break;
if (last && !can_merge_ctx(rq->ctx, last->ctx)) {
/*
* If we are on the second port and cannot
* combine this request with the last, then we
* are done.
*/
if (port != engine->execlist_port) {
__list_del_many(&p->requests,
&rq->priotree.link);
goto done;
}
/* If GVT overrides us we only ever submit port[0],
* leaving port[1] empty. Note that we also have
* to be careful that we don't queue the same
* context (even though a different request) to
* the second port.
*/
if (ctx_single_port_submission(last->ctx) ||
ctx_single_port_submission(cursor->ctx))
break;
/*
* If GVT overrides us we only ever submit
* port[0], leaving port[1] empty. Note that we
* also have to be careful that we don't queue
* the same context (even though a different
* request) to the second port.
*/
if (ctx_single_port_submission(last->ctx) ||
ctx_single_port_submission(rq->ctx)) {
__list_del_many(&p->requests,
&rq->priotree.link);
goto done;
}
GEM_BUG_ON(last->ctx == cursor->ctx);
GEM_BUG_ON(last->ctx == rq->ctx);
i915_gem_request_assign(&port->request, last);
port++;
if (submit)
port_assign(port, last);
port++;
}
INIT_LIST_HEAD(&rq->priotree.link);
rq->priotree.priority = INT_MAX;
__i915_gem_request_submit(rq);
trace_i915_gem_request_in(rq, port_index(port, engine));
last = rq;
submit = true;
}
rb = rb_next(rb);
rb_erase(&cursor->priotree.node, &engine->execlist_queue);
RB_CLEAR_NODE(&cursor->priotree.node);
cursor->priotree.priority = INT_MAX;
__i915_gem_request_submit(cursor);
trace_i915_gem_request_in(cursor, port - engine->execlist_port);
last = cursor;
submit = true;
}
if (submit) {
i915_gem_request_assign(&port->request, last);
engine->execlist_first = rb;
rb_erase(&p->node, &engine->execlist_queue);
INIT_LIST_HEAD(&p->requests);
if (p->priority != I915_PRIORITY_NORMAL)
kmem_cache_free(engine->i915->priorities, p);
}
done:
engine->execlist_first = rb;
if (submit)
port_assign(port, last);
spin_unlock_irq(&engine->timeline->lock);
if (submit)
execlists_submit_ports(engine);
}
static bool execlists_elsp_idle(struct intel_engine_cs *engine)
{
return !engine->execlist_port[0].request;
}
static bool execlists_elsp_ready(const struct intel_engine_cs *engine)
{
const struct execlist_port *port = engine->execlist_port;
return port[0].count + port[1].count < 2;
return port_count(&port[0]) + port_count(&port[1]) < 2;
}
/*
@ -515,6 +528,15 @@ static void intel_lrc_irq_handler(unsigned long data)
struct execlist_port *port = engine->execlist_port;
struct drm_i915_private *dev_priv = engine->i915;
/* We can skip acquiring intel_runtime_pm_get() here as it was taken
* on our behalf by the request (see i915_gem_mark_busy()) and it will
* not be relinquished until the device is idle (see
* i915_gem_idle_work_handler()). As a precaution, we make sure
* that all ELSP are drained i.e. we have processed the CSB,
* before allowing ourselves to idle and calling intel_runtime_pm_put().
*/
GEM_BUG_ON(!dev_priv->gt.awake);
intel_uncore_forcewake_get(dev_priv, engine->fw_domains);
/* Prefer doing test_and_clear_bit() as a two stage operation to avoid
@ -543,7 +565,9 @@ static void intel_lrc_irq_handler(unsigned long data)
tail = GEN8_CSB_WRITE_PTR(head);
head = GEN8_CSB_READ_PTR(head);
while (head != tail) {
struct drm_i915_gem_request *rq;
unsigned int status;
unsigned int count;
if (++head == GEN8_CSB_ENTRIES)
head = 0;
@ -571,22 +595,26 @@ static void intel_lrc_irq_handler(unsigned long data)
/* Check the context/desc id for this event matches */
GEM_DEBUG_BUG_ON(readl(buf + 2 * head + 1) !=
port[0].context_id);
port->context_id);
GEM_BUG_ON(port[0].count == 0);
if (--port[0].count == 0) {
rq = port_unpack(port, &count);
GEM_BUG_ON(count == 0);
if (--count == 0) {
GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
GEM_BUG_ON(!i915_gem_request_completed(port[0].request));
execlists_context_status_change(port[0].request,
INTEL_CONTEXT_SCHEDULE_OUT);
GEM_BUG_ON(!i915_gem_request_completed(rq));
execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT);
trace_i915_gem_request_out(rq);
i915_gem_request_put(rq);
trace_i915_gem_request_out(port[0].request);
i915_gem_request_put(port[0].request);
port[0] = port[1];
memset(&port[1], 0, sizeof(port[1]));
} else {
port_set(port, port_pack(rq, count));
}
GEM_BUG_ON(port[0].count == 0 &&
/* After the final element, the hw should be idle */
GEM_BUG_ON(port_count(port) == 0 &&
!(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
}
@ -600,28 +628,66 @@ static void intel_lrc_irq_handler(unsigned long data)
intel_uncore_forcewake_put(dev_priv, engine->fw_domains);
}
static bool insert_request(struct i915_priotree *pt, struct rb_root *root)
static bool
insert_request(struct intel_engine_cs *engine,
struct i915_priotree *pt,
int prio)
{
struct rb_node **p, *rb;
struct i915_priolist *p;
struct rb_node **parent, *rb;
bool first = true;
if (unlikely(engine->no_priolist))
prio = I915_PRIORITY_NORMAL;
find_priolist:
/* most positive priority is scheduled first, equal priorities fifo */
rb = NULL;
p = &root->rb_node;
while (*p) {
struct i915_priotree *pos;
rb = *p;
pos = rb_entry(rb, typeof(*pos), node);
if (pt->priority > pos->priority) {
p = &rb->rb_left;
} else {
p = &rb->rb_right;
parent = &engine->execlist_queue.rb_node;
while (*parent) {
rb = *parent;
p = rb_entry(rb, typeof(*p), node);
if (prio > p->priority) {
parent = &rb->rb_left;
} else if (prio < p->priority) {
parent = &rb->rb_right;
first = false;
} else {
list_add_tail(&pt->link, &p->requests);
return false;
}
}
rb_link_node(&pt->node, rb, p);
rb_insert_color(&pt->node, root);
if (prio == I915_PRIORITY_NORMAL) {
p = &engine->default_priolist;
} else {
p = kmem_cache_alloc(engine->i915->priorities, GFP_ATOMIC);
/* Convert an allocation failure to a priority bump */
if (unlikely(!p)) {
prio = I915_PRIORITY_NORMAL; /* recurses just once */
/* To maintain ordering with all rendering, after an
* allocation failure we have to disable all scheduling.
* Requests will then be executed in fifo, and schedule
* will ensure that dependencies are emitted in fifo.
* There will be still some reordering with existing
* requests, so if userspace lied about their
* dependencies that reordering may be visible.
*/
engine->no_priolist = true;
goto find_priolist;
}
}
p->priority = prio;
rb_link_node(&p->node, rb, parent);
rb_insert_color(&p->node, &engine->execlist_queue);
INIT_LIST_HEAD(&p->requests);
list_add_tail(&pt->link, &p->requests);
if (first)
engine->execlist_first = &p->node;
return first;
}
@ -634,12 +700,16 @@ static void execlists_submit_request(struct drm_i915_gem_request *request)
/* Will be called from irq-context when using foreign fences. */
spin_lock_irqsave(&engine->timeline->lock, flags);
if (insert_request(&request->priotree, &engine->execlist_queue)) {
engine->execlist_first = &request->priotree.node;
if (insert_request(engine,
&request->priotree,
request->priotree.priority)) {
if (execlists_elsp_ready(engine))
tasklet_hi_schedule(&engine->irq_tasklet);
}
GEM_BUG_ON(!engine->execlist_first);
GEM_BUG_ON(list_empty(&request->priotree.link));
spin_unlock_irqrestore(&engine->timeline->lock, flags);
}
@ -709,6 +779,19 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
list_safe_reset_next(dep, p, dfs_link);
}
/* If we didn't need to bump any existing priorities, and we haven't
* yet submitted this request (i.e. there is no potential race with
* execlists_submit_request()), we can set our own priority and skip
* acquiring the engine locks.
*/
if (request->priotree.priority == INT_MIN) {
GEM_BUG_ON(!list_empty(&request->priotree.link));
request->priotree.priority = prio;
if (stack.dfs_link.next == stack.dfs_link.prev)
return;
__list_del_entry(&stack.dfs_link);
}
engine = request->engine;
spin_lock_irq(&engine->timeline->lock);
@ -724,10 +807,9 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
continue;
pt->priority = prio;
if (!RB_EMPTY_NODE(&pt->node)) {
rb_erase(&pt->node, &engine->execlist_queue);
if (insert_request(pt, &engine->execlist_queue))
engine->execlist_first = &pt->node;
if (!list_empty(&pt->link)) {
__list_del_entry(&pt->link);
insert_request(engine, pt, prio);
}
}
@ -736,8 +818,9 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
/* XXX Do we need to preempt to make room for us and our deps? */
}
static int execlists_context_pin(struct intel_engine_cs *engine,
struct i915_gem_context *ctx)
static struct intel_ring *
execlists_context_pin(struct intel_engine_cs *engine,
struct i915_gem_context *ctx)
{
struct intel_context *ce = &ctx->engine[engine->id];
unsigned int flags;
@ -746,8 +829,8 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
lockdep_assert_held(&ctx->i915->drm.struct_mutex);
if (ce->pin_count++)
return 0;
if (likely(ce->pin_count++))
goto out;
GEM_BUG_ON(!ce->pin_count); /* no overflow please! */
if (!ce->state) {
@ -771,7 +854,7 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
goto unpin_vma;
}
ret = intel_ring_pin(ce->ring, ctx->ggtt_offset_bias);
ret = intel_ring_pin(ce->ring, ctx->i915, ctx->ggtt_offset_bias);
if (ret)
goto unpin_map;
@ -784,7 +867,8 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
ce->state->obj->mm.dirty = true;
i915_gem_context_get(ctx);
return 0;
out:
return ce->ring;
unpin_map:
i915_gem_object_unpin_map(ce->state->obj);
@ -792,7 +876,7 @@ unpin_vma:
__i915_vma_unpin(ce->state);
err:
ce->pin_count = 0;
return ret;
return ERR_PTR(ret);
}
static void execlists_context_unpin(struct intel_engine_cs *engine,
@ -829,9 +913,6 @@ static int execlists_request_alloc(struct drm_i915_gem_request *request)
*/
request->reserved_space += EXECLISTS_REQUEST_SIZE;
GEM_BUG_ON(!ce->ring);
request->ring = ce->ring;
if (i915.enable_guc_submission) {
/*
* Check that the GuC has space for the request before
@ -1139,14 +1220,12 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine)
return ret;
}
static u32 port_seqno(struct execlist_port *port)
{
return port->request ? port->request->global_seqno : 0;
}
static int gen8_init_common_ring(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
struct execlist_port *port = engine->execlist_port;
unsigned int n;
bool submit;
int ret;
ret = intel_mocs_init_engine(engine);
@ -1167,16 +1246,24 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine)
/* After a GPU reset, we may have requests to replay */
clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
if (!i915.enable_guc_submission && !execlists_elsp_idle(engine)) {
DRM_DEBUG_DRIVER("Restarting %s from requests [0x%x, 0x%x]\n",
engine->name,
port_seqno(&engine->execlist_port[0]),
port_seqno(&engine->execlist_port[1]));
engine->execlist_port[0].count = 0;
engine->execlist_port[1].count = 0;
execlists_submit_ports(engine);
submit = false;
for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++) {
if (!port_isset(&port[n]))
break;
DRM_DEBUG_DRIVER("Restarting %s:%d from 0x%x\n",
engine->name, n,
port_request(&port[n])->global_seqno);
/* Discard the current inflight count */
port_set(&port[n], port_request(&port[n]));
submit = true;
}
if (submit && !i915.enable_guc_submission)
execlists_submit_ports(engine);
return 0;
}
@ -1252,13 +1339,13 @@ static void reset_common_ring(struct intel_engine_cs *engine,
intel_ring_update_space(request->ring);
/* Catch up with any missed context-switch interrupts */
if (request->ctx != port[0].request->ctx) {
i915_gem_request_put(port[0].request);
if (request->ctx != port_request(port)->ctx) {
i915_gem_request_put(port_request(port));
port[0] = port[1];
memset(&port[1], 0, sizeof(port[1]));
}
GEM_BUG_ON(request->ctx != port[0].request->ctx);
GEM_BUG_ON(request->ctx != port_request(port)->ctx);
/* Reset WaIdleLiteRestore:bdw,skl as well */
request->tail =
@ -1907,44 +1994,6 @@ populate_lr_context(struct i915_gem_context *ctx,
return 0;
}
/**
* intel_lr_context_size() - return the size of the context for an engine
* @engine: which engine to find the context size for
*
* Each engine may require a different amount of space for a context image,
* so when allocating (or copying) an image, this function can be used to
* find the right size for the specific engine.
*
* Return: size (in bytes) of an engine-specific context image
*
* Note: this size includes the HWSP, which is part of the context image
* in LRC mode, but does not include the "shared data page" used with
* GuC submission. The caller should account for this if using the GuC.
*/
uint32_t intel_lr_context_size(struct intel_engine_cs *engine)
{
int ret = 0;
WARN_ON(INTEL_GEN(engine->i915) < 8);
switch (engine->id) {
case RCS:
if (INTEL_GEN(engine->i915) >= 9)
ret = GEN9_LR_CONTEXT_RENDER_SIZE;
else
ret = GEN8_LR_CONTEXT_RENDER_SIZE;
break;
case VCS:
case BCS:
case VECS:
case VCS2:
ret = GEN8_LR_CONTEXT_OTHER_SIZE;
break;
}
return ret;
}
static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
struct intel_engine_cs *engine)
{
@ -1957,8 +2006,7 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
WARN_ON(ce->state);
context_size = round_up(intel_lr_context_size(engine),
I915_GTT_PAGE_SIZE);
context_size = round_up(engine->context_size, I915_GTT_PAGE_SIZE);
/* One extra page as the sharing data between driver and GuC */
context_size += PAGE_SIZE * LRC_PPHWSP_PN;
@ -1989,7 +2037,7 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
ce->ring = ring;
ce->state = vma;
ce->initialised = engine->init_context == NULL;
ce->initialised |= engine->init_context == NULL;
return 0;
@ -2036,8 +2084,7 @@ void intel_lr_context_resume(struct drm_i915_private *dev_priv)
ce->state->obj->mm.dirty = true;
i915_gem_object_unpin_map(ce->state->obj);
ce->ring->head = ce->ring->tail = 0;
intel_ring_update_space(ce->ring);
intel_ring_reset(ce->ring, 0);
}
}
}

View File

@ -78,8 +78,6 @@ int logical_xcs_ring_init(struct intel_engine_cs *engine);
struct drm_i915_private;
struct i915_gem_context;
uint32_t intel_lr_context_size(struct intel_engine_cs *engine);
void intel_lr_context_resume(struct drm_i915_private *dev_priv);
uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx,
struct intel_engine_cs *engine);

View File

@ -888,10 +888,14 @@ static void pch_enable_backlight(struct intel_connector *connector)
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_panel *panel = &connector->panel;
enum pipe pipe = intel_get_pipe_from_connector(connector);
enum transcoder cpu_transcoder =
intel_pipe_to_cpu_transcoder(dev_priv, pipe);
enum transcoder cpu_transcoder;
u32 cpu_ctl2, pch_ctl1, pch_ctl2;
if (!WARN_ON_ONCE(pipe == INVALID_PIPE))
cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe);
else
cpu_transcoder = TRANSCODER_EDP;
cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2);
if (cpu_ctl2 & BLM_PWM_ENABLE) {
DRM_DEBUG_KMS("cpu backlight already enabled\n");
@ -973,6 +977,9 @@ static void i965_enable_backlight(struct intel_connector *connector)
enum pipe pipe = intel_get_pipe_from_connector(connector);
u32 ctl, ctl2, freq;
if (WARN_ON_ONCE(pipe == INVALID_PIPE))
pipe = PIPE_A;
ctl2 = I915_READ(BLC_PWM_CTL2);
if (ctl2 & BLM_PWM_ENABLE) {
DRM_DEBUG_KMS("backlight already enabled\n");
@ -1037,6 +1044,9 @@ static void bxt_enable_backlight(struct intel_connector *connector)
enum pipe pipe = intel_get_pipe_from_connector(connector);
u32 pwm_ctl, val;
if (WARN_ON_ONCE(pipe == INVALID_PIPE))
pipe = PIPE_A;
/* Controller 1 uses the utility pin. */
if (panel->backlight.controller == 1) {
val = I915_READ(UTIL_PIN_CTL);
@ -1093,7 +1103,8 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
if (!panel->backlight.present)
return;
DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
if (!WARN_ON_ONCE(pipe == INVALID_PIPE))
DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
mutex_lock(&dev_priv->backlight_lock);

View File

@ -513,16 +513,20 @@ static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv,
struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_A);
struct intel_crtc_state *pipe_config;
struct drm_atomic_state *state;
struct drm_modeset_acquire_ctx ctx;
int ret = 0;
drm_modeset_lock_all(dev);
drm_modeset_acquire_init(&ctx, 0);
state = drm_atomic_state_alloc(dev);
if (!state) {
ret = -ENOMEM;
goto unlock;
}
state->acquire_ctx = crtc->base.dev->mode_config.acquire_ctx;
state->acquire_ctx = &ctx;
retry:
pipe_config = intel_atomic_get_crtc_state(state, crtc);
if (IS_ERR(pipe_config)) {
ret = PTR_ERR(pipe_config);
@ -537,10 +541,17 @@ static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv,
ret = drm_atomic_commit(state);
put_state:
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
drm_modeset_backoff(&ctx);
goto retry;
}
drm_atomic_state_put(state);
unlock:
WARN(ret, "Toggling workaround to %i returns %i\n", enable, ret);
drm_modeset_unlock_all(dev);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
@ -842,19 +853,12 @@ static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf,
return -E2BIG;
}
tmpbuf = kmalloc(len + 1, GFP_KERNEL);
if (!tmpbuf)
return -ENOMEM;
if (copy_from_user(tmpbuf, ubuf, len)) {
ret = -EFAULT;
goto out;
}
tmpbuf[len] = '\0';
tmpbuf = memdup_user_nul(ubuf, len);
if (IS_ERR(tmpbuf))
return PTR_ERR(tmpbuf);
ret = display_crc_ctl_parse(dev_priv, tmpbuf, len);
out:
kfree(tmpbuf);
if (ret < 0)
return ret;

File diff suppressed because it is too large Load Diff

View File

@ -39,17 +39,27 @@
*/
#define LEGACY_REQUEST_SIZE 200
static int __intel_ring_space(int head, int tail, int size)
static unsigned int __intel_ring_space(unsigned int head,
unsigned int tail,
unsigned int size)
{
int space = head - tail;
if (space <= 0)
space += size;
return space - I915_RING_FREE_SPACE;
/*
* "If the Ring Buffer Head Pointer and the Tail Pointer are on the
* same cacheline, the Head Pointer must not be greater than the Tail
* Pointer."
*/
GEM_BUG_ON(!is_power_of_2(size));
return (head - tail - CACHELINE_BYTES) & (size - 1);
}
void intel_ring_update_space(struct intel_ring *ring)
unsigned int intel_ring_update_space(struct intel_ring *ring)
{
ring->space = __intel_ring_space(ring->head, ring->tail, ring->size);
unsigned int space;
space = __intel_ring_space(ring->head, ring->emit, ring->size);
ring->space = space;
return space;
}
static int
@ -538,9 +548,9 @@ static int init_ring_common(struct intel_engine_cs *engine)
I915_WRITE_CTL(engine, RING_CTL_SIZE(ring->size) | RING_VALID);
/* If the head is still not zero, the ring is dead */
if (intel_wait_for_register_fw(dev_priv, RING_CTL(engine->mmio_base),
RING_VALID, RING_VALID,
50)) {
if (intel_wait_for_register(dev_priv, RING_CTL(engine->mmio_base),
RING_VALID, RING_VALID,
50)) {
DRM_ERROR("%s initialization failed "
"ctl %08x (valid? %d) head %08x [%08x] tail %08x [%08x] start %08x [expected %08x]\n",
engine->name,
@ -774,8 +784,8 @@ static void i9xx_submit_request(struct drm_i915_gem_request *request)
i915_gem_request_submit(request);
assert_ring_tail_valid(request->ring, request->tail);
I915_WRITE_TAIL(request->engine, request->tail);
I915_WRITE_TAIL(request->engine,
intel_ring_set_tail(request->ring, request->tail));
}
static void i9xx_emit_breadcrumb(struct drm_i915_gem_request *req, u32 *cs)
@ -1259,6 +1269,8 @@ static int init_phys_status_page(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
GEM_BUG_ON(engine->id != RCS);
dev_priv->status_page_dmah =
drm_pci_alloc(&dev_priv->drm, PAGE_SIZE, PAGE_SIZE);
if (!dev_priv->status_page_dmah)
@ -1270,17 +1282,18 @@ static int init_phys_status_page(struct intel_engine_cs *engine)
return 0;
}
int intel_ring_pin(struct intel_ring *ring, unsigned int offset_bias)
int intel_ring_pin(struct intel_ring *ring,
struct drm_i915_private *i915,
unsigned int offset_bias)
{
unsigned int flags;
enum i915_map_type map;
enum i915_map_type map = HAS_LLC(i915) ? I915_MAP_WB : I915_MAP_WC;
struct i915_vma *vma = ring->vma;
unsigned int flags;
void *addr;
int ret;
GEM_BUG_ON(ring->vaddr);
map = HAS_LLC(ring->engine->i915) ? I915_MAP_WB : I915_MAP_WC;
flags = PIN_GLOBAL;
if (offset_bias)
@ -1316,11 +1329,23 @@ err:
return PTR_ERR(addr);
}
void intel_ring_reset(struct intel_ring *ring, u32 tail)
{
GEM_BUG_ON(!list_empty(&ring->request_list));
ring->tail = tail;
ring->head = tail;
ring->emit = tail;
intel_ring_update_space(ring);
}
void intel_ring_unpin(struct intel_ring *ring)
{
GEM_BUG_ON(!ring->vma);
GEM_BUG_ON(!ring->vaddr);
/* Discard any unused bytes beyond that submitted to hw. */
intel_ring_reset(ring, ring->tail);
if (i915_vma_is_map_and_fenceable(ring->vma))
i915_vma_unpin_iomap(ring->vma);
else
@ -1338,7 +1363,7 @@ intel_ring_create_vma(struct drm_i915_private *dev_priv, int size)
obj = i915_gem_object_create_stolen(dev_priv, size);
if (!obj)
obj = i915_gem_object_create(dev_priv, size);
obj = i915_gem_object_create_internal(dev_priv, size);
if (IS_ERR(obj))
return ERR_CAST(obj);
@ -1369,8 +1394,6 @@ intel_engine_create_ring(struct intel_engine_cs *engine, int size)
if (!ring)
return ERR_PTR(-ENOMEM);
ring->engine = engine;
INIT_LIST_HEAD(&ring->request_list);
ring->size = size;
@ -1424,22 +1447,73 @@ static int context_pin(struct i915_gem_context *ctx)
PIN_GLOBAL | PIN_HIGH);
}
static int intel_ring_context_pin(struct intel_engine_cs *engine,
struct i915_gem_context *ctx)
static struct i915_vma *
alloc_context_vma(struct intel_engine_cs *engine)
{
struct drm_i915_private *i915 = engine->i915;
struct drm_i915_gem_object *obj;
struct i915_vma *vma;
obj = i915_gem_object_create(i915, engine->context_size);
if (IS_ERR(obj))
return ERR_CAST(obj);
/*
* Try to make the context utilize L3 as well as LLC.
*
* On VLV we don't have L3 controls in the PTEs so we
* shouldn't touch the cache level, especially as that
* would make the object snooped which might have a
* negative performance impact.
*
* Snooping is required on non-llc platforms in execlist
* mode, but since all GGTT accesses use PAT entry 0 we
* get snooping anyway regardless of cache_level.
*
* This is only applicable for Ivy Bridge devices since
* later platforms don't have L3 control bits in the PTE.
*/
if (IS_IVYBRIDGE(i915)) {
/* Ignore any error, regard it as a simple optimisation */
i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC);
}
vma = i915_vma_instance(obj, &i915->ggtt.base, NULL);
if (IS_ERR(vma))
i915_gem_object_put(obj);
return vma;
}
static struct intel_ring *
intel_ring_context_pin(struct intel_engine_cs *engine,
struct i915_gem_context *ctx)
{
struct intel_context *ce = &ctx->engine[engine->id];
int ret;
lockdep_assert_held(&ctx->i915->drm.struct_mutex);
if (ce->pin_count++)
return 0;
if (likely(ce->pin_count++))
goto out;
GEM_BUG_ON(!ce->pin_count); /* no overflow please! */
if (!ce->state && engine->context_size) {
struct i915_vma *vma;
vma = alloc_context_vma(engine);
if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
goto err;
}
ce->state = vma;
}
if (ce->state) {
ret = context_pin(ctx);
if (ret)
goto error;
goto err;
ce->state->obj->mm.dirty = true;
}
@ -1455,11 +1529,14 @@ static int intel_ring_context_pin(struct intel_engine_cs *engine,
ce->initialised = true;
i915_gem_context_get(ctx);
return 0;
error:
out:
/* One ringbuffer to rule them all */
return engine->buffer;
err:
ce->pin_count = 0;
return ret;
return ERR_PTR(ret);
}
static void intel_ring_context_unpin(struct intel_engine_cs *engine,
@ -1481,78 +1558,70 @@ static void intel_ring_context_unpin(struct intel_engine_cs *engine,
static int intel_init_ring_buffer(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
struct intel_ring *ring;
int ret;
WARN_ON(engine->buffer);
int err;
intel_engine_setup_common(engine);
ret = intel_engine_init_common(engine);
if (ret)
goto error;
err = intel_engine_init_common(engine);
if (err)
goto err;
if (HWS_NEEDS_PHYSICAL(engine->i915))
err = init_phys_status_page(engine);
else
err = init_status_page(engine);
if (err)
goto err;
ring = intel_engine_create_ring(engine, 32 * PAGE_SIZE);
if (IS_ERR(ring)) {
ret = PTR_ERR(ring);
goto error;
}
if (HWS_NEEDS_PHYSICAL(dev_priv)) {
WARN_ON(engine->id != RCS);
ret = init_phys_status_page(engine);
if (ret)
goto error;
} else {
ret = init_status_page(engine);
if (ret)
goto error;
err = PTR_ERR(ring);
goto err_hws;
}
/* Ring wraparound at offset 0 sometimes hangs. No idea why. */
ret = intel_ring_pin(ring, I915_GTT_PAGE_SIZE);
if (ret) {
intel_ring_free(ring);
goto error;
}
err = intel_ring_pin(ring, engine->i915, I915_GTT_PAGE_SIZE);
if (err)
goto err_ring;
GEM_BUG_ON(engine->buffer);
engine->buffer = ring;
return 0;
error:
intel_engine_cleanup(engine);
return ret;
err_ring:
intel_ring_free(ring);
err_hws:
if (HWS_NEEDS_PHYSICAL(engine->i915))
cleanup_phys_status_page(engine);
else
cleanup_status_page(engine);
err:
intel_engine_cleanup_common(engine);
return err;
}
void intel_engine_cleanup(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv;
struct drm_i915_private *dev_priv = engine->i915;
dev_priv = engine->i915;
WARN_ON(INTEL_GEN(dev_priv) > 2 &&
(I915_READ_MODE(engine) & MODE_IDLE) == 0);
if (engine->buffer) {
WARN_ON(INTEL_GEN(dev_priv) > 2 &&
(I915_READ_MODE(engine) & MODE_IDLE) == 0);
intel_ring_unpin(engine->buffer);
intel_ring_free(engine->buffer);
engine->buffer = NULL;
}
intel_ring_unpin(engine->buffer);
intel_ring_free(engine->buffer);
if (engine->cleanup)
engine->cleanup(engine);
if (HWS_NEEDS_PHYSICAL(dev_priv)) {
WARN_ON(engine->id != RCS);
if (HWS_NEEDS_PHYSICAL(dev_priv))
cleanup_phys_status_page(engine);
} else {
else
cleanup_status_page(engine);
}
intel_engine_cleanup_common(engine);
engine->i915 = NULL;
dev_priv->engine[engine->id] = NULL;
kfree(engine);
}
@ -1562,8 +1631,9 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv)
struct intel_engine_cs *engine;
enum intel_engine_id id;
/* Restart from the beginning of the rings for convenience */
for_each_engine(engine, dev_priv, id)
engine->buffer->head = engine->buffer->tail;
intel_ring_reset(engine->buffer, 0);
}
static int ring_request_alloc(struct drm_i915_gem_request *request)
@ -1578,9 +1648,6 @@ static int ring_request_alloc(struct drm_i915_gem_request *request)
*/
request->reserved_space += LEGACY_REQUEST_SIZE;
GEM_BUG_ON(!request->engine->buffer);
request->ring = request->engine->buffer;
cs = intel_ring_begin(request, 0);
if (IS_ERR(cs))
return PTR_ERR(cs);
@ -1589,7 +1656,8 @@ static int ring_request_alloc(struct drm_i915_gem_request *request)
return 0;
}
static int wait_for_space(struct drm_i915_gem_request *req, int bytes)
static noinline int wait_for_space(struct drm_i915_gem_request *req,
unsigned int bytes)
{
struct intel_ring *ring = req->ring;
struct drm_i915_gem_request *target;
@ -1597,8 +1665,7 @@ static int wait_for_space(struct drm_i915_gem_request *req, int bytes)
lockdep_assert_held(&req->i915->drm.struct_mutex);
intel_ring_update_space(ring);
if (ring->space >= bytes)
if (intel_ring_update_space(ring) >= bytes)
return 0;
/*
@ -1613,12 +1680,9 @@ static int wait_for_space(struct drm_i915_gem_request *req, int bytes)
GEM_BUG_ON(!req->reserved_space);
list_for_each_entry(target, &ring->request_list, ring_link) {
unsigned space;
/* Would completion of this request free enough space? */
space = __intel_ring_space(target->postfix, ring->tail,
ring->size);
if (space >= bytes)
if (bytes <= __intel_ring_space(target->postfix,
ring->emit, ring->size))
break;
}
@ -1638,59 +1702,64 @@ static int wait_for_space(struct drm_i915_gem_request *req, int bytes)
return 0;
}
u32 *intel_ring_begin(struct drm_i915_gem_request *req, int num_dwords)
u32 *intel_ring_begin(struct drm_i915_gem_request *req,
unsigned int num_dwords)
{
struct intel_ring *ring = req->ring;
int remain_actual = ring->size - ring->tail;
int remain_usable = ring->effective_size - ring->tail;
int bytes = num_dwords * sizeof(u32);
int total_bytes, wait_bytes;
bool need_wrap = false;
const unsigned int remain_usable = ring->effective_size - ring->emit;
const unsigned int bytes = num_dwords * sizeof(u32);
unsigned int need_wrap = 0;
unsigned int total_bytes;
u32 *cs;
total_bytes = bytes + req->reserved_space;
GEM_BUG_ON(total_bytes > ring->effective_size);
if (unlikely(bytes > remain_usable)) {
/*
* Not enough space for the basic request. So need to flush
* out the remainder and then wait for base + reserved.
*/
wait_bytes = remain_actual + total_bytes;
need_wrap = true;
} else if (unlikely(total_bytes > remain_usable)) {
/*
* The base request will fit but the reserved space
* falls off the end. So we don't need an immediate wrap
* and only need to effectively wait for the reserved
* size space from the start of ringbuffer.
*/
wait_bytes = remain_actual + req->reserved_space;
} else {
/* No wrapping required, just waiting. */
wait_bytes = total_bytes;
if (unlikely(total_bytes > remain_usable)) {
const int remain_actual = ring->size - ring->emit;
if (bytes > remain_usable) {
/*
* Not enough space for the basic request. So need to
* flush out the remainder and then wait for
* base + reserved.
*/
total_bytes += remain_actual;
need_wrap = remain_actual | 1;
} else {
/*
* The base request will fit but the reserved space
* falls off the end. So we don't need an immediate
* wrap and only need to effectively wait for the
* reserved size from the start of ringbuffer.
*/
total_bytes = req->reserved_space + remain_actual;
}
}
if (wait_bytes > ring->space) {
int ret = wait_for_space(req, wait_bytes);
if (unlikely(total_bytes > ring->space)) {
int ret = wait_for_space(req, total_bytes);
if (unlikely(ret))
return ERR_PTR(ret);
}
if (unlikely(need_wrap)) {
GEM_BUG_ON(remain_actual > ring->space);
GEM_BUG_ON(ring->tail + remain_actual > ring->size);
need_wrap &= ~1;
GEM_BUG_ON(need_wrap > ring->space);
GEM_BUG_ON(ring->emit + need_wrap > ring->size);
/* Fill the tail with MI_NOOP */
memset(ring->vaddr + ring->tail, 0, remain_actual);
ring->tail = 0;
ring->space -= remain_actual;
memset(ring->vaddr + ring->emit, 0, need_wrap);
ring->emit = 0;
ring->space -= need_wrap;
}
GEM_BUG_ON(ring->tail > ring->size - bytes);
cs = ring->vaddr + ring->tail;
ring->tail += bytes;
GEM_BUG_ON(ring->emit > ring->size - bytes);
GEM_BUG_ON(ring->space < bytes);
cs = ring->vaddr + ring->emit;
GEM_DEBUG_EXEC(memset(cs, POISON_INUSE, bytes));
ring->emit += bytes;
ring->space -= bytes;
GEM_BUG_ON(ring->space < 0);
return cs;
}
@ -1699,7 +1768,7 @@ u32 *intel_ring_begin(struct drm_i915_gem_request *req, int num_dwords)
int intel_ring_cacheline_align(struct drm_i915_gem_request *req)
{
int num_dwords =
(req->ring->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t);
(req->ring->emit & (CACHELINE_BYTES - 1)) / sizeof(uint32_t);
u32 *cs;
if (num_dwords == 0)
@ -1736,11 +1805,11 @@ static void gen6_bsd_submit_request(struct drm_i915_gem_request *request)
I915_WRITE64_FW(GEN6_BSD_RNCID, 0x0);
/* Wait for the ring not to be idle, i.e. for it to wake up. */
if (intel_wait_for_register_fw(dev_priv,
GEN6_BSD_SLEEP_PSMI_CONTROL,
GEN6_BSD_SLEEP_INDICATOR,
0,
50))
if (__intel_wait_for_register_fw(dev_priv,
GEN6_BSD_SLEEP_PSMI_CONTROL,
GEN6_BSD_SLEEP_INDICATOR,
0,
1000, 0, NULL))
DRM_ERROR("timed out waiting for the BSD ring to wake up\n");
/* Now that the ring is fully powered up, update the tail */
@ -2182,20 +2251,6 @@ int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine)
return intel_init_ring_buffer(engine);
}
/**
* Initialize the second BSD ring (eg. Broadwell GT3, Skylake GT3)
*/
int intel_init_bsd2_ring_buffer(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
intel_ring_default_vfuncs(dev_priv, engine);
engine->emit_flush = gen6_bsd_ring_flush;
return intel_init_ring_buffer(engine);
}
int intel_init_blt_ring_buffer(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;

View File

@ -17,17 +17,6 @@
#define CACHELINE_BYTES 64
#define CACHELINE_DWORDS (CACHELINE_BYTES / sizeof(uint32_t))
/*
* Gen2 BSpec "1. Programming Environment" / 1.4.4.6 "Ring Buffer Use"
* Gen3 BSpec "vol1c Memory Interface Functions" / 2.3.4.5 "Ring Buffer Use"
* Gen4+ BSpec "vol1c Memory Interface and Command Stream" / 5.3.4.5 "Ring Buffer Use"
*
* "If the Ring Buffer Head Pointer and the Tail Pointer are on the same
* cacheline, the Head Pointer must not be greater than the Tail
* Pointer."
*/
#define I915_RING_FREE_SPACE 64
struct intel_hw_status_page {
struct i915_vma *vma;
u32 *page_addr;
@ -139,16 +128,15 @@ struct intel_ring {
struct i915_vma *vma;
void *vaddr;
struct intel_engine_cs *engine;
struct list_head request_list;
u32 head;
u32 tail;
u32 emit;
int space;
int size;
int effective_size;
u32 space;
u32 size;
u32 effective_size;
};
struct i915_gem_context;
@ -189,15 +177,28 @@ enum intel_engine_id {
VECS
};
struct i915_priolist {
struct rb_node node;
struct list_head requests;
int priority;
};
#define INTEL_ENGINE_CS_MAX_NAME 8
struct intel_engine_cs {
struct drm_i915_private *i915;
const char *name;
char name[INTEL_ENGINE_CS_MAX_NAME];
enum intel_engine_id id;
unsigned int exec_id;
unsigned int uabi_id;
unsigned int hw_id;
unsigned int guc_id;
u32 mmio_base;
u8 class;
u8 instance;
u32 context_size;
u32 mmio_base;
unsigned int irq_shift;
struct intel_ring *buffer;
struct intel_timeline *timeline;
@ -265,8 +266,8 @@ struct intel_engine_cs {
void (*set_default_submission)(struct intel_engine_cs *engine);
int (*context_pin)(struct intel_engine_cs *engine,
struct i915_gem_context *ctx);
struct intel_ring *(*context_pin)(struct intel_engine_cs *engine,
struct i915_gem_context *ctx);
void (*context_unpin)(struct intel_engine_cs *engine,
struct i915_gem_context *ctx);
int (*request_alloc)(struct drm_i915_gem_request *req);
@ -372,9 +373,18 @@ struct intel_engine_cs {
/* Execlists */
struct tasklet_struct irq_tasklet;
struct i915_priolist default_priolist;
bool no_priolist;
struct execlist_port {
struct drm_i915_gem_request *request;
unsigned int count;
struct drm_i915_gem_request *request_count;
#define EXECLIST_COUNT_BITS 2
#define port_request(p) ptr_mask_bits((p)->request_count, EXECLIST_COUNT_BITS)
#define port_count(p) ptr_unmask_bits((p)->request_count, EXECLIST_COUNT_BITS)
#define port_pack(rq, count) ptr_pack_bits(rq, count, EXECLIST_COUNT_BITS)
#define port_unpack(p, count) ptr_unpack_bits((p)->request_count, count, EXECLIST_COUNT_BITS)
#define port_set(p, packed) ((p)->request_count = (packed))
#define port_isset(p) ((p)->request_count)
#define port_index(p, e) ((p) - (e)->execlist_port)
GEM_DEBUG_DECL(u32 context_id);
} execlist_port[2];
struct rb_root execlist_queue;
@ -487,7 +497,11 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value)
struct intel_ring *
intel_engine_create_ring(struct intel_engine_cs *engine, int size);
int intel_ring_pin(struct intel_ring *ring, unsigned int offset_bias);
int intel_ring_pin(struct intel_ring *ring,
struct drm_i915_private *i915,
unsigned int offset_bias);
void intel_ring_reset(struct intel_ring *ring, u32 tail);
unsigned int intel_ring_update_space(struct intel_ring *ring);
void intel_ring_unpin(struct intel_ring *ring);
void intel_ring_free(struct intel_ring *ring);
@ -498,7 +512,8 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv);
int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req);
u32 __must_check *intel_ring_begin(struct drm_i915_gem_request *req, int n);
u32 __must_check *intel_ring_begin(struct drm_i915_gem_request *req,
unsigned int n);
static inline void
intel_ring_advance(struct drm_i915_gem_request *req, u32 *cs)
@ -511,7 +526,7 @@ intel_ring_advance(struct drm_i915_gem_request *req, u32 *cs)
* reserved for the command packet (i.e. the value passed to
* intel_ring_begin()).
*/
GEM_BUG_ON((req->ring->vaddr + req->ring->tail) != cs);
GEM_BUG_ON((req->ring->vaddr + req->ring->emit) != cs);
}
static inline u32
@ -538,9 +553,40 @@ assert_ring_tail_valid(const struct intel_ring *ring, unsigned int tail)
*/
GEM_BUG_ON(!IS_ALIGNED(tail, 8));
GEM_BUG_ON(tail >= ring->size);
/*
* "Ring Buffer Use"
* Gen2 BSpec "1. Programming Environment" / 1.4.4.6
* Gen3 BSpec "1c Memory Interface Functions" / 2.3.4.5
* Gen4+ BSpec "1c Memory Interface and Command Stream" / 5.3.4.5
* "If the Ring Buffer Head Pointer and the Tail Pointer are on the
* same cacheline, the Head Pointer must not be greater than the Tail
* Pointer."
*
* We use ring->head as the last known location of the actual RING_HEAD,
* it may have advanced but in the worst case it is equally the same
* as ring->head and so we should never program RING_TAIL to advance
* into the same cacheline as ring->head.
*/
#define cacheline(a) round_down(a, CACHELINE_BYTES)
GEM_BUG_ON(cacheline(tail) == cacheline(ring->head) &&
tail < ring->head);
#undef cacheline
}
void intel_ring_update_space(struct intel_ring *ring);
static inline unsigned int
intel_ring_set_tail(struct intel_ring *ring, unsigned int tail)
{
/* Whilst writes to the tail are strictly order, there is no
* serialisation between readers and the writers. The tail may be
* read by i915_gem_request_retire() just as it is being updated
* by execlists, as although the breadcrumb is complete, the context
* switch hasn't been seen.
*/
assert_ring_tail_valid(ring, tail);
ring->tail = tail;
return tail;
}
void intel_engine_init_global_seqno(struct intel_engine_cs *engine, u32 seqno);
@ -551,7 +597,6 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine);
int intel_init_render_ring_buffer(struct intel_engine_cs *engine);
int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine);
int intel_init_bsd2_ring_buffer(struct intel_engine_cs *engine);
int intel_init_blt_ring_buffer(struct intel_engine_cs *engine);
int intel_init_vebox_ring_buffer(struct intel_engine_cs *engine);
@ -652,7 +697,8 @@ bool intel_engine_add_wait(struct intel_engine_cs *engine,
struct intel_wait *wait);
void intel_engine_remove_wait(struct intel_engine_cs *engine,
struct intel_wait *wait);
void intel_engine_enable_signaling(struct drm_i915_gem_request *request);
void intel_engine_enable_signaling(struct drm_i915_gem_request *request,
bool wakeup);
void intel_engine_cancel_signaling(struct drm_i915_gem_request *request);
static inline bool intel_engine_has_waiter(const struct intel_engine_cs *engine)
@ -685,6 +731,7 @@ static inline u32 *gen8_emit_pipe_control(u32 *batch, u32 flags, u32 offset)
bool intel_engine_is_idle(struct intel_engine_cs *engine);
bool intel_engines_are_idle(struct drm_i915_private *dev_priv);
void intel_engines_mark_idle(struct drm_i915_private *i915);
void intel_engines_reset_default_submission(struct drm_i915_private *i915);
#endif /* _INTEL_RINGBUFFER_H_ */

View File

@ -2875,11 +2875,10 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
BUILD_BUG_ON(sizeof(enhancements) != 2);
enhancements.response = 0;
intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS,
&enhancements, sizeof(enhancements));
if (enhancements.response == 0) {
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS,
&enhancements, sizeof(enhancements)) ||
enhancements.response == 0) {
DRM_DEBUG_KMS("No enhancement is supported\n");
return true;
}

View File

@ -210,16 +210,14 @@ void intel_pipe_update_end(struct intel_crtc *crtc, struct intel_flip_work *work
}
static void
skl_update_plane(struct drm_plane *drm_plane,
skl_update_plane(struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct drm_device *dev = drm_plane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(drm_plane);
struct drm_framebuffer *fb = plane_state->base.fb;
enum plane_id plane_id = intel_plane->id;
enum pipe pipe = intel_plane->pipe;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
const struct drm_framebuffer *fb = plane_state->base.fb;
enum plane_id plane_id = plane->id;
enum pipe pipe = plane->pipe;
u32 plane_ctl = plane_state->ctl;
const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
u32 surf_addr = plane_state->main.offset;
@ -288,13 +286,11 @@ skl_update_plane(struct drm_plane *drm_plane,
}
static void
skl_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
skl_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc)
{
struct drm_device *dev = dplane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(dplane);
enum plane_id plane_id = intel_plane->id;
enum pipe pipe = intel_plane->pipe;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
enum plane_id plane_id = plane->id;
enum pipe pipe = plane->pipe;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
@ -308,10 +304,10 @@ skl_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
}
static void
chv_update_csc(struct intel_plane *intel_plane, uint32_t format)
chv_update_csc(struct intel_plane *plane, uint32_t format)
{
struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev);
enum plane_id plane_id = intel_plane->id;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
enum plane_id plane_id = plane->id;
/* Seems RGB data bypasses the CSC always */
if (!format_is_yuv(format))
@ -411,16 +407,14 @@ static u32 vlv_sprite_ctl(const struct intel_crtc_state *crtc_state,
}
static void
vlv_update_plane(struct drm_plane *dplane,
vlv_update_plane(struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct drm_device *dev = dplane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(dplane);
struct drm_framebuffer *fb = plane_state->base.fb;
enum pipe pipe = intel_plane->pipe;
enum plane_id plane_id = intel_plane->id;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
const struct drm_framebuffer *fb = plane_state->base.fb;
enum pipe pipe = plane->pipe;
enum plane_id plane_id = plane->id;
u32 sprctl = plane_state->ctl;
u32 sprsurf_offset = plane_state->main.offset;
u32 linear_offset;
@ -442,7 +436,7 @@ vlv_update_plane(struct drm_plane *dplane,
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B)
chv_update_csc(intel_plane, fb->format->format);
chv_update_csc(plane, fb->format->format);
if (key->flags) {
I915_WRITE_FW(SPKEYMINVAL(pipe, plane_id), key->min_value);
@ -469,13 +463,11 @@ vlv_update_plane(struct drm_plane *dplane,
}
static void
vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
vlv_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc)
{
struct drm_device *dev = dplane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(dplane);
enum pipe pipe = intel_plane->pipe;
enum plane_id plane_id = intel_plane->id;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
enum pipe pipe = plane->pipe;
enum plane_id plane_id = plane->id;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
@ -545,15 +537,13 @@ static u32 ivb_sprite_ctl(const struct intel_crtc_state *crtc_state,
}
static void
ivb_update_plane(struct drm_plane *plane,
ivb_update_plane(struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(plane);
struct drm_framebuffer *fb = plane_state->base.fb;
enum pipe pipe = intel_plane->pipe;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
const struct drm_framebuffer *fb = plane_state->base.fb;
enum pipe pipe = plane->pipe;
u32 sprctl = plane_state->ctl, sprscale = 0;
u32 sprsurf_offset = plane_state->main.offset;
u32 linear_offset;
@ -600,7 +590,7 @@ ivb_update_plane(struct drm_plane *plane,
I915_WRITE_FW(SPRLINOFF(pipe), linear_offset);
I915_WRITE_FW(SPRSIZE(pipe), (crtc_h << 16) | crtc_w);
if (intel_plane->can_scale)
if (plane->can_scale)
I915_WRITE_FW(SPRSCALE(pipe), sprscale);
I915_WRITE_FW(SPRCTL(pipe), sprctl);
I915_WRITE_FW(SPRSURF(pipe),
@ -611,19 +601,17 @@ ivb_update_plane(struct drm_plane *plane,
}
static void
ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
ivb_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc)
{
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(plane);
int pipe = intel_plane->pipe;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
enum pipe pipe = plane->pipe;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
I915_WRITE_FW(SPRCTL(pipe), 0);
/* Can't leave the scaler enabled... */
if (intel_plane->can_scale)
if (plane->can_scale)
I915_WRITE_FW(SPRSCALE(pipe), 0);
I915_WRITE_FW(SPRSURF(pipe), 0);
@ -632,7 +620,7 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
static u32 ilk_sprite_ctl(const struct intel_crtc_state *crtc_state,
static u32 g4x_sprite_ctl(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct drm_i915_private *dev_priv =
@ -686,15 +674,13 @@ static u32 ilk_sprite_ctl(const struct intel_crtc_state *crtc_state,
}
static void
ilk_update_plane(struct drm_plane *plane,
g4x_update_plane(struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(plane);
struct drm_framebuffer *fb = plane_state->base.fb;
int pipe = intel_plane->pipe;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
const struct drm_framebuffer *fb = plane_state->base.fb;
enum pipe pipe = plane->pipe;
u32 dvscntr = plane_state->ctl, dvsscale = 0;
u32 dvssurf_offset = plane_state->main.offset;
u32 linear_offset;
@ -747,12 +733,10 @@ ilk_update_plane(struct drm_plane *plane,
}
static void
ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
g4x_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc)
{
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_plane *intel_plane = to_intel_plane(plane);
int pipe = intel_plane->pipe;
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
enum pipe pipe = plane->pipe;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
@ -768,14 +752,12 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
}
static int
intel_check_sprite_plane(struct drm_plane *plane,
intel_check_sprite_plane(struct intel_plane *plane,
struct intel_crtc_state *crtc_state,
struct intel_plane_state *state)
{
struct drm_i915_private *dev_priv = to_i915(plane->dev);
struct drm_crtc *crtc = state->base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_plane *intel_plane = to_intel_plane(plane);
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
struct drm_framebuffer *fb = state->base.fb;
int crtc_x, crtc_y;
unsigned int crtc_w, crtc_h;
@ -797,7 +779,7 @@ intel_check_sprite_plane(struct drm_plane *plane,
}
/* Don't modify another pipe's plane */
if (intel_plane->pipe != intel_crtc->pipe) {
if (plane->pipe != crtc->pipe) {
DRM_DEBUG_KMS("Wrong plane <-> crtc mapping\n");
return -EINVAL;
}
@ -814,16 +796,16 @@ intel_check_sprite_plane(struct drm_plane *plane,
if (state->ckey.flags == I915_SET_COLORKEY_NONE) {
can_scale = 1;
min_scale = 1;
max_scale = skl_max_scale(intel_crtc, crtc_state);
max_scale = skl_max_scale(crtc, crtc_state);
} else {
can_scale = 0;
min_scale = DRM_PLANE_HELPER_NO_SCALING;
max_scale = DRM_PLANE_HELPER_NO_SCALING;
}
} else {
can_scale = intel_plane->can_scale;
max_scale = intel_plane->max_downscale << 16;
min_scale = intel_plane->can_scale ? 1 : (1 << 16);
can_scale = plane->can_scale;
max_scale = plane->max_downscale << 16;
min_scale = plane->can_scale ? 1 : (1 << 16);
}
/*
@ -967,7 +949,7 @@ intel_check_sprite_plane(struct drm_plane *plane,
if (ret)
return ret;
state->ctl = ilk_sprite_ctl(crtc_state, state);
state->ctl = g4x_sprite_ctl(crtc_state, state);
}
return 0;
@ -1027,7 +1009,7 @@ out:
return ret;
}
static const uint32_t ilk_plane_formats[] = {
static const uint32_t g4x_plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_YUYV,
DRM_FORMAT_YVYU,
@ -1131,15 +1113,15 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv,
intel_plane->can_scale = true;
intel_plane->max_downscale = 16;
intel_plane->update_plane = ilk_update_plane;
intel_plane->disable_plane = ilk_disable_plane;
intel_plane->update_plane = g4x_update_plane;
intel_plane->disable_plane = g4x_disable_plane;
if (IS_GEN6(dev_priv)) {
plane_formats = snb_plane_formats;
num_plane_formats = ARRAY_SIZE(snb_plane_formats);
} else {
plane_formats = ilk_plane_formats;
num_plane_formats = ARRAY_SIZE(ilk_plane_formats);
plane_formats = g4x_plane_formats;
num_plane_formats = ARRAY_SIZE(g4x_plane_formats);
}
}

View File

@ -48,41 +48,6 @@ struct intel_tv {
struct intel_encoder base;
int type;
const char *tv_format;
int margin[4];
u32 save_TV_H_CTL_1;
u32 save_TV_H_CTL_2;
u32 save_TV_H_CTL_3;
u32 save_TV_V_CTL_1;
u32 save_TV_V_CTL_2;
u32 save_TV_V_CTL_3;
u32 save_TV_V_CTL_4;
u32 save_TV_V_CTL_5;
u32 save_TV_V_CTL_6;
u32 save_TV_V_CTL_7;
u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
u32 save_TV_CSC_Y;
u32 save_TV_CSC_Y2;
u32 save_TV_CSC_U;
u32 save_TV_CSC_U2;
u32 save_TV_CSC_V;
u32 save_TV_CSC_V2;
u32 save_TV_CLR_KNOBS;
u32 save_TV_CLR_LEVEL;
u32 save_TV_WIN_POS;
u32 save_TV_WIN_SIZE;
u32 save_TV_FILTER_CTL_1;
u32 save_TV_FILTER_CTL_2;
u32 save_TV_FILTER_CTL_3;
u32 save_TV_H_LUMA[60];
u32 save_TV_H_CHROMA[60];
u32 save_TV_V_LUMA[43];
u32 save_TV_V_CHROMA[43];
u32 save_TV_DAC;
u32 save_TV_CTL;
};
struct video_levels {
@ -873,32 +838,18 @@ intel_disable_tv(struct intel_encoder *encoder,
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
}
static const struct tv_mode *
intel_tv_mode_lookup(const char *tv_format)
static const struct tv_mode *intel_tv_mode_find(struct drm_connector_state *conn_state)
{
int i;
int format = conn_state->tv.mode;
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
const struct tv_mode *tv_mode = &tv_modes[i];
if (!strcmp(tv_format, tv_mode->name))
return tv_mode;
}
return NULL;
}
static const struct tv_mode *
intel_tv_mode_find(struct intel_tv *intel_tv)
{
return intel_tv_mode_lookup(intel_tv->tv_format);
return &tv_modes[format];
}
static enum drm_mode_status
intel_tv_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
if (mode->clock > max_dotclk)
@ -925,8 +876,7 @@ intel_tv_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_tv *intel_tv = enc_to_tv(encoder);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
if (!tv_mode)
return false;
@ -1032,7 +982,7 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder,
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
struct intel_tv *intel_tv = enc_to_tv(encoder);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
u32 tv_ctl;
u32 scctl1, scctl2, scctl3;
int i, j;
@ -1135,12 +1085,12 @@ static void intel_tv_pre_enable(struct intel_encoder *encoder,
else
ysize = 2*tv_mode->nbr_end + 1;
xpos += intel_tv->margin[TV_MARGIN_LEFT];
ypos += intel_tv->margin[TV_MARGIN_TOP];
xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
intel_tv->margin[TV_MARGIN_RIGHT]);
ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
intel_tv->margin[TV_MARGIN_BOTTOM]);
xpos += conn_state->tv.margins.left;
ypos += conn_state->tv.margins.top;
xsize -= (conn_state->tv.margins.left +
conn_state->tv.margins.right);
ysize -= (conn_state->tv.margins.top +
conn_state->tv.margins.bottom);
I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
@ -1288,7 +1238,7 @@ intel_tv_detect_type(struct intel_tv *intel_tv,
static void intel_tv_find_better_format(struct drm_connector *connector)
{
struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
int i;
if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) ==
@ -1304,9 +1254,7 @@ static void intel_tv_find_better_format(struct drm_connector *connector)
break;
}
intel_tv->tv_format = tv_mode->name;
drm_object_property_set_value(&connector->base,
connector->dev->mode_config.tv_mode_property, i);
connector->state->tv.mode = i;
}
/**
@ -1347,16 +1295,15 @@ intel_tv_detect(struct drm_connector *connector,
connector_status_connected;
} else
status = connector_status_unknown;
if (status == connector_status_connected) {
intel_tv->type = type;
intel_tv_find_better_format(connector);
}
return status;
} else
return connector->status;
if (status != connector_status_connected)
return status;
intel_tv->type = type;
intel_tv_find_better_format(connector);
return connector_status_connected;
}
static const struct input_res {
@ -1376,12 +1323,9 @@ static const struct input_res {
* Chose preferred mode according to line number of TV format
*/
static void
intel_tv_chose_preferred_modes(struct drm_connector *connector,
intel_tv_choose_preferred_modes(const struct tv_mode *tv_mode,
struct drm_display_mode *mode_ptr)
{
struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
else if (tv_mode->nbr_end > 480) {
@ -1404,8 +1348,7 @@ static int
intel_tv_get_modes(struct drm_connector *connector)
{
struct drm_display_mode *mode_ptr;
struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
int j, count = 0;
u64 tmp;
@ -1448,7 +1391,7 @@ intel_tv_get_modes(struct drm_connector *connector)
mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER;
intel_tv_chose_preferred_modes(connector, mode_ptr);
intel_tv_choose_preferred_modes(tv_mode, mode_ptr);
drm_mode_probed_add(connector, mode_ptr);
count++;
}
@ -1463,74 +1406,47 @@ intel_tv_destroy(struct drm_connector *connector)
kfree(connector);
}
static int
intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
uint64_t val)
{
struct drm_device *dev = connector->dev;
struct intel_tv *intel_tv = intel_attached_tv(connector);
struct drm_crtc *crtc = intel_tv->base.base.crtc;
int ret = 0;
bool changed = false;
ret = drm_object_property_set_value(&connector->base, property, val);
if (ret < 0)
goto out;
if (property == dev->mode_config.tv_left_margin_property &&
intel_tv->margin[TV_MARGIN_LEFT] != val) {
intel_tv->margin[TV_MARGIN_LEFT] = val;
changed = true;
} else if (property == dev->mode_config.tv_right_margin_property &&
intel_tv->margin[TV_MARGIN_RIGHT] != val) {
intel_tv->margin[TV_MARGIN_RIGHT] = val;
changed = true;
} else if (property == dev->mode_config.tv_top_margin_property &&
intel_tv->margin[TV_MARGIN_TOP] != val) {
intel_tv->margin[TV_MARGIN_TOP] = val;
changed = true;
} else if (property == dev->mode_config.tv_bottom_margin_property &&
intel_tv->margin[TV_MARGIN_BOTTOM] != val) {
intel_tv->margin[TV_MARGIN_BOTTOM] = val;
changed = true;
} else if (property == dev->mode_config.tv_mode_property) {
if (val >= ARRAY_SIZE(tv_modes)) {
ret = -EINVAL;
goto out;
}
if (!strcmp(intel_tv->tv_format, tv_modes[val].name))
goto out;
intel_tv->tv_format = tv_modes[val].name;
changed = true;
} else {
ret = -EINVAL;
goto out;
}
if (changed && crtc)
intel_crtc_restore_mode(crtc);
out:
return ret;
}
static const struct drm_connector_funcs intel_tv_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
.destroy = intel_tv_destroy,
.set_property = intel_tv_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
.set_property = drm_atomic_helper_connector_set_property,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
};
static int intel_tv_atomic_check(struct drm_connector *connector,
struct drm_connector_state *new_state)
{
struct drm_crtc_state *new_crtc_state;
struct drm_connector_state *old_state;
if (!new_state->crtc)
return 0;
old_state = drm_atomic_get_old_connector_state(new_state->state, connector);
new_crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc);
if (old_state->tv.mode != new_state->tv.mode ||
old_state->tv.margins.left != new_state->tv.margins.left ||
old_state->tv.margins.right != new_state->tv.margins.right ||
old_state->tv.margins.top != new_state->tv.margins.top ||
old_state->tv.margins.bottom != new_state->tv.margins.bottom) {
/* Force a modeset. */
new_crtc_state->connectors_changed = true;
}
return 0;
}
static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
.detect_ctx = intel_tv_detect,
.mode_valid = intel_tv_mode_valid,
.get_modes = intel_tv_get_modes,
.atomic_check = intel_tv_atomic_check,
};
static const struct drm_encoder_funcs intel_tv_enc_funcs = {
@ -1548,6 +1464,7 @@ intel_tv_init(struct drm_i915_private *dev_priv)
u32 tv_dac_on, tv_dac_off, save_tv_dac;
const char *tv_format_names[ARRAY_SIZE(tv_modes)];
int i, initial_mode = 0;
struct drm_connector_state *state;
if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
return;
@ -1593,6 +1510,7 @@ intel_tv_init(struct drm_i915_private *dev_priv)
intel_encoder = &intel_tv->base;
connector = &intel_connector->base;
state = connector->state;
/* The documentation, for the older chipsets at least, recommend
* using a polling method rather than hotplug detection for TVs.
@ -1630,12 +1548,12 @@ intel_tv_init(struct drm_i915_private *dev_priv)
intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
/* BIOS margin values */
intel_tv->margin[TV_MARGIN_LEFT] = 54;
intel_tv->margin[TV_MARGIN_TOP] = 36;
intel_tv->margin[TV_MARGIN_RIGHT] = 46;
intel_tv->margin[TV_MARGIN_BOTTOM] = 37;
state->tv.margins.left = 54;
state->tv.margins.top = 36;
state->tv.margins.right = 46;
state->tv.margins.bottom = 37;
intel_tv->tv_format = tv_modes[initial_mode].name;
state->tv.mode = initial_mode;
drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
connector->interlace_allowed = false;
@ -1649,17 +1567,17 @@ intel_tv_init(struct drm_i915_private *dev_priv)
tv_format_names);
drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property,
initial_mode);
state->tv.mode);
drm_object_attach_property(&connector->base,
dev->mode_config.tv_left_margin_property,
intel_tv->margin[TV_MARGIN_LEFT]);
state->tv.margins.left);
drm_object_attach_property(&connector->base,
dev->mode_config.tv_top_margin_property,
intel_tv->margin[TV_MARGIN_TOP]);
state->tv.margins.top);
drm_object_attach_property(&connector->base,
dev->mode_config.tv_right_margin_property,
intel_tv->margin[TV_MARGIN_RIGHT]);
state->tv.margins.right);
drm_object_attach_property(&connector->base,
dev->mode_config.tv_bottom_margin_property,
intel_tv->margin[TV_MARGIN_BOTTOM]);
state->tv.margins.bottom);
}

View File

@ -94,12 +94,22 @@ void intel_uc_sanitize_options(struct drm_i915_private *dev_priv)
i915.enable_guc_submission = HAS_GUC_SCHED(dev_priv);
}
static void guc_write_irq_trigger(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
I915_WRITE(GUC_SEND_INTERRUPT, GUC_SEND_TRIGGER);
}
void intel_uc_init_early(struct drm_i915_private *dev_priv)
{
struct intel_guc *guc = &dev_priv->guc;
intel_guc_ct_init_early(&guc->ct);
mutex_init(&guc->send_mutex);
guc->send = intel_guc_send_mmio;
guc->send = intel_guc_send_nop;
guc->notify = guc_write_irq_trigger;
}
static void fetch_uc_fw(struct drm_i915_private *dev_priv,
@ -252,13 +262,81 @@ void intel_uc_fini_fw(struct drm_i915_private *dev_priv)
__intel_uc_fw_fini(&dev_priv->huc.fw);
}
static inline i915_reg_t guc_send_reg(struct intel_guc *guc, u32 i)
{
GEM_BUG_ON(!guc->send_regs.base);
GEM_BUG_ON(!guc->send_regs.count);
GEM_BUG_ON(i >= guc->send_regs.count);
return _MMIO(guc->send_regs.base + 4 * i);
}
static void guc_init_send_regs(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
enum forcewake_domains fw_domains = 0;
unsigned int i;
guc->send_regs.base = i915_mmio_reg_offset(SOFT_SCRATCH(0));
guc->send_regs.count = SOFT_SCRATCH_COUNT - 1;
for (i = 0; i < guc->send_regs.count; i++) {
fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
guc_send_reg(guc, i),
FW_REG_READ | FW_REG_WRITE);
}
guc->send_regs.fw_domains = fw_domains;
}
static void guc_capture_load_err_log(struct intel_guc *guc)
{
if (!guc->log.vma || i915.guc_log_level < 0)
return;
if (!guc->load_err_log)
guc->load_err_log = i915_gem_object_get(guc->log.vma->obj);
return;
}
static void guc_free_load_err_log(struct intel_guc *guc)
{
if (guc->load_err_log)
i915_gem_object_put(guc->load_err_log);
}
static int guc_enable_communication(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
guc_init_send_regs(guc);
if (HAS_GUC_CT(dev_priv))
return intel_guc_enable_ct(guc);
guc->send = intel_guc_send_mmio;
return 0;
}
static void guc_disable_communication(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
if (HAS_GUC_CT(dev_priv))
intel_guc_disable_ct(guc);
guc->send = intel_guc_send_nop;
}
int intel_uc_init_hw(struct drm_i915_private *dev_priv)
{
struct intel_guc *guc = &dev_priv->guc;
int ret, attempts;
if (!i915.enable_guc_loading)
return 0;
guc_disable_communication(guc);
gen9_reset_guc_interrupts(dev_priv);
/* We need to notify the guc whenever we change the GGTT */
@ -274,6 +352,11 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
goto err_guc;
}
/* init WOPCM */
I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
I915_WRITE(DMA_GUC_WOPCM_OFFSET,
GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC);
/* WaEnableuKernelHeaderValidFix:skl */
/* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */
if (IS_GEN9(dev_priv))
@ -301,7 +384,11 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
/* Did we succeded or run out of retries? */
if (ret)
goto err_submission;
goto err_log_capture;
ret = guc_enable_communication(guc);
if (ret)
goto err_log_capture;
intel_guc_auth_huc(dev_priv);
if (i915.enable_guc_submission) {
@ -325,7 +412,10 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
* marks the GPU as wedged until reset).
*/
err_interrupts:
guc_disable_communication(guc);
gen9_disable_guc_interrupts(dev_priv);
err_log_capture:
guc_capture_load_err_log(guc);
err_submission:
if (i915.enable_guc_submission)
i915_guc_submission_fini(dev_priv);
@ -351,25 +441,25 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv)
if (!i915.enable_guc_loading)
return;
if (i915.enable_guc_submission) {
guc_free_load_err_log(&dev_priv->guc);
if (i915.enable_guc_submission)
i915_guc_submission_disable(dev_priv);
guc_disable_communication(&dev_priv->guc);
if (i915.enable_guc_submission) {
gen9_disable_guc_interrupts(dev_priv);
i915_guc_submission_fini(dev_priv);
}
i915_ggtt_disable_guc(dev_priv);
}
/*
* Read GuC command/status register (SOFT_SCRATCH_0)
* Return true if it contains a response rather than a command
*/
static bool guc_recv(struct intel_guc *guc, u32 *status)
int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
u32 val = I915_READ(SOFT_SCRATCH(0));
*status = val;
return INTEL_GUC_RECV_IS_RESPONSE(val);
WARN(1, "Unexpected send: action=%#x\n", *action);
return -ENODEV;
}
/*
@ -382,30 +472,33 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len)
int i;
int ret;
if (WARN_ON(len < 1 || len > 15))
return -EINVAL;
GEM_BUG_ON(!len);
GEM_BUG_ON(len > guc->send_regs.count);
/* If CT is available, we expect to use MMIO only during init/fini */
GEM_BUG_ON(HAS_GUC_CT(dev_priv) &&
*action != INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER &&
*action != INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER);
mutex_lock(&guc->send_mutex);
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_BLITTER);
dev_priv->guc.action_count += 1;
dev_priv->guc.action_cmd = action[0];
intel_uncore_forcewake_get(dev_priv, guc->send_regs.fw_domains);
for (i = 0; i < len; i++)
I915_WRITE(SOFT_SCRATCH(i), action[i]);
I915_WRITE(guc_send_reg(guc, i), action[i]);
POSTING_READ(SOFT_SCRATCH(i - 1));
POSTING_READ(guc_send_reg(guc, i - 1));
I915_WRITE(GUC_SEND_INTERRUPT, GUC_SEND_TRIGGER);
intel_guc_notify(guc);
/*
* Fast commands should complete in less than 10us, so sample quickly
* up to that length of time, then switch to a slower sleep-wait loop.
* No inte_guc_send command should ever take longer than 10ms.
* No GuC command should ever take longer than 10ms.
* Fast commands should still complete in 10us.
*/
ret = wait_for_us(guc_recv(guc, &status), 10);
if (ret)
ret = wait_for(guc_recv(guc, &status), 10);
ret = __intel_wait_for_register_fw(dev_priv,
guc_send_reg(guc, 0),
INTEL_GUC_RECV_MASK,
INTEL_GUC_RECV_MASK,
10, 10, &status);
if (status != INTEL_GUC_STATUS_SUCCESS) {
/*
* Either the GuC explicitly returned an error (which
@ -418,13 +511,9 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len)
DRM_WARN("INTEL_GUC_SEND: Action 0x%X failed;"
" ret=%d status=0x%08X response=0x%08X\n",
action[0], ret, status, I915_READ(SOFT_SCRATCH(15)));
dev_priv->guc.action_fail += 1;
dev_priv->guc.action_err = ret;
}
dev_priv->guc.action_status = status;
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_BLITTER);
intel_uncore_forcewake_put(dev_priv, guc->send_regs.fw_domains);
mutex_unlock(&guc->send_mutex);
return ret;

View File

@ -27,7 +27,7 @@
#include "intel_guc_fwif.h"
#include "i915_guc_reg.h"
#include "intel_ringbuffer.h"
#include "intel_guc_ct.h"
#include "i915_vma.h"
struct drm_i915_gem_request;
@ -59,12 +59,6 @@ struct drm_i915_gem_request;
* available in the work queue (note, the queue is shared,
* not per-engine). It is OK for this to be nonzero, but
* it should not be huge!
* q_fail: failed to enqueue a work item. This should never happen,
* because we check for space beforehand.
* b_fail: failed to ring the doorbell. This should never happen, unless
* somehow the hardware misbehaves, or maybe if the GuC firmware
* crashes? We probably need to reset the GPU to recover.
* retcode: errno from last guc_submit()
*/
struct i915_guc_client {
struct i915_vma *vma;
@ -87,8 +81,6 @@ struct i915_guc_client {
uint32_t wq_tail;
uint32_t wq_rsvd;
uint32_t no_wq_space;
uint32_t b_fail;
int retcode;
/* Per-engine counts of GuC submissions */
uint64_t submissions[I915_NUM_ENGINES];
@ -181,6 +173,10 @@ struct intel_guc_log {
struct intel_guc {
struct intel_uc_fw fw;
struct intel_guc_log log;
struct intel_guc_ct ct;
/* Log snapshot if GuC errors during load */
struct drm_i915_gem_object *load_err_log;
/* intel_guc_recv interrupt related state */
bool interrupts_enabled;
@ -195,21 +191,21 @@ struct intel_guc {
DECLARE_BITMAP(doorbell_bitmap, GUC_NUM_DOORBELLS);
uint32_t db_cacheline; /* Cyclic counter mod pagesize */
/* Action status & statistics */
uint64_t action_count; /* Total commands issued */
uint32_t action_cmd; /* Last command word */
uint32_t action_status; /* Last return status */
uint32_t action_fail; /* Total number of failures */
int32_t action_err; /* Last error code */
uint64_t submissions[I915_NUM_ENGINES];
uint32_t last_seqno[I915_NUM_ENGINES];
/* GuC's FW specific registers used in MMIO send */
struct {
u32 base;
unsigned int count;
enum forcewake_domains fw_domains;
} send_regs;
/* To serialize the intel_guc_send actions */
struct mutex send_mutex;
/* GuC's FW specific send function */
int (*send)(struct intel_guc *guc, const u32 *data, u32 len);
/* GuC's FW specific notify function */
void (*notify)(struct intel_guc *guc);
};
struct intel_huc {
@ -227,12 +223,19 @@ void intel_uc_fini_fw(struct drm_i915_private *dev_priv);
int intel_uc_init_hw(struct drm_i915_private *dev_priv);
void intel_uc_fini_hw(struct drm_i915_private *dev_priv);
int intel_guc_sample_forcewake(struct intel_guc *guc);
int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len);
int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len);
static inline int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len)
{
return guc->send(guc, action, len);
}
static inline void intel_guc_notify(struct intel_guc *guc)
{
guc->notify(guc);
}
/* intel_guc_loader.c */
int intel_guc_select_fw(struct intel_guc *guc);
int intel_guc_init_hw(struct intel_guc *guc);
@ -266,7 +269,7 @@ static inline u32 guc_ggtt_offset(struct i915_vma *vma)
/* intel_huc.c */
void intel_huc_select_fw(struct intel_huc *huc);
int intel_huc_init_hw(struct intel_huc *huc);
void intel_huc_init_hw(struct intel_huc *huc);
void intel_guc_auth_huc(struct drm_i915_private *dev_priv);
#endif

View File

@ -29,6 +29,7 @@
#include <linux/pm_runtime.h>
#define FORCEWAKE_ACK_TIMEOUT_MS 50
#define GT_FIFO_TIMEOUT_MS 10
#define __raw_posting_read(dev_priv__, reg__) (void)__raw_i915_read32((dev_priv__), (reg__))
@ -172,22 +173,6 @@ static void fw_domains_get_with_thread_status(struct drm_i915_private *dev_priv,
__gen6_gt_wait_for_thread_c0(dev_priv);
}
static void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
{
u32 gtfifodbg;
gtfifodbg = __raw_i915_read32(dev_priv, GTFIFODBG);
if (WARN(gtfifodbg, "GT wake FIFO error 0x%x\n", gtfifodbg))
__raw_i915_write32(dev_priv, GTFIFODBG, gtfifodbg);
}
static void fw_domains_put_with_fifo(struct drm_i915_private *dev_priv,
enum forcewake_domains fw_domains)
{
fw_domains_put(dev_priv, fw_domains);
gen6_gt_check_fifodbg(dev_priv);
}
static inline u32 fifo_free_entries(struct drm_i915_private *dev_priv)
{
u32 count = __raw_i915_read32(dev_priv, GTFIFOCTL);
@ -195,30 +180,27 @@ static inline u32 fifo_free_entries(struct drm_i915_private *dev_priv)
return count & GT_FIFO_FREE_ENTRIES_MASK;
}
static int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
static void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
{
int ret = 0;
u32 n;
/* On VLV, FIFO will be shared by both SW and HW.
* So, we need to read the FREE_ENTRIES everytime */
if (IS_VALLEYVIEW(dev_priv))
dev_priv->uncore.fifo_count = fifo_free_entries(dev_priv);
n = fifo_free_entries(dev_priv);
else
n = dev_priv->uncore.fifo_count;
if (dev_priv->uncore.fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) {
int loop = 500;
u32 fifo = fifo_free_entries(dev_priv);
while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) {
udelay(10);
fifo = fifo_free_entries(dev_priv);
if (n <= GT_FIFO_NUM_RESERVED_ENTRIES) {
if (wait_for_atomic((n = fifo_free_entries(dev_priv)) >
GT_FIFO_NUM_RESERVED_ENTRIES,
GT_FIFO_TIMEOUT_MS)) {
DRM_DEBUG("GT_FIFO timeout, entries: %u\n", n);
return;
}
if (WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES))
++ret;
dev_priv->uncore.fifo_count = fifo;
}
dev_priv->uncore.fifo_count--;
return ret;
dev_priv->uncore.fifo_count = n - 1;
}
static enum hrtimer_restart
@ -232,6 +214,9 @@ intel_uncore_fw_release_timer(struct hrtimer *timer)
assert_rpm_device_not_suspended(dev_priv);
if (xchg(&domain->active, false))
return HRTIMER_RESTART;
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
if (WARN_ON(domain->wake_count == 0))
domain->wake_count++;
@ -262,6 +247,7 @@ static void intel_uncore_forcewake_reset(struct drm_i915_private *dev_priv,
active_domains = 0;
for_each_fw_domain(domain, dev_priv, tmp) {
smp_store_mb(domain->active, false);
if (hrtimer_cancel(&domain->timer) == 0)
continue;
@ -383,16 +369,36 @@ vlv_check_for_unclaimed_mmio(struct drm_i915_private *dev_priv)
return true;
}
static bool
gen6_check_for_fifo_debug(struct drm_i915_private *dev_priv)
{
u32 fifodbg;
fifodbg = __raw_i915_read32(dev_priv, GTFIFODBG);
if (unlikely(fifodbg)) {
DRM_DEBUG_DRIVER("GTFIFODBG = 0x08%x\n", fifodbg);
__raw_i915_write32(dev_priv, GTFIFODBG, fifodbg);
}
return fifodbg;
}
static bool
check_for_unclaimed_mmio(struct drm_i915_private *dev_priv)
{
bool ret = false;
if (HAS_FPGA_DBG_UNCLAIMED(dev_priv))
return fpga_check_for_unclaimed_mmio(dev_priv);
ret |= fpga_check_for_unclaimed_mmio(dev_priv);
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
return vlv_check_for_unclaimed_mmio(dev_priv);
ret |= vlv_check_for_unclaimed_mmio(dev_priv);
return false;
if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv))
ret |= gen6_check_for_fifo_debug(dev_priv);
return ret;
}
static void __intel_uncore_early_sanitize(struct drm_i915_private *dev_priv,
@ -404,11 +410,6 @@ static void __intel_uncore_early_sanitize(struct drm_i915_private *dev_priv,
if (check_for_unclaimed_mmio(dev_priv))
DRM_DEBUG("unclaimed mmio detected on uncore init, clearing\n");
/* clear out old GT FIFO errors */
if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv))
__raw_i915_write32(dev_priv, GTFIFODBG,
__raw_i915_read32(dev_priv, GTFIFODBG));
/* WaDisableShadowRegForCpd:chv */
if (IS_CHERRYVIEW(dev_priv)) {
__raw_i915_write32(dev_priv, GTFIFOCTL,
@ -454,9 +455,12 @@ static void __intel_uncore_forcewake_get(struct drm_i915_private *dev_priv,
fw_domains &= dev_priv->uncore.fw_domains;
for_each_fw_domain_masked(domain, fw_domains, dev_priv, tmp)
if (domain->wake_count++)
for_each_fw_domain_masked(domain, fw_domains, dev_priv, tmp) {
if (domain->wake_count++) {
fw_domains &= ~domain->mask;
domain->active = true;
}
}
if (fw_domains)
dev_priv->uncore.funcs.force_wake_get(dev_priv, fw_domains);
@ -521,8 +525,10 @@ static void __intel_uncore_forcewake_put(struct drm_i915_private *dev_priv,
if (WARN_ON(domain->wake_count == 0))
continue;
if (--domain->wake_count)
if (--domain->wake_count) {
domain->active = true;
continue;
}
fw_domain_arm_timer(domain);
}
@ -804,6 +810,18 @@ unclaimed_reg_debug(struct drm_i915_private *dev_priv,
__unclaimed_reg_debug(dev_priv, reg, read, before);
}
enum decoupled_power_domain {
GEN9_DECOUPLED_PD_BLITTER = 0,
GEN9_DECOUPLED_PD_RENDER,
GEN9_DECOUPLED_PD_MEDIA,
GEN9_DECOUPLED_PD_ALL
};
enum decoupled_ops {
GEN9_DECOUPLED_OP_WRITE = 0,
GEN9_DECOUPLED_OP_READ
};
static const enum decoupled_power_domain fw2dpd_domain[] = {
GEN9_DECOUPLED_PD_RENDER,
GEN9_DECOUPLED_PD_BLITTER,
@ -1047,15 +1065,10 @@ __gen2_write(32)
#define __gen6_write(x) \
static void \
gen6_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool trace) { \
u32 __fifo_ret = 0; \
GEN6_WRITE_HEADER; \
if (NEEDS_FORCE_WAKE(offset)) { \
__fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
} \
if (NEEDS_FORCE_WAKE(offset)) \
__gen6_gt_wait_for_fifo(dev_priv); \
__raw_i915_write##x(dev_priv, reg, val); \
if (unlikely(__fifo_ret)) { \
gen6_gt_check_fifodbg(dev_priv); \
} \
GEN6_WRITE_FOOTER; \
}
@ -1108,19 +1121,19 @@ __gen6_write(32)
#undef GEN6_WRITE_FOOTER
#undef GEN6_WRITE_HEADER
#define ASSIGN_WRITE_MMIO_VFUNCS(x) \
#define ASSIGN_WRITE_MMIO_VFUNCS(i915, x) \
do { \
dev_priv->uncore.funcs.mmio_writeb = x##_write8; \
dev_priv->uncore.funcs.mmio_writew = x##_write16; \
dev_priv->uncore.funcs.mmio_writel = x##_write32; \
(i915)->uncore.funcs.mmio_writeb = x##_write8; \
(i915)->uncore.funcs.mmio_writew = x##_write16; \
(i915)->uncore.funcs.mmio_writel = x##_write32; \
} while (0)
#define ASSIGN_READ_MMIO_VFUNCS(x) \
#define ASSIGN_READ_MMIO_VFUNCS(i915, x) \
do { \
dev_priv->uncore.funcs.mmio_readb = x##_read8; \
dev_priv->uncore.funcs.mmio_readw = x##_read16; \
dev_priv->uncore.funcs.mmio_readl = x##_read32; \
dev_priv->uncore.funcs.mmio_readq = x##_read64; \
(i915)->uncore.funcs.mmio_readb = x##_read8; \
(i915)->uncore.funcs.mmio_readw = x##_read16; \
(i915)->uncore.funcs.mmio_readl = x##_read32; \
(i915)->uncore.funcs.mmio_readq = x##_read64; \
} while (0)
@ -1190,11 +1203,7 @@ static void intel_uncore_fw_domains_init(struct drm_i915_private *dev_priv)
FORCEWAKE_MEDIA_GEN9, FORCEWAKE_ACK_MEDIA_GEN9);
} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
dev_priv->uncore.funcs.force_wake_get = fw_domains_get;
if (!IS_CHERRYVIEW(dev_priv))
dev_priv->uncore.funcs.force_wake_put =
fw_domains_put_with_fifo;
else
dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER,
FORCEWAKE_VLV, FORCEWAKE_ACK_VLV);
fw_domain_init(dev_priv, FW_DOMAIN_ID_MEDIA,
@ -1202,11 +1211,7 @@ static void intel_uncore_fw_domains_init(struct drm_i915_private *dev_priv)
} else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
dev_priv->uncore.funcs.force_wake_get =
fw_domains_get_with_thread_status;
if (IS_HASWELL(dev_priv))
dev_priv->uncore.funcs.force_wake_put =
fw_domains_put_with_fifo;
else
dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER,
FORCEWAKE_MT, FORCEWAKE_ACK_HSW);
} else if (IS_IVYBRIDGE(dev_priv)) {
@ -1223,8 +1228,7 @@ static void intel_uncore_fw_domains_init(struct drm_i915_private *dev_priv)
*/
dev_priv->uncore.funcs.force_wake_get =
fw_domains_get_with_thread_status;
dev_priv->uncore.funcs.force_wake_put =
fw_domains_put_with_fifo;
dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
/* We need to init first for ECOBUS access and then
* determine later if we want to reinit, in case of MT access is
@ -1242,7 +1246,7 @@ static void intel_uncore_fw_domains_init(struct drm_i915_private *dev_priv)
spin_lock_irq(&dev_priv->uncore.lock);
fw_domains_get_with_thread_status(dev_priv, FORCEWAKE_RENDER);
ecobus = __raw_i915_read32(dev_priv, ECOBUS);
fw_domains_put_with_fifo(dev_priv, FORCEWAKE_RENDER);
fw_domains_put(dev_priv, FORCEWAKE_RENDER);
spin_unlock_irq(&dev_priv->uncore.lock);
if (!(ecobus & FORCEWAKE_MT_ENABLE)) {
@ -1254,8 +1258,7 @@ static void intel_uncore_fw_domains_init(struct drm_i915_private *dev_priv)
} else if (IS_GEN6(dev_priv)) {
dev_priv->uncore.funcs.force_wake_get =
fw_domains_get_with_thread_status;
dev_priv->uncore.funcs.force_wake_put =
fw_domains_put_with_fifo;
dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER,
FORCEWAKE, FORCEWAKE_ACK);
}
@ -1310,34 +1313,34 @@ void intel_uncore_init(struct drm_i915_private *dev_priv)
i915_pmic_bus_access_notifier;
if (IS_GEN(dev_priv, 2, 4) || intel_vgpu_active(dev_priv)) {
ASSIGN_WRITE_MMIO_VFUNCS(gen2);
ASSIGN_READ_MMIO_VFUNCS(gen2);
ASSIGN_WRITE_MMIO_VFUNCS(dev_priv, gen2);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, gen2);
} else if (IS_GEN5(dev_priv)) {
ASSIGN_WRITE_MMIO_VFUNCS(gen5);
ASSIGN_READ_MMIO_VFUNCS(gen5);
ASSIGN_WRITE_MMIO_VFUNCS(dev_priv, gen5);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, gen5);
} else if (IS_GEN(dev_priv, 6, 7)) {
ASSIGN_WRITE_MMIO_VFUNCS(gen6);
ASSIGN_WRITE_MMIO_VFUNCS(dev_priv, gen6);
if (IS_VALLEYVIEW(dev_priv)) {
ASSIGN_FW_DOMAINS_TABLE(__vlv_fw_ranges);
ASSIGN_READ_MMIO_VFUNCS(fwtable);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, fwtable);
} else {
ASSIGN_READ_MMIO_VFUNCS(gen6);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, gen6);
}
} else if (IS_GEN8(dev_priv)) {
if (IS_CHERRYVIEW(dev_priv)) {
ASSIGN_FW_DOMAINS_TABLE(__chv_fw_ranges);
ASSIGN_WRITE_MMIO_VFUNCS(fwtable);
ASSIGN_READ_MMIO_VFUNCS(fwtable);
ASSIGN_WRITE_MMIO_VFUNCS(dev_priv, fwtable);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, fwtable);
} else {
ASSIGN_WRITE_MMIO_VFUNCS(gen8);
ASSIGN_READ_MMIO_VFUNCS(gen6);
ASSIGN_WRITE_MMIO_VFUNCS(dev_priv, gen8);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, gen6);
}
} else {
ASSIGN_FW_DOMAINS_TABLE(__gen9_fw_ranges);
ASSIGN_WRITE_MMIO_VFUNCS(fwtable);
ASSIGN_READ_MMIO_VFUNCS(fwtable);
ASSIGN_WRITE_MMIO_VFUNCS(dev_priv, fwtable);
ASSIGN_READ_MMIO_VFUNCS(dev_priv, fwtable);
if (HAS_DECOUPLED_MMIO(dev_priv)) {
dev_priv->uncore.funcs.mmio_readl =
gen9_decoupled_read32;
@ -1353,8 +1356,6 @@ void intel_uncore_init(struct drm_i915_private *dev_priv)
i915_check_and_clear_faults(dev_priv);
}
#undef ASSIGN_WRITE_MMIO_VFUNCS
#undef ASSIGN_READ_MMIO_VFUNCS
void intel_uncore_fini(struct drm_i915_private *dev_priv)
{
@ -1435,9 +1436,39 @@ out:
return ret;
}
static int i915_reset_complete(struct pci_dev *pdev)
static void gen3_stop_rings(struct drm_i915_private *dev_priv)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
for_each_engine(engine, dev_priv, id) {
const u32 base = engine->mmio_base;
const i915_reg_t mode = RING_MI_MODE(base);
I915_WRITE_FW(mode, _MASKED_BIT_ENABLE(STOP_RING));
if (intel_wait_for_register_fw(dev_priv,
mode,
MODE_IDLE,
MODE_IDLE,
500))
DRM_DEBUG_DRIVER("%s: timed out on STOP_RING\n",
engine->name);
I915_WRITE_FW(RING_CTL(base), 0);
I915_WRITE_FW(RING_HEAD(base), 0);
I915_WRITE_FW(RING_TAIL(base), 0);
/* Check acts as a post */
if (I915_READ_FW(RING_HEAD(base)) != 0)
DRM_DEBUG_DRIVER("%s: ring head not parked\n",
engine->name);
}
}
static bool i915_reset_complete(struct pci_dev *pdev)
{
u8 gdrst;
pci_read_config_byte(pdev, I915_GDRST, &gdrst);
return (gdrst & GRDOM_RESET_STATUS) == 0;
}
@ -1448,15 +1479,16 @@ static int i915_do_reset(struct drm_i915_private *dev_priv, unsigned engine_mask
/* assert reset for at least 20 usec */
pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE);
udelay(20);
usleep_range(50, 200);
pci_write_config_byte(pdev, I915_GDRST, 0);
return wait_for(i915_reset_complete(pdev), 500);
}
static int g4x_reset_complete(struct pci_dev *pdev)
static bool g4x_reset_complete(struct pci_dev *pdev)
{
u8 gdrst;
pci_read_config_byte(pdev, I915_GDRST, &gdrst);
return (gdrst & GRDOM_RESET_ENABLE) == 0;
}
@ -1464,6 +1496,10 @@ static int g4x_reset_complete(struct pci_dev *pdev)
static int g33_do_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
{
struct pci_dev *pdev = dev_priv->drm.pdev;
/* Stop engines before we reset; see g4x_do_reset() below for why. */
gen3_stop_rings(dev_priv);
pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE);
return wait_for(g4x_reset_complete(pdev), 500);
}
@ -1473,29 +1509,41 @@ static int g4x_do_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
struct pci_dev *pdev = dev_priv->drm.pdev;
int ret;
pci_write_config_byte(pdev, I915_GDRST,
GRDOM_RENDER | GRDOM_RESET_ENABLE);
ret = wait_for(g4x_reset_complete(pdev), 500);
if (ret)
return ret;
/* WaVcpClkGateDisableForMediaReset:ctg,elk */
I915_WRITE(VDECCLK_GATE_D, I915_READ(VDECCLK_GATE_D) | VCP_UNIT_CLOCK_GATE_DISABLE);
I915_WRITE(VDECCLK_GATE_D,
I915_READ(VDECCLK_GATE_D) | VCP_UNIT_CLOCK_GATE_DISABLE);
POSTING_READ(VDECCLK_GATE_D);
/* We stop engines, otherwise we might get failed reset and a
* dead gpu (on elk).
* WaMediaResetMainRingCleanup:ctg,elk (presumably)
*/
gen3_stop_rings(dev_priv);
pci_write_config_byte(pdev, I915_GDRST,
GRDOM_MEDIA | GRDOM_RESET_ENABLE);
ret = wait_for(g4x_reset_complete(pdev), 500);
if (ret)
return ret;
if (ret) {
DRM_DEBUG_DRIVER("Wait for media reset failed\n");
goto out;
}
/* WaVcpClkGateDisableForMediaReset:ctg,elk */
I915_WRITE(VDECCLK_GATE_D, I915_READ(VDECCLK_GATE_D) & ~VCP_UNIT_CLOCK_GATE_DISABLE);
POSTING_READ(VDECCLK_GATE_D);
pci_write_config_byte(pdev, I915_GDRST,
GRDOM_RENDER | GRDOM_RESET_ENABLE);
ret = wait_for(g4x_reset_complete(pdev), 500);
if (ret) {
DRM_DEBUG_DRIVER("Wait for render reset failed\n");
goto out;
}
out:
pci_write_config_byte(pdev, I915_GDRST, 0);
return 0;
I915_WRITE(VDECCLK_GATE_D,
I915_READ(VDECCLK_GATE_D) & ~VCP_UNIT_CLOCK_GATE_DISABLE);
POSTING_READ(VDECCLK_GATE_D);
return ret;
}
static int ironlake_do_reset(struct drm_i915_private *dev_priv,
@ -1503,41 +1551,51 @@ static int ironlake_do_reset(struct drm_i915_private *dev_priv,
{
int ret;
I915_WRITE(ILK_GDSR,
ILK_GRDOM_RENDER | ILK_GRDOM_RESET_ENABLE);
I915_WRITE(ILK_GDSR, ILK_GRDOM_RENDER | ILK_GRDOM_RESET_ENABLE);
ret = intel_wait_for_register(dev_priv,
ILK_GDSR, ILK_GRDOM_RESET_ENABLE, 0,
500);
if (ret)
return ret;
if (ret) {
DRM_DEBUG_DRIVER("Wait for render reset failed\n");
goto out;
}
I915_WRITE(ILK_GDSR,
ILK_GRDOM_MEDIA | ILK_GRDOM_RESET_ENABLE);
I915_WRITE(ILK_GDSR, ILK_GRDOM_MEDIA | ILK_GRDOM_RESET_ENABLE);
ret = intel_wait_for_register(dev_priv,
ILK_GDSR, ILK_GRDOM_RESET_ENABLE, 0,
500);
if (ret)
return ret;
if (ret) {
DRM_DEBUG_DRIVER("Wait for media reset failed\n");
goto out;
}
out:
I915_WRITE(ILK_GDSR, 0);
return 0;
POSTING_READ(ILK_GDSR);
return ret;
}
/* Reset the hardware domains (GENX_GRDOM_*) specified by mask */
static int gen6_hw_domain_reset(struct drm_i915_private *dev_priv,
u32 hw_domain_mask)
{
int err;
/* GEN6_GDRST is not in the gt power well, no need to check
* for fifo space for the write or forcewake the chip for
* the read
*/
__raw_i915_write32(dev_priv, GEN6_GDRST, hw_domain_mask);
/* Spin waiting for the device to ack the reset requests */
return intel_wait_for_register_fw(dev_priv,
/* Wait for the device to ack the reset requests */
err = intel_wait_for_register_fw(dev_priv,
GEN6_GDRST, hw_domain_mask, 0,
500);
if (err)
DRM_DEBUG_DRIVER("Wait for 0x%08x engines reset failed\n",
hw_domain_mask);
return err;
}
/**
@ -1585,19 +1643,23 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv,
}
/**
* intel_wait_for_register_fw - wait until register matches expected state
* __intel_wait_for_register_fw - wait until register matches expected state
* @dev_priv: the i915 device
* @reg: the register to read
* @mask: mask to apply to register value
* @value: expected value
* @timeout_ms: timeout in millisecond
* @fast_timeout_us: fast timeout in microsecond for atomic/tight wait
* @slow_timeout_ms: slow timeout in millisecond
* @out_value: optional placeholder to hold registry value
*
* This routine waits until the target register @reg contains the expected
* @value after applying the @mask, i.e. it waits until ::
*
* (I915_READ_FW(reg) & mask) == value
*
* Otherwise, the wait will timeout after @timeout_ms milliseconds.
* Otherwise, the wait will timeout after @slow_timeout_ms milliseconds.
* For atomic context @slow_timeout_ms must be zero and @fast_timeout_us
* must be not larger than 20,0000 microseconds.
*
* Note that this routine assumes the caller holds forcewake asserted, it is
* not suitable for very long waits. See intel_wait_for_register() if you
@ -1606,16 +1668,31 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv,
*
* Returns 0 if the register matches the desired condition, or -ETIMEOUT.
*/
int intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
i915_reg_t reg,
const u32 mask,
const u32 value,
const unsigned long timeout_ms)
int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
u32 value,
unsigned int fast_timeout_us,
unsigned int slow_timeout_ms,
u32 *out_value)
{
#define done ((I915_READ_FW(reg) & mask) == value)
int ret = wait_for_us(done, 2);
if (ret)
ret = wait_for(done, timeout_ms);
u32 uninitialized_var(reg_value);
#define done (((reg_value = I915_READ_FW(reg)) & mask) == value)
int ret;
/* Catch any overuse of this function */
might_sleep_if(slow_timeout_ms);
GEM_BUG_ON(fast_timeout_us > 20000);
ret = -ETIMEDOUT;
if (fast_timeout_us && fast_timeout_us <= 20000)
ret = _wait_for_atomic(done, fast_timeout_us, 0);
if (ret && slow_timeout_ms)
ret = wait_for(done, slow_timeout_ms);
if (out_value)
*out_value = reg_value;
return ret;
#undef done
}
@ -1639,18 +1716,26 @@ int intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
*/
int intel_wait_for_register(struct drm_i915_private *dev_priv,
i915_reg_t reg,
const u32 mask,
const u32 value,
const unsigned long timeout_ms)
u32 mask,
u32 value,
unsigned int timeout_ms)
{
unsigned fw =
intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
int ret;
intel_uncore_forcewake_get(dev_priv, fw);
ret = wait_for_us((I915_READ_FW(reg) & mask) == value, 2);
intel_uncore_forcewake_put(dev_priv, fw);
might_sleep();
spin_lock_irq(&dev_priv->uncore.lock);
intel_uncore_forcewake_get__locked(dev_priv, fw);
ret = __intel_wait_for_register_fw(dev_priv,
reg, mask, value,
2, 0, NULL);
intel_uncore_forcewake_put__locked(dev_priv, fw);
spin_unlock_irq(&dev_priv->uncore.lock);
if (ret)
ret = wait_for((I915_READ_NOTRACE(reg) & mask) == value,
timeout_ms);
@ -1658,7 +1743,7 @@ int intel_wait_for_register(struct drm_i915_private *dev_priv,
return ret;
}
static int gen8_request_engine_reset(struct intel_engine_cs *engine)
static int gen8_reset_engine_start(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
int ret;
@ -1677,7 +1762,7 @@ static int gen8_request_engine_reset(struct intel_engine_cs *engine)
return ret;
}
static void gen8_unrequest_engine_reset(struct intel_engine_cs *engine)
static void gen8_reset_engine_cancel(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
@ -1692,14 +1777,14 @@ static int gen8_reset_engines(struct drm_i915_private *dev_priv,
unsigned int tmp;
for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
if (gen8_request_engine_reset(engine))
if (gen8_reset_engine_start(engine))
goto not_ready;
return gen6_reset_engines(dev_priv, engine_mask);
not_ready:
for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
gen8_unrequest_engine_reset(engine);
gen8_reset_engine_cancel(engine);
return -EIO;
}
@ -1730,8 +1815,11 @@ static reset_func intel_get_gpu_reset(struct drm_i915_private *dev_priv)
int intel_gpu_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
{
reset_func reset;
int retry;
int ret;
might_sleep();
reset = intel_get_gpu_reset(dev_priv);
if (reset == NULL)
return -ENODEV;
@ -1740,7 +1828,13 @@ int intel_gpu_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
* request may be dropped and never completes (causing -EIO).
*/
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
ret = reset(dev_priv, engine_mask);
for (retry = 0; retry < 3; retry++) {
ret = reset(dev_priv, engine_mask);
if (ret != -ETIMEDOUT)
break;
cond_resched();
}
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
return ret;
@ -1754,17 +1848,12 @@ bool intel_has_gpu_reset(struct drm_i915_private *dev_priv)
int intel_guc_reset(struct drm_i915_private *dev_priv)
{
int ret;
unsigned long irqflags;
if (!HAS_GUC(dev_priv))
return -EINVAL;
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
ret = gen6_hw_domain_reset(dev_priv, GEN9_GRDOM_GUC);
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
return ret;
@ -1873,5 +1962,6 @@ intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/mock_uncore.c"
#include "selftests/intel_uncore.c"
#endif

View File

@ -0,0 +1,170 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __INTEL_UNCORE_H__
#define __INTEL_UNCORE_H__
struct drm_i915_private;
enum forcewake_domain_id {
FW_DOMAIN_ID_RENDER = 0,
FW_DOMAIN_ID_BLITTER,
FW_DOMAIN_ID_MEDIA,
FW_DOMAIN_ID_COUNT
};
enum forcewake_domains {
FORCEWAKE_RENDER = BIT(FW_DOMAIN_ID_RENDER),
FORCEWAKE_BLITTER = BIT(FW_DOMAIN_ID_BLITTER),
FORCEWAKE_MEDIA = BIT(FW_DOMAIN_ID_MEDIA),
FORCEWAKE_ALL = (FORCEWAKE_RENDER |
FORCEWAKE_BLITTER |
FORCEWAKE_MEDIA)
};
struct intel_uncore_funcs {
void (*force_wake_get)(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void (*force_wake_put)(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
uint8_t (*mmio_readb)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
uint16_t (*mmio_readw)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
uint32_t (*mmio_readl)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
uint64_t (*mmio_readq)(struct drm_i915_private *dev_priv,
i915_reg_t r, bool trace);
void (*mmio_writeb)(struct drm_i915_private *dev_priv,
i915_reg_t r, uint8_t val, bool trace);
void (*mmio_writew)(struct drm_i915_private *dev_priv,
i915_reg_t r, uint16_t val, bool trace);
void (*mmio_writel)(struct drm_i915_private *dev_priv,
i915_reg_t r, uint32_t val, bool trace);
};
struct intel_forcewake_range {
u32 start;
u32 end;
enum forcewake_domains domains;
};
struct intel_uncore {
spinlock_t lock; /** lock is also taken in irq contexts. */
const struct intel_forcewake_range *fw_domains_table;
unsigned int fw_domains_table_entries;
struct notifier_block pmic_bus_access_nb;
struct intel_uncore_funcs funcs;
unsigned int fifo_count;
enum forcewake_domains fw_domains;
enum forcewake_domains fw_domains_active;
u32 fw_set;
u32 fw_clear;
u32 fw_reset;
struct intel_uncore_forcewake_domain {
enum forcewake_domain_id id;
enum forcewake_domains mask;
unsigned int wake_count;
bool active;
struct hrtimer timer;
i915_reg_t reg_set;
i915_reg_t reg_ack;
} fw_domain[FW_DOMAIN_ID_COUNT];
int unclaimed_mmio_check;
};
/* Iterate over initialised fw domains */
#define for_each_fw_domain_masked(domain__, mask__, dev_priv__, tmp__) \
for (tmp__ = (mask__); \
tmp__ ? (domain__ = &(dev_priv__)->uncore.fw_domain[__mask_next_bit(tmp__)]), 1 : 0;)
#define for_each_fw_domain(domain__, dev_priv__, tmp__) \
for_each_fw_domain_masked(domain__, (dev_priv__)->uncore.fw_domains, dev_priv__, tmp__)
void intel_uncore_sanitize(struct drm_i915_private *dev_priv);
void intel_uncore_init(struct drm_i915_private *dev_priv);
bool intel_uncore_unclaimed_mmio(struct drm_i915_private *dev_priv);
bool intel_uncore_arm_unclaimed_mmio_detection(struct drm_i915_private *dev_priv);
void intel_uncore_fini(struct drm_i915_private *dev_priv);
void intel_uncore_suspend(struct drm_i915_private *dev_priv);
void intel_uncore_resume_early(struct drm_i915_private *dev_priv);
u64 intel_uncore_edram_size(struct drm_i915_private *dev_priv);
void assert_forcewakes_inactive(struct drm_i915_private *dev_priv);
const char *intel_uncore_forcewake_domain_to_str(const enum forcewake_domain_id id);
enum forcewake_domains
intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
i915_reg_t reg, unsigned int op);
#define FW_REG_READ (1)
#define FW_REG_WRITE (2)
void intel_uncore_forcewake_get(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void intel_uncore_forcewake_put(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
/* Like above but the caller must manage the uncore.lock itself.
* Must be used with I915_READ_FW and friends.
*/
void intel_uncore_forcewake_get__locked(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
int intel_wait_for_register(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
u32 value,
unsigned int timeout_ms);
int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
u32 value,
unsigned int fast_timeout_us,
unsigned int slow_timeout_ms,
u32 *out_value);
static inline
int intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
u32 value,
unsigned int timeout_ms)
{
return __intel_wait_for_register_fw(dev_priv, reg, mask, value,
2, timeout_ms, NULL);
}
#endif /* !__INTEL_UNCORE_H__ */

View File

@ -138,10 +138,7 @@ static int wc_set(struct drm_i915_gem_object *obj,
typeof(v) *map;
int err;
/* XXX GTT write followed by WC write go missing */
i915_gem_object_flush_gtt_write_domain(obj);
err = i915_gem_object_set_to_gtt_domain(obj, true);
err = i915_gem_object_set_to_wc_domain(obj, true);
if (err)
return err;
@ -162,10 +159,7 @@ static int wc_get(struct drm_i915_gem_object *obj,
typeof(v) map;
int err;
/* XXX WC write followed by GTT write go missing */
i915_gem_object_flush_gtt_write_domain(obj);
err = i915_gem_object_set_to_gtt_domain(obj, false);
err = i915_gem_object_set_to_wc_domain(obj, false);
if (err)
return err;

View File

@ -320,7 +320,7 @@ static unsigned long max_dwords(struct drm_i915_gem_object *obj)
static int igt_ctx_exec(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct drm_i915_gem_object *obj = NULL;
struct drm_file *file;
IGT_TIMEOUT(end_time);
LIST_HEAD(objects);
@ -359,7 +359,7 @@ static int igt_ctx_exec(void *arg)
}
for_each_engine(engine, i915, id) {
if (dw == 0) {
if (!obj) {
obj = create_test_object(ctx, file, &objects);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
@ -376,8 +376,10 @@ static int igt_ctx_exec(void *arg)
goto out_unlock;
}
if (++dw == max_dwords(obj))
if (++dw == max_dwords(obj)) {
obj = NULL;
dw = 0;
}
ndwords++;
}
ncontexts++;

View File

@ -271,6 +271,105 @@ err_obj:
return err;
}
static int igt_dmabuf_export_kmap(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct dma_buf *dmabuf;
void *ptr;
int err;
obj = i915_gem_object_create(i915, 2*PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
dmabuf = i915_gem_prime_export(&i915->drm, &obj->base, 0);
i915_gem_object_put(obj);
if (IS_ERR(dmabuf)) {
err = PTR_ERR(dmabuf);
pr_err("i915_gem_prime_export failed with err=%d\n", err);
return err;
}
ptr = dma_buf_kmap(dmabuf, 0);
if (!ptr) {
pr_err("dma_buf_kmap failed\n");
err = -ENOMEM;
goto err;
}
if (memchr_inv(ptr, 0, PAGE_SIZE)) {
dma_buf_kunmap(dmabuf, 0, ptr);
pr_err("Exported page[0] not initialiased to zero!\n");
err = -EINVAL;
goto err;
}
memset(ptr, 0xc5, PAGE_SIZE);
dma_buf_kunmap(dmabuf, 0, ptr);
ptr = i915_gem_object_pin_map(obj, I915_MAP_WB);
if (IS_ERR(ptr)) {
err = PTR_ERR(ptr);
pr_err("i915_gem_object_pin_map failed with err=%d\n", err);
goto err;
}
memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
i915_gem_object_unpin_map(obj);
ptr = dma_buf_kmap(dmabuf, 1);
if (!ptr) {
pr_err("dma_buf_kmap failed\n");
err = -ENOMEM;
goto err;
}
if (memchr_inv(ptr, 0xaa, PAGE_SIZE)) {
dma_buf_kunmap(dmabuf, 1, ptr);
pr_err("Exported page[1] not set to 0xaa!\n");
err = -EINVAL;
goto err;
}
memset(ptr, 0xc5, PAGE_SIZE);
dma_buf_kunmap(dmabuf, 1, ptr);
ptr = dma_buf_kmap(dmabuf, 0);
if (!ptr) {
pr_err("dma_buf_kmap failed\n");
err = -ENOMEM;
goto err;
}
if (memchr_inv(ptr, 0xc5, PAGE_SIZE)) {
dma_buf_kunmap(dmabuf, 0, ptr);
pr_err("Exported page[0] did not retain 0xc5!\n");
err = -EINVAL;
goto err;
}
dma_buf_kunmap(dmabuf, 0, ptr);
ptr = dma_buf_kmap(dmabuf, 2);
if (ptr) {
pr_err("Erroneously kmapped beyond the end of the object!\n");
dma_buf_kunmap(dmabuf, 2, ptr);
err = -EINVAL;
goto err;
}
ptr = dma_buf_kmap(dmabuf, -1);
if (ptr) {
pr_err("Erroneously kmapped before the start of the object!\n");
dma_buf_kunmap(dmabuf, -1, ptr);
err = -EINVAL;
goto err;
}
err = 0;
err:
dma_buf_put(dmabuf);
return err;
}
int i915_gem_dmabuf_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
@ -279,6 +378,7 @@ int i915_gem_dmabuf_mock_selftests(void)
SUBTEST(igt_dmabuf_import),
SUBTEST(igt_dmabuf_import_ownership),
SUBTEST(igt_dmabuf_export_vmap),
SUBTEST(igt_dmabuf_export_kmap),
};
struct drm_i915_private *i915;
int err;

View File

@ -266,7 +266,7 @@ static int check_partial_mapping(struct drm_i915_gem_object *obj,
if (offset >= obj->base.size)
continue;
i915_gem_object_flush_gtt_write_domain(obj);
flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
p = i915_gem_object_get_page(obj, offset >> PAGE_SHIFT);
cpu = kmap(p) + offset_in_page(offset);
@ -545,7 +545,9 @@ static int igt_mmap_offset_exhaustion(void *arg)
}
mutex_lock(&i915->drm.struct_mutex);
intel_runtime_pm_get(i915);
err = make_obj_busy(obj);
intel_runtime_pm_put(i915);
mutex_unlock(&i915->drm.struct_mutex);
if (err) {
pr_err("[loop %d] Failed to busy the object\n", loop);

View File

@ -580,7 +580,7 @@ static struct i915_vma *recursive_batch(struct drm_i915_private *i915)
if (err)
goto err;
err = i915_gem_object_set_to_gtt_domain(obj, true);
err = i915_gem_object_set_to_wc_domain(obj, true);
if (err)
goto err;

View File

@ -0,0 +1,299 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "../i915_selftest.h"
#include "i915_random.h"
#include "mock_gem_device.h"
#include "mock_timeline.h"
struct __igt_sync {
const char *name;
u32 seqno;
bool expected;
bool set;
};
static int __igt_sync(struct intel_timeline *tl,
u64 ctx,
const struct __igt_sync *p,
const char *name)
{
int ret;
if (__intel_timeline_sync_is_later(tl, ctx, p->seqno) != p->expected) {
pr_err("%s: %s(ctx=%llu, seqno=%u) expected passed %s but failed\n",
name, p->name, ctx, p->seqno, yesno(p->expected));
return -EINVAL;
}
if (p->set) {
ret = __intel_timeline_sync_set(tl, ctx, p->seqno);
if (ret)
return ret;
}
return 0;
}
static int igt_sync(void *arg)
{
const struct __igt_sync pass[] = {
{ "unset", 0, false, false },
{ "new", 0, false, true },
{ "0a", 0, true, true },
{ "1a", 1, false, true },
{ "1b", 1, true, true },
{ "0b", 0, true, false },
{ "2a", 2, false, true },
{ "4", 4, false, true },
{ "INT_MAX", INT_MAX, false, true },
{ "INT_MAX-1", INT_MAX-1, true, false },
{ "INT_MAX+1", (u32)INT_MAX+1, false, true },
{ "INT_MAX", INT_MAX, true, false },
{ "UINT_MAX", UINT_MAX, false, true },
{ "wrap", 0, false, true },
{ "unwrap", UINT_MAX, true, false },
{},
}, *p;
struct intel_timeline *tl;
int order, offset;
int ret;
tl = mock_timeline(0);
if (!tl)
return -ENOMEM;
for (p = pass; p->name; p++) {
for (order = 1; order < 64; order++) {
for (offset = -1; offset <= (order > 1); offset++) {
u64 ctx = BIT_ULL(order) + offset;
ret = __igt_sync(tl, ctx, p, "1");
if (ret)
goto out;
}
}
}
mock_timeline_destroy(tl);
tl = mock_timeline(0);
if (!tl)
return -ENOMEM;
for (order = 1; order < 64; order++) {
for (offset = -1; offset <= (order > 1); offset++) {
u64 ctx = BIT_ULL(order) + offset;
for (p = pass; p->name; p++) {
ret = __igt_sync(tl, ctx, p, "2");
if (ret)
goto out;
}
}
}
out:
mock_timeline_destroy(tl);
return ret;
}
static unsigned int random_engine(struct rnd_state *rnd)
{
return ((u64)prandom_u32_state(rnd) * I915_NUM_ENGINES) >> 32;
}
static int bench_sync(void *arg)
{
struct rnd_state prng;
struct intel_timeline *tl;
unsigned long end_time, count;
u64 prng32_1M;
ktime_t kt;
int order, last_order;
tl = mock_timeline(0);
if (!tl)
return -ENOMEM;
/* Lookups from cache are very fast and so the random number generation
* and the loop itself becomes a significant factor in the per-iteration
* timings. We try to compensate the results by measuring the overhead
* of the prng and subtract it from the reported results.
*/
prandom_seed_state(&prng, i915_selftest.random_seed);
count = 0;
kt = ktime_get();
end_time = jiffies + HZ/10;
do {
u32 x;
/* Make sure the compiler doesn't optimise away the prng call */
WRITE_ONCE(x, prandom_u32_state(&prng));
count++;
} while (!time_after(jiffies, end_time));
kt = ktime_sub(ktime_get(), kt);
pr_debug("%s: %lu random evaluations, %lluns/prng\n",
__func__, count, (long long)div64_ul(ktime_to_ns(kt), count));
prng32_1M = div64_ul(ktime_to_ns(kt) << 20, count);
/* Benchmark (only) setting random context ids */
prandom_seed_state(&prng, i915_selftest.random_seed);
count = 0;
kt = ktime_get();
end_time = jiffies + HZ/10;
do {
u64 id = i915_prandom_u64_state(&prng);
__intel_timeline_sync_set(tl, id, 0);
count++;
} while (!time_after(jiffies, end_time));
kt = ktime_sub(ktime_get(), kt);
kt = ktime_sub_ns(kt, (count * prng32_1M * 2) >> 20);
pr_info("%s: %lu random insertions, %lluns/insert\n",
__func__, count, (long long)div64_ul(ktime_to_ns(kt), count));
/* Benchmark looking up the exact same context ids as we just set */
prandom_seed_state(&prng, i915_selftest.random_seed);
end_time = count;
kt = ktime_get();
while (end_time--) {
u64 id = i915_prandom_u64_state(&prng);
if (!__intel_timeline_sync_is_later(tl, id, 0)) {
mock_timeline_destroy(tl);
pr_err("Lookup of %llu failed\n", id);
return -EINVAL;
}
}
kt = ktime_sub(ktime_get(), kt);
kt = ktime_sub_ns(kt, (count * prng32_1M * 2) >> 20);
pr_info("%s: %lu random lookups, %lluns/lookup\n",
__func__, count, (long long)div64_ul(ktime_to_ns(kt), count));
mock_timeline_destroy(tl);
cond_resched();
tl = mock_timeline(0);
if (!tl)
return -ENOMEM;
/* Benchmark setting the first N (in order) contexts */
count = 0;
kt = ktime_get();
end_time = jiffies + HZ/10;
do {
__intel_timeline_sync_set(tl, count++, 0);
} while (!time_after(jiffies, end_time));
kt = ktime_sub(ktime_get(), kt);
pr_info("%s: %lu in-order insertions, %lluns/insert\n",
__func__, count, (long long)div64_ul(ktime_to_ns(kt), count));
/* Benchmark looking up the exact same context ids as we just set */
end_time = count;
kt = ktime_get();
while (end_time--) {
if (!__intel_timeline_sync_is_later(tl, end_time, 0)) {
pr_err("Lookup of %lu failed\n", end_time);
mock_timeline_destroy(tl);
return -EINVAL;
}
}
kt = ktime_sub(ktime_get(), kt);
pr_info("%s: %lu in-order lookups, %lluns/lookup\n",
__func__, count, (long long)div64_ul(ktime_to_ns(kt), count));
mock_timeline_destroy(tl);
cond_resched();
tl = mock_timeline(0);
if (!tl)
return -ENOMEM;
/* Benchmark searching for a random context id and maybe changing it */
prandom_seed_state(&prng, i915_selftest.random_seed);
count = 0;
kt = ktime_get();
end_time = jiffies + HZ/10;
do {
u32 id = random_engine(&prng);
u32 seqno = prandom_u32_state(&prng);
if (!__intel_timeline_sync_is_later(tl, id, seqno))
__intel_timeline_sync_set(tl, id, seqno);
count++;
} while (!time_after(jiffies, end_time));
kt = ktime_sub(ktime_get(), kt);
kt = ktime_sub_ns(kt, (count * prng32_1M * 2) >> 20);
pr_info("%s: %lu repeated insert/lookups, %lluns/op\n",
__func__, count, (long long)div64_ul(ktime_to_ns(kt), count));
mock_timeline_destroy(tl);
cond_resched();
/* Benchmark searching for a known context id and changing the seqno */
for (last_order = 1, order = 1; order < 32;
({ int tmp = last_order; last_order = order; order += tmp; })) {
unsigned int mask = BIT(order) - 1;
tl = mock_timeline(0);
if (!tl)
return -ENOMEM;
count = 0;
kt = ktime_get();
end_time = jiffies + HZ/10;
do {
/* Without assuming too many details of the underlying
* implementation, try to identify its phase-changes
* (if any)!
*/
u64 id = (u64)(count & mask) << order;
__intel_timeline_sync_is_later(tl, id, 0);
__intel_timeline_sync_set(tl, id, 0);
count++;
} while (!time_after(jiffies, end_time));
kt = ktime_sub(ktime_get(), kt);
pr_info("%s: %lu cyclic/%d insert/lookups, %lluns/op\n",
__func__, count, order,
(long long)div64_ul(ktime_to_ns(kt), count));
mock_timeline_destroy(tl);
cond_resched();
}
return 0;
}
int i915_gem_timeline_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_sync),
SUBTEST(bench_sync),
};
return i915_subtests(tests, NULL);
}

View File

@ -9,9 +9,12 @@
* Tests are executed in order by igt/drv_selftest
*/
selftest(sanitycheck, i915_mock_sanitycheck) /* keep first (igt selfcheck) */
selftest(fence, i915_sw_fence_mock_selftests)
selftest(scatterlist, scatterlist_mock_selftests)
selftest(syncmap, i915_syncmap_mock_selftests)
selftest(uncore, intel_uncore_mock_selftests)
selftest(breadcrumbs, intel_breadcrumbs_mock_selftests)
selftest(timelines, i915_gem_timeline_mock_selftests)
selftest(requests, i915_gem_request_mock_selftests)
selftest(objects, i915_gem_object_mock_selftests)
selftest(dmabuf, i915_gem_dmabuf_mock_selftests)

View File

@ -30,6 +30,17 @@
#include "i915_random.h"
u64 i915_prandom_u64_state(struct rnd_state *rnd)
{
u64 x;
x = prandom_u32_state(rnd);
x <<= 32;
x |= prandom_u32_state(rnd);
return x;
}
static inline u32 i915_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state)
{
return upper_32_bits((u64)prandom_u32_state(state) * ep_ro);

View File

@ -41,6 +41,8 @@
#define I915_RND_SUBSTATE(name__, parent__) \
struct rnd_state name__ = I915_RND_STATE_INITIALIZER(prandom_u32_state(&(parent__)))
u64 i915_prandom_u64_state(struct rnd_state *rnd);
unsigned int *i915_random_order(unsigned int count,
struct rnd_state *state);
void i915_random_reorder(unsigned int *order,

View File

@ -0,0 +1,582 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include "../i915_selftest.h"
static int __i915_sw_fence_call
fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
{
switch (state) {
case FENCE_COMPLETE:
break;
case FENCE_FREE:
/* Leave the fence for the caller to free it after testing */
break;
}
return NOTIFY_DONE;
}
static struct i915_sw_fence *alloc_fence(void)
{
struct i915_sw_fence *fence;
fence = kmalloc(sizeof(*fence), GFP_KERNEL);
if (!fence)
return NULL;
i915_sw_fence_init(fence, fence_notify);
return fence;
}
static void free_fence(struct i915_sw_fence *fence)
{
i915_sw_fence_fini(fence);
kfree(fence);
}
static int __test_self(struct i915_sw_fence *fence)
{
if (i915_sw_fence_done(fence))
return -EINVAL;
i915_sw_fence_commit(fence);
if (!i915_sw_fence_done(fence))
return -EINVAL;
i915_sw_fence_wait(fence);
if (!i915_sw_fence_done(fence))
return -EINVAL;
return 0;
}
static int test_self(void *arg)
{
struct i915_sw_fence *fence;
int ret;
/* Test i915_sw_fence signaling and completion testing */
fence = alloc_fence();
if (!fence)
return -ENOMEM;
ret = __test_self(fence);
free_fence(fence);
return ret;
}
static int test_dag(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret = -EINVAL;
/* Test detection of cycles within the i915_sw_fence graphs */
if (!IS_ENABLED(CONFIG_DRM_I915_SW_FENCE_CHECK_DAG))
return 0;
A = alloc_fence();
if (!A)
return -ENOMEM;
if (i915_sw_fence_await_sw_fence_gfp(A, A, GFP_KERNEL) != -EINVAL) {
pr_err("recursive cycle not detected (AA)\n");
goto err_A;
}
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL);
if (i915_sw_fence_await_sw_fence_gfp(B, A, GFP_KERNEL) != -EINVAL) {
pr_err("single depth cycle not detected (BAB)\n");
goto err_B;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
if (i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL) == -EINVAL) {
pr_err("invalid cycle detected\n");
goto err_C;
}
if (i915_sw_fence_await_sw_fence_gfp(C, B, GFP_KERNEL) != -EINVAL) {
pr_err("single depth cycle not detected (CBC)\n");
goto err_C;
}
if (i915_sw_fence_await_sw_fence_gfp(C, A, GFP_KERNEL) != -EINVAL) {
pr_err("cycle not detected (BA, CB, AC)\n");
goto err_C;
}
if (i915_sw_fence_await_sw_fence_gfp(A, C, GFP_KERNEL) == -EINVAL) {
pr_err("invalid cycle detected\n");
goto err_C;
}
i915_sw_fence_commit(A);
i915_sw_fence_commit(B);
i915_sw_fence_commit(C);
ret = 0;
if (!i915_sw_fence_done(C)) {
pr_err("fence C not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(A)) {
pr_err("fence A not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_AB(void *arg)
{
struct i915_sw_fence *A, *B;
int ret;
/* Test i915_sw_fence (A) waiting on an event source (B) */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
ret = i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL);
if (ret < 0)
goto err_B;
if (ret == 0) {
pr_err("Incorrectly reported fence A was complete before await\n");
ret = -EINVAL;
goto err_B;
}
ret = -EINVAL;
i915_sw_fence_commit(A);
if (i915_sw_fence_done(A))
goto err_B;
i915_sw_fence_commit(B);
if (!i915_sw_fence_done(B)) {
pr_err("Fence B is not done\n");
goto err_B;
}
if (!i915_sw_fence_done(A)) {
pr_err("Fence A is not done\n");
goto err_B;
}
ret = 0;
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_ABC(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret;
/* Test a chain of fences, A waits on B who waits on C */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
ret = i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
pr_err("Incorrectly reported fence B was complete before await\n");
goto err_C;
}
ret = i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
pr_err("Incorrectly reported fence C was complete before await\n");
goto err_C;
}
ret = -EINVAL;
i915_sw_fence_commit(A);
if (i915_sw_fence_done(A)) {
pr_err("Fence A completed early\n");
goto err_C;
}
i915_sw_fence_commit(B);
if (i915_sw_fence_done(B)) {
pr_err("Fence B completed early\n");
goto err_C;
}
if (i915_sw_fence_done(A)) {
pr_err("Fence A completed early (after signaling B)\n");
goto err_C;
}
i915_sw_fence_commit(C);
ret = 0;
if (!i915_sw_fence_done(C)) {
pr_err("Fence C not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("Fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(A)) {
pr_err("Fence A not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_AB_C(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret = -EINVAL;
/* Test multiple fences (AB) waiting on a single event (C) */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
ret = i915_sw_fence_await_sw_fence_gfp(A, C, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
ret = i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
i915_sw_fence_commit(A);
i915_sw_fence_commit(B);
ret = 0;
if (i915_sw_fence_done(A)) {
pr_err("Fence A completed early\n");
ret = -EINVAL;
}
if (i915_sw_fence_done(B)) {
pr_err("Fence B completed early\n");
ret = -EINVAL;
}
i915_sw_fence_commit(C);
if (!i915_sw_fence_done(C)) {
pr_err("Fence C not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("Fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(A)) {
pr_err("Fence A not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_C_AB(void *arg)
{
struct i915_sw_fence *A, *B, *C;
int ret;
/* Test multiple event sources (A,B) for a single fence (C) */
A = alloc_fence();
if (!A)
return -ENOMEM;
B = alloc_fence();
if (!B) {
ret = -ENOMEM;
goto err_A;
}
C = alloc_fence();
if (!C) {
ret = -ENOMEM;
goto err_B;
}
ret = i915_sw_fence_await_sw_fence_gfp(C, A, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
ret = i915_sw_fence_await_sw_fence_gfp(C, B, GFP_KERNEL);
if (ret < 0)
goto err_C;
if (ret == 0) {
ret = -EINVAL;
goto err_C;
}
ret = 0;
i915_sw_fence_commit(C);
if (i915_sw_fence_done(C))
ret = -EINVAL;
i915_sw_fence_commit(A);
i915_sw_fence_commit(B);
if (!i915_sw_fence_done(A)) {
pr_err("Fence A not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(B)) {
pr_err("Fence B not done\n");
ret = -EINVAL;
}
if (!i915_sw_fence_done(C)) {
pr_err("Fence C not done\n");
ret = -EINVAL;
}
err_C:
free_fence(C);
err_B:
free_fence(B);
err_A:
free_fence(A);
return ret;
}
static int test_chain(void *arg)
{
int nfences = 4096;
struct i915_sw_fence **fences;
int ret, i;
/* Test a long chain of fences */
fences = kmalloc_array(nfences, sizeof(*fences), GFP_KERNEL);
if (!fences)
return -ENOMEM;
for (i = 0; i < nfences; i++) {
fences[i] = alloc_fence();
if (!fences[i]) {
nfences = i;
ret = -ENOMEM;
goto err;
}
if (i > 0) {
ret = i915_sw_fence_await_sw_fence_gfp(fences[i],
fences[i - 1],
GFP_KERNEL);
if (ret < 0) {
nfences = i + 1;
goto err;
}
i915_sw_fence_commit(fences[i]);
}
}
ret = 0;
for (i = nfences; --i; ) {
if (i915_sw_fence_done(fences[i])) {
if (ret == 0)
pr_err("Fence[%d] completed early\n", i);
ret = -EINVAL;
}
}
i915_sw_fence_commit(fences[0]);
for (i = 0; ret == 0 && i < nfences; i++) {
if (!i915_sw_fence_done(fences[i])) {
pr_err("Fence[%d] is not done\n", i);
ret = -EINVAL;
}
}
err:
for (i = 0; i < nfences; i++)
free_fence(fences[i]);
kfree(fences);
return ret;
}
struct task_ipc {
struct work_struct work;
struct completion started;
struct i915_sw_fence *in, *out;
int value;
};
static void task_ipc(struct work_struct *work)
{
struct task_ipc *ipc = container_of(work, typeof(*ipc), work);
complete(&ipc->started);
i915_sw_fence_wait(ipc->in);
smp_store_mb(ipc->value, 1);
i915_sw_fence_commit(ipc->out);
}
static int test_ipc(void *arg)
{
struct task_ipc ipc;
int ret = 0;
/* Test use of i915_sw_fence as an interprocess signaling mechanism */
ipc.in = alloc_fence();
if (!ipc.in)
return -ENOMEM;
ipc.out = alloc_fence();
if (!ipc.out) {
ret = -ENOMEM;
goto err_in;
}
/* use a completion to avoid chicken-and-egg testing */
init_completion(&ipc.started);
ipc.value = 0;
INIT_WORK_ONSTACK(&ipc.work, task_ipc);
schedule_work(&ipc.work);
wait_for_completion(&ipc.started);
usleep_range(1000, 2000);
if (READ_ONCE(ipc.value)) {
pr_err("worker updated value before i915_sw_fence was signaled\n");
ret = -EINVAL;
}
i915_sw_fence_commit(ipc.in);
i915_sw_fence_wait(ipc.out);
if (!READ_ONCE(ipc.value)) {
pr_err("worker signaled i915_sw_fence before value was posted\n");
ret = -EINVAL;
}
flush_work(&ipc.work);
destroy_work_on_stack(&ipc.work);
free_fence(ipc.out);
err_in:
free_fence(ipc.in);
return ret;
}
int i915_sw_fence_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(test_self),
SUBTEST(test_dag),
SUBTEST(test_AB),
SUBTEST(test_ABC),
SUBTEST(test_AB_C),
SUBTEST(test_C_AB),
SUBTEST(test_chain),
SUBTEST(test_ipc),
};
return i915_subtests(tests, NULL);
}

View File

@ -0,0 +1,616 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "../i915_selftest.h"
#include "i915_random.h"
static char *
__sync_print(struct i915_syncmap *p,
char *buf, unsigned long *sz,
unsigned int depth,
unsigned int last,
unsigned int idx)
{
unsigned long len;
unsigned int i, X;
if (depth) {
unsigned int d;
for (d = 0; d < depth - 1; d++) {
if (last & BIT(depth - d - 1))
len = scnprintf(buf, *sz, "| ");
else
len = scnprintf(buf, *sz, " ");
buf += len;
*sz -= len;
}
len = scnprintf(buf, *sz, "%x-> ", idx);
buf += len;
*sz -= len;
}
/* We mark bits after the prefix as "X" */
len = scnprintf(buf, *sz, "0x%016llx", p->prefix << p->height << SHIFT);
buf += len;
*sz -= len;
X = (p->height + SHIFT) / 4;
scnprintf(buf - X, *sz + X, "%*s", X, "XXXXXXXXXXXXXXXXX");
if (!p->height) {
for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
len = scnprintf(buf, *sz, " %x:%x,",
i, __sync_seqno(p)[i]);
buf += len;
*sz -= len;
}
buf -= 1;
*sz += 1;
}
len = scnprintf(buf, *sz, "\n");
buf += len;
*sz -= len;
if (p->height) {
for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
buf = __sync_print(__sync_child(p)[i], buf, sz,
depth + 1,
last << 1 | !!(p->bitmap >> (i + 1)),
i);
}
}
return buf;
}
static bool
i915_syncmap_print_to_buf(struct i915_syncmap *p, char *buf, unsigned long sz)
{
if (!p)
return false;
while (p->parent)
p = p->parent;
__sync_print(p, buf, &sz, 0, 1, 0);
return true;
}
static int check_syncmap_free(struct i915_syncmap **sync)
{
i915_syncmap_free(sync);
if (*sync) {
pr_err("sync not cleared after free\n");
return -EINVAL;
}
return 0;
}
static int dump_syncmap(struct i915_syncmap *sync, int err)
{
char *buf;
if (!err)
return check_syncmap_free(&sync);
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
goto skip;
if (i915_syncmap_print_to_buf(sync, buf, PAGE_SIZE))
pr_err("%s", buf);
kfree(buf);
skip:
i915_syncmap_free(&sync);
return err;
}
static int igt_syncmap_init(void *arg)
{
struct i915_syncmap *sync = (void *)~0ul;
/*
* Cursory check that we can initialise a random pointer and transform
* it into the root pointer of a syncmap.
*/
i915_syncmap_init(&sync);
return check_syncmap_free(&sync);
}
static int check_seqno(struct i915_syncmap *leaf, unsigned int idx, u32 seqno)
{
if (leaf->height) {
pr_err("%s: not a leaf, height is %d\n",
__func__, leaf->height);
return -EINVAL;
}
if (__sync_seqno(leaf)[idx] != seqno) {
pr_err("%s: seqno[%d], found %x, expected %x\n",
__func__, idx, __sync_seqno(leaf)[idx], seqno);
return -EINVAL;
}
return 0;
}
static int check_one(struct i915_syncmap **sync, u64 context, u32 seqno)
{
int err;
err = i915_syncmap_set(sync, context, seqno);
if (err)
return err;
if ((*sync)->height) {
pr_err("Inserting first context=%llx did not return leaf (height=%d, prefix=%llx\n",
context, (*sync)->height, (*sync)->prefix);
return -EINVAL;
}
if ((*sync)->parent) {
pr_err("Inserting first context=%llx created branches!\n",
context);
return -EINVAL;
}
if (hweight32((*sync)->bitmap) != 1) {
pr_err("First bitmap does not contain a single entry, found %x (count=%d)!\n",
(*sync)->bitmap, hweight32((*sync)->bitmap));
return -EINVAL;
}
err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
if (err)
return err;
if (!i915_syncmap_is_later(sync, context, seqno)) {
pr_err("Lookup of first context=%llx/seqno=%x failed!\n",
context, seqno);
return -EINVAL;
}
return 0;
}
static int igt_syncmap_one(void *arg)
{
I915_RND_STATE(prng);
IGT_TIMEOUT(end_time);
struct i915_syncmap *sync;
unsigned long max = 1;
int err;
/*
* Check that inserting a new id, creates a leaf and only that leaf.
*/
i915_syncmap_init(&sync);
do {
u64 context = i915_prandom_u64_state(&prng);
unsigned long loop;
err = check_syncmap_free(&sync);
if (err)
goto out;
for (loop = 0; loop <= max; loop++) {
err = check_one(&sync, context,
prandom_u32_state(&prng));
if (err)
goto out;
}
max++;
} while (!__igt_timeout(end_time, NULL));
pr_debug("%s: Completed %lu single insertions\n",
__func__, max * (max - 1) / 2);
out:
return dump_syncmap(sync, err);
}
static int check_leaf(struct i915_syncmap **sync, u64 context, u32 seqno)
{
int err;
err = i915_syncmap_set(sync, context, seqno);
if (err)
return err;
if ((*sync)->height) {
pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
context, (*sync)->height, (*sync)->prefix);
return -EINVAL;
}
if (hweight32((*sync)->bitmap) != 1) {
pr_err("First entry into leaf (context=%llx) does not contain a single entry, found %x (count=%d)!\n",
context, (*sync)->bitmap, hweight32((*sync)->bitmap));
return -EINVAL;
}
err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
if (err)
return err;
if (!i915_syncmap_is_later(sync, context, seqno)) {
pr_err("Lookup of first entry context=%llx/seqno=%x failed!\n",
context, seqno);
return -EINVAL;
}
return 0;
}
static int igt_syncmap_join_above(void *arg)
{
struct i915_syncmap *sync;
unsigned int pass, order;
int err;
i915_syncmap_init(&sync);
/*
* When we have a new id that doesn't fit inside the existing tree,
* we need to add a new layer above.
*
* 1: 0x00000001
* 2: 0x00000010
* 3: 0x00000100
* 4: 0x00001000
* ...
* Each pass the common prefix shrinks and we have to insert a join.
* Each join will only contain two branches, the latest of which
* is always a leaf.
*
* If we then reuse the same set of contexts, we expect to build an
* identical tree.
*/
for (pass = 0; pass < 3; pass++) {
for (order = 0; order < 64; order += SHIFT) {
u64 context = BIT_ULL(order);
struct i915_syncmap *join;
err = check_leaf(&sync, context, 0);
if (err)
goto out;
join = sync->parent;
if (!join) /* very first insert will have no parents */
continue;
if (!join->height) {
pr_err("Parent with no height!\n");
err = -EINVAL;
goto out;
}
if (hweight32(join->bitmap) != 2) {
pr_err("Join does not have 2 children: %x (%d)\n",
join->bitmap, hweight32(join->bitmap));
err = -EINVAL;
goto out;
}
if (__sync_child(join)[__sync_branch_idx(join, context)] != sync) {
pr_err("Leaf misplaced in parent!\n");
err = -EINVAL;
goto out;
}
}
}
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_join_below(void *arg)
{
struct i915_syncmap *sync;
unsigned int step, order, idx;
int err;
i915_syncmap_init(&sync);
/*
* Check that we can split a compacted branch by replacing it with
* a join.
*/
for (step = 0; step < KSYNCMAP; step++) {
for (order = 64 - SHIFT; order > 0; order -= SHIFT) {
u64 context = step * BIT_ULL(order);
err = i915_syncmap_set(&sync, context, 0);
if (err)
goto out;
if (sync->height) {
pr_err("Inserting context=%llx (order=%d, step=%d) did not return leaf (height=%d, prefix=%llx\n",
context, order, step, sync->height, sync->prefix);
err = -EINVAL;
goto out;
}
}
}
for (step = 0; step < KSYNCMAP; step++) {
for (order = SHIFT; order < 64; order += SHIFT) {
u64 context = step * BIT_ULL(order);
if (!i915_syncmap_is_later(&sync, context, 0)) {
pr_err("1: context %llx (order=%d, step=%d) not found\n",
context, order, step);
err = -EINVAL;
goto out;
}
for (idx = 1; idx < KSYNCMAP; idx++) {
if (i915_syncmap_is_later(&sync, context + idx, 0)) {
pr_err("1: context %llx (order=%d, step=%d) should not exist\n",
context + idx, order, step);
err = -EINVAL;
goto out;
}
}
}
}
for (order = SHIFT; order < 64; order += SHIFT) {
for (step = 0; step < KSYNCMAP; step++) {
u64 context = step * BIT_ULL(order);
if (!i915_syncmap_is_later(&sync, context, 0)) {
pr_err("2: context %llx (order=%d, step=%d) not found\n",
context, order, step);
err = -EINVAL;
goto out;
}
}
}
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_neighbours(void *arg)
{
I915_RND_STATE(prng);
IGT_TIMEOUT(end_time);
struct i915_syncmap *sync;
int err;
/*
* Each leaf holds KSYNCMAP seqno. Check that when we create KSYNCMAP
* neighbouring ids, they all fit into the same leaf.
*/
i915_syncmap_init(&sync);
do {
u64 context = i915_prandom_u64_state(&prng) & ~MASK;
unsigned int idx;
if (i915_syncmap_is_later(&sync, context, 0)) /* Skip repeats */
continue;
for (idx = 0; idx < KSYNCMAP; idx++) {
err = i915_syncmap_set(&sync, context + idx, 0);
if (err)
goto out;
if (sync->height) {
pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
context, sync->height, sync->prefix);
err = -EINVAL;
goto out;
}
if (sync->bitmap != BIT(idx + 1) - 1) {
pr_err("Inserting neighbouring context=0x%llx+%d, did not fit into the same leaf bitmap=%x (%d), expected %lx (%d)\n",
context, idx,
sync->bitmap, hweight32(sync->bitmap),
BIT(idx + 1) - 1, idx + 1);
err = -EINVAL;
goto out;
}
}
} while (!__igt_timeout(end_time, NULL));
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_compact(void *arg)
{
struct i915_syncmap *sync;
unsigned int idx, order;
int err;
i915_syncmap_init(&sync);
/*
* The syncmap are "space efficient" compressed radix trees - any
* branch with only one child is skipped and replaced by the child.
*
* If we construct a tree with ids that are neighbouring at a non-zero
* height, we form a join but each child of that join is directly a
* leaf holding the single id.
*/
for (order = SHIFT; order < 64; order += SHIFT) {
err = check_syncmap_free(&sync);
if (err)
goto out;
/* Create neighbours in the parent */
for (idx = 0; idx < KSYNCMAP; idx++) {
u64 context = idx * BIT_ULL(order) + idx;
err = i915_syncmap_set(&sync, context, 0);
if (err)
goto out;
if (sync->height) {
pr_err("Inserting context=%llx (order=%d, idx=%d) did not return leaf (height=%d, prefix=%llx\n",
context, order, idx,
sync->height, sync->prefix);
err = -EINVAL;
goto out;
}
}
sync = sync->parent;
if (sync->parent) {
pr_err("Parent (join) of last leaf was not the sync!\n");
err = -EINVAL;
goto out;
}
if (sync->height != order) {
pr_err("Join does not have the expected height, found %d, expected %d\n",
sync->height, order);
err = -EINVAL;
goto out;
}
if (sync->bitmap != BIT(KSYNCMAP) - 1) {
pr_err("Join is not full!, found %x (%d) expected %lx (%d)\n",
sync->bitmap, hweight32(sync->bitmap),
BIT(KSYNCMAP) - 1, KSYNCMAP);
err = -EINVAL;
goto out;
}
/* Each of our children should be a leaf */
for (idx = 0; idx < KSYNCMAP; idx++) {
struct i915_syncmap *leaf = __sync_child(sync)[idx];
if (leaf->height) {
pr_err("Child %d is a not leaf!\n", idx);
err = -EINVAL;
goto out;
}
if (leaf->parent != sync) {
pr_err("Child %d is not attached to us!\n",
idx);
err = -EINVAL;
goto out;
}
if (!is_power_of_2(leaf->bitmap)) {
pr_err("Child %d holds more than one id, found %x (%d)\n",
idx, leaf->bitmap, hweight32(leaf->bitmap));
err = -EINVAL;
goto out;
}
if (leaf->bitmap != BIT(idx)) {
pr_err("Child %d has wrong seqno idx, found %d, expected %d\n",
idx, ilog2(leaf->bitmap), idx);
err = -EINVAL;
goto out;
}
}
}
out:
return dump_syncmap(sync, err);
}
static int igt_syncmap_random(void *arg)
{
I915_RND_STATE(prng);
IGT_TIMEOUT(end_time);
struct i915_syncmap *sync;
unsigned long count, phase, i;
u32 seqno;
int err;
i915_syncmap_init(&sync);
/*
* Having tried to test the individual operations within i915_syncmap,
* run a smoketest exploring the entire u64 space with random
* insertions.
*/
count = 0;
phase = jiffies + HZ/100 + 1;
do {
u64 context = i915_prandom_u64_state(&prng);
err = i915_syncmap_set(&sync, context, 0);
if (err)
goto out;
count++;
} while (!time_after(jiffies, phase));
seqno = 0;
phase = 0;
do {
I915_RND_STATE(ctx);
u32 last_seqno = seqno;
bool expect;
seqno = prandom_u32_state(&prng);
expect = seqno_later(last_seqno, seqno);
for (i = 0; i < count; i++) {
u64 context = i915_prandom_u64_state(&ctx);
if (i915_syncmap_is_later(&sync, context, seqno) != expect) {
pr_err("context=%llu, last=%u this=%u did not match expectation (%d)\n",
context, last_seqno, seqno, expect);
err = -EINVAL;
goto out;
}
err = i915_syncmap_set(&sync, context, seqno);
if (err)
goto out;
}
phase++;
} while (!__igt_timeout(end_time, NULL));
pr_debug("Completed %lu passes, each of %lu contexts\n", phase, count);
out:
return dump_syncmap(sync, err);
}
int i915_syncmap_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_syncmap_init),
SUBTEST(igt_syncmap_one),
SUBTEST(igt_syncmap_join_above),
SUBTEST(igt_syncmap_join_below),
SUBTEST(igt_syncmap_neighbours),
SUBTEST(igt_syncmap_compact),
SUBTEST(igt_syncmap_random),
};
return i915_subtests(tests, NULL);
}

View File

@ -52,11 +52,12 @@ static void hw_delay_complete(unsigned long data)
spin_unlock(&engine->hw_lock);
}
static int mock_context_pin(struct intel_engine_cs *engine,
struct i915_gem_context *ctx)
static struct intel_ring *
mock_context_pin(struct intel_engine_cs *engine,
struct i915_gem_context *ctx)
{
i915_gem_context_get(ctx);
return 0;
return engine->buffer;
}
static void mock_context_unpin(struct intel_engine_cs *engine,
@ -72,7 +73,6 @@ static int mock_request_alloc(struct drm_i915_gem_request *request)
INIT_LIST_HEAD(&mock->link);
mock->delay = 0;
request->ring = request->engine->buffer;
return 0;
}
@ -112,7 +112,6 @@ static struct intel_ring *mock_ring(struct intel_engine_cs *engine)
if (!ring)
return NULL;
ring->engine = engine;
ring->size = sz;
ring->effective_size = sz;
ring->vaddr = (void *)(ring + 1);
@ -141,7 +140,7 @@ struct intel_engine_cs *mock_engine(struct drm_i915_private *i915,
/* minimal engine setup for requests */
engine->base.i915 = i915;
engine->base.name = name;
snprintf(engine->base.name, sizeof(engine->base.name), "%s", name);
engine->base.id = id++;
engine->base.status_page.page_addr = (void *)(engine + 1);

View File

@ -30,6 +30,7 @@
#include "mock_gem_device.h"
#include "mock_gem_object.h"
#include "mock_gtt.h"
#include "mock_uncore.h"
void mock_device_flush(struct drm_i915_private *i915)
{
@ -73,6 +74,7 @@ static void mock_device_release(struct drm_device *dev)
destroy_workqueue(i915->wq);
kmem_cache_destroy(i915->priorities);
kmem_cache_destroy(i915->dependencies);
kmem_cache_destroy(i915->requests);
kmem_cache_destroy(i915->vmas);
@ -119,6 +121,7 @@ struct drm_i915_private *mock_gem_device(void)
goto err;
device_initialize(&pdev->dev);
pdev->class = PCI_BASE_CLASS_DISPLAY << 16;
pdev->dev.release = release_dev;
dev_set_name(&pdev->dev, "mock");
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
@ -143,6 +146,7 @@ struct drm_i915_private *mock_gem_device(void)
mkwrite_device_info(i915)->gen = -1;
spin_lock_init(&i915->mm.object_stat_lock);
mock_uncore_init(i915);
init_waitqueue_head(&i915->gpu_error.wait_queue);
init_waitqueue_head(&i915->gpu_error.reset_queue);
@ -184,12 +188,16 @@ struct drm_i915_private *mock_gem_device(void)
if (!i915->dependencies)
goto err_requests;
i915->priorities = KMEM_CACHE(i915_priolist, SLAB_HWCACHE_ALIGN);
if (!i915->priorities)
goto err_dependencies;
mutex_lock(&i915->drm.struct_mutex);
INIT_LIST_HEAD(&i915->gt.timelines);
err = i915_gem_timeline_init__global(i915);
if (err) {
mutex_unlock(&i915->drm.struct_mutex);
goto err_dependencies;
goto err_priorities;
}
mock_init_ggtt(i915);
@ -209,6 +217,8 @@ struct drm_i915_private *mock_gem_device(void)
err_engine:
for_each_engine(engine, i915, id)
mock_engine_free(engine);
err_priorities:
kmem_cache_destroy(i915->priorities);
err_dependencies:
kmem_cache_destroy(i915->dependencies);
err_requests:

View File

@ -0,0 +1,45 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "mock_timeline.h"
struct intel_timeline *mock_timeline(u64 context)
{
static struct lock_class_key class;
struct intel_timeline *tl;
tl = kzalloc(sizeof(*tl), GFP_KERNEL);
if (!tl)
return NULL;
__intel_timeline_init(tl, NULL, context, &class, "mock");
return tl;
}
void mock_timeline_destroy(struct intel_timeline *tl)
{
__intel_timeline_fini(tl);
kfree(tl);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __MOCK_TIMELINE__
#define __MOCK_TIMELINE__
#include "../i915_gem_timeline.h"
struct intel_timeline *mock_timeline(u64 context);
void mock_timeline_destroy(struct intel_timeline *tl);
#endif /* !__MOCK_TIMELINE__ */

View File

@ -0,0 +1,46 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "mock_uncore.h"
#define __nop_write(x) \
static void \
nop_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool trace) { }
__nop_write(8)
__nop_write(16)
__nop_write(32)
#define __nop_read(x) \
static u##x \
nop_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { return 0; }
__nop_read(8)
__nop_read(16)
__nop_read(32)
__nop_read(64)
void mock_uncore_init(struct drm_i915_private *i915)
{
ASSIGN_WRITE_MMIO_VFUNCS(i915, nop);
ASSIGN_READ_MMIO_VFUNCS(i915, nop);
}

View File

@ -0,0 +1,30 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef __MOCK_UNCORE_H
#define __MOCK_UNCORE_H
void mock_uncore_init(struct drm_i915_private *i915);
#endif /* !__MOCK_UNCORE_H */

View File

@ -679,10 +679,12 @@
#define DP_EDP_PWMGEN_BIT_COUNT 0x724
#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN 0x725
#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX 0x726
# define DP_EDP_PWMGEN_BIT_COUNT_MASK (0x1f << 0)
#define DP_EDP_BACKLIGHT_CONTROL_STATUS 0x727
#define DP_EDP_BACKLIGHT_FREQ_SET 0x728
# define DP_EDP_BACKLIGHT_FREQ_BASE_KHZ 27000
#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MSB 0x72a
#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MID 0x72b

View File

@ -31,20 +31,20 @@ struct platform_device;
#define HDMI_MAX_ELD_BYTES 128
struct intel_hdmi_lpe_audio_eld {
int port_id;
int pipe_id;
unsigned char eld_data[HDMI_MAX_ELD_BYTES];
struct intel_hdmi_lpe_audio_port_pdata {
u8 eld[HDMI_MAX_ELD_BYTES];
int port;
int pipe;
int ls_clock;
bool dp_output;
};
struct intel_hdmi_lpe_audio_pdata {
bool notify_pending;
int tmds_clock_speed;
bool hdmi_connected;
bool dp_output;
int link_rate;
struct intel_hdmi_lpe_audio_eld eld;
void (*notify_audio_lpe)(struct platform_device *pdev);
struct intel_hdmi_lpe_audio_port_pdata port[3]; /* for ports B,C,D */
int num_ports;
int num_pipes;
void (*notify_audio_lpe)(struct platform_device *pdev, int port); /* port: 0==B,1==C,2==D */
spinlock_t lpe_audio_slock;
};

View File

@ -412,6 +412,12 @@ typedef struct drm_i915_irq_wait {
*/
#define I915_PARAM_HAS_EXEC_FENCE 44
/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to capture
* user specified bufffers for post-mortem debugging of GPU hangs. See
* EXEC_OBJECT_CAPTURE.
*/
#define I915_PARAM_HAS_EXEC_CAPTURE 45
typedef struct drm_i915_getparam {
__s32 param;
/*
@ -666,6 +672,8 @@ struct drm_i915_gem_relocation_entry {
#define I915_GEM_DOMAIN_VERTEX 0x00000020
/** GTT domain - aperture and scanout */
#define I915_GEM_DOMAIN_GTT 0x00000040
/** WC domain - uncached access */
#define I915_GEM_DOMAIN_WC 0x00000080
/** @} */
struct drm_i915_gem_exec_object {
@ -773,8 +781,15 @@ struct drm_i915_gem_exec_object2 {
* I915_PARAM_HAS_EXEC_FENCE to order execbufs and execute them asynchronously.
*/
#define EXEC_OBJECT_ASYNC (1<<6)
/* Request that the contents of this execobject be copied into the error
* state upon a GPU hang involving this batch for post-mortem debugging.
* These buffers are recorded in no particular order as "user" in
* /sys/class/drm/cardN/error. Query I915_PARAM_HAS_EXEC_CAPTURE to see
* if the kernel supports this flag.
*/
#define EXEC_OBJECT_CAPTURE (1<<7)
/* All remaining bits are MBZ and RESERVED FOR FUTURE USE */
#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_ASYNC<<1)
#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_CAPTURE<<1)
__u64 flags;
union {

View File

@ -42,6 +42,11 @@
#include <drm/intel_lpe_audio.h>
#include "intel_hdmi_audio.h"
#define for_each_pipe(card_ctx, pipe) \
for ((pipe) = 0; (pipe) < (card_ctx)->num_pipes; (pipe)++)
#define for_each_port(card_ctx, port) \
for ((port) = 0; (port) < (card_ctx)->num_ports; (port)++)
/*standard module options for ALSA. This module supports only one card*/
static int hdmi_card_index = SNDRV_DEFAULT_IDX1;
static char *hdmi_card_id = SNDRV_DEFAULT_STR1;
@ -189,15 +194,30 @@ static void had_substream_put(struct snd_intelhad *intelhaddata)
spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags);
}
/* Register access functions */
static u32 had_read_register_raw(struct snd_intelhad *ctx, u32 reg)
static u32 had_config_offset(int pipe)
{
return ioread32(ctx->mmio_start + ctx->had_config_offset + reg);
switch (pipe) {
default:
case 0:
return AUDIO_HDMI_CONFIG_A;
case 1:
return AUDIO_HDMI_CONFIG_B;
case 2:
return AUDIO_HDMI_CONFIG_C;
}
}
static void had_write_register_raw(struct snd_intelhad *ctx, u32 reg, u32 val)
/* Register access functions */
static u32 had_read_register_raw(struct snd_intelhad_card *card_ctx,
int pipe, u32 reg)
{
iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg);
return ioread32(card_ctx->mmio_start + had_config_offset(pipe) + reg);
}
static void had_write_register_raw(struct snd_intelhad_card *card_ctx,
int pipe, u32 reg, u32 val)
{
iowrite32(val, card_ctx->mmio_start + had_config_offset(pipe) + reg);
}
static void had_read_register(struct snd_intelhad *ctx, u32 reg, u32 *val)
@ -205,13 +225,13 @@ static void had_read_register(struct snd_intelhad *ctx, u32 reg, u32 *val)
if (!ctx->connected)
*val = 0;
else
*val = had_read_register_raw(ctx, reg);
*val = had_read_register_raw(ctx->card_ctx, ctx->pipe, reg);
}
static void had_write_register(struct snd_intelhad *ctx, u32 reg, u32 val)
{
if (ctx->connected)
had_write_register_raw(ctx, reg, val);
had_write_register_raw(ctx->card_ctx, ctx->pipe, reg, val);
}
/*
@ -1358,6 +1378,9 @@ static void had_process_hot_plug(struct snd_intelhad *intelhaddata)
return;
}
/* Disable Audio */
had_enable_audio(intelhaddata, false);
intelhaddata->connected = true;
dev_dbg(intelhaddata->dev,
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n",
@ -1519,22 +1542,32 @@ static const struct snd_kcontrol_new had_controls[] = {
*/
static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id)
{
struct snd_intelhad *ctx = dev_id;
u32 audio_stat;
struct snd_intelhad_card *card_ctx = dev_id;
u32 audio_stat[3] = {};
int pipe, port;
/* use raw register access to ack IRQs even while disconnected */
audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS);
for_each_pipe(card_ctx, pipe) {
/* use raw register access to ack IRQs even while disconnected */
audio_stat[pipe] = had_read_register_raw(card_ctx, pipe,
AUD_HDMI_STATUS) &
(HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE);
if (audio_stat & HDMI_AUDIO_UNDERRUN) {
had_write_register_raw(ctx, AUD_HDMI_STATUS,
HDMI_AUDIO_UNDERRUN);
had_process_buffer_underrun(ctx);
if (audio_stat[pipe])
had_write_register_raw(card_ctx, pipe,
AUD_HDMI_STATUS, audio_stat[pipe]);
}
if (audio_stat & HDMI_AUDIO_BUFFER_DONE) {
had_write_register_raw(ctx, AUD_HDMI_STATUS,
HDMI_AUDIO_BUFFER_DONE);
had_process_buffer_done(ctx);
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
int pipe = ctx->pipe;
if (pipe < 0)
continue;
if (audio_stat[pipe] & HDMI_AUDIO_BUFFER_DONE)
had_process_buffer_done(ctx);
if (audio_stat[pipe] & HDMI_AUDIO_UNDERRUN)
had_process_buffer_underrun(ctx);
}
return IRQ_HANDLED;
@ -1543,9 +1576,10 @@ static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id)
/*
* monitor plug/unplug notification from i915; just kick off the work
*/
static void notify_audio_lpe(struct platform_device *pdev)
static void notify_audio_lpe(struct platform_device *pdev, int port)
{
struct snd_intelhad *ctx = platform_get_drvdata(pdev);
struct snd_intelhad_card *card_ctx = platform_get_drvdata(pdev);
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
schedule_work(&ctx->hdmi_audio_wq);
}
@ -1556,47 +1590,51 @@ static void had_audio_wq(struct work_struct *work)
struct snd_intelhad *ctx =
container_of(work, struct snd_intelhad, hdmi_audio_wq);
struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data;
struct intel_hdmi_lpe_audio_port_pdata *ppdata = &pdata->port[ctx->port];
pm_runtime_get_sync(ctx->dev);
mutex_lock(&ctx->mutex);
if (!pdata->hdmi_connected) {
dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n",
__func__);
if (ppdata->pipe < 0) {
dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG : port = %d\n",
__func__, ctx->port);
memset(ctx->eld, 0, sizeof(ctx->eld)); /* clear the old ELD */
ctx->dp_output = false;
ctx->tmds_clock_speed = 0;
ctx->link_rate = 0;
/* Shut down the stream */
had_process_hot_unplug(ctx);
ctx->pipe = -1;
} else {
struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld;
dev_dbg(ctx->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n",
__func__, eld->port_id, pdata->tmds_clock_speed);
__func__, ctx->port, ppdata->ls_clock);
switch (eld->pipe_id) {
case 0:
ctx->had_config_offset = AUDIO_HDMI_CONFIG_A;
break;
case 1:
ctx->had_config_offset = AUDIO_HDMI_CONFIG_B;
break;
case 2:
ctx->had_config_offset = AUDIO_HDMI_CONFIG_C;
break;
default:
dev_dbg(ctx->dev, "Invalid pipe %d\n",
eld->pipe_id);
break;
memcpy(ctx->eld, ppdata->eld, sizeof(ctx->eld));
ctx->dp_output = ppdata->dp_output;
if (ctx->dp_output) {
ctx->tmds_clock_speed = 0;
ctx->link_rate = ppdata->ls_clock;
} else {
ctx->tmds_clock_speed = ppdata->ls_clock;
ctx->link_rate = 0;
}
memcpy(ctx->eld, eld->eld_data, sizeof(ctx->eld));
ctx->dp_output = pdata->dp_output;
ctx->tmds_clock_speed = pdata->tmds_clock_speed;
ctx->link_rate = pdata->link_rate;
/*
* Shut down the stream before we change
* the pipe assignment for this pcm device
*/
had_process_hot_plug(ctx);
/* Process mode change if stream is active */
ctx->pipe = ppdata->pipe;
/* Restart the stream if necessary */
had_process_mode_change(ctx);
}
mutex_unlock(&ctx->mutex);
pm_runtime_mark_last_busy(ctx->dev);
pm_runtime_put_autosuspend(ctx->dev);
@ -1605,11 +1643,17 @@ static void had_audio_wq(struct work_struct *work)
/*
* Jack interface
*/
static int had_create_jack(struct snd_intelhad *ctx)
static int had_create_jack(struct snd_intelhad *ctx,
struct snd_pcm *pcm)
{
char hdmi_str[32];
int err;
err = snd_jack_new(ctx->card, "HDMI/DP", SND_JACK_AVOUT, &ctx->jack,
snprintf(hdmi_str, sizeof(hdmi_str),
"HDMI/DP,pcm=%d", pcm->device);
err = snd_jack_new(ctx->card_ctx->card, hdmi_str,
SND_JACK_AVOUT, &ctx->jack,
true, false);
if (err < 0)
return err;
@ -1623,13 +1667,18 @@ static int had_create_jack(struct snd_intelhad *ctx)
static int hdmi_lpe_audio_runtime_suspend(struct device *dev)
{
struct snd_intelhad *ctx = dev_get_drvdata(dev);
struct snd_pcm_substream *substream;
struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev);
int port;
substream = had_substream_get(ctx);
if (substream) {
snd_pcm_suspend(substream);
had_substream_put(ctx);
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
struct snd_pcm_substream *substream;
substream = had_substream_get(ctx);
if (substream) {
snd_pcm_suspend(substream);
had_substream_put(ctx);
}
}
return 0;
@ -1637,12 +1686,12 @@ static int hdmi_lpe_audio_runtime_suspend(struct device *dev)
static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev)
{
struct snd_intelhad *ctx = dev_get_drvdata(dev);
struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev);
int err;
err = hdmi_lpe_audio_runtime_suspend(dev);
if (!err)
snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D3hot);
snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D3hot);
return err;
}
@ -1654,24 +1703,34 @@ static int hdmi_lpe_audio_runtime_resume(struct device *dev)
static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev)
{
struct snd_intelhad *ctx = dev_get_drvdata(dev);
struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev);
hdmi_lpe_audio_runtime_resume(dev);
snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D0);
snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D0);
return 0;
}
/* release resources */
static void hdmi_lpe_audio_free(struct snd_card *card)
{
struct snd_intelhad *ctx = card->private_data;
struct snd_intelhad_card *card_ctx = card->private_data;
struct intel_hdmi_lpe_audio_pdata *pdata = card_ctx->dev->platform_data;
int port;
cancel_work_sync(&ctx->hdmi_audio_wq);
spin_lock_irq(&pdata->lpe_audio_slock);
pdata->notify_audio_lpe = NULL;
spin_unlock_irq(&pdata->lpe_audio_slock);
if (ctx->mmio_start)
iounmap(ctx->mmio_start);
if (ctx->irq >= 0)
free_irq(ctx->irq, ctx);
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
cancel_work_sync(&ctx->hdmi_audio_wq);
}
if (card_ctx->mmio_start)
iounmap(card_ctx->mmio_start);
if (card_ctx->irq >= 0)
free_irq(card_ctx->irq, card_ctx);
}
/*
@ -1683,12 +1742,12 @@ static void hdmi_lpe_audio_free(struct snd_card *card)
static int hdmi_lpe_audio_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_intelhad *ctx;
struct snd_intelhad_card *card_ctx;
struct snd_pcm *pcm;
struct intel_hdmi_lpe_audio_pdata *pdata;
int irq;
struct resource *res_mmio;
int i, ret;
int port, ret;
pdata = pdev->dev.platform_data;
if (!pdata) {
@ -1711,39 +1770,30 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
/* create a card instance with ALSA framework */
ret = snd_card_new(&pdev->dev, hdmi_card_index, hdmi_card_id,
THIS_MODULE, sizeof(*ctx), &card);
THIS_MODULE, sizeof(*card_ctx), &card);
if (ret)
return ret;
ctx = card->private_data;
spin_lock_init(&ctx->had_spinlock);
mutex_init(&ctx->mutex);
ctx->connected = false;
ctx->dev = &pdev->dev;
ctx->card = card;
ctx->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
card_ctx = card->private_data;
card_ctx->dev = &pdev->dev;
card_ctx->card = card;
strcpy(card->driver, INTEL_HAD);
strcpy(card->shortname, "Intel HDMI/DP LPE Audio");
strcpy(card->longname, "Intel HDMI/DP LPE Audio");
ctx->irq = -1;
ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5;
INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq);
card_ctx->irq = -1;
card->private_free = hdmi_lpe_audio_free;
/* assume pipe A as default */
ctx->had_config_offset = AUDIO_HDMI_CONFIG_A;
platform_set_drvdata(pdev, ctx);
platform_set_drvdata(pdev, card_ctx);
dev_dbg(&pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n",
__func__, (unsigned int)res_mmio->start,
(unsigned int)res_mmio->end);
ctx->mmio_start = ioremap_nocache(res_mmio->start,
(size_t)(resource_size(res_mmio)));
if (!ctx->mmio_start) {
card_ctx->mmio_start = ioremap_nocache(res_mmio->start,
(size_t)(resource_size(res_mmio)));
if (!card_ctx->mmio_start) {
dev_err(&pdev->dev, "Could not get ioremap\n");
ret = -EACCES;
goto err;
@ -1751,74 +1801,98 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
/* setup interrupt handler */
ret = request_irq(irq, display_pipe_interrupt_handler, 0,
pdev->name, ctx);
pdev->name, card_ctx);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq failed\n");
goto err;
}
ctx->irq = irq;
ret = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS,
MAX_CAP_STREAMS, &pcm);
if (ret)
goto err;
/* setup private data which can be retrieved when required */
pcm->private_data = ctx;
pcm->info_flags = 0;
strncpy(pcm->name, card->shortname, strlen(card->shortname));
/* setup the ops for playabck */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops);
card_ctx->irq = irq;
/* only 32bit addressable */
dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
/* allocate dma pages;
* try to allocate 600k buffer as default which is large enough
*/
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, NULL,
HAD_DEFAULT_BUFFER, HAD_MAX_BUFFER);
init_channel_allocations();
/* create controls */
for (i = 0; i < ARRAY_SIZE(had_controls); i++) {
ret = snd_ctl_add(card, snd_ctl_new1(&had_controls[i], ctx));
card_ctx->num_pipes = pdata->num_pipes;
card_ctx->num_ports = pdata->num_ports;
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
int i;
ctx->card_ctx = card_ctx;
ctx->dev = card_ctx->dev;
ctx->port = port;
ctx->pipe = -1;
INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq);
ret = snd_pcm_new(card, INTEL_HAD, port, MAX_PB_STREAMS,
MAX_CAP_STREAMS, &pcm);
if (ret)
goto err;
/* setup private data which can be retrieved when required */
pcm->private_data = ctx;
pcm->info_flags = 0;
strncpy(pcm->name, card->shortname, strlen(card->shortname));
/* setup the ops for playabck */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops);
/* allocate dma pages;
* try to allocate 600k buffer as default which is large enough
*/
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, NULL,
HAD_DEFAULT_BUFFER, HAD_MAX_BUFFER);
/* create controls */
for (i = 0; i < ARRAY_SIZE(had_controls); i++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&had_controls[i], ctx);
if (!kctl) {
ret = -ENOMEM;
goto err;
}
kctl->id.device = pcm->device;
ret = snd_ctl_add(card, kctl);
if (ret < 0)
goto err;
}
/* Register channel map controls */
ret = had_register_chmap_ctls(ctx, pcm);
if (ret < 0)
goto err;
ret = had_create_jack(ctx, pcm);
if (ret < 0)
goto err;
}
init_channel_allocations();
/* Register channel map controls */
ret = had_register_chmap_ctls(ctx, pcm);
if (ret < 0)
goto err;
ret = had_create_jack(ctx);
if (ret < 0)
goto err;
ret = snd_card_register(card);
if (ret)
goto err;
spin_lock_irq(&pdata->lpe_audio_slock);
pdata->notify_audio_lpe = notify_audio_lpe;
pdata->notify_pending = false;
spin_unlock_irq(&pdata->lpe_audio_slock);
/* runtime PM isn't enabled as default, since it won't save much on
* BYT/CHT devices; user who want the runtime PM should adjust the
* power/ontrol and power/autosuspend_delay_ms sysfs entries instead
*/
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__);
schedule_work(&ctx->hdmi_audio_wq);
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
schedule_work(&ctx->hdmi_audio_wq);
}
return 0;
@ -1834,9 +1908,9 @@ err:
*/
static int hdmi_lpe_audio_remove(struct platform_device *pdev)
{
struct snd_intelhad *ctx = platform_get_drvdata(pdev);
struct snd_intelhad_card *card_ctx = platform_get_drvdata(pdev);
snd_card_free(ctx->card);
snd_card_free(card_ctx->card);
return 0;
}

View File

@ -32,7 +32,6 @@
#include "intel_hdmi_lpe_audio.h"
#define PCM_INDEX 0
#define MAX_PB_STREAMS 1
#define MAX_CAP_STREAMS 0
#define BYTES_PER_WORD 0x4
@ -101,7 +100,7 @@ struct pcm_stream_info {
* @chmap: holds channel map info
*/
struct snd_intelhad {
struct snd_card *card;
struct snd_intelhad_card *card_ctx;
bool connected;
struct pcm_stream_info stream_info;
unsigned char eld[HDMI_MAX_ELD_BYTES];
@ -112,6 +111,8 @@ struct snd_intelhad {
struct snd_pcm_chmap *chmap;
int tmds_clock_speed;
int link_rate;
int port; /* fixed */
int pipe; /* can change dynamically */
/* ring buffer (BD) position index */
unsigned int bd_head;
@ -123,9 +124,6 @@ struct snd_intelhad {
unsigned int period_bytes; /* PCM period size in bytes */
/* internal stuff */
int irq;
void __iomem *mmio_start;
unsigned int had_config_offset;
union aud_cfg aud_config; /* AUD_CONFIG reg value cache */
struct work_struct hdmi_audio_wq;
struct mutex mutex; /* for protecting chmap and eld */
@ -133,4 +131,16 @@ struct snd_intelhad {
struct snd_jack *jack;
};
struct snd_intelhad_card {
struct snd_card *card;
struct device *dev;
/* internal stuff */
int irq;
void __iomem *mmio_start;
int num_pipes;
int num_ports;
struct snd_intelhad pcm_ctx[3]; /* one for each port */
};
#endif /* _INTEL_HDMI_AUDIO_ */