From 1347f5b46a270db1991625f9f57af91e23a4b512 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 17 Mar 2015 11:39:27 +0200 Subject: drm/i915/bxt: Add BXT PCI ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2: Switch to info->ring_mask and add VEBOX support. v3: Fold in update from Damien. v4: Add GEN_DEFAULT_PIPEOFFSETS and IVB_CURSOR_OFFSETS v5: set no-LLC (imre) Signed-off-by: Damien Lespiau (v1,v4) Signed-off-by: Daniel Vetter (v4) Signed-off-by: Imre Deak Reviewed-by: Antti Koskipää Signed-off-by: Daniel Vetter --- include/drm/i915_pciids.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 613372375ada..bd0d644d2a03 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -287,4 +287,10 @@ INTEL_SKL_GT3_IDS(info) +#define INTEL_BXT_IDS(info) \ + INTEL_VGA_DEVICE(0x0A84, info), \ + INTEL_VGA_DEVICE(0x0A85, info), \ + INTEL_VGA_DEVICE(0x0A86, info), \ + INTEL_VGA_DEVICE(0x0A87, info) + #endif /* _I915_PCIIDS_H */ -- cgit v1.2.3 From ea9da4e4608104108c6d5eca7b178cec2720ab22 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 2 Apr 2015 10:35:08 +0100 Subject: drm/i915: Allow disabling the destination colorkey for overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes userspace wants a true overlay that is never clipped. In such cases, we need to disable the destination colorkey. However, it is currently unconditionally enabled in the overlay with no means of disabling. So rectify that by always default to on, and extending the UPDATE_ATTR ioctl to support explicit disabling of the colorkey. This is contrast to the spite code which requires explicit enabling of either the destination or source colorkey. Handling source colorkey is still todo for the overlay. (Of course it may be worth migrating overlay to sprite before then.) Signed-off-by: Chris Wilson Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_overlay.c | 30 +++++++++++++++++++----------- include/uapi/drm/i915_drm.h | 1 + 2 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index b291f1301c93..5fd2d5ac02e2 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -175,7 +175,8 @@ struct intel_overlay { bool active; bool pfit_active; u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */ - u32 color_key; + u32 color_key:24; + u32 color_key_enabled:1; u32 brightness, contrast, saturation; u32 old_xscale, old_yscale; /* register access */ @@ -630,31 +631,36 @@ static void update_colorkey(struct intel_overlay *overlay, struct overlay_registers __iomem *regs) { u32 key = overlay->color_key; + u32 flags; + + flags = 0; + if (overlay->color_key_enabled) + flags |= DST_KEY_ENABLE; switch (overlay->crtc->base.primary->fb->bits_per_pixel) { case 8: - iowrite32(0, ®s->DCLRKV); - iowrite32(CLK_RGB8I_MASK | DST_KEY_ENABLE, ®s->DCLRKM); + key = 0; + flags |= CLK_RGB8I_MASK; break; case 16: if (overlay->crtc->base.primary->fb->depth == 15) { - iowrite32(RGB15_TO_COLORKEY(key), ®s->DCLRKV); - iowrite32(CLK_RGB15_MASK | DST_KEY_ENABLE, - ®s->DCLRKM); + key = RGB15_TO_COLORKEY(key); + flags |= CLK_RGB15_MASK; } else { - iowrite32(RGB16_TO_COLORKEY(key), ®s->DCLRKV); - iowrite32(CLK_RGB16_MASK | DST_KEY_ENABLE, - ®s->DCLRKM); + key = RGB16_TO_COLORKEY(key); + flags |= CLK_RGB16_MASK; } break; case 24: case 32: - iowrite32(key, ®s->DCLRKV); - iowrite32(CLK_RGB24_MASK | DST_KEY_ENABLE, ®s->DCLRKM); + flags |= CLK_RGB24_MASK; break; } + + iowrite32(key, ®s->DCLRKV); + iowrite32(flags, ®s->DCLRKM); } static u32 overlay_cmd_reg(struct put_image_params *params) @@ -1329,6 +1335,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, I915_WRITE(OGAMC5, attrs->gamma5); } } + overlay->color_key_enabled = (attrs->flags & I915_OVERLAY_DISABLE_DEST_COLORKEY) == 0; ret = 0; out_unlock: @@ -1392,6 +1399,7 @@ void intel_setup_overlay(struct drm_device *dev) /* init all values */ overlay->color_key = 0x0101fe; + overlay->color_key_enabled = true; overlay->brightness = -19; overlay->contrast = 75; overlay->saturation = 146; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 551b6737f5df..4851d660243c 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -996,6 +996,7 @@ struct drm_intel_overlay_put_image { /* flags */ #define I915_OVERLAY_UPDATE_ATTRS (1<<0) #define I915_OVERLAY_UPDATE_GAMMA (1<<1) +#define I915_OVERLAY_DISABLE_DEST_COLORKEY (1<<2) struct drm_intel_overlay_attrs { __u32 flags; __u32 color_key; -- cgit v1.2.3 From f81338a52a82009863b0dc9d597fe1000d1caff6 Mon Sep 17 00:00:00 2001 From: Chandra Konduru Date: Thu, 9 Apr 2015 17:36:21 -0700 Subject: drm: Adding drm helper function drm_plane_from_index(). Adding drm helper function to return plane pointer from index where index is a returned by drm_plane_index. v2: -avoided nested loop by adding loop count (Daniel) v3: -updated patch header prefix to 'drm' (Matt) v4: -fixed a kerneldoc issue (kbuild-internal) Cc: dri-devel@lists.freedesktop.org Signed-off-by: Chandra Konduru Reviewed-by: Matt Roper Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 23 +++++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9f970c2d4819..6254942141d3 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1285,6 +1285,29 @@ unsigned int drm_plane_index(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_index); +/** + * drm_plane_from_index - find the registered plane at an index + * @dev: DRM device + * @idx: index of registered plane to find for + * + * Given a plane index, return the registered plane from DRM device's + * list of planes with matching index. + */ +struct drm_plane * +drm_plane_from_index(struct drm_device *dev, int idx) +{ + struct drm_plane *plane; + unsigned int i = 0; + + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + if (i == idx) + return plane; + i++; + } + return NULL; +} +EXPORT_SYMBOL(drm_plane_from_index); + /** * drm_plane_force_disable - Forcibly disable a plane * @plane: plane to disable diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index adc9ea5acf02..ed769e79c675 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1264,6 +1264,7 @@ extern int drm_plane_init(struct drm_device *dev, bool is_primary); extern void drm_plane_cleanup(struct drm_plane *plane); extern unsigned int drm_plane_index(struct drm_plane *plane); +extern struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx); extern void drm_plane_force_disable(struct drm_plane *plane); extern int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format); -- cgit v1.2.3 From 99264a61dfcda41d86d0960cf2d4c0fc2758a773 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 15 Apr 2015 19:34:43 +0200 Subject: drm/vblank: Fixup and document timestamp update/read barriers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a bit too much cargo-culted, so lets make it solid: - vblank->count doesn't need to be an atomic, writes are always done under the protection of dev->vblank_time_lock. Switch to an unsigned long instead and update comments. Note that atomic_read is just a normal read of a volatile variable, so no need to audit all the read-side access specifically. - The barriers for the vblank counter seqlock weren't complete: The read-side was missing the first barrier between the counter read and the timestamp read, it only had a barrier between the ts and the counter read. We need both. - Barriers weren't properly documented. Since barriers only work if you have them on boths sides of the transaction it's prudent to reference where the other side is. To avoid duplicating the write-side comment 3 times extract a little store_vblank() helper. In that helper also assert that we do indeed hold dev->vblank_time_lock, since in some cases the lock is acquired a few functions up in the callchain. Spotted while reviewing a patch from Chris Wilson to add a fastpath to the vblank_wait ioctl. v2: Add comment to better explain how store_vblank works, suggested by Chris. v3: Peter noticed that as-is the 2nd smp_wmb is redundant with the implicit barrier in the spin_unlock. But that can only be proven by auditing all callers and my point in extracting this little helper was to localize all the locking into just one place. Hence I think that additional optimization is too risky. Cc: Chris Wilson Cc: Mario Kleiner Cc: Ville Syrjälä Cc: Michel Dänzer Cc: Peter Hurley Reviewed-by: Chris Wilson Reviewed-and-tested-by: Mario Kleiner Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 95 +++++++++++++++++++++++++---------------------- include/drm/drmP.h | 8 +++- 2 files changed, 57 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c8a34476570a..8694b77d0002 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -74,6 +74,36 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); +static void store_vblank(struct drm_device *dev, int crtc, + unsigned vblank_count_inc, + struct timeval *t_vblank) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + u32 tslot; + + assert_spin_locked(&dev->vblank_time_lock); + + if (t_vblank) { + /* All writers hold the spinlock, but readers are serialized by + * the latching of vblank->count below. + */ + tslot = vblank->count + vblank_count_inc; + vblanktimestamp(dev, crtc, tslot) = *t_vblank; + } + + /* + * vblank timestamp updates are protected on the write side with + * vblank_time_lock, but on the read side done locklessly using a + * sequence-lock on the vblank counter. Ensure correct ordering using + * memory barrriers. We need the barrier both before and also after the + * counter update to synchronize with the next timestamp write. + * The read-side barriers for this are in drm_vblank_count_and_time. + */ + smp_wmb(); + vblank->count += vblank_count_inc; + smp_wmb(); +} + /** * drm_update_vblank_count - update the master vblank counter * @dev: DRM device @@ -93,7 +123,7 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); static void drm_update_vblank_count(struct drm_device *dev, int crtc) { struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; - u32 cur_vblank, diff, tslot; + u32 cur_vblank, diff; bool rc; struct timeval t_vblank; @@ -129,18 +159,12 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) if (diff == 0) return; - /* Reinitialize corresponding vblank timestamp if high-precision query - * available. Skip this step if query unsupported or failed. Will - * reinitialize delayed at next vblank interrupt in that case. + /* + * Only reinitialize corresponding vblank timestamp if high-precision query + * available and didn't fail. Will reinitialize delayed at next vblank + * interrupt in that case. */ - if (rc) { - tslot = atomic_read(&vblank->count) + diff; - vblanktimestamp(dev, crtc, tslot) = t_vblank; - } - - smp_mb__before_atomic(); - atomic_add(diff, &vblank->count); - smp_mb__after_atomic(); + store_vblank(dev, crtc, diff, rc ? &t_vblank : NULL); } /* @@ -218,7 +242,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) /* Compute time difference to stored timestamp of last vblank * as updated by last invocation of drm_handle_vblank() in vblank irq. */ - vblcount = atomic_read(&vblank->count); + vblcount = vblank->count; diff_ns = timeval_to_ns(&tvblank) - timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); @@ -234,17 +258,8 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) * available. In that case we can't account for this and just * hope for the best. */ - if (vblrc && (abs64(diff_ns) > 1000000)) { - /* Store new timestamp in ringbuffer. */ - vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; - - /* Increment cooked vblank count. This also atomically commits - * the timestamp computed above. - */ - smp_mb__before_atomic(); - atomic_inc(&vblank->count); - smp_mb__after_atomic(); - } + if (vblrc && (abs64(diff_ns) > 1000000)) + store_vblank(dev, crtc, 1, &tvblank); spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); } @@ -852,7 +867,7 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) if (WARN_ON(crtc >= dev->num_crtcs)) return 0; - return atomic_read(&vblank->count); + return vblank->count; } EXPORT_SYMBOL(drm_vblank_count); @@ -897,16 +912,17 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, if (WARN_ON(crtc >= dev->num_crtcs)) return 0; - /* Read timestamp from slot of _vblank_time ringbuffer - * that corresponds to current vblank count. Retry if - * count has incremented during readout. This works like - * a seqlock. + /* + * Vblank timestamps are read lockless. To ensure consistency the vblank + * counter is rechecked and ordering is ensured using memory barriers. + * This works like a seqlock. The write-side barriers are in store_vblank. */ do { - cur_vblank = atomic_read(&vblank->count); + cur_vblank = vblank->count; + smp_rmb(); *vblanktime = vblanktimestamp(dev, crtc, cur_vblank); smp_rmb(); - } while (cur_vblank != atomic_read(&vblank->count)); + } while (cur_vblank != vblank->count); return cur_vblank; } @@ -1715,7 +1731,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) */ /* Get current timestamp and count. */ - vblcount = atomic_read(&vblank->count); + vblcount = vblank->count; drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ); /* Compute time difference to timestamp of last vblank */ @@ -1731,20 +1747,11 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) * e.g., due to spurious vblank interrupts. We need to * ignore those for accounting. */ - if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) { - /* Store new timestamp in ringbuffer. */ - vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; - - /* Increment cooked vblank count. This also atomically commits - * the timestamp computed above. - */ - smp_mb__before_atomic(); - atomic_inc(&vblank->count); - smp_mb__after_atomic(); - } else { + if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) + store_vblank(dev, crtc, 1, &tvblank); + else DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", crtc, (int) diff_ns); - } spin_unlock(&dev->vblank_time_lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 62c40777c009..4c31a2cc5a33 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -686,9 +686,13 @@ struct drm_pending_vblank_event { struct drm_vblank_crtc { struct drm_device *dev; /* pointer to the drm_device */ wait_queue_head_t queue; /**< VBLANK wait queue */ - struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */ struct timer_list disable_timer; /* delayed disable timer */ - atomic_t count; /**< number of VBLANK interrupts */ + + /* vblank counter, protected by dev->vblank_time_lock for writes */ + unsigned long count; + /* vblank timestamps, protected by dev->vblank_time_lock for writes */ + struct timeval time[DRM_VBLANKTIME_RBSIZE]; + atomic_t refcount; /* number of users of vblank interruptsper crtc */ u32 last; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ -- cgit v1.2.3 From acab18b5c3a7025640abc84ace5e94c76ebd3d10 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 4 May 2015 16:05:12 +0200 Subject: drm: drop unused 'magicfree' list This list is write-only. It's never used for read-access, so no reason to keep it around. Drop it! Signed-off-by: David Herrmann Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_auth.c | 3 --- drivers/gpu/drm/drm_drv.c | 1 - include/drm/drmP.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index fc8e8aaa34fb..8a37524d0867 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -37,7 +37,6 @@ #include "drm_internal.h" struct drm_magic_entry { - struct list_head head; struct drm_hash_item hash_item; struct drm_file *priv; }; @@ -93,7 +92,6 @@ static int drm_add_magic(struct drm_master *master, struct drm_file *priv, entry->hash_item.key = (unsigned long)magic; mutex_lock(&dev->struct_mutex); drm_ht_insert_item(&master->magiclist, &entry->hash_item); - list_add_tail(&entry->head, &master->magicfree); mutex_unlock(&dev->struct_mutex); return 0; @@ -123,7 +121,6 @@ int drm_remove_magic(struct drm_master *master, drm_magic_t magic) } pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); drm_ht_remove_item(&master->magiclist, hash); - list_del(&pt->head); mutex_unlock(&dev->struct_mutex); kfree(pt); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 48f7359e2a6b..26ed9feb3cfd 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -109,7 +109,6 @@ struct drm_master *drm_master_create(struct drm_minor *minor) kfree(master); return NULL; } - INIT_LIST_HEAD(&master->magicfree); master->minor = minor; return master; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4c31a2cc5a33..e2b101b49112 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -356,7 +356,6 @@ struct drm_lock_data { * @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex. * @unique_len: Length of unique field. Protected by drm_global_mutex. * @magiclist: Hash of used authentication tokens. Protected by struct_mutex. - * @magicfree: List of used authentication tokens. Protected by struct_mutex. * @lock: DRI lock information. * @driver_priv: Pointer to driver-private information. */ @@ -366,7 +365,6 @@ struct drm_master { char *unique; int unique_len; struct drm_open_hash magiclist; - struct list_head magicfree; struct drm_lock_data lock; void *driver_priv; }; -- cgit v1.2.3 From 32e7b94a3fa8e137aab9f2c65dff86be73245fc8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 4 May 2015 21:01:30 +0200 Subject: drm: simplify authentication management The magic auth tokens we have are a simple map from cyclic IDs to drm_file objects. Remove all the old bulk of code and replace it with a simple, direct IDR. The previous behavior is kept. Especially calling authmagic multiple times on the same magic results in EINVAL except on the first call. The only difference in behavior is that we never allocate IDs multiple times as long as a client has its FD open. v2: - Fix return code of GetMagic() - Use non-cyclic IDR allocator - fix off-by-one in "magic > INT_MAX" sanity check v3: - drop redundant "magic > INT_MAX" check Signed-off-by: David Herrmann Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_auth.c | 175 +++++++++-------------------------------- drivers/gpu/drm/drm_drv.c | 12 +-- drivers/gpu/drm/drm_fops.c | 7 +- drivers/gpu/drm/drm_internal.h | 1 - include/drm/drmP.h | 4 +- 5 files changed, 42 insertions(+), 157 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 8a37524d0867..50d0baa06db0 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -1,11 +1,3 @@ -/** - * \file drm_auth.c - * IOCTLs for authentication - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - /* * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com * @@ -13,6 +5,9 @@ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * + * Author Rickard E. (Rik) Faith + * Author Gareth Hughes + * * 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 @@ -36,151 +31,47 @@ #include #include "drm_internal.h" -struct drm_magic_entry { - struct drm_hash_item hash_item; - struct drm_file *priv; -}; - -/** - * Find the file with the given magic number. - * - * \param dev DRM device. - * \param magic magic number. - * - * Searches in drm_device::magiclist within all files with the same hash key - * the one with matching magic number, while holding the drm_device::struct_mutex - * lock. - */ -static struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic) -{ - struct drm_file *retval = NULL; - struct drm_magic_entry *pt; - struct drm_hash_item *hash; - struct drm_device *dev = master->minor->dev; - - mutex_lock(&dev->struct_mutex); - if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) { - pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); - retval = pt->priv; - } - mutex_unlock(&dev->struct_mutex); - return retval; -} - -/** - * Adds a magic number. - * - * \param dev DRM device. - * \param priv file private data. - * \param magic magic number. - * - * Creates a drm_magic_entry structure and appends to the linked list - * associated the magic number hash key in drm_device::magiclist, while holding - * the drm_device::struct_mutex lock. - */ -static int drm_add_magic(struct drm_master *master, struct drm_file *priv, - drm_magic_t magic) -{ - struct drm_magic_entry *entry; - struct drm_device *dev = master->minor->dev; - DRM_DEBUG("%d\n", magic); - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - entry->priv = priv; - entry->hash_item.key = (unsigned long)magic; - mutex_lock(&dev->struct_mutex); - drm_ht_insert_item(&master->magiclist, &entry->hash_item); - mutex_unlock(&dev->struct_mutex); - - return 0; -} - -/** - * Remove a magic number. - * - * \param dev DRM device. - * \param magic magic number. - * - * Searches and unlinks the entry in drm_device::magiclist with the magic - * number hash key, while holding the drm_device::struct_mutex lock. - */ -int drm_remove_magic(struct drm_master *master, drm_magic_t magic) -{ - struct drm_magic_entry *pt; - struct drm_hash_item *hash; - struct drm_device *dev = master->minor->dev; - - DRM_DEBUG("%d\n", magic); - - mutex_lock(&dev->struct_mutex); - if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); - drm_ht_remove_item(&master->magiclist, hash); - mutex_unlock(&dev->struct_mutex); - - kfree(pt); - - return 0; -} - /** - * Get a unique magic number (ioctl). + * drm_getmagic - Get unique magic of a client + * @dev: DRM device to operate on + * @data: ioctl data containing the drm_auth object + * @file_priv: DRM file that performs the operation * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a resulting drm_auth structure. - * \return zero on success, or a negative number on failure. + * This looks up the unique magic of the passed client and returns it. If the + * client did not have a magic assigned, yet, a new one is registered. The magic + * is stored in the passed drm_auth object. * - * If there is a magic number in drm_file::magic then use it, otherwise - * searches an unique non-zero magic number and add it associating it with \p - * file_priv. - * This ioctl needs protection by the drm_global_mutex, which protects - * struct drm_file::magic and struct drm_magic_entry::priv. + * Returns: 0 on success, negative error code on failure. */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { - static drm_magic_t sequence = 0; - static DEFINE_SPINLOCK(lock); struct drm_auth *auth = data; + int ret = 0; - /* Find unique magic */ - if (file_priv->magic) { - auth->magic = file_priv->magic; - } else { - do { - spin_lock(&lock); - if (!sequence) - ++sequence; /* reserve 0 */ - auth->magic = sequence++; - spin_unlock(&lock); - } while (drm_find_file(file_priv->master, auth->magic)); - file_priv->magic = auth->magic; - drm_add_magic(file_priv->master, file_priv, auth->magic); + mutex_lock(&dev->struct_mutex); + if (!file_priv->magic) { + ret = idr_alloc(&file_priv->master->magic_map, file_priv, + 1, 0, GFP_KERNEL); + if (ret >= 0) + file_priv->magic = ret; } + auth->magic = file_priv->magic; + mutex_unlock(&dev->struct_mutex); DRM_DEBUG("%u\n", auth->magic); - return 0; + return ret < 0 ? ret : 0; } /** - * Authenticate with a magic. + * drm_authmagic - Authenticate client with a magic + * @dev: DRM device to operate on + * @data: ioctl data containing the drm_auth object + * @file_priv: DRM file that performs the operation * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg pointer to a drm_auth structure. - * \return zero if authentication successed, or a negative number otherwise. + * This looks up a DRM client by the passed magic and authenticates it. * - * Checks if \p file_priv is associated with the magic number passed in \arg. - * This ioctl needs protection by the drm_global_mutex, which protects - * struct drm_file::magic and struct drm_magic_entry::priv. + * Returns: 0 on success, negative error code on failure. */ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -189,10 +80,14 @@ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file; DRM_DEBUG("%u\n", auth->magic); - if ((file = drm_find_file(file_priv->master, auth->magic))) { + + mutex_lock(&dev->struct_mutex); + file = idr_find(&file_priv->master->magic_map, auth->magic); + if (file) { file->authenticated = 1; - drm_remove_magic(file_priv->master, auth->magic); - return 0; + idr_replace(&file_priv->master->magic_map, NULL, auth->magic); } - return -EINVAL; + mutex_unlock(&dev->struct_mutex); + + return file ? 0 : -EINVAL; } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 26ed9feb3cfd..88b594c0593a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -92,8 +92,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...) } EXPORT_SYMBOL(drm_ut_debug_printk); -#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ - struct drm_master *drm_master_create(struct drm_minor *minor) { struct drm_master *master; @@ -105,10 +103,7 @@ struct drm_master *drm_master_create(struct drm_minor *minor) kref_init(&master->refcount); spin_lock_init(&master->lock.spinlock); init_waitqueue_head(&master->lock.lock_queue); - if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) { - kfree(master); - return NULL; - } + idr_init(&master->magic_map); master->minor = minor; return master; @@ -143,10 +138,9 @@ static void drm_master_destroy(struct kref *kref) master->unique = NULL; master->unique_len = 0; } - - drm_ht_remove(&master->magiclist); - mutex_unlock(&dev->struct_mutex); + + idr_destroy(&master->magic_map); kfree(master); } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 076dd606b580..0f6a5c8801e3 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -380,6 +380,8 @@ int drm_release(struct inode *inode, struct file *filp) mutex_lock(&dev->struct_mutex); list_del(&file_priv->lhead); + if (file_priv->magic) + idr_remove(&file_priv->master->magic_map, file_priv->magic); mutex_unlock(&dev->struct_mutex); if (dev->driver->preclose) @@ -394,11 +396,6 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->kdev->devt), dev->open_count); - /* Release any auth tokens that might point to this file_priv, - (do that under the drm_global_mutex) */ - if (file_priv->magic) - (void) drm_remove_magic(file_priv->master, file_priv->magic); - /* if the master has gone away we can't do anything with the lock */ if (file_priv->minor->master) drm_master_release(dev, filp); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 12a61d706827..059af01bd07a 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -69,7 +69,6 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* drm_sysfs.c */ extern struct class *drm_class; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index e2b101b49112..df6d9970d9a4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -355,7 +355,7 @@ struct drm_lock_data { * @minor: Link back to minor char device we are master for. Immutable. * @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex. * @unique_len: Length of unique field. Protected by drm_global_mutex. - * @magiclist: Hash of used authentication tokens. Protected by struct_mutex. + * @magic_map: Map of used authentication tokens. Protected by struct_mutex. * @lock: DRI lock information. * @driver_priv: Pointer to driver-private information. */ @@ -364,7 +364,7 @@ struct drm_master { struct drm_minor *minor; char *unique; int unique_len; - struct drm_open_hash magiclist; + struct idr magic_map; struct drm_lock_data lock; void *driver_priv; }; -- cgit v1.2.3 From 6ba2bd3da7d3f9af3db83f704ca055943fd9ee40 Mon Sep 17 00:00:00 2001 From: Todd Previte Date: Tue, 21 Apr 2015 11:09:41 -0700 Subject: drm: Add edid_corrupt flag for Displayport Link CTS 4.2.2.6 Displayport compliance test 4.2.2.6 requires that a source device be capable of detecting a corrupt EDID. The test specification states that the sink device sets up the EDID with an invalid checksum. To do this, the sink sets up an invalid EDID header, expecting the source device to generate the checksum and compare it to the value stored in the last byte of the block data. Unfortunately, the DRM EDID reading and parsing functions are actually too good in this case; the header is fixed before the checksum is computed and thus the test never sees the invalid checksum. This results in a failure to pass the compliance test. To correct this issue, when the EDID code detects that the header is invalid, a flag is set to indicate that the EDID is corrupted. In this case, it sets edid_corrupt flag and continues with its fix-up code. This flag is also set in the case of a more seriously damaged header (fixup score less than the threshold). For consistency, the edid_corrupt flag is also set when the checksum is invalid as well. V2: - Removed the static bool global - Added a bool to the drm_connector struct to reaplce the static one for holding the status of raw edid header corruption detection - Modified the function signature of the is_valid function to take an additional parameter to store the corruption detected value - Fixed the other callers of the above is_valid function V3: - Updated the commit message to be more clear about what and why this patch does what it does. - Added comment in code to clarify the operations there - Removed compliance variable and check_link_status update; those have been moved to a later patch - Removed variable assignment from the bottom of the test handler V4: - Removed i915 tag from subject line as the patch is not i915-specific V5: - Moved code causing a compilation error to this patch where the variable is actually declared - Maintained blank lines / spacing so as to not contaminate the patch V6: - Removed extra debug messages - Added documentation to for the added parameter on drm_edid_block_valid - Fixed more whitespace issues in check_link_status - Added a clear of the header_corrupt flag to the end of the test handler in intel_dp.c - Changed the usage of the new function prototype in several places to use NULL where it is not needed by compliance testing V7: - Updated to account for long_pulse flag propagation V8: - Removed clearing of header_corrupt flag from the test handler in intel_dp.c - Added clearing of header_corrupt flag in the drm_edid_block_valid function V9: - Renamed header_corrupt flag to edid_corrupt to more accurately reflect its value and purpose - Updated commit message V10: - Updated for versioning and patch swizzle - Revised the title to more accurately reflect the nature and contents of the patch - Fixed formatting/whitespace problems - Added set flag when computed checksum is invalid Signed-off-by: Todd Previte Cc: dri-devel@lists.freedesktop.org Acked-by: Dave Airlie Reviewed-by: Alex Deucher Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 32 ++++++++++++++++++++++++++------ drivers/gpu/drm/drm_edid_load.c | 7 +++++-- include/drm/drm_crtc.h | 8 +++++++- 3 files changed, 38 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 53bc7a628909..be6713c2fc9d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1041,13 +1041,15 @@ static bool drm_edid_is_zero(const u8 *in_edid, int length) * @raw_edid: pointer to raw EDID block * @block: type of block to validate (0 for base, extension otherwise) * @print_bad_edid: if true, dump bad EDID blocks to the console + * @edid_corrupt: if true, the header or checksum is invalid * * Validate a base or extension EDID block and optionally dump bad blocks to * the console. * * Return: True if the block is valid, false otherwise. */ -bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) +bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, + bool *edid_corrupt) { u8 csum; struct edid *edid = (struct edid *)raw_edid; @@ -1060,11 +1062,22 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) if (block == 0) { int score = drm_edid_header_is_valid(raw_edid); - if (score == 8) ; - else if (score >= edid_fixup) { + if (score == 8) { + if (edid_corrupt) + *edid_corrupt = 0; + } else if (score >= edid_fixup) { + /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6 + * The corrupt flag needs to be set here otherwise, the + * fix-up code here will correct the problem, the + * checksum is correct and the test fails + */ + if (edid_corrupt) + *edid_corrupt = 1; DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); memcpy(raw_edid, edid_header, sizeof(edid_header)); } else { + if (edid_corrupt) + *edid_corrupt = 1; goto bad; } } @@ -1075,6 +1088,9 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); } + if (edid_corrupt) + *edid_corrupt = 1; + /* allow CEA to slide through, switches mangle this */ if (raw_edid[0] != 0x02) goto bad; @@ -1129,7 +1145,7 @@ bool drm_edid_is_valid(struct edid *edid) return false; for (i = 0; i <= edid->extensions; i++) - if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true)) + if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL)) return false; return true; @@ -1232,7 +1248,8 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, for (i = 0; i < 4; i++) { if (get_edid_block(data, block, 0, EDID_LENGTH)) goto out; - if (drm_edid_block_valid(block, 0, print_bad_edid)) + if (drm_edid_block_valid(block, 0, print_bad_edid, + &connector->edid_corrupt)) break; if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { connector->null_edid_counter++; @@ -1257,7 +1274,10 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, block + (valid_extensions + 1) * EDID_LENGTH, j, EDID_LENGTH)) goto out; - if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) { + if (drm_edid_block_valid(block + (valid_extensions + 1) + * EDID_LENGTH, j, + print_bad_edid, + NULL)) { valid_extensions++; break; } diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 4c0aa97aaf03..c5605fe4907e 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -216,7 +216,8 @@ static void *edid_load(struct drm_connector *connector, const char *name, goto out; } - if (!drm_edid_block_valid(edid, 0, print_bad_edid)) { + if (!drm_edid_block_valid(edid, 0, print_bad_edid, + &connector->edid_corrupt)) { connector->bad_edid_counter++; DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", name); @@ -229,7 +230,9 @@ static void *edid_load(struct drm_connector *connector, const char *name, if (i != valid_extensions + 1) memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, edid + i * EDID_LENGTH, EDID_LENGTH); - if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid)) + if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, + print_bad_edid, + NULL)) valid_extensions++; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index db9a30f10bc4..b9fcdc824997 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -719,6 +719,11 @@ struct drm_connector { int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ unsigned bad_edid_counter; + /* Flag for raw EDID header corruption - used in Displayport + * compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6 + */ + bool edid_corrupt; + struct dentry *debugfs_entry; struct drm_connector_state *state; @@ -1443,7 +1448,8 @@ extern void drm_set_preferred_mode(struct drm_connector *connector, int hpref, int vpref); extern int drm_edid_header_is_valid(const u8 *raw_edid); -extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid); +extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, + bool *edid_corrupt); extern bool drm_edid_is_valid(struct edid *edid); extern struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, -- cgit v1.2.3 From 8fb6e7a579670d5b71fc0d5641c1523b3df612e8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 20 Apr 2015 19:22:54 +0100 Subject: drm: Introduce blob_lock Create a new global blob_lock mutex, which protects the blob property list from insertion and/or deletion. Signed-off-by: Daniel Stone Reviewed-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 18 +++++++++++++++--- include/drm/drm_crtc.h | 3 +++ 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fd14db401517..3b6573527e7f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4216,25 +4216,34 @@ drm_property_create_blob(struct drm_device *dev, size_t length, if (!blob) return NULL; + blob->length = length; + + memcpy(blob->data, data, length); + + mutex_lock(&dev->mode_config.blob_lock); + ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); if (ret) { kfree(blob); + mutex_unlock(&dev->mode_config.blob_lock); return NULL; } - blob->length = length; + list_add_tail(&blob->head, &dev->mode_config.property_blob_list); - memcpy(blob->data, data, length); + mutex_unlock(&dev->mode_config.blob_lock); - list_add_tail(&blob->head, &dev->mode_config.property_blob_list); return blob; } static void drm_property_destroy_blob(struct drm_device *dev, struct drm_property_blob *blob) { + mutex_lock(&dev->mode_config.blob_lock); drm_mode_object_put(dev, &blob->base); list_del(&blob->head); + mutex_unlock(&dev->mode_config.blob_lock); + kfree(blob); } @@ -4341,6 +4350,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.blob_lock); blob = drm_property_blob_find(dev, out_resp->blob_id); if (!blob) { ret = -ENOENT; @@ -4357,6 +4367,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, out_resp->length = blob->length; done: + mutex_unlock(&dev->mode_config.blob_lock); drm_modeset_unlock_all(dev); return ret; } @@ -5490,6 +5501,7 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_lock_init(&dev->mode_config.connection_mutex); mutex_init(&dev->mode_config.idr_mutex); mutex_init(&dev->mode_config.fb_lock); + mutex_init(&dev->mode_config.blob_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.connector_list); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ca71c03143d1..55ed8f9f45be 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1048,6 +1048,7 @@ struct drm_mode_group { * @poll_running: track polling status for this device * @output_poll_work: delayed work for polling in process context * @property_blob_list: list of all the blob property objects + * @blob_lock: mutex for blob property allocation and management * @*_property: core property tracking * @preferred_depth: preferred RBG pixel depth, used by fb helpers * @prefer_shadow: hint to userspace to prefer shadow-fb rendering @@ -1103,6 +1104,8 @@ struct drm_mode_config { bool delayed_event; struct delayed_work output_poll_work; + struct mutex blob_lock; + /* pointers to standard properties */ struct list_head property_blob_list; struct drm_property *edid_property; -- cgit v1.2.3 From ac6f2e29bb08a2313b0480c6cea94b01ab274970 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 8 May 2015 16:15:41 +0200 Subject: drm/edid: Kerneldoc for newly added edid_corrupt Also treat it as a proper boolean. Cc: Todd Previte Cc: Paulo Zanoni Cc: dri-devel@lists.freedesktop.org Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 8 ++++---- drivers/gpu/drm/i915/intel_dp.c | 2 +- include/drm/drm_crtc.h | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index be6713c2fc9d..e426223482fb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1064,7 +1064,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, int score = drm_edid_header_is_valid(raw_edid); if (score == 8) { if (edid_corrupt) - *edid_corrupt = 0; + *edid_corrupt = false; } else if (score >= edid_fixup) { /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6 * The corrupt flag needs to be set here otherwise, the @@ -1072,12 +1072,12 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, * checksum is correct and the test fails */ if (edid_corrupt) - *edid_corrupt = 1; + *edid_corrupt = true; DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); memcpy(raw_edid, edid_header, sizeof(edid_header)); } else { if (edid_corrupt) - *edid_corrupt = 1; + *edid_corrupt = true; goto bad; } } @@ -1089,7 +1089,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, } if (edid_corrupt) - *edid_corrupt = 1; + *edid_corrupt = true; /* allow CEA to slide through, switches mangle this */ if (raw_edid[0] != 0x02) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2b1ed66c748f..9cf9208aeaff 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4120,7 +4120,7 @@ static uint8_t intel_dp_autotest_edid(struct intel_dp *intel_dp) struct drm_connector *connector = &intel_connector->base; if (intel_connector->detect_edid == NULL || - connector->edid_corrupt == 1 || + connector->edid_corrupt || intel_dp->aux.i2c_defer_count > 6) { /* Check EDID read for NACKs, DEFERs and corruption * (DP CTS 1.2 Core r1.1) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b9fcdc824997..0a4a040d6bb7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -647,6 +647,7 @@ struct drm_encoder { * @audio_latency: audio latency info from ELD, if found * @null_edid_counter: track sinks that give us all zeros for the EDID * @bad_edid_counter: track sinks that give us an EDID with invalid checksum + * @edid_corrupt: indicates whether the last read EDID was corrupt * @debugfs_entry: debugfs directory for this connector * @state: current atomic state for this connector * @has_tile: is this connector connected to a tiled monitor -- cgit v1.2.3 From 6bcacf51d050d412e5c302e0dd5e582212c5f7be Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 20 Apr 2015 19:22:55 +0100 Subject: drm: Add reference counting to blob properties Reference-count drm_property_blob objects, changing the API to ref/unref. Signed-off-by: Daniel Stone Reviewed-by: Maarten Lankhorst [danvet: Squash in kerneldoc fixup from Daniel Stone.] [danvet: Squash in Oops fix from Thiery Reding.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 163 +++++++++++++++++++++++++++++++++++++++++---- include/drm/drm_crtc.h | 17 ++--- 2 files changed, 158 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3b6573527e7f..2e26988a9762 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -352,7 +352,9 @@ static struct drm_mode_object *_object_find(struct drm_device *dev, if (obj && obj->id != id) obj = NULL; /* don't leak out unref'd fb's */ - if (obj && (obj->type == DRM_MODE_OBJECT_FB)) + if (obj && + (obj->type == DRM_MODE_OBJECT_FB || + obj->type == DRM_MODE_OBJECT_BLOB)) obj = NULL; mutex_unlock(&dev->mode_config.idr_mutex); @@ -377,7 +379,7 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, /* Framebuffers are reference counted and need their own lookup * function.*/ - WARN_ON(type == DRM_MODE_OBJECT_FB); + WARN_ON(type == DRM_MODE_OBJECT_FB || type == DRM_MODE_OBJECT_BLOB); obj = _object_find(dev, id, type); return obj; } @@ -4202,7 +4204,7 @@ done: return ret; } -static struct drm_property_blob * +struct drm_property_blob * drm_property_create_blob(struct drm_device *dev, size_t length, const void *data) { @@ -4217,6 +4219,7 @@ drm_property_create_blob(struct drm_device *dev, size_t length, return NULL; blob->length = length; + blob->dev = dev; memcpy(blob->data, data, length); @@ -4229,24 +4232,146 @@ drm_property_create_blob(struct drm_device *dev, size_t length, return NULL; } + kref_init(&blob->refcount); + list_add_tail(&blob->head, &dev->mode_config.property_blob_list); mutex_unlock(&dev->mode_config.blob_lock); return blob; } +EXPORT_SYMBOL(drm_property_create_blob); -static void drm_property_destroy_blob(struct drm_device *dev, - struct drm_property_blob *blob) +/** + * drm_property_free_blob - Blob property destructor + * + * Internal free function for blob properties; must not be used directly. + * + * @param kref Reference + */ +static void drm_property_free_blob(struct kref *kref) { - mutex_lock(&dev->mode_config.blob_lock); - drm_mode_object_put(dev, &blob->base); + struct drm_property_blob *blob = + container_of(kref, struct drm_property_blob, refcount); + + WARN_ON(!mutex_is_locked(&blob->dev->mode_config.blob_lock)); + list_del(&blob->head); - mutex_unlock(&dev->mode_config.blob_lock); + drm_mode_object_put(blob->dev, &blob->base); kfree(blob); } +/** + * drm_property_unreference_blob - Unreference a blob property + * + * Drop a reference on a blob property. May free the object. + * + * @param blob Pointer to blob property + */ +void drm_property_unreference_blob(struct drm_property_blob *blob) +{ + struct drm_device *dev; + + if (!blob) + return; + + dev = blob->dev; + + DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount)); + + if (kref_put_mutex(&blob->refcount, drm_property_free_blob, + &dev->mode_config.blob_lock)) + mutex_unlock(&dev->mode_config.blob_lock); + else + might_lock(&dev->mode_config.blob_lock); + +} +EXPORT_SYMBOL(drm_property_unreference_blob); + +/** + * drm_property_unreference_blob_locked - Unreference a blob property with blob_lock held + * + * Drop a reference on a blob property. May free the object. This must be + * called with blob_lock held. + * + * @param dev Device the blob was created on + * @param blob Pointer to blob property + */ +static void drm_property_unreference_blob_locked(struct drm_property_blob *blob) +{ + if (!blob) + return; + + DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount)); + + kref_put(&blob->refcount, drm_property_free_blob); +} + +/** + * drm_property_reference_blob - Take a reference on an existing property + * + * Take a new reference on an existing blob property. + * + * @param blob Pointer to blob property + */ +struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) +{ + DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount)); + kref_get(&blob->refcount); + return blob; +} +EXPORT_SYMBOL(drm_property_reference_blob); + +/* + * Like drm_property_lookup_blob, but does not return an additional reference. + * Must be called with blob_lock held. + */ +static struct drm_property_blob *__drm_property_lookup_blob(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj = NULL; + struct drm_property_blob *blob; + + WARN_ON(!mutex_is_locked(&dev->mode_config.blob_lock)); + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != DRM_MODE_OBJECT_BLOB) || (obj->id != id)) + blob = NULL; + else + blob = obj_to_blob(obj); + mutex_unlock(&dev->mode_config.idr_mutex); + + return blob; +} + +/** + * drm_property_lookup_blob - look up a blob property and take a reference + * @dev: drm device + * @id: id of the blob property + * + * If successful, this takes an additional reference to the blob property. + * callers need to make sure to eventually unreference the returned property + * again, using @drm_property_unreference_blob. + */ +struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, + uint32_t id) +{ + struct drm_property_blob *blob; + + mutex_lock(&dev->mode_config.blob_lock); + blob = __drm_property_lookup_blob(dev, id); + if (blob) { + if (!kref_get_unless_zero(&blob->refcount)) + blob = NULL; + } + mutex_unlock(&dev->mode_config.blob_lock); + + return blob; +} +EXPORT_SYMBOL(drm_property_lookup_blob); + /** * drm_property_replace_global_blob - atomically replace existing blob property * @dev: drm device @@ -4313,14 +4438,14 @@ static int drm_property_replace_global_blob(struct drm_device *dev, } if (old_blob) - drm_property_destroy_blob(dev, old_blob); + drm_property_unreference_blob(old_blob); *replace = new_blob; return 0; err_created: - drm_property_destroy_blob(dev, new_blob); + drm_property_unreference_blob(new_blob); return ret; } @@ -4351,7 +4476,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, drm_modeset_lock_all(dev); mutex_lock(&dev->mode_config.blob_lock); - blob = drm_property_blob_find(dev, out_resp->blob_id); + blob = __drm_property_lookup_blob(dev, out_resp->blob_id); if (!blob) { ret = -ENOENT; goto done; @@ -4515,8 +4640,18 @@ bool drm_property_change_valid_get(struct drm_property *property, valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - /* Only the driver knows */ - return true; + struct drm_property_blob *blob; + + if (value == 0) + return true; + + blob = drm_property_lookup_blob(property->dev, value); + if (blob) { + *ref = &blob->base; + return true; + } else { + return false; + } } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { /* a zero value for an object property translates to null: */ if (value == 0) @@ -5566,7 +5701,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, head) { - drm_property_destroy_blob(dev, blob); + drm_property_unreference_blob(blob); } /* diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 55ed8f9f45be..5626191f3af0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -216,6 +216,8 @@ struct drm_framebuffer { struct drm_property_blob { struct drm_mode_object base; + struct drm_device *dev; + struct kref refcount; struct list_head head; size_t length; unsigned char data[]; @@ -1365,6 +1367,13 @@ struct drm_property *drm_property_create_object(struct drm_device *dev, int flags, const char *name, uint32_t type); struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, const char *name); +struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, + size_t length, + const void *data); +struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, + uint32_t id); +struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob); +void drm_property_unreference_blob(struct drm_property_blob *blob); extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); extern int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name); @@ -1528,14 +1537,6 @@ static inline struct drm_property *drm_property_find(struct drm_device *dev, return mo ? obj_to_property(mo) : NULL; } -static inline struct drm_property_blob * -drm_property_blob_find(struct drm_device *dev, uint32_t id) -{ - struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB); - return mo ? obj_to_blob(mo) : NULL; -} - /* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \ -- cgit v1.2.3 From 4c18d3010bb722e6ab7d0b80fcf78dd6f95a5a15 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 12 May 2015 15:27:37 +0200 Subject: drm/atomic-helpers: Export drm_atomic_helper_update_legacy_modeset_state This is useful for drivers which have their own modeset infrastructure but want to reuse most of the legacy state frobbery from the helpers. i915 wants this. v2: Add header declaration. Cc: Maarten Lankhorst Reviewed-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 23 ++++++++++++++++++++--- include/drm/drm_atomic_helper.h | 4 ++++ 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d414e9eea9d8..1bcd700e3baa 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -619,8 +619,22 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) } } -static void -set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) +/** + * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state + * @dev: DRM device + * @old_state: atomic state object with old state structures + * + * This function updates all the various legacy modeset state pointers in + * connectors, encoders and crtcs. It also updates the timestamping constants + * used for precise vblank timestamps by calling + * drm_calc_timestamping_constants(). + * + * Drivers can use this for building their own atomic commit if they don't have + * a pure helper-based modeset implementation. + */ +void +drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, + struct drm_atomic_state *old_state) { struct drm_connector *connector; struct drm_connector_state *old_conn_state; @@ -663,6 +677,7 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) &crtc->state->adjusted_mode); } } +EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state); static void crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) @@ -741,7 +756,9 @@ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, struct drm_atomic_state *old_state) { disable_outputs(dev, old_state); - set_routing_links(dev, old_state); + + drm_atomic_helper_update_legacy_modeset_state(dev, old_state); + crtc_set_mode(dev, old_state); } EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index d665781eb542..6ee0ee5b6143 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -43,6 +43,10 @@ int drm_atomic_helper_commit(struct drm_device *dev, void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *old_state); +void +drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, + struct drm_atomic_state *old_state); + void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, -- cgit v1.2.3 From 1b26a5e1932beb34c4213934d8faf02217fc40e4 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 13 May 2015 10:37:25 +0200 Subject: drm/atomic: add drm_atomic_get_existing_*_state helpers There are cases where we want to test if a given object is part of the state, but don't want to add them if they're not. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 18 +++++++--------- include/drm/drm_atomic.h | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 6e3b78ee7d16..c6277a4a1f2f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -203,13 +203,12 @@ struct drm_crtc_state * drm_atomic_get_crtc_state(struct drm_atomic_state *state, struct drm_crtc *crtc) { - int ret, index; + int ret, index = drm_crtc_index(crtc); struct drm_crtc_state *crtc_state; - index = drm_crtc_index(crtc); - - if (state->crtc_states[index]) - return state->crtc_states[index]; + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + if (crtc_state) + return crtc_state; ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); if (ret) @@ -337,13 +336,12 @@ struct drm_plane_state * drm_atomic_get_plane_state(struct drm_atomic_state *state, struct drm_plane *plane) { - int ret, index; + int ret, index = drm_plane_index(plane); struct drm_plane_state *plane_state; - index = drm_plane_index(plane); - - if (state->plane_states[index]) - return state->plane_states[index]; + plane_state = drm_atomic_get_existing_plane_state(state, plane); + if (plane_state) + return plane_state; ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx); if (ret) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index c157103492b0..d78543067700 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -54,6 +54,56 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, uint64_t val); +/** + * drm_atomic_get_existing_crtc_state - get crtc state, if it exists + * @state: global atomic state object + * @crtc: crtc to grab + * + * This function returns the crtc state for the given crtc, or NULL + * if the crtc is not part of the global atomic state. + */ +static inline struct drm_crtc_state * +drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + return state->crtc_states[drm_crtc_index(crtc)]; +} + +/** + * drm_atomic_get_existing_plane_state - get plane state, if it exists + * @state: global atomic state object + * @plane: plane to grab + * + * This function returns the plane state for the given plane, or NULL + * if the plane is not part of the global atomic state. + */ +static inline struct drm_plane_state * +drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + return state->plane_states[drm_plane_index(plane)]; +} + +/** + * drm_atomic_get_existing_connector_state - get connector state, if it exists + * @state: global atomic state object + * @connector: connector to grab + * + * This function returns the connector state for the given connector, + * or NULL if the connector is not part of the global atomic state. + */ +static inline struct drm_connector_state * +drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, + struct drm_connector *connector) +{ + int index = drm_connector_index(connector); + + if (index >= state->num_connector) + return NULL; + + return state->connector_states[index]; +} + int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, struct drm_crtc *crtc); -- cgit v1.2.3 From 6921f88ba85338b0a78ef50d2be79ef3f6c6f647 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 13 May 2015 12:30:46 +0100 Subject: drm/dp: Fix comment in DP helper Commit 4f71d0cb76339 ("drm/dp: add a hw mutex around the transfer functions. (v2)"), renamed the functions drm_dp_aux_register_i2c_bus() and drm_dp_aux_unregister_i2c_bus() to drm_dp_aux_register() and drm_dp_aux_unregister(), respectively. However, a comment referring to the original names was not updated in the DP helper header file. Hence, correct these names. Signed-off-by: Jon Hunter Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 523f04c90dea..2e86f642fc33 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -679,9 +679,9 @@ struct drm_dp_aux_msg { * An AUX channel can also be used to transport I2C messages to a sink. A * typical application of that is to access an EDID that's present in the * sink device. The .transfer() function can also be used to execute such - * transactions. The drm_dp_aux_register_i2c_bus() function registers an - * I2C adapter that can be passed to drm_probe_ddc(). Upon removal, drivers - * should call drm_dp_aux_unregister_i2c_bus() to remove the I2C adapter. + * transactions. The drm_dp_aux_register() function registers an I2C + * adapter that can be passed to drm_probe_ddc(). Upon removal, drivers + * should call drm_dp_aux_unregister() to remove the I2C adapter. * The I2C adapter uses long transfers by default; if a partial response is * received, the adapter will drop down to the size given by the partial * response for this transaction only. -- cgit v1.2.3 From 036ef5733ba433760a3512bb5f7a155946e2df05 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 18 May 2015 10:06:40 +0200 Subject: drm/atomic: Allow drivers to subclass drm_atomic_state, v3 Drivers may need to store the state of shared resources, such as PLLs or FIFO space, into the atomic state. Allow this by making it possible to subclass drm_atomic_state. Changes since v1: - Change member names for functions to atomic_state_(alloc,clear) - Change __drm_atomic_state_new to drm_atomic_state_init - Allow free function to be overridden too, in case extra memory is allocated in alloc. Changes since v2: - Rename *_default_free to default_release, to make clear it doesn't free the state object itself. Cc: dri-devel@lists.freedesktop.org Acked-by: Ander Conselvan de Oliveira Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 116 ++++++++++++++++++++++++++++++++----------- include/drm/drm_atomic.h | 5 ++ include/drm/drm_crtc.h | 6 +++ 3 files changed, 99 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c6277a4a1f2f..cd1b16b25716 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -30,7 +30,15 @@ #include #include -static void kfree_state(struct drm_atomic_state *state) +/** + * drm_atomic_state_default_release - + * release memory initialized by drm_atomic_state_init + * @state: atomic state + * + * Free all the memory allocated by drm_atomic_state_init. + * This is useful for drivers that subclass the atomic state. + */ +void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors); kfree(state->connector_states); @@ -38,24 +46,20 @@ static void kfree_state(struct drm_atomic_state *state) kfree(state->crtc_states); kfree(state->planes); kfree(state->plane_states); - kfree(state); } +EXPORT_SYMBOL(drm_atomic_state_default_release); /** - * drm_atomic_state_alloc - allocate atomic state + * drm_atomic_state_init - init new atomic state * @dev: DRM device + * @state: atomic state * - * This allocates an empty atomic state to track updates. + * Default implementation for filling in a new atomic state. + * This is useful for drivers that subclass the atomic state. */ -struct drm_atomic_state * -drm_atomic_state_alloc(struct drm_device *dev) +int +drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) { - struct drm_atomic_state *state; - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return NULL; - /* TODO legacy paths should maybe do a better job about * setting this appropriately? */ @@ -92,31 +96,50 @@ drm_atomic_state_alloc(struct drm_device *dev) state->dev = dev; - DRM_DEBUG_ATOMIC("Allocate atomic state %p\n", state); + DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state); - return state; + return 0; fail: - kfree_state(state); + drm_atomic_state_default_release(state); + return -ENOMEM; +} +EXPORT_SYMBOL(drm_atomic_state_init); + +/** + * drm_atomic_state_alloc - allocate atomic state + * @dev: DRM device + * + * This allocates an empty atomic state to track updates. + */ +struct drm_atomic_state * +drm_atomic_state_alloc(struct drm_device *dev) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_atomic_state *state; + + if (!config->funcs->atomic_state_alloc) { + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + if (drm_atomic_state_init(dev, state) < 0) { + kfree(state); + return NULL; + } + return state; + } - return NULL; + return config->funcs->atomic_state_alloc(dev); } EXPORT_SYMBOL(drm_atomic_state_alloc); /** - * drm_atomic_state_clear - clear state object + * drm_atomic_state_default_clear - clear base atomic state * @state: atomic state * - * When the w/w mutex algorithm detects a deadlock we need to back off and drop - * all locks. So someone else could sneak in and change the current modeset - * configuration. Which means that all the state assembled in @state is no - * longer an atomic update to the current state, but to some arbitrary earlier - * state. Which could break assumptions the driver's ->atomic_check likely - * relies on. - * - * Hence we must clear all cached state and completely start over, using this - * function. + * Default implementation for clearing atomic state. + * This is useful for drivers that subclass the atomic state. */ -void drm_atomic_state_clear(struct drm_atomic_state *state) +void drm_atomic_state_default_clear(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; struct drm_mode_config *config = &dev->mode_config; @@ -162,6 +185,32 @@ void drm_atomic_state_clear(struct drm_atomic_state *state) state->plane_states[i] = NULL; } } +EXPORT_SYMBOL(drm_atomic_state_default_clear); + +/** + * drm_atomic_state_clear - clear state object + * @state: atomic state + * + * When the w/w mutex algorithm detects a deadlock we need to back off and drop + * all locks. So someone else could sneak in and change the current modeset + * configuration. Which means that all the state assembled in @state is no + * longer an atomic update to the current state, but to some arbitrary earlier + * state. Which could break assumptions the driver's ->atomic_check likely + * relies on. + * + * Hence we must clear all cached state and completely start over, using this + * function. + */ +void drm_atomic_state_clear(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (config->funcs->atomic_state_clear) + config->funcs->atomic_state_clear(state); + else + drm_atomic_state_default_clear(state); +} EXPORT_SYMBOL(drm_atomic_state_clear); /** @@ -173,14 +222,25 @@ EXPORT_SYMBOL(drm_atomic_state_clear); */ void drm_atomic_state_free(struct drm_atomic_state *state) { + struct drm_device *dev; + struct drm_mode_config *config; + if (!state) return; + dev = state->dev; + config = &dev->mode_config; + drm_atomic_state_clear(state); DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state); - kfree_state(state); + if (config->funcs->atomic_state_free) { + config->funcs->atomic_state_free(state); + } else { + drm_atomic_state_default_release(state); + kfree(state); + } } EXPORT_SYMBOL(drm_atomic_state_free); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index d78543067700..f0d3a7387d99 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -35,6 +35,11 @@ drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); void drm_atomic_state_free(struct drm_atomic_state *state); +int __must_check +drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state); +void drm_atomic_state_default_clear(struct drm_atomic_state *state); +void drm_atomic_state_default_release(struct drm_atomic_state *state); + struct drm_crtc_state * __must_check drm_atomic_get_crtc_state(struct drm_atomic_state *state, struct drm_crtc *crtc); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 5626191f3af0..37c44f27cb9f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -979,6 +979,9 @@ struct drm_mode_set { * @atomic_check: check whether a given atomic state update is possible * @atomic_commit: commit an atomic state update previously verified with * atomic_check() + * @atomic_state_alloc: allocate a new atomic state + * @atomic_state_clear: clear the atomic state + * @atomic_state_free: free the atomic state * * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that * involve drivers. @@ -994,6 +997,9 @@ struct drm_mode_config_funcs { int (*atomic_commit)(struct drm_device *dev, struct drm_atomic_state *a, bool async); + struct drm_atomic_state *(*atomic_state_alloc)(struct drm_device *dev); + void (*atomic_state_clear)(struct drm_atomic_state *state); + void (*atomic_state_free)(struct drm_atomic_state *state); }; /** -- cgit v1.2.3 From 29a5d3eb9a7612b26ba098a0db65e54372612d07 Mon Sep 17 00:00:00 2001 From: Andrew Lewycky Date: Sun, 7 Dec 2014 17:05:11 +0200 Subject: drm/amdkfd: add events IOCTL set definitions - AMDKFD_IOC_CREATE_EVENT: Creates a new event of a specified type - AMDKFD_IOC_DESTROY_EVENT: Destroys an existing event - AMDKFD_IOC_SET_EVENT: Signal an existing event - AMDKFD_IOC_RESET_EVENT: Reset an existing event - AMDKFD_IOC_WAIT_EVENTS: Wait on event(s) until they are signaled v2: - Move the limit of the signal events to kfd_ioctl.h so it can be used by userspace v3: - Change all bool fields in struct kfd_memory_exception_failure to uint32_t Signed-off-by: Andrew Lewycky Signed-off-by: Oded Gabbay --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 45 +++++++++++++++ include/uapi/linux/kfd_ioctl.h | 96 +++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 19a4fba46e4e..9933b2efe5dd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -514,6 +514,36 @@ static int kfd_ioctl_get_process_apertures(struct file *filp, return 0; } +static int kfd_ioctl_create_event(struct file *filp, struct kfd_process *p, + void *data) +{ + return -ENODEV; +} + +static int kfd_ioctl_destroy_event(struct file *filp, struct kfd_process *p, + void *data) +{ + return -ENODEV; +} + +static int kfd_ioctl_set_event(struct file *filp, struct kfd_process *p, + void *data) +{ + return -ENODEV; +} + +static int kfd_ioctl_reset_event(struct file *filp, struct kfd_process *p, + void *data) +{ + return -ENODEV; +} + +static int kfd_ioctl_wait_events(struct file *filp, struct kfd_process *p, + void *data) +{ + return -ENODEV; +} + #define AMDKFD_IOCTL_DEF(ioctl, _func, _flags) \ [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl} @@ -539,6 +569,21 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { AMDKFD_IOCTL_DEF(AMDKFD_IOC_UPDATE_QUEUE, kfd_ioctl_update_queue, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_CREATE_EVENT, + kfd_ioctl_create_event, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_DESTROY_EVENT, + kfd_ioctl_destroy_event, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_SET_EVENT, + kfd_ioctl_set_event, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_RESET_EVENT, + kfd_ioctl_reset_event, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_WAIT_EVENTS, + kfd_ioctl_wait_events, 0), }; #define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls) diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index af94f31e33ac..4ca35a8f9891 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -27,7 +27,7 @@ #include #define KFD_IOCTL_MAJOR_VERSION 1 -#define KFD_IOCTL_MINOR_VERSION 0 +#define KFD_IOCTL_MINOR_VERSION 1 struct kfd_ioctl_get_version_args { uint32_t major_version; /* from KFD */ @@ -128,6 +128,83 @@ struct kfd_ioctl_get_process_apertures_args { uint32_t pad; }; +/* Matching HSA_EVENTTYPE */ +#define KFD_IOC_EVENT_SIGNAL 0 +#define KFD_IOC_EVENT_NODECHANGE 1 +#define KFD_IOC_EVENT_DEVICESTATECHANGE 2 +#define KFD_IOC_EVENT_HW_EXCEPTION 3 +#define KFD_IOC_EVENT_SYSTEM_EVENT 4 +#define KFD_IOC_EVENT_DEBUG_EVENT 5 +#define KFD_IOC_EVENT_PROFILE_EVENT 6 +#define KFD_IOC_EVENT_QUEUE_EVENT 7 +#define KFD_IOC_EVENT_MEMORY 8 + +#define KFD_IOC_WAIT_RESULT_COMPLETE 0 +#define KFD_IOC_WAIT_RESULT_TIMEOUT 1 +#define KFD_IOC_WAIT_RESULT_FAIL 2 + +#define KFD_SIGNAL_EVENT_LIMIT 256 + +struct kfd_ioctl_create_event_args { + uint64_t event_page_offset; /* from KFD */ + uint32_t event_trigger_data; /* from KFD - signal events only */ + uint32_t event_type; /* to KFD */ + uint32_t auto_reset; /* to KFD */ + uint32_t node_id; /* to KFD - only valid for certain + event types */ + uint32_t event_id; /* from KFD */ + uint32_t event_slot_index; /* from KFD */ +}; + +struct kfd_ioctl_destroy_event_args { + uint32_t event_id; /* to KFD */ + uint32_t pad; +}; + +struct kfd_ioctl_set_event_args { + uint32_t event_id; /* to KFD */ + uint32_t pad; +}; + +struct kfd_ioctl_reset_event_args { + uint32_t event_id; /* to KFD */ + uint32_t pad; +}; + +struct kfd_memory_exception_failure { + uint32_t NotPresent; /* Page not present or supervisor privilege */ + uint32_t ReadOnly; /* Write access to a read-only page */ + uint32_t NoExecute; /* Execute access to a page marked NX */ + uint32_t pad; +}; + +/* memory exception data*/ +struct kfd_hsa_memory_exception_data { + struct kfd_memory_exception_failure failure; + uint64_t va; + uint32_t gpu_id; + uint32_t pad; +}; + +/* Event data*/ +struct kfd_event_data { + union { + struct kfd_hsa_memory_exception_data memory_exception_data; + }; /* From KFD */ + uint64_t kfd_event_data_ext; /* pointer to an extension structure + for future exception types */ + uint32_t event_id; /* to KFD */ + uint32_t pad; +}; + +struct kfd_ioctl_wait_events_args { + uint64_t events_ptr; /* to KFD */ + uint32_t num_events; /* to KFD */ + uint32_t wait_for_all; /* to KFD */ + uint32_t timeout; /* to KFD */ + uint32_t wait_result; /* from KFD */ +}; + #define AMDKFD_IOCTL_BASE 'K' #define AMDKFD_IO(nr) _IO(AMDKFD_IOCTL_BASE, nr) #define AMDKFD_IOR(nr, type) _IOR(AMDKFD_IOCTL_BASE, nr, type) @@ -155,7 +232,22 @@ struct kfd_ioctl_get_process_apertures_args { #define AMDKFD_IOC_UPDATE_QUEUE \ AMDKFD_IOW(0x07, struct kfd_ioctl_update_queue_args) +#define AMDKFD_IOC_CREATE_EVENT \ + AMDKFD_IOWR(0x08, struct kfd_ioctl_create_event_args) + +#define AMDKFD_IOC_DESTROY_EVENT \ + AMDKFD_IOW(0x09, struct kfd_ioctl_destroy_event_args) + +#define AMDKFD_IOC_SET_EVENT \ + AMDKFD_IOW(0x0A, struct kfd_ioctl_set_event_args) + +#define AMDKFD_IOC_RESET_EVENT \ + AMDKFD_IOW(0x0B, struct kfd_ioctl_reset_event_args) + +#define AMDKFD_IOC_WAIT_EVENTS \ + AMDKFD_IOWR(0x0C, struct kfd_ioctl_wait_events_args) + #define AMDKFD_COMMAND_START 0x01 -#define AMDKFD_COMMAND_END 0x08 +#define AMDKFD_COMMAND_END 0x0D #endif -- cgit v1.2.3 From ee87697f8bc4da0aea6fe1a825c734fb5e4a5b3b Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 15 May 2015 19:43:56 +0100 Subject: drm/i915/bxt: Update the Broxton PCI ids Cc: Imre Deak Signed-off-by: Damien Lespiau Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- include/drm/i915_pciids.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index bd0d644d2a03..17c445612e01 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -286,11 +286,9 @@ INTEL_SKL_GT2_IDS(info), \ INTEL_SKL_GT3_IDS(info) - #define INTEL_BXT_IDS(info) \ INTEL_VGA_DEVICE(0x0A84, info), \ - INTEL_VGA_DEVICE(0x0A85, info), \ - INTEL_VGA_DEVICE(0x0A86, info), \ - INTEL_VGA_DEVICE(0x0A87, info) + INTEL_VGA_DEVICE(0x1A84, info), \ + INTEL_VGA_DEVICE(0x5A84, info) #endif /* _I915_PCIIDS_H */ -- cgit v1.2.3 From de28d0212a3ea7b92a9795cd0e5bfc7d15214677 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 19 May 2015 16:41:01 +0200 Subject: drm/atomic: add commit_planes_on_crtc helper drm_atomic_helper_commit_planes calls all atomic_begin's first, then updates all planes, finally calling atomic_flush. Some drivers may want to things like disabling irq's from their atomic_begin, in which case a second call to atomic_begin will splat. By using commit_planes_on_crtc on each crtc in the atomic state they'll evade that issue. Signed-off-by: Maarten Lankhorst [danvet: Extend kerneldoc a bit as discussed with Maarten on irc.] [danvet: Squash in fixup to check for crtc_funcs in all places. Reported by Dan Carpenter.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 62 +++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic_helper.h | 1 + 2 files changed, 63 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index b82ef6262469..424a98bfa686 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1127,6 +1127,10 @@ EXPORT_SYMBOL(drm_atomic_helper_prepare_planes); * * It still requires the global state object @old_state to know which planes and * crtcs need to be updated though. + * + * Note that this function does all plane updates across all CRTCs in one step. + * If the hardware can't support this approach look at + * drm_atomic_helper_commit_planes_on_crtc() instead. */ void drm_atomic_helper_commit_planes(struct drm_device *dev, struct drm_atomic_state *old_state) @@ -1180,6 +1184,64 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, } EXPORT_SYMBOL(drm_atomic_helper_commit_planes); +/** + * drm_atomic_helper_commit_planes_on_crtc - commit plane state for a crtc + * @old_crtc_state: atomic state object with the old crtc state + * + * This function commits the new plane state using the plane and atomic helper + * functions for planes on the specific crtc. It assumes that the atomic state + * has already been pushed into the relevant object state pointers, since this + * step can no longer fail. + * + * This function is useful when plane updates should be done crtc-by-crtc + * instead of one global step like drm_atomic_helper_commit_planes() does. + * + * This function can only be savely used when planes are not allowed to move + * between different CRTCs because this function doesn't handle inter-CRTC + * depencies. Callers need to ensure that either no such depencies exist, + * resolve them through ordering of commit calls or through some other means. + */ +void +drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state) +{ + const struct drm_crtc_helper_funcs *crtc_funcs; + struct drm_crtc *crtc = old_crtc_state->crtc; + struct drm_atomic_state *old_state = old_crtc_state->state; + struct drm_plane *plane; + unsigned plane_mask; + + plane_mask = old_crtc_state->plane_mask; + plane_mask |= crtc->state->plane_mask; + + crtc_funcs = crtc->helper_private; + if (crtc_funcs && crtc_funcs->atomic_begin) + crtc_funcs->atomic_begin(crtc); + + drm_for_each_plane_mask(plane, crtc->dev, plane_mask) { + struct drm_plane_state *old_plane_state = + drm_atomic_get_existing_plane_state(old_state, plane); + const struct drm_plane_helper_funcs *plane_funcs; + + plane_funcs = plane->helper_private; + + if (!old_plane_state || !plane_funcs) + continue; + + WARN_ON(plane->state->crtc && plane->state->crtc != crtc); + + if (drm_atomic_plane_disabling(plane, old_plane_state) && + plane_funcs->atomic_disable) + plane_funcs->atomic_disable(plane, old_plane_state); + else if (plane->state->crtc || + drm_atomic_plane_disabling(plane, old_plane_state)) + plane_funcs->atomic_update(plane, old_plane_state); + } + + if (crtc_funcs && crtc_funcs->atomic_flush) + crtc_funcs->atomic_flush(crtc); +} +EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); + /** * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit * @dev: DRM device diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 6ee0ee5b6143..cc1fee8a12d0 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -58,6 +58,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_helper_cleanup_planes(struct drm_device *dev, struct drm_atomic_state *old_state); +void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state); void drm_atomic_helper_swap_state(struct drm_device *dev, struct drm_atomic_state *state); -- cgit v1.2.3 From e01e9f75a0c4e6cdbf1f139e37e9161408e49b7c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 19 May 2015 16:41:02 +0200 Subject: drm/atomic: add drm_atomic_add_affected_planes This is a convenience function to add all planes for a crtc, similar to add_affected_connectors. This will be used in drm_atomic_helper_check_modeset, but drivers can call it too when they need to recalculate all state. Signed-off-by: Maarten Lankhorst [danvet: Amend kerneldoc a bit.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 39 +++++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 4 ++++ 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index cd1b16b25716..3134f506b762 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -955,6 +955,45 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_add_affected_connectors); +/** + * drm_atomic_add_affected_planes - add planes for crtc + * @state: atomic state + * @crtc: DRM crtc + * + * This function walks the current configuration and adds all planes + * currently used by @crtc to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled plane on + * @crtc, e.g. when changing the mode. It's also useful when re-enabling a CRTC + * to avoid special code to force-enable all planes. + * + * Since acquiring a plane state will always also acquire the w/w mutex of the + * current CRTC for that plane (if there is any) adding all the plane states for + * a CRTC will not reduce parallism of atomic updates. + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_affected_planes(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + struct drm_plane *plane; + + WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); + + drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); + + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + } + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_planes); + /** * drm_atomic_connectors_for_crtc - count number of connected outputs * @state: atomic state diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f0d3a7387d99..e89db0c377ba 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -120,6 +120,10 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, int __must_check drm_atomic_add_affected_connectors(struct drm_atomic_state *state, struct drm_crtc *crtc); +int __must_check +drm_atomic_add_affected_planes(struct drm_atomic_state *state, + struct drm_crtc *crtc); + int drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, struct drm_crtc *crtc); -- cgit v1.2.3 From 862e686ce40299250d21ba425ea5078e947397ff Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 21 May 2015 11:03:16 +0530 Subject: drm: bridge: Allow daisy chaining of bridges Allow drm_bridge objects to link to each other in order to form an encoder chain. The requirement for creating a chain of bridges comes because the MSM drm driver uses up its encoder and bridge objects for blocks within the SoC itself. There isn't anything left to use if the SoC display output is connected to an external encoder IC. Having an additional bridge connected to the existing bridge helps here. In general, it is possible for platforms to have multiple devices between the encoder and the connector/panel that require some sort of configuration. We create drm bridge helper functions corresponding to each op in 'drm_bridge_funcs'. These helpers call the corresponding 'drm_bridge_funcs' op for the entire chain of bridges. These helpers are used internally by drm_atomic_helper.c and drm_crtc_helper.c. The drm_bridge_enable/pre_enable helpers execute enable/pre_enable ops of the bridge closet to the encoder, and proceed until the last bridge in the chain is enabled. The same holds for drm_bridge_mode_set/mode_fixup helpers. The drm_bridge_disable/post_disable helpers disable the last bridge in the chain first, and proceed until the first bridge in the chain is disabled. drm_bridge_attach() remains the same. As before, the driver calling this function should make sure it has set the links correctly. The order in which the bridges are connected to each other determines the order in which the calls are made. One requirement is that every bridge in the chain should point the parent encoder object. This is required since bridge drivers expect a valid encoder pointer in drm_bridge. For example, consider a chain where an encoder's output is connected to bridge1, and bridge1's output is connected to bridge2: /* Like before, attach bridge to an encoder */ bridge1->encoder = encoder; ret = drm_bridge_attach(dev, bridge1); .. /* * set the first bridge's 'next' bridge to bridge2, set its encoder * as bridge1's encoder */ bridge1->next = bridge2 bridge2->encoder = bridge1->encoder; ret = drm_bridge_attach(dev, bridge2); ... ... This method of bridge chaining isn't intrusive and existing drivers that use drm_bridge will behave the same way as before. The bridge helpers also cleans up the atomic and crtc helper files a bit. Reviewed-by: Jani Nikula Reviewed-by: Rob Clark Reviewed-by: Daniel Vetter Signed-off-by: Archit Taneja Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 29 +++---- drivers/gpu/drm/drm_bridge.c | 147 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc_helper.c | 54 +++++-------- include/drm/drm_crtc.h | 14 ++++ 4 files changed, 191 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 21259e70adee..a64bacdcf263 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -283,14 +283,11 @@ mode_fixup(struct drm_atomic_state *state) if (!funcs) continue; - if (encoder->bridge && encoder->bridge->funcs->mode_fixup) { - ret = encoder->bridge->funcs->mode_fixup( - encoder->bridge, &crtc_state->mode, - &crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); - return -EINVAL; - } + ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode, + &crtc_state->adjusted_mode); + if (!ret) { + DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); + return -EINVAL; } if (funcs->atomic_check) { @@ -587,8 +584,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice. */ - if (encoder->bridge) - encoder->bridge->funcs->disable(encoder->bridge); + drm_bridge_disable(encoder->bridge); /* Right function depends upon target state. */ if (connector->state->crtc && funcs->prepare) @@ -598,8 +594,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) else funcs->dpms(encoder, DRM_MODE_DPMS_OFF); - if (encoder->bridge) - encoder->bridge->funcs->post_disable(encoder->bridge); + drm_bridge_post_disable(encoder->bridge); } for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { @@ -741,9 +736,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) if (funcs->mode_set) funcs->mode_set(encoder, mode, adjusted_mode); - if (encoder->bridge && encoder->bridge->funcs->mode_set) - encoder->bridge->funcs->mode_set(encoder->bridge, - mode, adjusted_mode); + drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); } } @@ -839,16 +832,14 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice. */ - if (encoder->bridge) - encoder->bridge->funcs->pre_enable(encoder->bridge); + drm_bridge_pre_enable(encoder->bridge); if (funcs->enable) funcs->enable(encoder); else funcs->commit(encoder); - if (encoder->bridge) - encoder->bridge->funcs->enable(encoder->bridge); + drm_bridge_enable(encoder->bridge); } } EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index eaa5790c2a6f..c3a85ce5a7c4 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -66,6 +66,153 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_attach); +/** + * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the + * encoder chain + * @bridge: bridge control structure + * @mode: desired mode to be set for the bridge + * @adjusted_mode: updated mode that works for this bridge + * + * Calls 'mode_fixup' drm_bridge_funcs op for all the bridges in the + * encoder chain, starting from the first bridge to the last. + * + * Note: the bridge passed should be the one closest to the encoder + * + * RETURNS: + * true on success, false on failure + */ +bool drm_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + bool ret = true; + + if (!bridge) + return true; + + if (bridge->funcs->mode_fixup) + ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode); + + ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode); + + return ret; +} +EXPORT_SYMBOL(drm_bridge_mode_fixup); + +/** + * drm_bridge_disable - calls 'disable' drm_bridge_funcs op for all + * bridges in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'disable' drm_bridge_funcs op for all the bridges in the encoder + * chain, starting from the last bridge to the first. These are called before + * calling the encoder's prepare op. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_disable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + drm_bridge_disable(bridge->next); + + bridge->funcs->disable(bridge); +} +EXPORT_SYMBOL(drm_bridge_disable); + +/** + * drm_bridge_post_disable - calls 'post_disable' drm_bridge_funcs op for + * all bridges in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'post_disable' drm_bridge_funcs op for all the bridges in the + * encoder chain, starting from the first bridge to the last. These are called + * after completing the encoder's prepare op. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_post_disable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + bridge->funcs->post_disable(bridge); + + drm_bridge_post_disable(bridge->next); +} +EXPORT_SYMBOL(drm_bridge_post_disable); + +/** + * drm_bridge_mode_set - set proposed mode for all bridges in the + * encoder chain + * @bridge: bridge control structure + * @mode: desired mode to be set for the bridge + * @adjusted_mode: updated mode that works for this bridge + * + * Calls 'mode_set' drm_bridge_funcs op for all the bridges in the + * encoder chain, starting from the first bridge to the last. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (!bridge) + return; + + if (bridge->funcs->mode_set) + bridge->funcs->mode_set(bridge, mode, adjusted_mode); + + drm_bridge_mode_set(bridge->next, mode, adjusted_mode); +} +EXPORT_SYMBOL(drm_bridge_mode_set); + +/** + * drm_bridge_pre_enable - calls 'pre_enable' drm_bridge_funcs op for all + * bridges in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'pre_enable' drm_bridge_funcs op for all the bridges in the encoder + * chain, starting from the last bridge to the first. These are called + * before calling the encoder's commit op. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_pre_enable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + drm_bridge_pre_enable(bridge->next); + + bridge->funcs->pre_enable(bridge); +} +EXPORT_SYMBOL(drm_bridge_pre_enable); + +/** + * drm_bridge_enable - calls 'enable' drm_bridge_funcs op for all bridges + * in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'enable' drm_bridge_funcs op for all the bridges in the encoder + * chain, starting from the first bridge to the last. These are called + * after completing the encoder's commit op. + * + * Note that the bridge passed should be the one closest to the encoder + */ +void drm_bridge_enable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + bridge->funcs->enable(bridge); + + drm_bridge_enable(bridge->next); +} +EXPORT_SYMBOL(drm_bridge_enable); + #ifdef CONFIG_OF struct drm_bridge *of_drm_find_bridge(struct device_node *np) { diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index d727b73fba3a..f2f42b17967a 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -163,16 +163,14 @@ drm_encoder_disable(struct drm_encoder *encoder) { const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - if (encoder->bridge) - encoder->bridge->funcs->disable(encoder->bridge); + drm_bridge_disable(encoder->bridge); if (encoder_funcs->disable) (*encoder_funcs->disable)(encoder); else (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); - if (encoder->bridge) - encoder->bridge->funcs->post_disable(encoder->bridge); + drm_bridge_post_disable(encoder->bridge); } static void __drm_helper_disable_unused_functions(struct drm_device *dev) @@ -312,13 +310,11 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (encoder->crtc != crtc) continue; - if (encoder->bridge && encoder->bridge->funcs->mode_fixup) { - ret = encoder->bridge->funcs->mode_fixup( - encoder->bridge, mode, adjusted_mode); - if (!ret) { - DRM_DEBUG_KMS("Bridge fixup failed\n"); - goto done; - } + ret = drm_bridge_mode_fixup(encoder->bridge, + mode, adjusted_mode); + if (!ret) { + DRM_DEBUG_KMS("Bridge fixup failed\n"); + goto done; } encoder_funcs = encoder->helper_private; @@ -343,15 +339,13 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (encoder->crtc != crtc) continue; - if (encoder->bridge) - encoder->bridge->funcs->disable(encoder->bridge); + drm_bridge_disable(encoder->bridge); encoder_funcs = encoder->helper_private; /* Disable the encoders as the first thing we do. */ encoder_funcs->prepare(encoder); - if (encoder->bridge) - encoder->bridge->funcs->post_disable(encoder->bridge); + drm_bridge_post_disable(encoder->bridge); } drm_crtc_prepare_encoders(dev); @@ -376,9 +370,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, encoder_funcs = encoder->helper_private; encoder_funcs->mode_set(encoder, mode, adjusted_mode); - if (encoder->bridge && encoder->bridge->funcs->mode_set) - encoder->bridge->funcs->mode_set(encoder->bridge, mode, - adjusted_mode); + drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ @@ -389,14 +381,12 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, if (encoder->crtc != crtc) continue; - if (encoder->bridge) - encoder->bridge->funcs->pre_enable(encoder->bridge); + drm_bridge_pre_enable(encoder->bridge); encoder_funcs = encoder->helper_private; encoder_funcs->commit(encoder); - if (encoder->bridge) - encoder->bridge->funcs->enable(encoder->bridge); + drm_bridge_enable(encoder->bridge); } /* Calculate and store various constants which @@ -735,23 +725,19 @@ static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) struct drm_bridge *bridge = encoder->bridge; const struct drm_encoder_helper_funcs *encoder_funcs; - if (bridge) { - if (mode == DRM_MODE_DPMS_ON) - bridge->funcs->pre_enable(bridge); - else - bridge->funcs->disable(bridge); - } + if (mode == DRM_MODE_DPMS_ON) + drm_bridge_pre_enable(bridge); + else + drm_bridge_disable(bridge); encoder_funcs = encoder->helper_private; if (encoder_funcs->dpms) encoder_funcs->dpms(encoder, mode); - if (bridge) { - if (mode == DRM_MODE_DPMS_ON) - bridge->funcs->enable(bridge); - else - bridge->funcs->post_disable(bridge); - } + if (mode == DRM_MODE_DPMS_ON) + drm_bridge_enable(bridge); + else + drm_bridge_post_disable(bridge); } static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index bff25b0cada9..dace1b635685 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -903,6 +903,8 @@ struct drm_bridge_funcs { /** * struct drm_bridge - central DRM bridge control structure * @dev: DRM device this bridge belongs to + * @encoder: encoder to which this bridge is connected + * @next: the next bridge in the encoder chain * @of_node: device node pointer to the bridge * @list: to keep track of all added bridges * @base: base mode object @@ -912,6 +914,7 @@ struct drm_bridge_funcs { struct drm_bridge { struct drm_device *dev; struct drm_encoder *encoder; + struct drm_bridge *next; #ifdef CONFIG_OF struct device_node *of_node; #endif @@ -1247,6 +1250,17 @@ extern void drm_bridge_remove(struct drm_bridge *bridge); extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge); +bool drm_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void drm_bridge_disable(struct drm_bridge *bridge); +void drm_bridge_post_disable(struct drm_bridge *bridge); +void drm_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void drm_bridge_pre_enable(struct drm_bridge *bridge); +void drm_bridge_enable(struct drm_bridge *bridge); + extern int drm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, const struct drm_encoder_funcs *funcs, -- cgit v1.2.3 From 934a8a899a7275ed187810fe9a15a93397e88c6b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 22 May 2015 13:34:48 +0100 Subject: drm/mode: Unstatic kernel-userspace mode conversion Move the drm_display_mode <-> drm_mode_modeinfo conversion functions from drm_crtc.c to drm_modes.c, and make them non-static so that others can use them. Signed-off-by: Daniel Stone Tested-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 95 ++------------------------------------------- drivers/gpu/drm/drm_modes.c | 87 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_modes.h | 4 ++ 3 files changed, 95 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index baad4e856006..f6b0332aad7f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1735,93 +1735,6 @@ void drm_reinit_primary_mode_group(struct drm_device *dev) } EXPORT_SYMBOL(drm_reinit_primary_mode_group); -/** - * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo - * @out: drm_mode_modeinfo struct to return to the user - * @in: drm_display_mode to use - * - * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to - * the user. - */ -static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, - const struct drm_display_mode *in) -{ - WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || - in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || - in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || - in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || - in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX, - "timing values too large for mode info\n"); - - out->clock = in->clock; - out->hdisplay = in->hdisplay; - out->hsync_start = in->hsync_start; - out->hsync_end = in->hsync_end; - out->htotal = in->htotal; - out->hskew = in->hskew; - out->vdisplay = in->vdisplay; - out->vsync_start = in->vsync_start; - out->vsync_end = in->vsync_end; - out->vtotal = in->vtotal; - out->vscan = in->vscan; - out->vrefresh = in->vrefresh; - out->flags = in->flags; - out->type = in->type; - strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); - out->name[DRM_DISPLAY_MODE_LEN-1] = 0; -} - -/** - * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode - * @out: drm_display_mode to return to the user - * @in: drm_mode_modeinfo to use - * - * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to - * the caller. - * - * Returns: - * Zero on success, negative errno on failure. - */ -static int drm_crtc_convert_umode(struct drm_display_mode *out, - const struct drm_mode_modeinfo *in) -{ - int ret = -EINVAL; - - if (in->clock > INT_MAX || in->vrefresh > INT_MAX) { - ret = -ERANGE; - goto out; - } - - if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) - goto out; - - out->clock = in->clock; - out->hdisplay = in->hdisplay; - out->hsync_start = in->hsync_start; - out->hsync_end = in->hsync_end; - out->htotal = in->htotal; - out->hskew = in->hskew; - out->vdisplay = in->vdisplay; - out->vsync_start = in->vsync_start; - out->vsync_end = in->vsync_end; - out->vtotal = in->vtotal; - out->vscan = in->vscan; - out->vrefresh = in->vrefresh; - out->flags = in->flags; - out->type = in->type; - strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); - out->name[DRM_DISPLAY_MODE_LEN-1] = 0; - - out->status = drm_mode_validate_basic(out); - if (out->status != MODE_OK) - goto out; - - ret = 0; - -out: - return ret; -} - /** * drm_mode_getresources - get graphics configuration * @dev: drm device for the ioctl @@ -2048,7 +1961,7 @@ int drm_mode_getcrtc(struct drm_device *dev, crtc_resp->x = crtc->primary->state->src_x >> 16; crtc_resp->y = crtc->primary->state->src_y >> 16; if (crtc->state->enable) { - drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); + drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); crtc_resp->mode_valid = 1; } else { @@ -2058,7 +1971,7 @@ int drm_mode_getcrtc(struct drm_device *dev, crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; if (crtc->enabled) { - drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); + drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode); crtc_resp->mode_valid = 1; } else { @@ -2215,7 +2128,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, if (!drm_mode_expose_to_userspace(mode, file_priv)) continue; - drm_crtc_convert_to_umode(&u_mode, mode); + drm_mode_convert_to_umode(&u_mode, mode); if (copy_to_user(mode_ptr + copied, &u_mode, sizeof(u_mode))) { ret = -EFAULT; @@ -2826,7 +2739,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } - ret = drm_crtc_convert_umode(mode, &crtc_req->mode); + ret = drm_mode_convert_umode(mode, &crtc_req->mode); if (ret) { DRM_DEBUG_KMS("Invalid mode\n"); goto out; diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 213b11ea69b5..cd74a0953f42 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1405,3 +1405,90 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, return mode; } EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode); + +/** + * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo + * @out: drm_mode_modeinfo struct to return to the user + * @in: drm_display_mode to use + * + * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to + * the user. + */ +void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out, + const struct drm_display_mode *in) +{ + WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || + in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || + in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || + in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || + in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX, + "timing values too large for mode info\n"); + + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + out->type = in->type; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode + * @out: drm_display_mode to return to the user + * @in: drm_mode_modeinfo to use + * + * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to + * the caller. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_convert_umode(struct drm_display_mode *out, + const struct drm_mode_modeinfo *in) +{ + int ret = -EINVAL; + + if (in->clock > INT_MAX || in->vrefresh > INT_MAX) { + ret = -ERANGE; + goto out; + } + + if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) + goto out; + + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + out->vrefresh = in->vrefresh; + out->flags = in->flags; + out->type = in->type; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; + + out->status = drm_mode_validate_basic(out); + if (out->status != MODE_OK) + goto out; + + ret = 0; + +out: + return ret; +} \ No newline at end of file diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 0616188c7801..08a8cac9e555 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -182,6 +182,10 @@ struct drm_cmdline_mode; struct drm_display_mode *drm_mode_create(struct drm_device *dev); void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); +void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out, + const struct drm_display_mode *in); +int drm_mode_convert_umode(struct drm_display_mode *out, + const struct drm_mode_modeinfo *in); void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); void drm_mode_debug_printmodeline(const struct drm_display_mode *mode); -- cgit v1.2.3 From e2f5d2ea479b9b2619965d43db70939589afe43a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 22 May 2015 13:34:51 +0100 Subject: drm/mode: Add user blob-creation ioctl Add an ioctl which allows users to create blob properties from supplied data. Currently this only supports modes, creating a drm_display_mode from the userspace drm_mode_modeinfo. v2: Removed size/type checks. Rebased on new patches to allow error propagation from create_blob, as well as avoiding double-allocation. Signed-off-by: Daniel Stone Reviewed-by: Maarten Lankhorst Tested-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 139 +++++++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_fops.c | 5 +- drivers/gpu/drm/drm_ioctl.c | 2 + include/drm/drmP.h | 4 ++ include/drm/drm_crtc.h | 9 ++- include/uapi/drm/drm.h | 2 + include/uapi/drm/drm_mode.h | 20 +++++++ 7 files changed, 176 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f661589b1dea..e548c50edc94 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4173,6 +4173,9 @@ drm_property_create_blob(struct drm_device *dev, size_t length, if (!blob) return ERR_PTR(-ENOMEM); + /* This must be explicitly initialised, so we can safely call list_del + * on it in the removal handler, even if it isn't in a file list. */ + INIT_LIST_HEAD(&blob->head_file); blob->length = length; blob->dev = dev; @@ -4190,7 +4193,8 @@ drm_property_create_blob(struct drm_device *dev, size_t length, kref_init(&blob->refcount); - list_add_tail(&blob->head, &dev->mode_config.property_blob_list); + list_add_tail(&blob->head_global, + &dev->mode_config.property_blob_list); mutex_unlock(&dev->mode_config.blob_lock); @@ -4212,7 +4216,8 @@ static void drm_property_free_blob(struct kref *kref) WARN_ON(!mutex_is_locked(&blob->dev->mode_config.blob_lock)); - list_del(&blob->head); + list_del(&blob->head_global); + list_del(&blob->head_file); drm_mode_object_put(blob->dev, &blob->base); kfree(blob); @@ -4263,6 +4268,26 @@ static void drm_property_unreference_blob_locked(struct drm_property_blob *blob) kref_put(&blob->refcount, drm_property_free_blob); } +/** + * drm_property_destroy_user_blobs - destroy all blobs created by this client + * @dev: DRM device + * @file_priv: destroy all blobs owned by this file handle + */ +void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct drm_property_blob *blob, *bt; + + mutex_lock(&dev->mode_config.blob_lock); + + list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { + list_del_init(&blob->head_file); + drm_property_unreference_blob_locked(blob); + } + + mutex_unlock(&dev->mode_config.blob_lock); +} + /** * drm_property_reference_blob - Take a reference on an existing property * @@ -4452,6 +4477,114 @@ done: return ret; } +/** + * drm_mode_createblob_ioctl - create a new blob property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function creates a new blob property with user-defined values. In order + * to give us sensible validation and checking when creating, rather than at + * every potential use, we also require a type to be provided upfront. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_createblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_create_blob *out_resp = data; + struct drm_property_blob *blob; + void __user *blob_ptr; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_create_blob(dev, out_resp->length, NULL); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + blob_ptr = (void __user *)(unsigned long)out_resp->data; + if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { + ret = -EFAULT; + goto out_blob; + } + + /* Dropping the lock between create_blob and our access here is safe + * as only the same file_priv can remove the blob; at this point, it is + * not associated with any file_priv. */ + mutex_lock(&dev->mode_config.blob_lock); + out_resp->blob_id = blob->base.id; + list_add_tail(&file_priv->blobs, &blob->head_file); + mutex_unlock(&dev->mode_config.blob_lock); + + return 0; + +out_blob: + drm_property_unreference_blob(blob); + return ret; +} + +/** + * drm_mode_destroyblob_ioctl - destroy a user blob property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Destroy an existing user-defined blob property. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_destroyblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_destroy_blob *out_resp = data; + struct drm_property_blob *blob = NULL, *bt; + bool found = false; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + mutex_lock(&dev->mode_config.blob_lock); + blob = __drm_property_lookup_blob(dev, out_resp->blob_id); + if (!blob) { + ret = -ENOENT; + goto err; + } + + /* Ensure the property was actually created by this user. */ + list_for_each_entry(bt, &file_priv->blobs, head_file) { + if (bt == blob) { + found = true; + break; + } + } + + if (!found) { + ret = -EPERM; + goto err; + } + + /* We must drop head_file here, because we may not be the last + * reference on the blob. */ + list_del_init(&blob->head_file); + drm_property_unreference_blob_locked(blob); + mutex_unlock(&dev->mode_config.blob_lock); + + return 0; + +err: + mutex_unlock(&dev->mode_config.blob_lock); + return ret; +} + /** * drm_mode_connector_set_path_property - set tile property on connector * @connector: connector to set property on. @@ -5655,7 +5788,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) } list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, - head) { + head_global) { drm_property_unreference_blob(blob); } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 0f6a5c8801e3..c59ce4d0ef75 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -167,6 +167,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); mutex_init(&priv->fbs_lock); + INIT_LIST_HEAD(&priv->blobs); INIT_LIST_HEAD(&priv->event_list); init_waitqueue_head(&priv->event_wait); priv->event_space = 4096; /* set aside 4k for event buffer */ @@ -405,8 +406,10 @@ int drm_release(struct inode *inode, struct file *filp) drm_events_release(file_priv); - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_MODESET)) { drm_fb_release(file_priv); + drm_property_destroy_user_blobs(dev, file_priv); + } if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_release(dev, file_priv); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 266dcd6cdf3b..9bac1b7479af 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -641,6 +641,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATEPROPBLOB, drm_mode_createblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROYPROPBLOB, drm_mode_destroyblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index df6d9970d9a4..9fa6366f47c2 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -326,6 +326,10 @@ struct drm_file { struct list_head fbs; struct mutex fbs_lock; + /** User-created blob properties; this retains a reference on the + * property. */ + struct list_head blobs; + wait_queue_head_t event_wait; struct list_head event_list; int event_space; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dace1b635685..72b60dbe0891 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -218,7 +218,8 @@ struct drm_property_blob { struct drm_mode_object base; struct drm_device *dev; struct kref refcount; - struct list_head head; + struct list_head head_global; + struct list_head head_file; size_t length; unsigned char data[]; }; @@ -1315,6 +1316,8 @@ extern const char *drm_get_dvi_i_select_name(int val); extern const char *drm_get_tv_subconnector_name(int val); extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); +extern void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern void drm_mode_group_destroy(struct drm_mode_group *group); extern void drm_reinit_primary_mode_group(struct drm_device *dev); @@ -1460,6 +1463,10 @@ extern int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_mode_createblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_destroyblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); extern int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_getencoder(struct drm_device *dev, diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index ff6ef62d084b..3801584a0c53 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -786,6 +786,8 @@ struct drm_prime_handle { #define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) #define DRM_IOCTL_MODE_CURSOR2 DRM_IOWR(0xBB, struct drm_mode_cursor2) #define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic) +#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob) +#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob) /** * Device specific ioctls should only be in their respective headers diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index dbeba949462a..359107ab629e 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -558,4 +558,24 @@ struct drm_mode_atomic { __u64 user_data; }; +/** + * Create a new 'blob' data property, copying length bytes from data pointer, + * and returning new blob ID. + */ +struct drm_mode_create_blob { + /** Pointer to data to copy. */ + __u64 data; + /** Length of data to copy. */ + __u32 length; + /** Return: new property ID. */ + __u32 blob_id; +}; + +/** + * Destroy a user-created blob property. + */ +struct drm_mode_destroy_blob { + __u32 blob_id; +}; + #endif -- cgit v1.2.3 From 819364da20fd914aba2fd03e95ee0467286752f5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 26 May 2015 14:36:48 +0100 Subject: drm: Add drm_atomic_set_mode_for_crtc Add a new helper, to be used later for blob property management, that sets the mode for a CRTC state, as well as updating the CRTC enable/active state at the same time. v2: Do not touch active/mode_changed in CRTC state. Document return value. Remove stray drm_atomic_set_mode_prop_for_crtc declaration. v3: Remove i915 changes, and leave it directly bashing crtc_state->mode for the meantime. Signed-off-by: Daniel Stone Tested-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_atomic_helper.c | 11 ++++++++--- drivers/gpu/drm/drm_crtc_helper.c | 5 +++-- include/drm/drm_atomic.h | 3 +++ 4 files changed, 50 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3134f506b762..e749287390a3 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -289,6 +289,42 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_crtc_state); +/** + * drm_atomic_set_mode_for_crtc - set mode for CRTC + * @state: the CRTC whose incoming state to update + * @mode: kernel-internal mode to use for the CRTC, or NULL to disable + * + * Set a mode (originating from the kernel) on the desired CRTC state. Does + * not change any other state properties, including enable, active, or + * mode_changed. + * + * RETURNS: + * Zero on success, error code on failure. Cannot return -EDEADLK. + */ +int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, + struct drm_display_mode *mode) +{ + /* Early return for no change. */ + if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) + return 0; + + if (mode) { + drm_mode_copy(&state->mode, mode); + state->enable = true; + DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", + mode->name, state); + } else { + memset(&state->mode, 0, sizeof(state->mode)); + state->enable = false; + DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", + state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); + + /** * drm_atomic_crtc_set_property - set property on CRTC * @crtc: the drm CRTC to set a property on diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index a64bacdcf263..e69d4847e237 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1607,7 +1607,10 @@ retry: WARN_ON(set->fb); WARN_ON(set->num_connectors); - crtc_state->enable = false; + ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); + if (ret != 0) + goto fail; + crtc_state->active = false; ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); @@ -1622,9 +1625,11 @@ retry: WARN_ON(!set->fb); WARN_ON(!set->num_connectors); - crtc_state->enable = true; + ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode); + if (ret != 0) + goto fail; + crtc_state->active = true; - drm_mode_copy(&crtc_state->mode, set->mode); ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); if (ret != 0) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 9297a61a7c95..393114df88a3 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -937,10 +937,11 @@ int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mod crtc_state->crtc = crtc; } - crtc_state->enable = true; crtc_state->planes_changed = true; crtc_state->mode_changed = true; - drm_mode_copy(&crtc_state->mode, mode); + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret) + goto out; drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode); if (crtc_funcs->atomic_check) { diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index e89db0c377ba..1e8c61f23294 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -109,6 +109,9 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, return state->connector_states[index]; } +int __must_check +drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, + struct drm_display_mode *mode); int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, struct drm_crtc *crtc); -- cgit v1.2.3 From 99cf4a29fa24461bbfe22125967188a18383eb5c Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 25 May 2015 19:11:51 +0100 Subject: drm/atomic: Add current-mode blob to CRTC state Add a blob property tracking the current mode to the CRTC state, and ensure it is properly updated and referenced. v2: Continue using crtc_state->mode inside getcrtc, instead of reading out the mode blob. Use IS_ERR and PTR_ERR from create_blob. Move set_mode_prop_for_crtc to later patch where it actually gets used. Enforce !!state->enable == !!state->mode_blob inside drm_atomic_crtc_check. Signed-off-by: Daniel Stone Tested-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 32 +++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_atomic_helper.c | 11 ++++++----- include/drm/drm_crtc.h | 3 +++ 3 files changed, 40 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index e749287390a3..327ccc71012f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -304,11 +304,25 @@ EXPORT_SYMBOL(drm_atomic_get_crtc_state); int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, struct drm_display_mode *mode) { + struct drm_mode_modeinfo umode; + /* Early return for no change. */ if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) return 0; + if (state->mode_blob) + drm_property_unreference_blob(state->mode_blob); + state->mode_blob = NULL; + if (mode) { + drm_mode_convert_to_umode(&umode, mode); + state->mode_blob = + drm_property_create_blob(state->crtc->dev, + sizeof(umode), + &umode); + if (IS_ERR(state->mode_blob)) + return PTR_ERR(state->mode_blob); + drm_mode_copy(&state->mode, mode); state->enable = true; DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", @@ -324,7 +338,6 @@ int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, } EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); - /** * drm_atomic_crtc_set_property - set property on CRTC * @crtc: the drm CRTC to set a property on @@ -410,6 +423,23 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc, return -EINVAL; } + /* The state->enable vs. state->mode_blob checks can be WARN_ON, + * as this is a kernel-internal detail that userspace should never + * be able to trigger. */ + if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && + WARN_ON(state->enable && !state->mode_blob)) { + DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n", + crtc->base.id); + return -EINVAL; + } + + if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && + WARN_ON(!state->enable && state->mode_blob)) { + DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n", + crtc->base.id); + return -EINVAL; + } + return 0; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e69d4847e237..a900858fa265 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2044,6 +2044,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms); */ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) { + if (crtc->state && crtc->state->mode_blob) + drm_property_unreference_blob(crtc->state->mode_blob); kfree(crtc->state); crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); @@ -2065,6 +2067,8 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, { memcpy(state, crtc->state, sizeof(*state)); + if (state->mode_blob) + drm_property_reference_blob(state->mode_blob); state->mode_changed = false; state->active_changed = false; state->planes_changed = false; @@ -2107,11 +2111,8 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { - /* - * This is currently a placeholder so that drivers that subclass the - * state will automatically do the right thing if code is ever added - * to this function. - */ + if (state->mode_blob) + drm_property_unreference_blob(state->mode_blob); } EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 72b60dbe0891..c54fa4add792 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -299,6 +299,9 @@ struct drm_crtc_state { struct drm_display_mode mode; + /* blob property to expose current mode to atomic userspace */ + struct drm_property_blob *mode_blob; + struct drm_pending_vblank_event *event; struct drm_atomic_state *state; -- cgit v1.2.3 From 955f3c334f0fb2b843efad5cc6d3b7e141e9d666 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 25 May 2015 19:11:52 +0100 Subject: drm/atomic: Add MODE_ID property Atomic modesetting: now with modesetting support. v2: Moved drm_atomic_set_mode_prop_for_crtc from previous patch; removed state->active fiddling, documented return code. Changed property type to DRM_MODE_PROP_BLOB. Signed-off-by: Daniel Stone Tested-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 57 +++++++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_crtc.c | 8 +++++++ include/drm/drm_atomic.h | 3 +++ include/drm/drm_crtc.h | 1 + 4 files changed, 68 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 327ccc71012f..c7e59b074e62 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -338,6 +338,51 @@ int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, } EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); +/** + * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC + * @state: the CRTC whose incoming state to update + * @blob: pointer to blob property to use for mode + * + * Set a mode (originating from a blob property) on the desired CRTC state. + * This function will take a reference on the blob property for the CRTC state, + * and release the reference held on the state's existing mode property, if any + * was set. + * + * RETURNS: + * Zero on success, error code on failure. Cannot return -EDEADLK. + */ +int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, + struct drm_property_blob *blob) +{ + if (blob == state->mode_blob) + return 0; + + if (state->mode_blob) + drm_property_unreference_blob(state->mode_blob); + state->mode_blob = NULL; + + if (blob) { + if (blob->length != sizeof(struct drm_mode_modeinfo) || + drm_mode_convert_umode(&state->mode, + (const struct drm_mode_modeinfo *) + blob->data)) + return -EINVAL; + + state->mode_blob = drm_property_reference_blob(blob); + state->enable = true; + DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", + state->mode.name, state); + } else { + memset(&state->mode, 0, sizeof(state->mode)); + state->enable = false; + DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", + state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); + /** * drm_atomic_crtc_set_property - set property on CRTC * @crtc: the drm CRTC to set a property on @@ -360,10 +405,18 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct drm_mode_config *config = &dev->mode_config; + int ret; - /* FIXME: Mode prop is missing, which also controls ->enable. */ if (property == config->prop_active) state->active = val; + else if (property == config->prop_mode_id) { + struct drm_property_blob *mode = + drm_property_lookup_blob(dev, val); + ret = drm_atomic_set_mode_prop_for_crtc(state, mode); + if (mode) + drm_property_unreference_blob(mode); + return ret; + } else if (crtc->funcs->atomic_set_property) return crtc->funcs->atomic_set_property(crtc, state, property, val); else @@ -388,6 +441,8 @@ int drm_atomic_crtc_get_property(struct drm_crtc *crtc, if (property == config->prop_active) *val = state->active; + else if (property == config->prop_mode_id) + *val = (state->mode_blob) ? state->mode_blob->base.id : 0; else if (crtc->funcs->atomic_get_property) return crtc->funcs->atomic_get_property(crtc, state, property, val); else diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 098c94e53fdf..77f87b23a6e7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -688,6 +688,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { drm_object_attach_property(&crtc->base, config->prop_active, 0); + drm_object_attach_property(&crtc->base, config->prop_mode_id, 0); } return 0; @@ -1454,6 +1455,13 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.prop_active = prop; + prop = drm_property_create(dev, + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB, + "MODE_ID", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_mode_id = prop; + return 0; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 1e8c61f23294..55f46049e4a0 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -113,6 +113,9 @@ int __must_check drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, struct drm_display_mode *mode); int __must_check +drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, + struct drm_property_blob *blob); +int __must_check drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, struct drm_crtc *crtc); void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c54fa4add792..3b4d8a4a23fb 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1146,6 +1146,7 @@ struct drm_mode_config { struct drm_property *prop_fb_id; struct drm_property *prop_crtc_id; struct drm_property *prop_active; + struct drm_property *prop_mode_id; /* DVI-I properties */ struct drm_property *dvi_i_subconnector_property; -- cgit v1.2.3 From 72b9076b2887add930d3b102760f09d02ffbfbe7 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Wed, 29 Apr 2015 19:40:33 +0200 Subject: drm/radeon: add a GPU reset counter queryable by userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userspace will be able to tell whether a GPU reset occured by comparing an old referece value of the counter with a new value. Reviewed-by: Christian König Signed-off-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_device.c | 2 ++ drivers/gpu/drm/radeon/radeon_drv.c | 3 ++- drivers/gpu/drm/radeon/radeon_kms.c | 3 +++ include/uapi/drm/radeon_drm.h | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 46eb0fa75a61..352870cbb8b8 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -2435,6 +2435,7 @@ struct radeon_device { atomic64_t vram_usage; atomic64_t gtt_usage; atomic64_t num_bytes_moved; + atomic_t gpu_reset_counter; /* ACPI interface */ struct radeon_atif atif; struct radeon_atcs atcs; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index b7ca4c514621..13e207e0dff0 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1725,6 +1725,8 @@ int radeon_gpu_reset(struct radeon_device *rdev) return 0; } + atomic_inc(&rdev->gpu_reset_counter); + radeon_save_bios_scratch_regs(rdev); /* block TTM */ resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 7d620d4b3f31..5751446677d3 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -90,9 +90,10 @@ * CS to GPU on >= r600 * 2.41.0 - evergreen/cayman: Add SET_BASE/DRAW_INDIRECT command parsing support * 2.42.0 - Add VCE/VUI (Video Usability Information) support + * 2.43.0 - RADEON_INFO_GPU_RESET_COUNTER */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 42 +#define KMS_DRIVER_MINOR 43 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 7b2a7335cc5d..9632e886ddc3 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -576,6 +576,9 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file if (radeon_get_allowed_info_register(rdev, *value, value)) return -EINVAL; break; + case RADEON_INFO_GPU_RESET_COUNTER: + *value = atomic_read(&rdev->gpu_reset_counter); + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 871e73f99a4d..573cb86a3d6e 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -1038,6 +1038,7 @@ struct drm_radeon_cs { #define RADEON_INFO_CURRENT_GPU_SCLK 0x22 #define RADEON_INFO_CURRENT_GPU_MCLK 0x23 #define RADEON_INFO_READ_REG 0x24 +#define RADEON_INFO_GPU_RESET_COUNTER 0x25 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 21631f10ea08a9551eb32651448baad5ef64de6c Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 26 May 2015 14:57:19 +0100 Subject: drm/i915: Fix the confusing comment about the ioctl limits It was reported that this comment was confusing, and indeed it is. v2: (one year later!) Add the range for the DRM_I915_* iotcl defines (Daniel) Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- include/uapi/drm/i915_drm.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 4851d660243c..6e1a2ed116cb 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -171,8 +171,12 @@ typedef struct _drm_i915_sarea { #define I915_BOX_TEXTURE_LOAD 0x8 #define I915_BOX_LOST_CONTEXT 0x10 -/* I915 specific ioctls - * The device specific ioctl range is 0x40 to 0x79. +/* + * i915 specific ioctls. + * + * The device specific ioctl range is [DRM_COMMAND_BASE, DRM_COMMAND_END) ie + * [0x40, 0xa0) (a0 is excluded). The numbers below are defined as offset + * against DRM_COMMAND_BASE and should be between [0x0, 0x60). */ #define DRM_I915_INIT 0x00 #define DRM_I915_FLUSH 0x01 -- cgit v1.2.3 From 60f207a5b6d8f23c2e8388b415e8d5c7311cc79d Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Mon, 25 May 2015 13:29:44 +0300 Subject: drm/atomic: fix out of bounds read in for_each_*_in_state helpers for_each_*_in_state validate array index after access to array elements, thus perform out of bounds read. Fix this by validating index in the first place and read array element iff validation was successful. Fixes: df63b9994eaf ("drm/atomic: Add for_each_{connector,crtc,plane}_in_state helper macros") Signed-off-by: Andrey Ryabinin Signed-off-by: Daniel Vetter --- include/drm/drm_atomic.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 55f46049e4a0..1bbfedf466b9 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -142,26 +142,26 @@ int __must_check drm_atomic_async_commit(struct drm_atomic_state *state); #define for_each_connector_in_state(state, connector, connector_state, __i) \ for ((__i) = 0; \ - (connector) = (state)->connectors[__i], \ - (connector_state) = (state)->connector_states[__i], \ - (__i) < (state)->num_connector; \ + (__i) < (state)->num_connector && \ + ((connector) = (state)->connectors[__i], \ + (connector_state) = (state)->connector_states[__i], 1); \ (__i)++) \ if (connector) #define for_each_crtc_in_state(state, crtc, crtc_state, __i) \ for ((__i) = 0; \ - (crtc) = (state)->crtcs[__i], \ - (crtc_state) = (state)->crtc_states[__i], \ - (__i) < (state)->dev->mode_config.num_crtc; \ + (__i) < (state)->dev->mode_config.num_crtc && \ + ((crtc) = (state)->crtcs[__i], \ + (crtc_state) = (state)->crtc_states[__i], 1); \ (__i)++) \ if (crtc_state) -#define for_each_plane_in_state(state, plane, plane_state, __i) \ - for ((__i) = 0; \ - (plane) = (state)->planes[__i], \ - (plane_state) = (state)->plane_states[__i], \ - (__i) < (state)->dev->mode_config.num_total_plane; \ - (__i)++) \ +#define for_each_plane_in_state(state, plane, plane_state, __i) \ + for ((__i) = 0; \ + (__i) < (state)->dev->mode_config.num_total_plane && \ + ((plane) = (state)->planes[__i], \ + (plane_state) = (state)->plane_states[__i], 1); \ + (__i)++) \ if (plane_state) #endif /* DRM_ATOMIC_H_ */ -- cgit v1.2.3 From aef11009c45ca594c18ecc822f101e3908ca3fb4 Mon Sep 17 00:00:00 2001 From: Yair Shachar Date: Sun, 7 Dec 2014 17:05:22 +0200 Subject: drm/amdkfd: add H/W debugger IOCTL set definitions This patch adds four new IOCTLs to amdkfd. These IOCTLs expose a H/W debugger functionality to the userspace. The IOCTLs are: - AMDKFD_IOC_DBG_REGISTER: The purpose of this IOCTL is to notify amdkfd that a process wants to use GPU debugging facilities on itself only. It is expected that this IOCTL would be called before any other H/W debugger requests are sent to amdkfd and for each GPU where the H/W debugging needs to be enabled. The use of this IOCTL ensures that only one instance of a debugger is active in the system. - AMDKFD_IOC_DBG_UNREGISTER: This IOCTL detaches the debugger/debugged process from the H/W Debug which was established by the AMDKFD_IOC_DBG_REGISTER IOCTL. - AMDKFD_IOC_DBG_ADDRESS_WATCH: This IOCTL allows to set different watchpoints with various conditions as indicated by the IOCTL's arguments. The available number of watchpoints is retrieved from topology. This operation is confined to the current debugged process, which was registered through AMDKFD_IOC_DBG_REGISTER. - AMDKFD_IOC_DBG_WAVE_CONTROL: This IOCTL allows to control a wavefront as indicated by the IOCTL's arguments. For example, you can halt/resume or kill either a single wavefront or a set of wavefronts. This operation is confined to the current debugged process, which was registered through AMDKFD_IOC_DBG_REGISTER. Because the arguments for the address watch IOCTL and wave control IOCTL are dynamic, meaning that they could vary in size, the userspace passes a pointer to a structure (in userspace) that contains the value of the arguments. The kernel driver is responsible to parse this structure and validate its contents. v2: change void* to uint64_t inside ioctl arguments Signed-off-by: Yair Shachar Signed-off-by: Oded Gabbay --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 54 ++++++++++++++++++++++++++++++++ include/uapi/linux/kfd_ioctl.h | 43 +++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index b2c6109bd7af..b358e910378f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -432,6 +432,48 @@ out: return err; } +static int kfd_ioctl_dbg_register(struct file *filep, + struct kfd_process *p, void *data) +{ + long status = -EFAULT; + + return status; +} + +static int kfd_ioctl_dbg_unrgesiter(struct file *filep, + struct kfd_process *p, void *data) +{ + long status = -EFAULT; + + return status; +} + +/* + * Parse and generate variable size data structure for address watch. + * Total size of the buffer and # watch points is limited in order + * to prevent kernel abuse. (no bearing to the much smaller HW limitation + * which is enforced by dbgdev module) + * please also note that the watch address itself are not "copied from user", + * since it be set into the HW in user mode values. + * + */ +static int kfd_ioctl_dbg_address_watch(struct file *filep, + struct kfd_process *p, void *data) +{ + long status = -EFAULT; + + return status; +} + +/* Parse and generate fixed size data structure for wave control */ +static int kfd_ioctl_dbg_wave_control(struct file *filep, + struct kfd_process *p, void *data) +{ + long status = -EFAULT; + + return status; +} + static int kfd_ioctl_get_clock_counters(struct file *filep, struct kfd_process *p, void *data) { @@ -612,6 +654,18 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { AMDKFD_IOCTL_DEF(AMDKFD_IOC_WAIT_EVENTS, kfd_ioctl_wait_events, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_DBG_REGISTER, + kfd_ioctl_dbg_register, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_DBG_UNREGISTER, + kfd_ioctl_dbg_unrgesiter, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_DBG_ADDRESS_WATCH, + kfd_ioctl_dbg_address_watch, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_DBG_WAVE_CONTROL, + kfd_ioctl_dbg_wave_control, 0), }; #define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls) diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index 4ca35a8f9891..d6833426fdef 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -128,6 +128,32 @@ struct kfd_ioctl_get_process_apertures_args { uint32_t pad; }; +#define MAX_ALLOWED_NUM_POINTS 100 +#define MAX_ALLOWED_AW_BUFF_SIZE 4096 +#define MAX_ALLOWED_WAC_BUFF_SIZE 128 + +struct kfd_ioctl_dbg_register_args { + uint32_t gpu_id; /* to KFD */ + uint32_t pad; +}; + +struct kfd_ioctl_dbg_unregister_args { + uint32_t gpu_id; /* to KFD */ + uint32_t pad; +}; + +struct kfd_ioctl_dbg_address_watch_args { + uint64_t content_ptr; /* a pointer to the actual content */ + uint32_t gpu_id; /* to KFD */ + uint32_t buf_size_in_bytes; /*including gpu_id and buf_size */ +}; + +struct kfd_ioctl_dbg_wave_control_args { + uint64_t content_ptr; /* a pointer to the actual content */ + uint32_t gpu_id; /* to KFD */ + uint32_t buf_size_in_bytes; /*including gpu_id and buf_size */ +}; + /* Matching HSA_EVENTTYPE */ #define KFD_IOC_EVENT_SIGNAL 0 #define KFD_IOC_EVENT_NODECHANGE 1 @@ -198,7 +224,8 @@ struct kfd_event_data { }; struct kfd_ioctl_wait_events_args { - uint64_t events_ptr; /* to KFD */ + uint64_t events_ptr; /* pointed to struct + kfd_event_data array, to KFD */ uint32_t num_events; /* to KFD */ uint32_t wait_for_all; /* to KFD */ uint32_t timeout; /* to KFD */ @@ -247,7 +274,19 @@ struct kfd_ioctl_wait_events_args { #define AMDKFD_IOC_WAIT_EVENTS \ AMDKFD_IOWR(0x0C, struct kfd_ioctl_wait_events_args) +#define AMDKFD_IOC_DBG_REGISTER \ + AMDKFD_IOW(0x0D, struct kfd_ioctl_dbg_register_args) + +#define AMDKFD_IOC_DBG_UNREGISTER \ + AMDKFD_IOW(0x0E, struct kfd_ioctl_dbg_unregister_args) + +#define AMDKFD_IOC_DBG_ADDRESS_WATCH \ + AMDKFD_IOW(0x0F, struct kfd_ioctl_dbg_address_watch_args) + +#define AMDKFD_IOC_DBG_WAVE_CONTROL \ + AMDKFD_IOW(0x10, struct kfd_ioctl_dbg_wave_control_args) + #define AMDKFD_COMMAND_START 0x01 -#define AMDKFD_COMMAND_END 0x0D +#define AMDKFD_COMMAND_END 0x11 #endif -- cgit v1.2.3 From dc5698e80cf724770283e10414054662bdf6ccfa Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 9 Sep 2013 10:02:56 +1000 Subject: Add virtio gpu driver. This patch adds a kms driver for the virtio gpu. The xorg modesetting driver can handle the device just fine, the framebuffer for fbcon is there too. Qemu patches for the host side are under review currently. The pci version of the device comes in two variants: with and without vga compatibility. The former has a extra memory bar for the vga framebuffer, the later is a pure virtio device. The only concern for this driver is that in the virtio-vga case we have to kick out the firmware framebuffer. Initial revision has only 2d support, 3d (virgl) support requires some more work on the qemu side and will be added later. Signed-off-by: Dave Airlie Signed-off-by: Gerd Hoffmann Acked-by: Michael S. Tsirkin --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/virtio/Kconfig | 14 + drivers/gpu/drm/virtio/Makefile | 11 + drivers/gpu/drm/virtio/virtgpu_debugfs.c | 64 ++++ drivers/gpu/drm/virtio/virtgpu_display.c | 473 ++++++++++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 91 +++++ drivers/gpu/drm/virtio/virtgpu_drv.c | 136 +++++++ drivers/gpu/drm/virtio/virtgpu_drv.h | 350 ++++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_fb.c | 431 ++++++++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_fence.c | 119 ++++++ drivers/gpu/drm/virtio/virtgpu_gem.c | 140 +++++++ drivers/gpu/drm/virtio/virtgpu_kms.c | 173 +++++++++ drivers/gpu/drm/virtio/virtgpu_object.c | 170 +++++++++ drivers/gpu/drm/virtio/virtgpu_plane.c | 120 ++++++ drivers/gpu/drm/virtio/virtgpu_ttm.c | 469 +++++++++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_vq.c | 614 +++++++++++++++++++++++++++++++ include/drm/drmP.h | 1 + include/uapi/linux/Kbuild | 1 + include/uapi/linux/virtio_gpu.h | 204 ++++++++++ include/uapi/linux/virtio_ids.h | 1 + 21 files changed, 3585 insertions(+) create mode 100644 drivers/gpu/drm/virtio/Kconfig create mode 100644 drivers/gpu/drm/virtio/Makefile create mode 100644 drivers/gpu/drm/virtio/virtgpu_debugfs.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_display.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_drm_bus.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.h create mode 100644 drivers/gpu/drm/virtio/virtgpu_fb.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_fence.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_gem.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_kms.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_object.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_plane.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_ttm.c create mode 100644 drivers/gpu/drm/virtio/virtgpu_vq.c create mode 100644 include/uapi/linux/virtio_gpu.h (limited to 'include') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 47f2ce81b412..d4b65457122d 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -206,6 +206,8 @@ source "drivers/gpu/drm/qxl/Kconfig" source "drivers/gpu/drm/bochs/Kconfig" +source "drivers/gpu/drm/virtio/Kconfig" + source "drivers/gpu/drm/msm/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4de8d9b006ec..8e75f8194bde 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_BOCHS) += bochs/ +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig new file mode 100644 index 000000000000..9983eadb81b6 --- /dev/null +++ b/drivers/gpu/drm/virtio/Kconfig @@ -0,0 +1,14 @@ +config DRM_VIRTIO_GPU + tristate "Virtio GPU driver" + depends on DRM && VIRTIO + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_TTM + help + This is the virtual GPU driver for virtio. It can be used with + QEMU based VMMs (like KVM or Xen). + + If unsure say M. diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile new file mode 100644 index 000000000000..2ee1602d77d4 --- /dev/null +++ b/drivers/gpu/drm/virtio/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm + +virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o \ + virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o \ + virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o + +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c new file mode 100644 index 000000000000..db8b49101a8b --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "drmP.h" +#include "virtgpu_drv.h" + +static int +virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct virtio_gpu_device *vgdev = node->minor->dev->dev_private; + + seq_printf(m, "fence %ld %lld\n", + atomic64_read(&vgdev->fence_drv.last_seq), + vgdev->fence_drv.sync_seq); + return 0; +} + +static struct drm_info_list virtio_gpu_debugfs_list[] = { + { "irq_fence", virtio_gpu_debugfs_irq_info, 0, NULL }, +}; + +#define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list) + +int +virtio_gpu_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(virtio_gpu_debugfs_list, + VIRTIO_GPU_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); + return 0; +} + +void +virtio_gpu_debugfs_takedown(struct drm_minor *minor) +{ + drm_debugfs_remove_files(virtio_gpu_debugfs_list, + VIRTIO_GPU_DEBUGFS_ENTRIES, + minor); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c new file mode 100644 index 000000000000..4e160efc9402 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Authors: + * Dave Airlie + * Alon Levy + * + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "virtgpu_drv.h" +#include +#include + +#define XRES_MIN 320 +#define YRES_MIN 200 + +#define XRES_DEF 1024 +#define YRES_DEF 768 + +#define XRES_MAX 8192 +#define YRES_MAX 8192 + +static void virtio_gpu_crtc_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t start, uint32_t size) +{ + /* TODO */ +} + +static void +virtio_gpu_hide_cursor(struct virtio_gpu_device *vgdev, + struct virtio_gpu_output *output) +{ + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); + output->cursor.resource_id = 0; + virtio_gpu_cursor_ping(vgdev, output); +} + +static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height, + int32_t hot_x, int32_t hot_y) +{ + struct virtio_gpu_device *vgdev = crtc->dev->dev_private; + struct virtio_gpu_output *output = + container_of(crtc, struct virtio_gpu_output, crtc); + struct drm_gem_object *gobj = NULL; + struct virtio_gpu_object *qobj = NULL; + struct virtio_gpu_fence *fence = NULL; + int ret = 0; + + if (handle == 0) { + virtio_gpu_hide_cursor(vgdev, output); + return 0; + } + + /* lookup the cursor */ + gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle); + if (gobj == NULL) + return -ENOENT; + + qobj = gem_to_virtio_gpu_obj(gobj); + + if (!qobj->hw_res_handle) { + ret = -EINVAL; + goto out; + } + + virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0, + cpu_to_le32(64), + cpu_to_le32(64), + 0, 0, &fence); + + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); + output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle); + output->cursor.hot_x = cpu_to_le32(hot_x); + output->cursor.hot_y = cpu_to_le32(hot_y); + virtio_gpu_cursor_ping(vgdev, output); + ret = 0; + +out: + drm_gem_object_unreference_unlocked(gobj); + return ret; +} + +static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y) +{ + struct virtio_gpu_device *vgdev = crtc->dev->dev_private; + struct virtio_gpu_output *output = + container_of(crtc, struct virtio_gpu_output, crtc); + + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR); + output->cursor.pos.x = cpu_to_le32(x); + output->cursor.pos.y = cpu_to_le32(y); + virtio_gpu_cursor_ping(vgdev, output); + return 0; +} + +static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = { + .cursor_set2 = virtio_gpu_crtc_cursor_set, + .cursor_move = virtio_gpu_crtc_cursor_move, + .gamma_set = virtio_gpu_crtc_gamma_set, + .set_config = drm_atomic_helper_set_config, + .destroy = drm_crtc_cleanup, + +#if 0 /* not (yet) working without vblank support according to docs */ + .page_flip = drm_atomic_helper_page_flip, +#endif + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static void virtio_gpu_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct virtio_gpu_framebuffer *virtio_gpu_fb + = to_virtio_gpu_framebuffer(fb); + + if (virtio_gpu_fb->obj) + drm_gem_object_unreference_unlocked(virtio_gpu_fb->obj); + drm_framebuffer_cleanup(fb); + kfree(virtio_gpu_fb); +} + +static int +virtio_gpu_framebuffer_surface_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + struct virtio_gpu_framebuffer *virtio_gpu_fb + = to_virtio_gpu_framebuffer(fb); + + return virtio_gpu_surface_dirty(virtio_gpu_fb, clips, num_clips); +} + +static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = { + .destroy = virtio_gpu_user_framebuffer_destroy, + .dirty = virtio_gpu_framebuffer_surface_dirty, +}; + +int +virtio_gpu_framebuffer_init(struct drm_device *dev, + struct virtio_gpu_framebuffer *vgfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + struct virtio_gpu_object *bo; + vgfb->obj = obj; + + bo = gem_to_virtio_gpu_obj(obj); + + ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs); + if (ret) { + vgfb->obj = NULL; + return ret; + } + drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd); + + spin_lock_init(&vgfb->dirty_lock); + vgfb->x1 = vgfb->y1 = INT_MAX; + vgfb->x2 = vgfb->y2 = 0; + return 0; +} + +static bool virtio_gpu_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); + + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, + crtc->mode.hdisplay, + crtc->mode.vdisplay, 0, 0); +} + +static void virtio_gpu_crtc_enable(struct drm_crtc *crtc) +{ +} + +static void virtio_gpu_crtc_disable(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); + + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0); +} + +static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return 0; +} + +static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = { + .enable = virtio_gpu_crtc_enable, + .disable = virtio_gpu_crtc_disable, + .mode_fixup = virtio_gpu_crtc_mode_fixup, + .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb, + .atomic_check = virtio_gpu_crtc_atomic_check, +}; + +static bool virtio_gpu_enc_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void virtio_gpu_enc_enable(struct drm_encoder *encoder) +{ +} + +static void virtio_gpu_enc_disable(struct drm_encoder *encoder) +{ +} + +static int virtio_gpu_conn_get_modes(struct drm_connector *connector) +{ + struct virtio_gpu_output *output = + drm_connector_to_virtio_gpu_output(connector); + struct drm_display_mode *mode = NULL; + int count, width, height; + + width = le32_to_cpu(output->info.r.width); + height = le32_to_cpu(output->info.r.height); + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); + + if (width == 0 || height == 0) { + width = XRES_DEF; + height = YRES_DEF; + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); + } else { + DRM_DEBUG("add mode: %dx%d\n", width, height); + mode = drm_cvt_mode(connector->dev, width, height, 60, + false, false, false); + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + count++; + } + + return count; +} + +static int virtio_gpu_conn_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct virtio_gpu_output *output = + drm_connector_to_virtio_gpu_output(connector); + int width, height; + + width = le32_to_cpu(output->info.r.width); + height = le32_to_cpu(output->info.r.height); + + if (!(mode->type & DRM_MODE_TYPE_PREFERRED)) + return MODE_OK; + if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF) + return MODE_OK; + if (mode->hdisplay <= width && mode->hdisplay >= width - 16 && + mode->vdisplay <= height && mode->vdisplay >= height - 16) + return MODE_OK; + + DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay); + return MODE_BAD; +} + +static struct drm_encoder* +virtio_gpu_best_encoder(struct drm_connector *connector) +{ + struct virtio_gpu_output *virtio_gpu_output = + drm_connector_to_virtio_gpu_output(connector); + + return &virtio_gpu_output->enc; +} + +static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = { + .mode_fixup = virtio_gpu_enc_mode_fixup, + .mode_set = virtio_gpu_enc_mode_set, + .enable = virtio_gpu_enc_enable, + .disable = virtio_gpu_enc_disable, +}; + +static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = { + .get_modes = virtio_gpu_conn_get_modes, + .mode_valid = virtio_gpu_conn_mode_valid, + .best_encoder = virtio_gpu_best_encoder, +}; + +static void virtio_gpu_conn_save(struct drm_connector *connector) +{ + DRM_DEBUG("\n"); +} + +static void virtio_gpu_conn_restore(struct drm_connector *connector) +{ + DRM_DEBUG("\n"); +} + +static enum drm_connector_status virtio_gpu_conn_detect( + struct drm_connector *connector, + bool force) +{ + struct virtio_gpu_output *output = + drm_connector_to_virtio_gpu_output(connector); + + if (output->info.enabled) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void virtio_gpu_conn_destroy(struct drm_connector *connector) +{ + struct virtio_gpu_output *virtio_gpu_output = + drm_connector_to_virtio_gpu_output(connector); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(virtio_gpu_output); +} + +static const struct drm_connector_funcs virtio_gpu_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .save = virtio_gpu_conn_save, + .restore = virtio_gpu_conn_restore, + .detect = virtio_gpu_conn_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = virtio_gpu_conn_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_encoder_funcs virtio_gpu_enc_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) +{ + struct drm_device *dev = vgdev->ddev; + struct virtio_gpu_output *output = vgdev->outputs + index; + struct drm_connector *connector = &output->conn; + struct drm_encoder *encoder = &output->enc; + struct drm_crtc *crtc = &output->crtc; + struct drm_plane *plane; + + output->index = index; + if (index == 0) { + output->info.enabled = cpu_to_le32(true); + output->info.r.width = cpu_to_le32(XRES_DEF); + output->info.r.height = cpu_to_le32(YRES_DEF); + } + + plane = virtio_gpu_plane_init(vgdev, index); + if (IS_ERR(plane)) + return PTR_ERR(plane); + drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &virtio_gpu_crtc_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs); + plane->crtc = crtc; + + drm_connector_init(dev, connector, &virtio_gpu_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs); + + drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs, + DRM_MODE_ENCODER_VIRTUAL); + drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs); + encoder->possible_crtcs = 1 << index; + + drm_mode_connector_attach_encoder(connector, encoder); + drm_connector_register(connector); + return 0; +} + +static struct drm_framebuffer * +virtio_gpu_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj = NULL; + struct virtio_gpu_framebuffer *virtio_gpu_fb; + int ret; + + /* lookup object associated with res handle */ + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); + if (!obj) + return ERR_PTR(-EINVAL); + + virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL); + if (virtio_gpu_fb == NULL) + return ERR_PTR(-ENOMEM); + + ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj); + if (ret) { + kfree(virtio_gpu_fb); + if (obj) + drm_gem_object_unreference_unlocked(obj); + return NULL; + } + + return &virtio_gpu_fb->base; +} + +static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = { + .fb_create = virtio_gpu_user_framebuffer_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) +{ + int i; + + drm_mode_config_init(vgdev->ddev); + vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs; + + /* modes will be validated against the framebuffer size */ + vgdev->ddev->mode_config.min_width = XRES_MIN; + vgdev->ddev->mode_config.min_height = YRES_MIN; + vgdev->ddev->mode_config.max_width = XRES_MAX; + vgdev->ddev->mode_config.max_height = YRES_MAX; + + for (i = 0 ; i < vgdev->num_scanouts; ++i) + vgdev_output_init(vgdev, i); + + drm_mode_config_reset(vgdev->ddev); + return 0; +} + +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev) +{ + virtio_gpu_fbdev_fini(vgdev); + drm_mode_config_cleanup(vgdev->ddev); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c new file mode 100644 index 000000000000..f4ec816e9468 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "virtgpu_drv.h" + +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master) +{ + struct pci_dev *pdev = dev->pdev; + + if (pdev) { + return drm_pci_set_busid(dev, master); + } + return 0; +} + +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) +{ + struct drm_device *dev; + int ret; + + dev = drm_dev_alloc(driver, &vdev->dev); + if (!dev) + return -ENOMEM; + dev->virtdev = vdev; + vdev->priv = dev; + + if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) { + struct pci_dev *pdev = to_pci_dev(vdev->dev.parent); + bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; + + if (vga) { + /* + * Need to make sure we don't have two drivers + * for the same hardware here. Some day we + * will simply kick out the firmware + * (vesa/efi) framebuffer. + * + * Virtual hardware specs for virtio-vga are + * not finalized yet, therefore we can't add + * code for that yet. + * + * So ignore the device for the time being, + * and suggest to the user use the device + * variant without vga compatibility mode. + */ + DRM_ERROR("virtio-vga not (yet) supported\n"); + DRM_ERROR("please use virtio-gpu-pci instead\n"); + ret = -ENODEV; + goto err_free; + } + dev->pdev = pdev; + } + + ret = drm_dev_register(dev, 0); + if (ret) + goto err_free; + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, + driver->major, driver->minor, driver->patchlevel, + driver->date, dev->primary->index); + + return 0; + +err_free: + drm_dev_unref(dev); + return ret; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c new file mode 100644 index 000000000000..7d9610aaeff9 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "drmP.h" +#include "drm/drm.h" + +#include "virtgpu_drv.h" +static struct drm_driver driver; + +static int virtio_gpu_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, virtio_gpu_modeset, int, 0400); + +static int virtio_gpu_probe(struct virtio_device *vdev) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && virtio_gpu_modeset == -1) + return -EINVAL; +#endif + + if (virtio_gpu_modeset == 0) + return -EINVAL; + + return drm_virtio_init(&driver, vdev); +} + +static void virtio_gpu_remove(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + drm_put_dev(dev); +} + +static void virtio_gpu_config_changed(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + + schedule_work(&vgdev->config_changed_work); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { +}; +static struct virtio_driver virtio_gpu_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_gpu_probe, + .remove = virtio_gpu_remove, + .config_changed = virtio_gpu_config_changed +}; + +module_virtio_driver(virtio_gpu_driver); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio GPU driver"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Dave Airlie "); +MODULE_AUTHOR("Gerd Hoffmann "); +MODULE_AUTHOR("Alon Levy"); + +static const struct file_operations virtio_gpu_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = virtio_gpu_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + + +static struct drm_driver driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM, + .set_busid = drm_virtio_set_busid, + .load = virtio_gpu_driver_load, + .unload = virtio_gpu_driver_unload, + + .dumb_create = virtio_gpu_mode_dumb_create, + .dumb_map_offset = virtio_gpu_mode_dumb_mmap, + .dumb_destroy = virtio_gpu_mode_dumb_destroy, + +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = virtio_gpu_debugfs_init, + .debugfs_cleanup = virtio_gpu_debugfs_takedown, +#endif + + .gem_free_object = virtio_gpu_gem_free_object, + .fops = &virtio_gpu_driver_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h new file mode 100644 index 000000000000..e5a2c092460b --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef VIRTIO_DRV_H +#define VIRTIO_DRV_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "virtio_gpu" +#define DRIVER_DESC "virtio GPU" +#define DRIVER_DATE "0" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 1 + +/* virtgpu_drm_bus.c */ +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master); +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev); + +struct virtio_gpu_object { + struct drm_gem_object gem_base; + uint32_t hw_res_handle; + + struct sg_table *pages; + void *vmap; + bool dumb; + struct ttm_place placement_code; + struct ttm_placement placement; + struct ttm_buffer_object tbo; + struct ttm_bo_kmap_obj kmap; +}; +#define gem_to_virtio_gpu_obj(gobj) \ + container_of((gobj), struct virtio_gpu_object, gem_base) + +struct virtio_gpu_vbuffer; +struct virtio_gpu_device; + +typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf); + +struct virtio_gpu_fence_driver { + atomic64_t last_seq; + uint64_t sync_seq; + struct list_head fences; + spinlock_t lock; +}; + +struct virtio_gpu_fence { + struct fence f; + struct virtio_gpu_fence_driver *drv; + struct list_head node; + uint64_t seq; +}; +#define to_virtio_fence(x) \ + container_of(x, struct virtio_gpu_fence, f) + +struct virtio_gpu_vbuffer { + char *buf; + int size; + + void *data_buf; + uint32_t data_size; + + char *resp_buf; + int resp_size; + + virtio_gpu_resp_cb resp_cb; + + struct list_head list; +}; + +struct virtio_gpu_output { + int index; + struct drm_crtc crtc; + struct drm_connector conn; + struct drm_encoder enc; + struct virtio_gpu_display_one info; + struct virtio_gpu_update_cursor cursor; + int cur_x; + int cur_y; +}; +#define drm_crtc_to_virtio_gpu_output(x) \ + container_of(x, struct virtio_gpu_output, crtc) +#define drm_connector_to_virtio_gpu_output(x) \ + container_of(x, struct virtio_gpu_output, conn) +#define drm_encoder_to_virtio_gpu_output(x) \ + container_of(x, struct virtio_gpu_output, enc) + +struct virtio_gpu_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; + uint32_t hw_res_handle; +}; +#define to_virtio_gpu_framebuffer(x) \ + container_of(x, struct virtio_gpu_framebuffer, base) + +struct virtio_gpu_mman { + struct ttm_bo_global_ref bo_global_ref; + struct drm_global_reference mem_global_ref; + bool mem_global_referenced; + struct ttm_bo_device bdev; +}; + +struct virtio_gpu_fbdev; + +struct virtio_gpu_queue { + struct virtqueue *vq; + spinlock_t qlock; + wait_queue_head_t ack_queue; + struct work_struct dequeue_work; +}; + +struct virtio_gpu_device { + struct device *dev; + struct drm_device *ddev; + + struct virtio_device *vdev; + + struct virtio_gpu_mman mman; + + /* pointer to fbdev info structure */ + struct virtio_gpu_fbdev *vgfbdev; + struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS]; + uint32_t num_scanouts; + + struct virtio_gpu_queue ctrlq; + struct virtio_gpu_queue cursorq; + struct list_head free_vbufs; + void *vbufs; + bool vqs_ready; + + struct idr resource_idr; + spinlock_t resource_idr_lock; + + wait_queue_head_t resp_wq; + /* current display info */ + spinlock_t display_info_lock; + + struct virtio_gpu_fence_driver fence_drv; + + struct idr ctx_id_idr; + spinlock_t ctx_id_idr_lock; + + struct work_struct config_changed_work; +}; + +struct virtio_gpu_fpriv { + uint32_t ctx_id; +}; + +/* virtio_ioctl.c */ +#define DRM_VIRTIO_NUM_IOCTLS 10 +extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS]; + +/* virtio_kms.c */ +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags); +int virtio_gpu_driver_unload(struct drm_device *dev); + +/* virtio_gem.c */ +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj); +int virtio_gpu_gem_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_gem_fini(struct virtio_gpu_device *vgdev); +int virtio_gpu_gem_create(struct drm_file *file, + struct drm_device *dev, + uint64_t size, + struct drm_gem_object **obj_p, + uint32_t *handle_p); +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev, + size_t size, bool kernel, + bool pinned); +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); + +/* virtio_fb */ +#define VIRTIO_GPUFB_CONN_LIMIT 1 +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev); +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb, + struct drm_clip_rect *clips, + unsigned num_clips); +/* virtio vg */ +int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev); +void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev); +void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid); +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id); +void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t format, + uint32_t width, + uint32_t height); +void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id); +void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, + uint32_t resource_id, uint64_t offset, + __le32 width, __le32 height, + __le32 x, __le32 y, + struct virtio_gpu_fence **fence); +void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height); +void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, + uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + uint32_t x, uint32_t y); +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *obj, + uint32_t resource_id, + struct virtio_gpu_fence **fence); +int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); +int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev); +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, + struct virtio_gpu_output *output); +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev); +void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, + uint32_t resource_id); +void virtio_gpu_ctrl_ack(struct virtqueue *vq); +void virtio_gpu_cursor_ack(struct virtqueue *vq); +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work); +void virtio_gpu_dequeue_cursor_func(struct work_struct *work); + +/* virtio_gpu_display.c */ +int virtio_gpu_framebuffer_init(struct drm_device *dev, + struct virtio_gpu_framebuffer *vgfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev); + +/* virtio_gpu_plane.c */ +struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, + int index); + +/* virtio_gpu_ttm.c */ +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev); +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma); + +/* virtio_gpu_fence.c */ +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, + struct virtio_gpu_ctrl_hdr *cmd_hdr, + struct virtio_gpu_fence **fence); +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev, + u64 last_seq); + +/* virtio_gpu_object */ +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + unsigned long size, bool kernel, bool pinned, + struct virtio_gpu_object **bo_ptr); +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr); +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, + struct virtio_gpu_object *bo); +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo); +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait); + +static inline struct virtio_gpu_object* +virtio_gpu_object_ref(struct virtio_gpu_object *bo) +{ + ttm_bo_reference(&bo->tbo); + return bo; +} + +static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + tbo = &((*bo)->tbo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; +} + +static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo) +{ + return drm_vma_node_offset_addr(&bo->tbo.vma_node); +} + +static inline int virtio_gpu_object_reserve(struct virtio_gpu_object *bo, + bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) { + struct virtio_gpu_device *qdev = + bo->gem_base.dev->dev_private; + dev_err(qdev->dev, "%p reserve failed\n", bo); + } + return r; + } + return 0; +} + +static inline void virtio_gpu_object_unreserve(struct virtio_gpu_object *bo) +{ + ttm_bo_unreserve(&bo->tbo); +} + +/* virgl debufs */ +int virtio_gpu_debugfs_init(struct drm_minor *minor); +void virtio_gpu_debugfs_takedown(struct drm_minor *minor); + +#endif diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c new file mode 100644 index 000000000000..25bf333d5a45 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "virtgpu_drv.h" + +#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60) + +struct virtio_gpu_fbdev { + struct drm_fb_helper helper; + struct virtio_gpu_framebuffer vgfb; + struct list_head fbdev_list; + struct virtio_gpu_device *vgdev; + struct delayed_work work; +}; + +static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb, + bool store, int x, int y, + int width, int height) +{ + struct drm_device *dev = fb->base.dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + bool store_for_later = false; + int bpp = fb->base.bits_per_pixel / 8; + int x2, y2; + unsigned long flags; + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj); + + if ((width <= 0) || + (x + width > fb->base.width) || + (y + height > fb->base.height)) { + DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n", + width, height, x, y, + fb->base.width, fb->base.height); + return -EINVAL; + } + + /* + * Can be called with pretty much any context (console output + * path). If we are in atomic just store the dirty rect info + * to send out the update later. + * + * Can't test inside spin lock. + */ + if (in_atomic() || store) + store_for_later = true; + + x2 = x + width - 1; + y2 = y + height - 1; + + spin_lock_irqsave(&fb->dirty_lock, flags); + + if (fb->y1 < y) + y = fb->y1; + if (fb->y2 > y2) + y2 = fb->y2; + if (fb->x1 < x) + x = fb->x1; + if (fb->x2 > x2) + x2 = fb->x2; + + if (store_for_later) { + fb->x1 = x; + fb->x2 = x2; + fb->y1 = y; + fb->y2 = y2; + spin_unlock_irqrestore(&fb->dirty_lock, flags); + return 0; + } + + fb->x1 = fb->y1 = INT_MAX; + fb->x2 = fb->y2 = 0; + + spin_unlock_irqrestore(&fb->dirty_lock, flags); + + { + uint32_t offset; + uint32_t w = x2 - x + 1; + uint32_t h = y2 - y + 1; + + offset = (y * fb->base.pitches[0]) + x * bpp; + + virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle, + offset, + cpu_to_le32(w), + cpu_to_le32(h), + cpu_to_le32(x), + cpu_to_le32(y), + NULL); + + } + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, + x, y, x2 - x + 1, y2 - y + 1); + return 0; +} + +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private; + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->obj); + struct drm_clip_rect norect; + struct drm_clip_rect *clips_ptr; + int left, right, top, bottom; + int i; + int inc = 1; + if (!num_clips) { + num_clips = 1; + clips = &norect; + norect.x1 = norect.y1 = 0; + norect.x2 = vgfb->base.width; + norect.y2 = vgfb->base.height; + } + left = clips->x1; + right = clips->x2; + top = clips->y1; + bottom = clips->y2; + + /* skip the first clip rect */ + for (i = 1, clips_ptr = clips + inc; + i < num_clips; i++, clips_ptr += inc) { + left = min_t(int, left, (int)clips_ptr->x1); + right = max_t(int, right, (int)clips_ptr->x2); + top = min_t(int, top, (int)clips_ptr->y1); + bottom = max_t(int, bottom, (int)clips_ptr->y2); + } + + if (obj->dumb) + return virtio_gpu_dirty_update(vgfb, false, left, top, + right - left, bottom - top); + + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, + left, top, right - left, bottom - top); + return 0; +} + +static void virtio_gpu_fb_dirty_work(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct virtio_gpu_fbdev *vfbdev = + container_of(delayed_work, struct virtio_gpu_fbdev, work); + struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb; + + virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1, + vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1); +} + +static void virtio_gpu_3d_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct virtio_gpu_fbdev *vfbdev = info->par; + sys_fillrect(info, rect); + virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy, + rect->width, rect->height); + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); +} + +static void virtio_gpu_3d_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct virtio_gpu_fbdev *vfbdev = info->par; + sys_copyarea(info, area); + virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy, + area->width, area->height); + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); +} + +static void virtio_gpu_3d_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct virtio_gpu_fbdev *vfbdev = info->par; + sys_imageblit(info, image); + virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy, + image->width, image->height); + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); +} + +static struct fb_ops virtio_gpufb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */ + .fb_fillrect = virtio_gpu_3d_fillrect, + .fb_copyarea = virtio_gpu_3d_copyarea, + .fb_imageblit = virtio_gpu_3d_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static int virtio_gpu_vmap_fb(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *obj) +{ + return virtio_gpu_object_kmap(obj, NULL); +} + +static int virtio_gpufb_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct virtio_gpu_fbdev *vfbdev = + container_of(helper, struct virtio_gpu_fbdev, helper); + struct drm_device *dev = helper->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct fb_info *info; + struct drm_framebuffer *fb; + struct drm_mode_fb_cmd2 mode_cmd = {}; + struct virtio_gpu_object *obj; + struct device *device = vgdev->dev; + uint32_t resid, format, size; + int ret; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = mode_cmd.width * 4; + mode_cmd.pixel_format = drm_mode_legacy_fb_format(32, 24); + + switch (mode_cmd.pixel_format) { +#ifdef __BIG_ENDIAN + case DRM_FORMAT_XRGB8888: + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; + break; + case DRM_FORMAT_ARGB8888: + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case DRM_FORMAT_BGRX8888: + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; + break; + case DRM_FORMAT_BGRA8888: + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; + break; + case DRM_FORMAT_RGBX8888: + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case DRM_FORMAT_RGBA8888: + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; + break; + case DRM_FORMAT_XBGR8888: + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; + break; + case DRM_FORMAT_ABGR8888: + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; +#else + case DRM_FORMAT_XRGB8888: + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; + break; + case DRM_FORMAT_ARGB8888: + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; + break; + case DRM_FORMAT_BGRX8888: + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; + break; + case DRM_FORMAT_BGRA8888: + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case DRM_FORMAT_RGBX8888: + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; + break; + case DRM_FORMAT_RGBA8888: + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; + case DRM_FORMAT_XBGR8888: + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case DRM_FORMAT_ABGR8888: + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; + break; +#endif + default: + DRM_ERROR("failed to find virtio gpu format for %d\n", + mode_cmd.pixel_format); + return -EINVAL; + } + + size = mode_cmd.pitches[0] * mode_cmd.height; + obj = virtio_gpu_alloc_object(dev, size, false, true); + if (!obj) + return -ENOMEM; + + virtio_gpu_resource_id_get(vgdev, &resid); + virtio_gpu_cmd_create_resource(vgdev, resid, format, + mode_cmd.width, mode_cmd.height); + + ret = virtio_gpu_vmap_fb(vgdev, obj); + if (ret) { + DRM_ERROR("failed to vmap fb %d\n", ret); + goto err_obj_vmap; + } + + /* attach the object to the resource */ + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); + if (ret) + goto err_obj_attach; + + info = framebuffer_alloc(0, device); + if (!info) { + ret = -ENOMEM; + goto err_fb_alloc; + } + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto err_fb_alloc_cmap; + } + + info->par = helper; + + ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb, + &mode_cmd, &obj->gem_base); + if (ret) + goto err_fb_init; + + fb = &vfbdev->vgfb.base; + + vfbdev->helper.fb = fb; + vfbdev->helper.fbdev = info; + + strcpy(info->fix.id, "virtiodrmfb"); + info->flags = FBINFO_DEFAULT; + info->fbops = &virtio_gpufb_ops; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + info->screen_base = obj->vmap; + info->screen_size = obj->gem_base.size; + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &vfbdev->helper, + sizes->fb_width, sizes->fb_height); + + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + return 0; + +err_fb_init: + fb_dealloc_cmap(&info->cmap); +err_fb_alloc_cmap: + framebuffer_release(info); +err_fb_alloc: + virtio_gpu_cmd_resource_inval_backing(vgdev, resid); +err_obj_attach: +err_obj_vmap: + virtio_gpu_gem_free_object(&obj->gem_base); + return ret; +} + +static int virtio_gpu_fbdev_destroy(struct drm_device *dev, + struct virtio_gpu_fbdev *vgfbdev) +{ + struct fb_info *info; + struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb; + + if (vgfbdev->helper.fbdev) { + info = vgfbdev->helper.fbdev; + + unregister_framebuffer(info); + framebuffer_release(info); + } + if (vgfb->obj) + vgfb->obj = NULL; + drm_fb_helper_fini(&vgfbdev->helper); + drm_framebuffer_cleanup(&vgfb->base); + + return 0; +} +static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = { + .fb_probe = virtio_gpufb_create, +}; + +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_fbdev *vgfbdev; + int bpp_sel = 32; /* TODO: parameter from somewhere? */ + int ret; + + vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL); + if (!vgfbdev) + return -ENOMEM; + + vgfbdev->vgdev = vgdev; + vgdev->vgfbdev = vgfbdev; + INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work); + + drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper, + &virtio_gpu_fb_helper_funcs); + ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper, + vgdev->num_scanouts, + VIRTIO_GPUFB_CONN_LIMIT); + if (ret) { + kfree(vgfbdev); + return ret; + } + + drm_fb_helper_single_add_all_connectors(&vgfbdev->helper); + drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel); + return 0; +} + +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev) +{ + if (!vgdev->vgfbdev) + return; + + virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev); + kfree(vgdev->vgfbdev); + vgdev->vgfbdev = NULL; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c new file mode 100644 index 000000000000..1da632631dac --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "virtgpu_drv.h" + +static const char *virtio_get_driver_name(struct fence *f) +{ + return "virtio_gpu"; +} + +static const char *virtio_get_timeline_name(struct fence *f) +{ + return "controlq"; +} + +static bool virtio_enable_signaling(struct fence *f) +{ + return true; +} + +static bool virtio_signaled(struct fence *f) +{ + struct virtio_gpu_fence *fence = to_virtio_fence(f); + + if (atomic64_read(&fence->drv->last_seq) >= fence->seq) + return true; + return false; +} + +static void virtio_fence_value_str(struct fence *f, char *str, int size) +{ + struct virtio_gpu_fence *fence = to_virtio_fence(f); + + snprintf(str, size, "%llu", fence->seq); +} + +static void virtio_timeline_value_str(struct fence *f, char *str, int size) +{ + struct virtio_gpu_fence *fence = to_virtio_fence(f); + + snprintf(str, size, "%lu", atomic64_read(&fence->drv->last_seq)); +} + +static const struct fence_ops virtio_fence_ops = { + .get_driver_name = virtio_get_driver_name, + .get_timeline_name = virtio_get_timeline_name, + .enable_signaling = virtio_enable_signaling, + .signaled = virtio_signaled, + .wait = fence_default_wait, + .fence_value_str = virtio_fence_value_str, + .timeline_value_str = virtio_timeline_value_str, +}; + +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, + struct virtio_gpu_ctrl_hdr *cmd_hdr, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; + unsigned long irq_flags; + + *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL); + if ((*fence) == NULL) + return -ENOMEM; + + spin_lock_irqsave(&drv->lock, irq_flags); + (*fence)->drv = drv; + (*fence)->seq = ++drv->sync_seq; + fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock, + 0, (*fence)->seq); + fence_get(&(*fence)->f); + list_add_tail(&(*fence)->node, &drv->fences); + spin_unlock_irqrestore(&drv->lock, irq_flags); + + cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE); + cmd_hdr->fence_id = cpu_to_le64((*fence)->seq); + return 0; +} + +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev, + u64 last_seq) +{ + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; + struct virtio_gpu_fence *fence, *tmp; + unsigned long irq_flags; + + spin_lock_irqsave(&drv->lock, irq_flags); + atomic64_set(&vgdev->fence_drv.last_seq, last_seq); + list_for_each_entry_safe(fence, tmp, &drv->fences, node) { + if (last_seq < fence->seq) + continue; + fence_signal_locked(&fence->f); + list_del(&fence->node); + fence_put(&fence->f); + } + spin_unlock_irqrestore(&drv->lock, irq_flags); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c new file mode 100644 index 000000000000..cfa0d27150bd --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "virtgpu_drv.h" + +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj) +{ + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj); + + if (obj) + virtio_gpu_object_unref(&obj); +} + +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev, + size_t size, bool kernel, + bool pinned) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_object *obj; + int ret; + + ret = virtio_gpu_object_create(vgdev, size, kernel, pinned, &obj); + if (ret) + return ERR_PTR(ret); + + return obj; +} + +int virtio_gpu_gem_create(struct drm_file *file, + struct drm_device *dev, + uint64_t size, + struct drm_gem_object **obj_p, + uint32_t *handle_p) +{ + struct virtio_gpu_object *obj; + int ret; + u32 handle; + + obj = virtio_gpu_alloc_object(dev, size, false, false); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + ret = drm_gem_handle_create(file, &obj->gem_base, &handle); + if (ret) { + drm_gem_object_release(&obj->gem_base); + return ret; + } + + *obj_p = &obj->gem_base; + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(&obj->gem_base); + + *handle_p = handle; + return 0; +} + +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + struct drm_gem_object *gobj; + struct virtio_gpu_object *obj; + int ret; + uint32_t pitch; + uint32_t resid; + + pitch = args->width * ((args->bpp + 1) / 8); + args->size = pitch * args->height; + args->size = ALIGN(args->size, PAGE_SIZE); + + ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj, + &args->handle); + if (ret) + goto fail; + + virtio_gpu_resource_id_get(vgdev, &resid); + virtio_gpu_cmd_create_resource(vgdev, resid, + 2, args->width, args->height); + + /* attach the object to the resource */ + obj = gem_to_virtio_gpu_obj(gobj); + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); + if (ret) + goto fail; + + obj->dumb = true; + args->pitch = pitch; + return ret; + +fail: + return ret; +} + +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} + +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p) +{ + struct drm_gem_object *gobj; + struct virtio_gpu_object *obj; + BUG_ON(!offset_p); + gobj = drm_gem_object_lookup(dev, file_priv, handle); + if (gobj == NULL) + return -ENOENT; + obj = gem_to_virtio_gpu_obj(gobj); + *offset_p = virtio_gpu_object_mmap_offset(obj); + drm_gem_object_unreference_unlocked(gobj); + return 0; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c new file mode 100644 index 000000000000..132405f15389 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "virtgpu_drv.h" + +static int virtio_gpu_fbdev = 1; + +MODULE_PARM_DESC(fbdev, "Disable/Enable framebuffer device & console"); +module_param_named(fbdev, virtio_gpu_fbdev, int, 0400); + +static void virtio_gpu_config_changed_work_func(struct work_struct *work) +{ + struct virtio_gpu_device *vgdev = + container_of(work, struct virtio_gpu_device, + config_changed_work); + u32 events_read, events_clear = 0; + + /* read the config space */ + virtio_cread(vgdev->vdev, struct virtio_gpu_config, + events_read, &events_read); + if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { + virtio_gpu_cmd_get_display_info(vgdev); + drm_helper_hpd_irq_event(vgdev->ddev); + events_clear |= VIRTIO_GPU_EVENT_DISPLAY; + } + virtio_cwrite(vgdev->vdev, struct virtio_gpu_config, + events_clear, &events_clear); +} + +static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, + void (*work_func)(struct work_struct *work)) +{ + spin_lock_init(&vgvq->qlock); + init_waitqueue_head(&vgvq->ack_queue); + INIT_WORK(&vgvq->dequeue_work, work_func); +} + +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) +{ + static vq_callback_t *callbacks[] = { + virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack + }; + static const char *names[] = { "control", "cursor" }; + + struct virtio_gpu_device *vgdev; + /* this will expand later */ + struct virtqueue *vqs[2]; + u32 num_scanouts; + int ret; + + if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1)) + return -ENODEV; + + vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); + if (!vgdev) + return -ENOMEM; + + vgdev->ddev = dev; + dev->dev_private = vgdev; + vgdev->vdev = dev->virtdev; + vgdev->dev = dev->dev; + + spin_lock_init(&vgdev->display_info_lock); + spin_lock_init(&vgdev->ctx_id_idr_lock); + idr_init(&vgdev->ctx_id_idr); + spin_lock_init(&vgdev->resource_idr_lock); + idr_init(&vgdev->resource_idr); + init_waitqueue_head(&vgdev->resp_wq); + virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); + virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); + + spin_lock_init(&vgdev->fence_drv.lock); + INIT_LIST_HEAD(&vgdev->fence_drv.fences); + INIT_WORK(&vgdev->config_changed_work, + virtio_gpu_config_changed_work_func); + + ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs, + callbacks, names); + if (ret) { + DRM_ERROR("failed to find virt queues\n"); + goto err_vqs; + } + vgdev->ctrlq.vq = vqs[0]; + vgdev->cursorq.vq = vqs[1]; + ret = virtio_gpu_alloc_vbufs(vgdev); + if (ret) { + DRM_ERROR("failed to alloc vbufs\n"); + goto err_vbufs; + } + + ret = virtio_gpu_ttm_init(vgdev); + if (ret) { + DRM_ERROR("failed to init ttm %d\n", ret); + goto err_ttm; + } + + /* get display info */ + virtio_cread(vgdev->vdev, struct virtio_gpu_config, + num_scanouts, &num_scanouts); + vgdev->num_scanouts = min_t(uint32_t, num_scanouts, + VIRTIO_GPU_MAX_SCANOUTS); + if (!vgdev->num_scanouts) { + DRM_ERROR("num_scanouts is zero\n"); + ret = -EINVAL; + goto err_scanouts; + } + + ret = virtio_gpu_modeset_init(vgdev); + if (ret) + goto err_modeset; + + virtio_device_ready(vgdev->vdev); + vgdev->vqs_ready = true; + + if (virtio_gpu_fbdev) + virtio_gpu_fbdev_init(vgdev); + virtio_gpu_cmd_get_display_info(vgdev); + + return 0; + +err_modeset: +err_scanouts: + virtio_gpu_ttm_fini(vgdev); +err_ttm: + virtio_gpu_free_vbufs(vgdev); +err_vbufs: + vgdev->vdev->config->del_vqs(vgdev->vdev); +err_vqs: + kfree(vgdev); + return ret; +} + +int virtio_gpu_driver_unload(struct drm_device *dev) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + + vgdev->vqs_ready = false; + flush_work(&vgdev->ctrlq.dequeue_work); + flush_work(&vgdev->cursorq.dequeue_work); + flush_work(&vgdev->config_changed_work); + vgdev->vdev->config->del_vqs(vgdev->vdev); + + virtio_gpu_modeset_fini(vgdev); + virtio_gpu_ttm_fini(vgdev); + virtio_gpu_free_vbufs(vgdev); + kfree(vgdev); + return 0; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c new file mode 100644 index 000000000000..2c624c784c1d --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "virtgpu_drv.h" + +static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) +{ + struct virtio_gpu_object *bo; + struct virtio_gpu_device *vgdev; + + bo = container_of(tbo, struct virtio_gpu_object, tbo); + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; + + if (bo->hw_res_handle) + virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle); + if (bo->pages) + virtio_gpu_object_free_sg_table(bo); + drm_gem_object_release(&bo->gem_base); + kfree(bo); +} + +static void virtio_gpu_init_ttm_placement(struct virtio_gpu_object *vgbo, + bool pinned) +{ + u32 c = 1; + u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; + + vgbo->placement.placement = &vgbo->placement_code; + vgbo->placement.busy_placement = &vgbo->placement_code; + vgbo->placement_code.fpfn = 0; + vgbo->placement_code.lpfn = 0; + vgbo->placement_code.flags = + TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT | pflag; + vgbo->placement.num_placement = c; + vgbo->placement.num_busy_placement = c; + +} + +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + unsigned long size, bool kernel, bool pinned, + struct virtio_gpu_object **bo_ptr) +{ + struct virtio_gpu_object *bo; + enum ttm_bo_type type; + size_t acc_size; + int ret; + + if (kernel) + type = ttm_bo_type_kernel; + else + type = ttm_bo_type_device; + *bo_ptr = NULL; + + acc_size = ttm_bo_dma_acc_size(&vgdev->mman.bdev, size, + sizeof(struct virtio_gpu_object)); + + bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL); + if (bo == NULL) + return -ENOMEM; + size = roundup(size, PAGE_SIZE); + ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); + if (ret != 0) + goto err_gem_init; + bo->dumb = false; + virtio_gpu_init_ttm_placement(bo, pinned); + + ret = ttm_bo_init(&vgdev->mman.bdev, &bo->tbo, size, type, + &bo->placement, 0, !kernel, NULL, acc_size, + NULL, NULL, &virtio_gpu_ttm_bo_destroy); + if (ret != 0) + goto err_ttm_init; + + *bo_ptr = bo; + return 0; + +err_ttm_init: + drm_gem_object_release(&bo->gem_base); +err_gem_init: + kfree(bo); + return ret; +} + +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr) +{ + bool is_iomem; + int r; + + if (bo->vmap) { + if (ptr) + *ptr = bo->vmap; + return 0; + } + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); + if (r) + return r; + bo->vmap = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); + if (ptr) + *ptr = bo->vmap; + return 0; +} + +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, + struct virtio_gpu_object *bo) +{ + int ret; + struct page **pages = bo->tbo.ttm->pages; + int nr_pages = bo->tbo.num_pages; + + /* wtf swapping */ + if (bo->pages) + return 0; + + if (bo->tbo.ttm->state == tt_unpopulated) + bo->tbo.ttm->bdev->driver->ttm_tt_populate(bo->tbo.ttm); + bo->pages = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!bo->pages) + goto out; + + ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0, + nr_pages << PAGE_SHIFT, GFP_KERNEL); + if (ret) + goto out; + return 0; +out: + kfree(bo->pages); + bo->pages = NULL; + return -ENOMEM; +} + +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo) +{ + sg_free_table(bo->pages); + kfree(bo->pages); + bo->pages = NULL; +} + +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); + if (unlikely(r != 0)) + return r; + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); + ttm_bo_unreserve(&bo->tbo); + return r; +} + diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c new file mode 100644 index 000000000000..4a74129c5708 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "virtgpu_drv.h" +#include +#include + +static const uint32_t virtio_gpu_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, +}; + +static void virtio_gpu_plane_destroy(struct drm_plane *plane) +{ + kfree(plane); +} + +static const struct drm_plane_funcs virtio_gpu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = virtio_gpu_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + return 0; +} + +static void virtio_gpu_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(plane->crtc); + struct virtio_gpu_framebuffer *vgfb; + struct virtio_gpu_object *bo; + uint32_t handle; + + if (plane->fb) { + vgfb = to_virtio_gpu_framebuffer(plane->fb); + bo = gem_to_virtio_gpu_obj(vgfb->obj); + handle = bo->hw_res_handle; + } else { + handle = 0; + } + + DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d\n", handle, + plane->state->crtc_w, plane->state->crtc_h, + plane->state->crtc_x, plane->state->crtc_y); + virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, + plane->state->crtc_w, + plane->state->crtc_h, + plane->state->crtc_x, + plane->state->crtc_y); +} + + +static const struct drm_plane_helper_funcs virtio_gpu_plane_helper_funcs = { + .atomic_check = virtio_gpu_plane_atomic_check, + .atomic_update = virtio_gpu_plane_atomic_update, +}; + +struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, + int index) +{ + struct drm_device *dev = vgdev->ddev; + struct drm_plane *plane; + int ret; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) + return ERR_PTR(-ENOMEM); + + ret = drm_universal_plane_init(dev, plane, 1 << index, + &virtio_gpu_plane_funcs, + virtio_gpu_formats, + ARRAY_SIZE(virtio_gpu_formats), + DRM_PLANE_TYPE_PRIMARY); + if (ret) + goto err_plane_init; + + drm_plane_helper_add(plane, &virtio_gpu_plane_helper_funcs); + return plane; + +err_plane_init: + kfree(plane); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c new file mode 100644 index 000000000000..e0e74c6bb959 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Authors: + * Dave Airlie + * Alon Levy + * + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 +#include +#include +#include +#include +#include +#include +#include "virtgpu_drv.h" + +#include + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +static struct +virtio_gpu_device *virtio_gpu_get_vgdev(struct ttm_bo_device *bdev) +{ + struct virtio_gpu_mman *mman; + struct virtio_gpu_device *vgdev; + + mman = container_of(bdev, struct virtio_gpu_mman, bdev); + vgdev = container_of(mman, struct virtio_gpu_device, mman); + return vgdev; +} + +static int virtio_gpu_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void virtio_gpu_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int virtio_gpu_ttm_global_init(struct virtio_gpu_device *vgdev) +{ + struct drm_global_reference *global_ref; + int r; + + vgdev->mman.mem_global_referenced = false; + global_ref = &vgdev->mman.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &virtio_gpu_ttm_mem_global_init; + global_ref->release = &virtio_gpu_ttm_mem_global_release; + + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + vgdev->mman.bo_global_ref.mem_glob = + vgdev->mman.mem_global_ref.object; + global_ref = &vgdev->mman.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&vgdev->mman.mem_global_ref); + return r; + } + + vgdev->mman.mem_global_referenced = true; + return 0; +} + +static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev) +{ + if (vgdev->mman.mem_global_referenced) { + drm_global_item_unref(&vgdev->mman.bo_global_ref.ref); + drm_global_item_unref(&vgdev->mman.mem_global_ref); + vgdev->mman.mem_global_referenced = false; + } +} + +#if 0 +/* + * Hmm, seems to not do anything useful. Leftover debug hack? + * Something like printing pagefaults to kernel log? + */ +static struct vm_operations_struct virtio_gpu_ttm_vm_ops; +static const struct vm_operations_struct *ttm_vm_ops; + +static int virtio_gpu_ttm_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo; + struct virtio_gpu_device *vgdev; + int r; + + bo = (struct ttm_buffer_object *)vma->vm_private_data; + if (bo == NULL) + return VM_FAULT_NOPAGE; + vgdev = virtio_gpu_get_vgdev(bo->bdev); + r = ttm_vm_ops->fault(vma, vmf); + return r; +} +#endif + +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct virtio_gpu_device *vgdev; + int r; + + file_priv = filp->private_data; + vgdev = file_priv->minor->dev->dev_private; + if (vgdev == NULL) { + DRM_ERROR( + "filp->private_data->minor->dev->dev_private == NULL\n"); + return -EINVAL; + } + r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev); +#if 0 + if (unlikely(r != 0)) + return r; + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + virtio_gpu_ttm_vm_ops = *ttm_vm_ops; + virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault; + } + vma->vm_ops = &virtio_gpu_ttm_vm_ops; + return 0; +#else + return r; +#endif +} + +static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev, + uint32_t flags) +{ + return 0; +} + +static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_mem_reg *mem) +{ + mem->mm_node = (void *)1; + return 0; +} + +static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man, + struct ttm_mem_reg *mem) +{ + mem->mm_node = (void *)NULL; + return; +} + +static int ttm_bo_man_init(struct ttm_mem_type_manager *man, + unsigned long p_size) +{ + return 0; +} + +static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man) +{ + return 0; +} + +static void ttm_bo_man_debug(struct ttm_mem_type_manager *man, + const char *prefix) +{ +} + +static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = { + ttm_bo_man_init, + ttm_bo_man_takedown, + ttm_bo_man_get_node, + ttm_bo_man_put_node, + ttm_bo_man_debug +}; + +static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + struct virtio_gpu_device *vgdev; + + vgdev = virtio_gpu_get_vgdev(bdev); + + switch (type) { + case TTM_PL_SYSTEM: + /* System memory */ + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_TT: + man->func = &virtio_gpu_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void virtio_gpu_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + static struct ttm_place placements = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM, + }; + + placement->placement = &placements; + placement->busy_placement = &placements; + placement->num_placement = 1; + placement->num_busy_placement = 1; + return; +} + +static int virtio_gpu_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + return 0; +} + +static int virtio_gpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + case TTM_PL_TT: + /* system memory */ + return 0; + default: + return -EINVAL; + } + return 0; +} + +static void virtio_gpu_ttm_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ +} + +/* + * TTM backend functions. + */ +struct virtio_gpu_ttm_tt { + struct ttm_dma_tt ttm; + struct virtio_gpu_device *vgdev; + u64 offset; +}; + +static int virtio_gpu_ttm_backend_bind(struct ttm_tt *ttm, + struct ttm_mem_reg *bo_mem) +{ + struct virtio_gpu_ttm_tt *gtt = (void *)ttm; + + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); + if (!ttm->num_pages) + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", + ttm->num_pages, bo_mem, ttm); + + /* Not implemented */ + return 0; +} + +static int virtio_gpu_ttm_backend_unbind(struct ttm_tt *ttm) +{ + /* Not implemented */ + return 0; +} + +static void virtio_gpu_ttm_backend_destroy(struct ttm_tt *ttm) +{ + struct virtio_gpu_ttm_tt *gtt = (void *)ttm; + + ttm_dma_tt_fini(>t->ttm); + kfree(gtt); +} + +static struct ttm_backend_func virtio_gpu_backend_func = { + .bind = &virtio_gpu_ttm_backend_bind, + .unbind = &virtio_gpu_ttm_backend_unbind, + .destroy = &virtio_gpu_ttm_backend_destroy, +}; + +static int virtio_gpu_ttm_tt_populate(struct ttm_tt *ttm) +{ + if (ttm->state != tt_unpopulated) + return 0; + + return ttm_pool_populate(ttm); +} + +static void virtio_gpu_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +static struct ttm_tt *virtio_gpu_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, + uint32_t page_flags, + struct page *dummy_read_page) +{ + struct virtio_gpu_device *vgdev; + struct virtio_gpu_ttm_tt *gtt; + + vgdev = virtio_gpu_get_vgdev(bdev); + gtt = kzalloc(sizeof(struct virtio_gpu_ttm_tt), GFP_KERNEL); + if (gtt == NULL) + return NULL; + gtt->ttm.ttm.func = &virtio_gpu_backend_func; + gtt->vgdev = vgdev; + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags, + dummy_read_page)) { + kfree(gtt); + return NULL; + } + return >t->ttm.ttm; +} + +static void virtio_gpu_move_null(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + + BUG_ON(old_mem->mm_node != NULL); + *old_mem = *new_mem; + new_mem->mm_node = NULL; +} + +static int virtio_gpu_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + virtio_gpu_move_null(bo, new_mem); + return 0; +} + +static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo, + struct ttm_mem_reg *new_mem) +{ + struct virtio_gpu_object *bo; + struct virtio_gpu_device *vgdev; + + bo = container_of(tbo, struct virtio_gpu_object, tbo); + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; + + if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) { + if (bo->hw_res_handle) + virtio_gpu_cmd_resource_inval_backing(vgdev, + bo->hw_res_handle); + + } else if (new_mem->placement & TTM_PL_FLAG_TT) { + if (bo->hw_res_handle) { + virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle, + NULL); + } + } +} + +static void virtio_gpu_bo_swap_notify(struct ttm_buffer_object *tbo) +{ + struct virtio_gpu_object *bo; + struct virtio_gpu_device *vgdev; + + bo = container_of(tbo, struct virtio_gpu_object, tbo); + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; + + if (bo->pages) + virtio_gpu_object_free_sg_table(bo); +} + +static struct ttm_bo_driver virtio_gpu_bo_driver = { + .ttm_tt_create = &virtio_gpu_ttm_tt_create, + .ttm_tt_populate = &virtio_gpu_ttm_tt_populate, + .ttm_tt_unpopulate = &virtio_gpu_ttm_tt_unpopulate, + .invalidate_caches = &virtio_gpu_invalidate_caches, + .init_mem_type = &virtio_gpu_init_mem_type, + .evict_flags = &virtio_gpu_evict_flags, + .move = &virtio_gpu_bo_move, + .verify_access = &virtio_gpu_verify_access, + .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve, + .io_mem_free = &virtio_gpu_ttm_io_mem_free, + .move_notify = &virtio_gpu_bo_move_notify, + .swap_notify = &virtio_gpu_bo_swap_notify, +}; + +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev) +{ + int r; + + r = virtio_gpu_ttm_global_init(vgdev); + if (r) + return r; + /* No others user of address space so set it to 0 */ + r = ttm_bo_device_init(&vgdev->mman.bdev, + vgdev->mman.bo_global_ref.ref.object, + &virtio_gpu_bo_driver, + vgdev->ddev->anon_inode->i_mapping, + DRM_FILE_PAGE_OFFSET, 0); + if (r) { + DRM_ERROR("failed initializing buffer object driver(%d).\n", r); + goto err_dev_init; + return r; + } + + r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0); + if (r) { + DRM_ERROR("Failed initializing GTT heap.\n"); + goto err_mm_init; + return r; + } + return 0; + +err_mm_init: + ttm_bo_device_release(&vgdev->mman.bdev); +err_dev_init: + virtio_gpu_ttm_global_fini(vgdev); + return r; +} + +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev) +{ + ttm_bo_device_release(&vgdev->mman.bdev); + virtio_gpu_ttm_global_fini(vgdev); + DRM_INFO("virtio_gpu: ttm finalized\n"); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c new file mode 100644 index 000000000000..8fa6513eb3bc --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "virtgpu_drv.h" +#include +#include +#include + +#define MAX_INLINE_CMD_SIZE 96 +#define MAX_INLINE_RESP_SIZE 24 +#define VBUFFER_SIZE (sizeof(struct virtio_gpu_vbuffer) \ + + MAX_INLINE_CMD_SIZE \ + + MAX_INLINE_RESP_SIZE) + +void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid) +{ + int handle; + + idr_preload(GFP_KERNEL); + spin_lock(&vgdev->resource_idr_lock); + handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT); + spin_unlock(&vgdev->resource_idr_lock); + idr_preload_end(); + *resid = handle; +} + +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) +{ + spin_lock(&vgdev->resource_idr_lock); + idr_remove(&vgdev->resource_idr, id); + spin_unlock(&vgdev->resource_idr_lock); +} + +void virtio_gpu_ctrl_ack(struct virtqueue *vq) +{ + struct drm_device *dev = vq->vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + schedule_work(&vgdev->ctrlq.dequeue_work); +} + +void virtio_gpu_cursor_ack(struct virtqueue *vq) +{ + struct drm_device *dev = vq->vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + schedule_work(&vgdev->cursorq.dequeue_work); +} + +int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_vbuffer *vbuf; + int i, size, count = 0; + void *ptr; + + INIT_LIST_HEAD(&vgdev->free_vbufs); + count += virtqueue_get_vring_size(vgdev->ctrlq.vq); + count += virtqueue_get_vring_size(vgdev->cursorq.vq); + size = count * VBUFFER_SIZE; + DRM_INFO("virtio vbuffers: %d bufs, %zdB each, %dkB total.\n", + count, VBUFFER_SIZE, size / 1024); + + vgdev->vbufs = kzalloc(size, GFP_KERNEL); + if (!vgdev->vbufs) + return -ENOMEM; + + for (i = 0, ptr = vgdev->vbufs; + i < count; + i++, ptr += VBUFFER_SIZE) { + vbuf = ptr; + list_add(&vbuf->list, &vgdev->free_vbufs); + } + return 0; +} + +void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_vbuffer *vbuf; + int i, count = 0; + + count += virtqueue_get_vring_size(vgdev->ctrlq.vq); + count += virtqueue_get_vring_size(vgdev->cursorq.vq); + + for (i = 0; i < count; i++) { + if (WARN_ON(list_empty(&vgdev->free_vbufs))) + return; + vbuf = list_first_entry(&vgdev->free_vbufs, + struct virtio_gpu_vbuffer, list); + list_del(&vbuf->list); + } + kfree(vgdev->vbufs); +} + +static struct virtio_gpu_vbuffer* +virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, + int size, int resp_size, void *resp_buf, + virtio_gpu_resp_cb resp_cb) +{ + struct virtio_gpu_vbuffer *vbuf; + + BUG_ON(list_empty(&vgdev->free_vbufs)); + vbuf = list_first_entry(&vgdev->free_vbufs, + struct virtio_gpu_vbuffer, list); + list_del(&vbuf->list); + memset(vbuf, 0, VBUFFER_SIZE); + + BUG_ON(size > MAX_INLINE_CMD_SIZE); + vbuf->buf = (void *)vbuf + sizeof(*vbuf); + vbuf->size = size; + + vbuf->resp_cb = resp_cb; + vbuf->resp_size = resp_size; + if (resp_size <= MAX_INLINE_RESP_SIZE) + vbuf->resp_buf = (void *)vbuf->buf + size; + else + vbuf->resp_buf = resp_buf; + BUG_ON(!vbuf->resp_buf); + return vbuf; +} + +static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer **vbuffer_p, + int size) +{ + struct virtio_gpu_vbuffer *vbuf; + + vbuf = virtio_gpu_get_vbuf(vgdev, size, + sizeof(struct virtio_gpu_ctrl_hdr), + NULL, NULL); + if (IS_ERR(vbuf)) { + *vbuffer_p = NULL; + return ERR_CAST(vbuf); + } + *vbuffer_p = vbuf; + return vbuf->buf; +} + +static struct virtio_gpu_update_cursor* +virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer **vbuffer_p) +{ + struct virtio_gpu_vbuffer *vbuf; + + vbuf = virtio_gpu_get_vbuf + (vgdev, sizeof(struct virtio_gpu_update_cursor), + 0, NULL, NULL); + if (IS_ERR(vbuf)) { + *vbuffer_p = NULL; + return ERR_CAST(vbuf); + } + *vbuffer_p = vbuf; + return (struct virtio_gpu_update_cursor *)vbuf->buf; +} + +static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev, + virtio_gpu_resp_cb cb, + struct virtio_gpu_vbuffer **vbuffer_p, + int cmd_size, int resp_size, + void *resp_buf) +{ + struct virtio_gpu_vbuffer *vbuf; + + vbuf = virtio_gpu_get_vbuf(vgdev, cmd_size, + resp_size, resp_buf, cb); + if (IS_ERR(vbuf)) { + *vbuffer_p = NULL; + return ERR_CAST(vbuf); + } + *vbuffer_p = vbuf; + return (struct virtio_gpu_command *)vbuf->buf; +} + +static void free_vbuf(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + if (vbuf->resp_size > MAX_INLINE_RESP_SIZE) + kfree(vbuf->resp_buf); + kfree(vbuf->data_buf); + list_add(&vbuf->list, &vgdev->free_vbufs); +} + +static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list) +{ + struct virtio_gpu_vbuffer *vbuf; + unsigned int len; + int freed = 0; + + while ((vbuf = virtqueue_get_buf(vq, &len))) { + list_add_tail(&vbuf->list, reclaim_list); + freed++; + } + if (freed == 0) + DRM_DEBUG("Huh? zero vbufs reclaimed"); +} + +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work) +{ + struct virtio_gpu_device *vgdev = + container_of(work, struct virtio_gpu_device, + ctrlq.dequeue_work); + struct list_head reclaim_list; + struct virtio_gpu_vbuffer *entry, *tmp; + struct virtio_gpu_ctrl_hdr *resp; + u64 fence_id = 0; + + INIT_LIST_HEAD(&reclaim_list); + spin_lock(&vgdev->ctrlq.qlock); + do { + virtqueue_disable_cb(vgdev->ctrlq.vq); + reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list); + + } while (!virtqueue_enable_cb(vgdev->ctrlq.vq)); + spin_unlock(&vgdev->ctrlq.qlock); + + list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { + resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf; + if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA)) + DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type)); + if (resp->flags & cpu_to_le32(VIRTIO_GPU_FLAG_FENCE)) { + u64 f = le64_to_cpu(resp->fence_id); + + if (fence_id > f) { + DRM_ERROR("%s: Oops: fence %llx -> %llx\n", + __func__, fence_id, f); + } else { + fence_id = f; + } + } + if (entry->resp_cb) + entry->resp_cb(vgdev, entry); + + list_del(&entry->list); + free_vbuf(vgdev, entry); + } + wake_up(&vgdev->ctrlq.ack_queue); + + if (fence_id) + virtio_gpu_fence_event_process(vgdev, fence_id); +} + +void virtio_gpu_dequeue_cursor_func(struct work_struct *work) +{ + struct virtio_gpu_device *vgdev = + container_of(work, struct virtio_gpu_device, + cursorq.dequeue_work); + struct list_head reclaim_list; + struct virtio_gpu_vbuffer *entry, *tmp; + + INIT_LIST_HEAD(&reclaim_list); + spin_lock(&vgdev->cursorq.qlock); + do { + virtqueue_disable_cb(vgdev->cursorq.vq); + reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list); + } while (!virtqueue_enable_cb(vgdev->cursorq.vq)); + spin_unlock(&vgdev->cursorq.qlock); + + list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { + list_del(&entry->list); + free_vbuf(vgdev, entry); + } + wake_up(&vgdev->cursorq.ack_queue); +} + +static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtqueue *vq = vgdev->ctrlq.vq; + struct scatterlist *sgs[3], vcmd, vout, vresp; + int outcnt = 0, incnt = 0; + int ret; + + if (!vgdev->vqs_ready) + return -ENODEV; + + sg_init_one(&vcmd, vbuf->buf, vbuf->size); + sgs[outcnt+incnt] = &vcmd; + outcnt++; + + if (vbuf->data_size) { + sg_init_one(&vout, vbuf->data_buf, vbuf->data_size); + sgs[outcnt + incnt] = &vout; + outcnt++; + } + + if (vbuf->resp_size) { + sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size); + sgs[outcnt + incnt] = &vresp; + incnt++; + } + + spin_lock(&vgdev->ctrlq.qlock); +retry: + ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC); + if (ret == -ENOSPC) { + spin_unlock(&vgdev->ctrlq.qlock); + wait_event(vgdev->ctrlq.ack_queue, vq->num_free); + spin_lock(&vgdev->ctrlq.qlock); + goto retry; + } else { + virtqueue_kick(vq); + } + spin_unlock(&vgdev->ctrlq.qlock); + + if (!ret) + ret = vq->num_free; + return ret; +} + +static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtqueue *vq = vgdev->cursorq.vq; + struct scatterlist *sgs[1], ccmd; + int ret; + int outcnt; + + if (!vgdev->vqs_ready) + return -ENODEV; + + sg_init_one(&ccmd, vbuf->buf, vbuf->size); + sgs[0] = &ccmd; + outcnt = 1; + + spin_lock(&vgdev->cursorq.qlock); +retry: + ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC); + if (ret == -ENOSPC) { + spin_unlock(&vgdev->cursorq.qlock); + wait_event(vgdev->cursorq.ack_queue, vq->num_free); + spin_lock(&vgdev->cursorq.qlock); + goto retry; + } else { + virtqueue_kick(vq); + } + + spin_unlock(&vgdev->cursorq.qlock); + + if (!ret) + ret = vq->num_free; + return ret; +} + +/* just create gem objects for userspace and long lived objects, + just use dma_alloced pages for the queue objects? */ + +/* create a basic resource */ +void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t format, + uint32_t width, + uint32_t height) +{ + struct virtio_gpu_resource_create_2d *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->format = cpu_to_le32(format); + cmd_p->width = cpu_to_le32(width); + cmd_p->height = cpu_to_le32(height); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id) +{ + struct virtio_gpu_resource_unref *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF); + cmd_p->resource_id = cpu_to_le32(resource_id); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, + uint32_t resource_id) +{ + struct virtio_gpu_resource_detach_backing *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING); + cmd_p->resource_id = cpu_to_le32(resource_id); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, + uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + uint32_t x, uint32_t y) +{ + struct virtio_gpu_set_scanout *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SET_SCANOUT); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->scanout_id = cpu_to_le32(scanout_id); + cmd_p->r.width = cpu_to_le32(width); + cmd_p->r.height = cpu_to_le32(height); + cmd_p->r.x = cpu_to_le32(x); + cmd_p->r.y = cpu_to_le32(y); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height) +{ + struct virtio_gpu_resource_flush *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->r.width = cpu_to_le32(width); + cmd_p->r.height = cpu_to_le32(height); + cmd_p->r.x = cpu_to_le32(x); + cmd_p->r.y = cpu_to_le32(y); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, + uint32_t resource_id, uint64_t offset, + __le32 width, __le32 height, + __le32 x, __le32 y, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_transfer_to_host_2d *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->offset = cpu_to_le64(offset); + cmd_p->r.width = width; + cmd_p->r.height = height; + cmd_p->r.x = x; + cmd_p->r.y = y; + + if (fence) + virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +static void +virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + struct virtio_gpu_mem_entry *ents, + uint32_t nents, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_resource_attach_backing *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->nr_entries = cpu_to_le32(nents); + + vbuf->data_buf = ents; + vbuf->data_size = sizeof(*ents) * nents; + + if (fence) + virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtio_gpu_resp_display_info *resp = + (struct virtio_gpu_resp_display_info *)vbuf->resp_buf; + int i; + + spin_lock(&vgdev->display_info_lock); + for (i = 0; i < vgdev->num_scanouts; i++) { + vgdev->outputs[i].info = resp->pmodes[i]; + if (resp->pmodes[i].enabled) { + DRM_DEBUG("output %d: %dx%d+%d+%d", i, + le32_to_cpu(resp->pmodes[i].r.width), + le32_to_cpu(resp->pmodes[i].r.height), + le32_to_cpu(resp->pmodes[i].r.x), + le32_to_cpu(resp->pmodes[i].r.y)); + } else { + DRM_DEBUG("output %d: disabled", i); + } + } + + spin_unlock(&vgdev->display_info_lock); + wake_up(&vgdev->resp_wq); + + if (!drm_helper_hpd_irq_event(vgdev->ddev)) + drm_kms_helper_hotplug_event(vgdev->ddev); +} + +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_ctrl_hdr *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + void *resp_buf; + + resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_display_info), + GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + cmd_p = virtio_gpu_alloc_cmd_resp + (vgdev, &virtio_gpu_cmd_get_display_info_cb, &vbuf, + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_display_info), + resp_buf); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->type = cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); + return 0; +} + +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *obj, + uint32_t resource_id, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_mem_entry *ents; + struct scatterlist *sg; + int si; + + if (!obj->pages) { + int ret; + ret = virtio_gpu_object_get_sg_table(vgdev, obj); + if (ret) + return ret; + } + + /* gets freed when the ring has consumed it */ + ents = kmalloc_array(obj->pages->nents, + sizeof(struct virtio_gpu_mem_entry), + GFP_KERNEL); + if (!ents) { + DRM_ERROR("failed to allocate ent list\n"); + return -ENOMEM; + } + + for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) { + ents[si].addr = cpu_to_le64(sg_phys(sg)); + ents[si].length = cpu_to_le32(sg->length); + ents[si].padding = 0; + } + + virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id, + ents, obj->pages->nents, + fence); + obj->hw_res_handle = resource_id; + return 0; +} + +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, + struct virtio_gpu_output *output) +{ + struct virtio_gpu_vbuffer *vbuf; + struct virtio_gpu_update_cursor *cur_p; + + output->cursor.pos.scanout_id = cpu_to_le32(output->index); + cur_p = virtio_gpu_alloc_cursor(vgdev, &vbuf); + memcpy(cur_p, &output->cursor, sizeof(output->cursor)); + virtio_gpu_queue_cursor(vgdev, vbuf); +} diff --git a/include/drm/drmP.h b/include/drm/drmP.h index df6d9970d9a4..59ce5587ed90 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -814,6 +814,7 @@ struct drm_device { #endif struct platform_device *platformdev; /**< Platform device struture */ + struct virtio_device *virtdev; struct drm_sg_mem *sg; /**< Scatter gather memory */ unsigned int num_crtcs; /**< Number of CRTCs on this device */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 1a0006a76b00..4460e5820b0e 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -430,6 +430,7 @@ header-y += virtio_balloon.h header-y += virtio_blk.h header-y += virtio_config.h header-y += virtio_console.h +header-y += virtio_gpu.h header-y += virtio_ids.h header-y += virtio_input.h header-y += virtio_net.h diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h new file mode 100644 index 000000000000..571c4cbd3f93 --- /dev/null +++ b/include/uapi/linux/virtio_gpu.h @@ -0,0 +1,204 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This header is BSD licensed so anyone can use the definitions + * to implement compatible drivers/servers: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef VIRTIO_GPU_HW_H +#define VIRTIO_GPU_HW_H + +enum virtio_gpu_ctrl_type { + VIRTIO_GPU_UNDEFINED = 0, + + /* 2d commands */ + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_UNREF, + VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + + /* cursor commands */ + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, + VIRTIO_GPU_CMD_MOVE_CURSOR, + + /* success responses */ + VIRTIO_GPU_RESP_OK_NODATA = 0x1100, + VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + + /* error responses */ + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, +}; + +#define VIRTIO_GPU_FLAG_FENCE (1 << 0) + +struct virtio_gpu_ctrl_hdr { + __le32 type; + __le32 flags; + __le64 fence_id; + __le32 ctx_id; + __le32 padding; +}; + +/* data passed in the cursor vq */ + +struct virtio_gpu_cursor_pos { + __le32 scanout_id; + __le32 x; + __le32 y; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */ +struct virtio_gpu_update_cursor { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_cursor_pos pos; /* update & move */ + __le32 resource_id; /* update only */ + __le32 hot_x; /* update only */ + __le32 hot_y; /* update only */ + __le32 padding; +}; + +/* data passed in the control vq, 2d related */ + +struct virtio_gpu_rect { + __le32 x; + __le32 y; + __le32 width; + __le32 height; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_UNREF */ +struct virtio_gpu_resource_unref { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */ +struct virtio_gpu_resource_create_2d { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 format; + __le32 width; + __le32 height; +}; + +/* VIRTIO_GPU_CMD_SET_SCANOUT */ +struct virtio_gpu_set_scanout { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + __le32 scanout_id; + __le32 resource_id; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */ +struct virtio_gpu_resource_flush { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + __le32 resource_id; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */ +struct virtio_gpu_transfer_to_host_2d { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + __le64 offset; + __le32 resource_id; + __le32 padding; +}; + +struct virtio_gpu_mem_entry { + __le64 addr; + __le32 length; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */ +struct virtio_gpu_resource_attach_backing { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 nr_entries; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */ +struct virtio_gpu_resource_detach_backing { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 padding; +}; + +/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */ +#define VIRTIO_GPU_MAX_SCANOUTS 16 +struct virtio_gpu_resp_display_info { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_display_one { + struct virtio_gpu_rect r; + __le32 enabled; + __le32 flags; + } pmodes[VIRTIO_GPU_MAX_SCANOUTS]; +}; + +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) + +struct virtio_gpu_config { + __u32 events_read; + __u32 events_clear; + __u32 num_scanouts; + __u32 reserved; +}; + +/* simple formats for fbcon/X use */ +enum virtio_gpu_formats { + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, + + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, + + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, +}; + +#endif diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 5f60aa4be50a..77925f587b15 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -39,6 +39,7 @@ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ #define VIRTIO_ID_CAIF 12 /* Virtio caif */ +#define VIRTIO_ID_GPU 16 /* virtio GPU */ #define VIRTIO_ID_INPUT 18 /* virtio input */ #endif /* _LINUX_VIRTIO_IDS_H */ -- cgit v1.2.3 From 81629cba1f12683f2a312e6fb41a3aa662f99f89 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 20 Apr 2015 16:42:01 -0400 Subject: drm/amdgpu: add amdgpu uapi header (v4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header defines the ioctl interface to the driver. v2: remove stale tiling defines v3: add appropriate padding v4: remove executable bits on header Acked-by: Christian König Acked-by: Jammy Zhou Signed-off-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 590 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 include/uapi/drm/amdgpu_drm.h (limited to 'include') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h new file mode 100644 index 000000000000..9e771fb858b4 --- /dev/null +++ b/include/uapi/drm/amdgpu_drm.h @@ -0,0 +1,590 @@ +/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __AMDGPU_DRM_H__ +#define __AMDGPU_DRM_H__ + +#include + +#define DRM_AMDGPU_GEM_CREATE 0x00 +#define DRM_AMDGPU_GEM_MMAP 0x01 +#define DRM_AMDGPU_CTX 0x02 +#define DRM_AMDGPU_BO_LIST 0x03 +#define DRM_AMDGPU_CS 0x04 +#define DRM_AMDGPU_INFO 0x05 +#define DRM_AMDGPU_GEM_METADATA 0x06 +#define DRM_AMDGPU_GEM_WAIT_IDLE 0x07 +#define DRM_AMDGPU_GEM_VA 0x08 +#define DRM_AMDGPU_WAIT_CS 0x09 +#define DRM_AMDGPU_GEM_OP 0x10 +#define DRM_AMDGPU_GEM_USERPTR 0x11 + +#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) +#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) +#define DRM_IOCTL_AMDGPU_CTX DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx) +#define DRM_IOCTL_AMDGPU_BO_LIST DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list) +#define DRM_IOCTL_AMDGPU_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs) +#define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info) +#define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata) +#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle) +#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, union drm_amdgpu_gem_va) +#define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs) +#define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op) +#define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) + +#define AMDGPU_GEM_DOMAIN_CPU 0x1 +#define AMDGPU_GEM_DOMAIN_GTT 0x2 +#define AMDGPU_GEM_DOMAIN_VRAM 0x4 +#define AMDGPU_GEM_DOMAIN_GDS 0x8 +#define AMDGPU_GEM_DOMAIN_GWS 0x10 +#define AMDGPU_GEM_DOMAIN_OA 0x20 + +#define AMDGPU_GEM_DOMAIN_MASK 0x3F + +/* Flag that CPU access will be required for the case of VRAM domain */ +#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) +/* Flag that CPU access will not work, this VRAM domain is invisible */ +#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) +/* Flag that un-cached attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_UC (1 << 2) +/* Flag that USWC attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_WC (1 << 3) + +/* Flag mask for GTT domain_flags */ +#define AMDGPU_GEM_CREATE_CPU_GTT_MASK \ + (AMDGPU_GEM_CREATE_CPU_GTT_WC | \ + AMDGPU_GEM_CREATE_CPU_GTT_UC | \ + AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | \ + AMDGPU_GEM_CREATE_NO_CPU_ACCESS) + +struct drm_amdgpu_gem_create_in { + /** the requested memory size */ + uint64_t bo_size; + /** physical start_addr alignment in bytes for some HW requirements */ + uint64_t alignment; + /** the requested memory domains */ + uint64_t domains; + /** allocation flags */ + uint64_t domain_flags; +}; + +struct drm_amdgpu_gem_create_out { + /** returned GEM object handle */ + uint32_t handle; + uint32_t _pad; +}; + +union drm_amdgpu_gem_create { + struct drm_amdgpu_gem_create_in in; + struct drm_amdgpu_gem_create_out out; +}; + +/** Opcode to create new residency list. */ +#define AMDGPU_BO_LIST_OP_CREATE 0 +/** Opcode to destroy previously created residency list */ +#define AMDGPU_BO_LIST_OP_DESTROY 1 +/** Opcode to update resource information in the list */ +#define AMDGPU_BO_LIST_OP_UPDATE 2 + +struct drm_amdgpu_bo_list_in { + /** Type of operation */ + uint32_t operation; + /** Handle of list or 0 if we want to create one */ + uint32_t list_handle; + /** Number of BOs in list */ + uint32_t bo_number; + /** Size of each element describing BO */ + uint32_t bo_info_size; + /** Pointer to array describing BOs */ + uint64_t bo_info_ptr; +}; + +struct drm_amdgpu_bo_list_entry { + /** Handle of BO */ + uint32_t bo_handle; + /** New (if specified) BO priority to be used during migration */ + uint32_t bo_priority; +}; + +struct drm_amdgpu_bo_list_out { + /** Handle of resource list */ + uint32_t list_handle; + uint32_t _pad; +}; + +union drm_amdgpu_bo_list { + struct drm_amdgpu_bo_list_in in; + struct drm_amdgpu_bo_list_out out; +}; + +/* context related */ +#define AMDGPU_CTX_OP_ALLOC_CTX 1 +#define AMDGPU_CTX_OP_FREE_CTX 2 +#define AMDGPU_CTX_OP_QUERY_STATE 3 + +#define AMDGPU_CTX_OP_STATE_RUNNING 1 + +struct drm_amdgpu_ctx_in { + uint32_t op; + uint32_t flags; + uint32_t ctx_id; + uint32_t _pad; +}; + +union drm_amdgpu_ctx_out { + struct { + uint32_t ctx_id; + uint32_t _pad; + } alloc; + + struct { + uint64_t flags; + uint64_t hangs; + } state; +}; + +union drm_amdgpu_ctx { + struct drm_amdgpu_ctx_in in; + union drm_amdgpu_ctx_out out; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define AMDGPU_GEM_USERPTR_READONLY (1 << 0) +#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1) +#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2) +#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_amdgpu_gem_userptr { + uint64_t addr; + uint64_t size; + uint32_t flags; + uint32_t handle; +}; + +#define AMDGPU_TILING_MACRO 0x1 +#define AMDGPU_TILING_MICRO 0x2 +#define AMDGPU_TILING_SWAP_16BIT 0x4 +#define AMDGPU_TILING_R600_NO_SCANOUT AMDGPU_TILING_SWAP_16BIT +#define AMDGPU_TILING_SWAP_32BIT 0x8 +/* this object requires a surface when mapped - i.e. front buffer */ +#define AMDGPU_TILING_SURFACE 0x10 +#define AMDGPU_TILING_MICRO_SQUARE 0x20 +#define AMDGPU_TILING_EG_BANKW_SHIFT 8 +#define AMDGPU_TILING_EG_BANKW_MASK 0xf +#define AMDGPU_TILING_EG_BANKH_SHIFT 12 +#define AMDGPU_TILING_EG_BANKH_MASK 0xf +#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 +#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf +#define AMDGPU_TILING_EG_TILE_SPLIT_SHIFT 24 +#define AMDGPU_TILING_EG_TILE_SPLIT_MASK 0xf +#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 +#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf + +#define AMDGPU_GEM_METADATA_OP_SET_METADATA 1 +#define AMDGPU_GEM_METADATA_OP_GET_METADATA 2 + +/** The same structure is shared for input/output */ +struct drm_amdgpu_gem_metadata { + uint32_t handle; /* GEM Object handle */ + uint32_t op; /** Do we want get or set metadata */ + struct { + uint64_t flags; + uint64_t tiling_info; /* family specific tiling info */ + uint32_t data_size_bytes; + uint32_t data[64]; + } data; +}; + +struct drm_amdgpu_gem_mmap_in { + uint32_t handle; /** the GEM object handle */ + uint32_t _pad; +}; + +struct drm_amdgpu_gem_mmap_out { + uint64_t addr_ptr; /** mmap offset from the vma offset manager */ +}; + +union drm_amdgpu_gem_mmap { + struct drm_amdgpu_gem_mmap_in in; + struct drm_amdgpu_gem_mmap_out out; +}; + +struct drm_amdgpu_gem_wait_idle_in { + uint32_t handle; /* GEM object handle */ + uint32_t flags; + uint64_t timeout; /* Timeout to wait. If 0 then returned immediately with the status */ +}; + +struct drm_amdgpu_gem_wait_idle_out { + uint32_t status; /* BO status: 0 - BO is idle, 1 - BO is busy */ + uint32_t domain; /* Returned current memory domain */ +}; + +union drm_amdgpu_gem_wait_idle { + struct drm_amdgpu_gem_wait_idle_in in; + struct drm_amdgpu_gem_wait_idle_out out; +}; + +struct drm_amdgpu_wait_cs_in { + uint64_t handle; + uint64_t timeout; + uint32_t ip_type; + uint32_t ip_instance; + uint32_t ring; + uint32_t _pad; +}; + +struct drm_amdgpu_wait_cs_out { + uint64_t status; +}; + +union drm_amdgpu_wait_cs { + struct drm_amdgpu_wait_cs_in in; + struct drm_amdgpu_wait_cs_out out; +}; + +/* Sets or returns a value associated with a buffer. */ +struct drm_amdgpu_gem_op { + uint32_t handle; /* buffer */ + uint32_t op; /* AMDGPU_GEM_OP_* */ + uint64_t value; /* input or return value */ +}; + +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_INITIAL_DOMAIN 1 + +#define AMDGPU_VA_OP_MAP 1 +#define AMDGPU_VA_OP_UNMAP 2 + +#define AMDGPU_VA_RESULT_OK 0 +#define AMDGPU_VA_RESULT_ERROR 1 +#define AMDGPU_VA_RESULT_VA_INVALID_ALIGNMENT 2 + +/* Mapping flags */ +/* readable mapping */ +#define AMDGPU_VM_PAGE_READABLE (1 << 1) +/* writable mapping */ +#define AMDGPU_VM_PAGE_WRITEABLE (1 << 2) +/* executable mapping, new for VI */ +#define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) + +struct drm_amdgpu_gem_va_in { + /* GEM object handle */ + uint32_t handle; + uint32_t _pad; + /* map or unmap*/ + uint32_t operation; + /* specify mapping flags */ + uint32_t flags; + /* va address to assign . Must be correctly aligned.*/ + uint64_t va_address; + /* Specify offset inside of BO to assign. Must be correctly aligned.*/ + uint64_t offset_in_bo; + /* Specify mapping size. If 0 and offset is 0 then map the whole BO.*/ + /* Must be correctly aligned. */ + uint64_t map_size; +}; + +struct drm_amdgpu_gem_va_out { + uint32_t result; + uint32_t _pad; +}; + +union drm_amdgpu_gem_va { + struct drm_amdgpu_gem_va_in in; + struct drm_amdgpu_gem_va_out out; +}; + +#define AMDGPU_HW_IP_GFX 0 +#define AMDGPU_HW_IP_COMPUTE 1 +#define AMDGPU_HW_IP_DMA 2 +#define AMDGPU_HW_IP_UVD 3 +#define AMDGPU_HW_IP_VCE 4 +#define AMDGPU_HW_IP_NUM 5 + +#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1 + +#define AMDGPU_CHUNK_ID_IB 0x01 +#define AMDGPU_CHUNK_ID_FENCE 0x02 +struct drm_amdgpu_cs_chunk { + uint32_t chunk_id; + uint32_t length_dw; + uint64_t chunk_data; +}; + +struct drm_amdgpu_cs_in { + /** Rendering context id */ + uint32_t ctx_id; + /** Handle of resource list associated with CS */ + uint32_t bo_list_handle; + uint32_t num_chunks; + uint32_t _pad; + /* this points to uint64_t * which point to cs chunks */ + uint64_t chunks; +}; + +struct drm_amdgpu_cs_out { + uint64_t handle; +}; + +union drm_amdgpu_cs { + struct drm_amdgpu_cs_in in; + struct drm_amdgpu_cs_out out; +}; + +/* Specify flags to be used for IB */ + +/* This IB should be submitted to CE */ +#define AMDGPU_IB_FLAG_CE (1<<0) + +/* GDS is used by this IB */ +#define AMDGPU_IB_FLAG_GDS (1<<1) + +struct drm_amdgpu_cs_chunk_ib { + /** + * Handle of GEM object to be used as IB or 0 if it is already in + * residency list. + */ + uint32_t handle; + uint32_t flags; /* IB Flags */ + uint64_t va_start; /* Virtual address to begin IB execution */ + uint32_t ib_bytes; /* Size of submission */ + uint32_t ip_type; /* HW IP to submit to */ + uint32_t ip_instance; /* HW IP index of the same type to submit to */ + uint32_t ring; /* Ring index to submit to */ +}; + +struct drm_amdgpu_cs_chunk_fence { + uint32_t handle; + uint32_t offset; +}; + +struct drm_amdgpu_cs_chunk_data { + union { + struct drm_amdgpu_cs_chunk_ib ib_data; + struct drm_amdgpu_cs_chunk_fence fence_data; + }; +}; + +/** + * Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU + * + */ +#define AMDGPU_IDS_FLAGS_FUSION 0x1 + +/* indicate if acceleration can be working */ +#define AMDGPU_INFO_ACCEL_WORKING 0x00 +/* get the crtc_id from the mode object id? */ +#define AMDGPU_INFO_CRTC_FROM_ID 0x01 +/* query hw IP info */ +#define AMDGPU_INFO_HW_IP_INFO 0x02 +/* query hw IP instance count for the specified type */ +#define AMDGPU_INFO_HW_IP_COUNT 0x03 +/* timestamp for GL_ARB_timer_query */ +#define AMDGPU_INFO_TIMESTAMP 0x05 +/* Query the firmware version */ +#define AMDGPU_INFO_FW_VERSION 0x0e + /* Subquery id: Query VCE firmware version */ + #define AMDGPU_INFO_FW_VCE 0x1 + /* Subquery id: Query UVD firmware version */ + #define AMDGPU_INFO_FW_UVD 0x2 + /* Subquery id: Query GMC firmware version */ + #define AMDGPU_INFO_FW_GMC 0x03 + /* Subquery id: Query GFX ME firmware version */ + #define AMDGPU_INFO_FW_GFX_ME 0x04 + /* Subquery id: Query GFX PFP firmware version */ + #define AMDGPU_INFO_FW_GFX_PFP 0x05 + /* Subquery id: Query GFX CE firmware version */ + #define AMDGPU_INFO_FW_GFX_CE 0x06 + /* Subquery id: Query GFX RLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC 0x07 + /* Subquery id: Query GFX MEC firmware version */ + #define AMDGPU_INFO_FW_GFX_MEC 0x08 + /* Subquery id: Query SMC firmware version */ + #define AMDGPU_INFO_FW_SMC 0x0a + /* Subquery id: Query SDMA firmware version */ + #define AMDGPU_INFO_FW_SDMA 0x0b +/* number of bytes moved for TTM migration */ +#define AMDGPU_INFO_NUM_BYTES_MOVED 0x0f +/* the used VRAM size */ +#define AMDGPU_INFO_VRAM_USAGE 0x10 +/* the used GTT size */ +#define AMDGPU_INFO_GTT_USAGE 0x11 +/* Information about GDS, etc. resource configuration */ +#define AMDGPU_INFO_GDS_CONFIG 0x13 +/* Query information about VRAM and GTT domains */ +#define AMDGPU_INFO_VRAM_GTT 0x14 +/* Query information about register in MMR address space*/ +#define AMDGPU_INFO_READ_MMR_REG 0x15 +/* Query information about device: rev id, family, etc. */ +#define AMDGPU_INFO_DEV_INFO 0x16 +/* visible vram usage */ +#define AMDGPU_INFO_VIS_VRAM_USAGE 0x17 + +#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0 +#define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff +#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT 8 +#define AMDGPU_INFO_MMR_SH_INDEX_MASK 0xff + +/* Input structure for the INFO ioctl */ +struct drm_amdgpu_info { + /* Where the return value will be stored */ + uint64_t return_pointer; + /* The size of the return value. Just like "size" in "snprintf", + * it limits how many bytes the kernel can write. */ + uint32_t return_size; + /* The query request id. */ + uint32_t query; + + union { + struct { + uint32_t id; + uint32_t _pad; + } mode_crtc; + + struct { + /** AMDGPU_HW_IP_* */ + uint32_t type; + /** + * Index of the IP if there are more IPs of the same type. + * Ignored by AMDGPU_INFO_HW_IP_COUNT. + */ + uint32_t ip_instance; + } query_hw_ip; + + struct { + uint32_t dword_offset; + uint32_t count; /* number of registers to read */ + uint32_t instance; + uint32_t flags; + } read_mmr_reg; + + struct { + /** AMDGPU_INFO_FW_* */ + uint32_t fw_type; + /** Index of the IP if there are more IPs of the same type. */ + uint32_t ip_instance; + /** + * Index of the engine. Whether this is used depends + * on the firmware type. (e.g. MEC, SDMA) + */ + uint32_t index; + uint32_t _pad; + } query_fw; + }; +}; + +struct drm_amdgpu_info_gds { + /** GDS GFX partition size */ + uint32_t gds_gfx_partition_size; + /** GDS compute partition size */ + uint32_t compute_partition_size; + /** total GDS memory size */ + uint32_t gds_total_size; + /** GWS size per GFX partition */ + uint32_t gws_per_gfx_partition; + /** GSW size per compute partition */ + uint32_t gws_per_compute_partition; + /** OA size per GFX partition */ + uint32_t oa_per_gfx_partition; + /** OA size per compute partition */ + uint32_t oa_per_compute_partition; + uint32_t _pad; +}; + +struct drm_amdgpu_info_vram_gtt { + uint64_t vram_size; + uint64_t vram_cpu_accessible_size; + uint64_t gtt_size; +}; + +struct drm_amdgpu_info_firmware { + uint32_t ver; + uint32_t feature; +}; + +struct drm_amdgpu_info_device { + /** PCI Device ID */ + uint32_t device_id; + /** Internal chip revision: A0, A1, etc.) */ + uint32_t chip_rev; + uint32_t external_rev; + /** Revision id in PCI Config space */ + uint32_t pci_rev; + uint32_t family; + uint32_t num_shader_engines; + uint32_t num_shader_arrays_per_engine; + uint32_t gpu_counter_freq; /* in KHz */ + uint64_t max_engine_clock; /* in KHz */ + /* cu information */ + uint32_t cu_active_number; + uint32_t cu_ao_mask; + uint32_t cu_bitmap[4][4]; + /** Render backend pipe mask. One render backend is CB+DB. */ + uint32_t enabled_rb_pipes_mask; + uint32_t num_rb_pipes; + uint32_t num_hw_gfx_contexts; + uint32_t _pad; + uint64_t ids_flags; + /** Starting virtual address for UMDs. */ + uint64_t virtual_address_offset; + /** Required alignment of virtual addresses. */ + uint32_t virtual_address_alignment; + /** Page table entry - fragment size */ + uint32_t pte_fragment_size; + uint32_t gart_page_size; +}; + +struct drm_amdgpu_info_hw_ip { + /** Version of h/w IP */ + uint32_t hw_ip_version_major; + uint32_t hw_ip_version_minor; + /** Capabilities */ + uint64_t capabilities_flags; + /** Bitmask of available rings. Bit 0 means ring 0, etc. */ + uint32_t available_rings; + uint32_t _pad; +}; + +/* + * Supported GPU families + */ +#define AMDGPU_FAMILY_UNKNOWN 0 +#define AMDGPU_FAMILY_CI 120 /* Bonaire, Hawaii */ +#define AMDGPU_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */ +#define AMDGPU_FAMILY_VI 130 /* Iceland, Tonga */ +#define AMDGPU_FAMILY_CZ 135 /* Carrizo */ + +#endif -- cgit v1.2.3 From 886712881da15b7f455c43a4ce4121f20035c0fa Mon Sep 17 00:00:00 2001 From: Jammy Zhou Date: Wed, 6 May 2015 18:44:29 +0800 Subject: drm/amdgpu: remove AMDGPU_GEM_CREATE_CPU_GTT_UC This flag isn't used by user mode drivers, remove it to avoid confusion. And rename GTT_WC to GTT_USWC to make it clear. Signed-off-by: Jammy Zhou Reviewed-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 10 ++-------- include/uapi/drm/amdgpu_drm.h | 7 ++----- 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index f5e17f95e812..992b7f5843bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -132,10 +132,7 @@ void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *rbo, u32 domain) } if (domain & AMDGPU_GEM_DOMAIN_GTT) { - if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_UC) { - rbo->placements[c].fpfn = 0; - rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_TT; - } else if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_WC) { + if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) { rbo->placements[c].fpfn = 0; rbo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED; @@ -146,10 +143,7 @@ void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *rbo, u32 domain) } if (domain & AMDGPU_GEM_DOMAIN_CPU) { - if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_UC) { - rbo->placements[c].fpfn = 0; - rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_SYSTEM; - } else if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_WC) { + if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) { rbo->placements[c].fpfn = 0; rbo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_UNCACHED; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 9e771fb858b4..77bc5740fd7c 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -73,15 +73,12 @@ #define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) /* Flag that CPU access will not work, this VRAM domain is invisible */ #define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) -/* Flag that un-cached attributes should be used for GTT */ -#define AMDGPU_GEM_CREATE_CPU_GTT_UC (1 << 2) /* Flag that USWC attributes should be used for GTT */ -#define AMDGPU_GEM_CREATE_CPU_GTT_WC (1 << 3) +#define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2) /* Flag mask for GTT domain_flags */ #define AMDGPU_GEM_CREATE_CPU_GTT_MASK \ - (AMDGPU_GEM_CREATE_CPU_GTT_WC | \ - AMDGPU_GEM_CREATE_CPU_GTT_UC | \ + (AMDGPU_GEM_CREATE_CPU_GTT_USWC | \ AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | \ AMDGPU_GEM_CREATE_NO_CPU_ACCESS) -- cgit v1.2.3 From 66b3cf2ab38f47db2d07fe24a00972fbf822cd74 Mon Sep 17 00:00:00 2001 From: Jammy Zhou Date: Fri, 8 May 2015 17:29:40 +0800 Subject: drm/amdgpu: add ctx_id to the WAIT_CS IOCTL (v4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is required to support fence per context. v2: add amdgpu_ctx_get/put v3: improve get/put v4: squash hlock fix Signed-off-by: Jammy Zhou Reviewed-by: Christian König --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 6 ++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 30 ++++++++++++++++++++++++++++++ include/uapi/drm/amdgpu_drm.h | 2 +- 4 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index cef3a43ab0aa..bf0c607de195 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1902,6 +1902,8 @@ int amdgpu_ctx_query(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id,struct amdgpu_ctx_state *state); void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv); +struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id); +int amdgpu_ctx_put(struct amdgpu_ctx *ctx); extern int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index ffbe9aa9f232..86b93245bf9d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -768,8 +768,13 @@ int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data, uint64_t seq[AMDGPU_MAX_RINGS] = {0}; struct amdgpu_ring *ring = NULL; unsigned long timeout = amdgpu_gem_timeout(wait->in.timeout); + struct amdgpu_ctx *ctx; long r; + ctx = amdgpu_ctx_get(filp->driver_priv, wait->in.ctx_id); + if (ctx == NULL) + return -EINVAL; + r = amdgpu_cs_get_ring(adev, wait->in.ip_type, wait->in.ip_instance, wait->in.ring, &ring); if (r) @@ -778,6 +783,7 @@ int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data, seq[ring->idx] = wait->in.handle; r = amdgpu_fence_wait_seq_timeout(adev, seq, true, timeout); + amdgpu_ctx_put(ctx); if (r < 0) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 0dc3a4ebd5d3..bcd332e085f6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -151,3 +151,33 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, return r; } + +struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id) +{ + struct amdgpu_ctx *ctx; + struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; + + mutex_lock(&mgr->lock); + ctx = idr_find(&mgr->ctx_handles, id); + if (ctx) + kref_get(&ctx->refcount); + mutex_unlock(&mgr->lock); + return ctx; +} + +int amdgpu_ctx_put(struct amdgpu_ctx *ctx) +{ + struct amdgpu_fpriv *fpriv; + struct amdgpu_ctx_mgr *mgr; + + if (ctx == NULL) + return -EINVAL; + + fpriv = ctx->fpriv; + mgr = &fpriv->ctx_mgr; + mutex_lock(&mgr->lock); + kref_put(&ctx->refcount, amdgpu_ctx_do_release); + mutex_unlock(&mgr->lock); + + return 0; +} diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 77bc5740fd7c..ca0ea1efa3f4 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -260,7 +260,7 @@ struct drm_amdgpu_wait_cs_in { uint32_t ip_type; uint32_t ip_instance; uint32_t ring; - uint32_t _pad; + uint32_t ctx_id; }; struct drm_amdgpu_wait_cs_out { -- cgit v1.2.3 From aa2bdb2476206c7de4473850039daa705230c27b Mon Sep 17 00:00:00 2001 From: Jammy Zhou Date: Mon, 11 May 2015 23:49:34 +0800 Subject: drm/amdgpu: add CE preamble flag v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CE preamble IB can be dropped for the same context v2: use the flags directly v3: remove 'CE' for potential preamble usage by other rings Signed-off-by: Jammy Zhou Reviewed-by: Christian König --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 7 +++++++ drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 7 +++++++ include/uapi/drm/amdgpu_drm.h | 3 +++ 3 files changed, 17 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 7428c4305418..cec46ebae5f7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2518,6 +2518,13 @@ static void gfx_v7_0_ring_emit_ib(struct amdgpu_ring *ring, { u32 header, control = 0; u32 next_rptr = ring->wptr + 5; + + /* drop the CE preamble IB for the same context */ + if ((ring->type == AMDGPU_RING_TYPE_GFX) && + (ib->flags & AMDGPU_IB_FLAG_PREAMBLE) && + !ring->need_ctx_switch) + return; + if (ring->type == AMDGPU_RING_TYPE_COMPUTE) control |= INDIRECT_BUFFER_VALID; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 48de9204ff5e..fc8c46209db4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -3647,6 +3647,13 @@ static void gfx_v8_0_ring_emit_ib(struct amdgpu_ring *ring, { u32 header, control = 0; u32 next_rptr = ring->wptr + 5; + + /* drop the CE preamble IB for the same context */ + if ((ring->type == AMDGPU_RING_TYPE_GFX) && + (ib->flags & AMDGPU_IB_FLAG_PREAMBLE) && + !ring->need_ctx_switch) + return; + if (ring->type == AMDGPU_RING_TYPE_COMPUTE) control |= INDIRECT_BUFFER_VALID; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index ca0ea1efa3f4..fb428fe54186 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -369,6 +369,9 @@ union drm_amdgpu_cs { /* GDS is used by this IB */ #define AMDGPU_IB_FLAG_GDS (1<<1) +/* CE Preamble */ +#define AMDGPU_IB_FLAG_PREAMBLE (1<<2) + struct drm_amdgpu_cs_chunk_ib { /** * Handle of GEM object to be used as IB or 0 if it is already in -- cgit v1.2.3 From 02b70c8c9f0351f5ddf70716b9049f3fe50d62e7 Mon Sep 17 00:00:00 2001 From: Jammy Zhou Date: Tue, 12 May 2015 22:46:45 +0800 Subject: drm/amdgpu: expose the max virtual address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jammy Zhou Reviewed-by: Christian König --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 1 + include/uapi/drm/amdgpu_drm.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 02c450d0be1a..35185d6b7d46 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -428,6 +428,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file if (adev->flags & AMDGPU_IS_APU) dev_info.ids_flags |= AMDGPU_IDS_FLAGS_FUSION; dev_info.virtual_address_offset = AMDGPU_VA_RESERVED_SIZE; + dev_info.virtual_address_max = (uint64_t)adev->vm_manager.max_pfn * AMDGPU_GPU_PAGE_SIZE; dev_info.virtual_address_alignment = max(PAGE_SIZE, 0x10000UL); dev_info.pte_fragment_size = (1 << AMDGPU_LOG2_PAGES_PER_FRAG) * AMDGPU_GPU_PAGE_SIZE; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index fb428fe54186..65da7cd16c0f 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -560,6 +560,8 @@ struct drm_amdgpu_info_device { uint64_t ids_flags; /** Starting virtual address for UMDs. */ uint64_t virtual_address_offset; + /** The maximum virtual address */ + uint64_t virtual_address_max; /** Required alignment of virtual addresses. */ uint32_t virtual_address_alignment; /** Page table entry - fragment size */ -- cgit v1.2.3 From d94aed5a6c947b1fda346aff1fa316dacf4a1a5a Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Tue, 5 May 2015 21:13:49 +0200 Subject: drm/amdgpu: add and implement the GPU reset status query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Olšák Reviewed-by: Christian König Reviewed-by: Jammy Zhou --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 6 ++--- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 36 +++++++++++++++++++----------- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 1 + include/uapi/drm/amdgpu_drm.h | 11 ++++++++- 4 files changed, 37 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 66b5bd058799..ebff89eb2f4c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1040,7 +1040,7 @@ struct amdgpu_vm_manager { struct amdgpu_ctx_state { uint64_t flags; - uint64_t hangs; + uint32_t hangs; }; struct amdgpu_ctx { @@ -1049,6 +1049,7 @@ struct amdgpu_ctx { struct amdgpu_fpriv *fpriv; struct amdgpu_ctx_state state; uint32_t id; + unsigned reset_counter; }; struct amdgpu_ctx_mgr { @@ -1897,8 +1898,6 @@ int amdgpu_ctx_alloc(struct amdgpu_device *adev,struct amdgpu_fpriv *fpriv, uint32_t *id,uint32_t flags); int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id); -int amdgpu_ctx_query(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, - uint32_t id,struct amdgpu_ctx_state *state); void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv); struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id); @@ -2006,6 +2005,7 @@ struct amdgpu_device { atomic64_t vram_vis_usage; atomic64_t gtt_usage; atomic64_t num_bytes_moved; + atomic_t gpu_reset_counter; /* display */ struct amdgpu_mode_info mode_info; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index bcd332e085f6..6c66ac8a1891 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -81,21 +81,36 @@ int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint return -EINVAL; } -int amdgpu_ctx_query(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id, struct amdgpu_ctx_state *state) +static int amdgpu_ctx_query(struct amdgpu_device *adev, + struct amdgpu_fpriv *fpriv, uint32_t id, + union drm_amdgpu_ctx_out *out) { struct amdgpu_ctx *ctx; struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; + unsigned reset_counter; mutex_lock(&mgr->lock); ctx = idr_find(&mgr->ctx_handles, id); - if (ctx) { - /* state should alter with CS activity */ - *state = ctx->state; + if (!ctx) { mutex_unlock(&mgr->lock); - return 0; + return -EINVAL; } + + /* TODO: these two are always zero */ + out->state.flags = ctx->state.flags; + out->state.hangs = ctx->state.hangs; + + /* determine if a GPU reset has occured since the last call */ + reset_counter = atomic_read(&adev->gpu_reset_counter); + /* TODO: this should ideally return NO, GUILTY, or INNOCENT. */ + if (ctx->reset_counter == reset_counter) + out->state.reset_status = AMDGPU_CTX_NO_RESET; + else + out->state.reset_status = AMDGPU_CTX_UNKNOWN_RESET; + ctx->reset_counter = reset_counter; + mutex_unlock(&mgr->lock); - return -EINVAL; + return 0; } void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv) @@ -115,12 +130,11 @@ void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv) } int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, - struct drm_file *filp) + struct drm_file *filp) { int r; uint32_t id; uint32_t flags; - struct amdgpu_ctx_state state; union drm_amdgpu_ctx *args = data; struct amdgpu_device *adev = dev->dev_private; @@ -139,11 +153,7 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, r = amdgpu_ctx_free(adev, fpriv, id); break; case AMDGPU_CTX_OP_QUERY_STATE: - r = amdgpu_ctx_query(adev, fpriv, id, &state); - if (r == 0) { - args->out.state.flags = state.flags; - args->out.state.hangs = state.hangs; - } + r = amdgpu_ctx_query(adev, fpriv, id, &args->out); break; default: return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 61cf5ad78857..3448d9fe88cd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1781,6 +1781,7 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev) } adev->needs_reset = false; + atomic_inc(&adev->gpu_reset_counter); /* block TTM */ resched = ttm_bo_lock_delayed_workqueue(&adev->mman.bdev); diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 65da7cd16c0f..46580e950036 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -149,6 +149,12 @@ union drm_amdgpu_bo_list { #define AMDGPU_CTX_OP_STATE_RUNNING 1 +/* GPU reset status */ +#define AMDGPU_CTX_NO_RESET 0 +#define AMDGPU_CTX_GUILTY_RESET 1 /* this the context caused it */ +#define AMDGPU_CTX_INNOCENT_RESET 2 /* some other context caused it */ +#define AMDGPU_CTX_UNKNOWN_RESET 3 /* unknown cause */ + struct drm_amdgpu_ctx_in { uint32_t op; uint32_t flags; @@ -164,7 +170,10 @@ union drm_amdgpu_ctx_out { struct { uint64_t flags; - uint64_t hangs; + /** Number of resets caused by this context so far. */ + uint32_t hangs; + /** Reset status since the last call of the ioctl. */ + uint32_t reset_status; } state; }; -- cgit v1.2.3 From fbd76d59efe061c89d4ba14eef3a2cac1e3056c2 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Thu, 14 May 2015 23:48:26 +0200 Subject: drm/amdgpu: rework tiling flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Olšák Reviewed-by: Alex Deucher Acked-by: Christian König --- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 3 +- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 43 +------------- drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 92 ++++------------------------- drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 93 ++++-------------------------- drivers/gpu/drm/amd/amdgpu/dce_v8_0.c | 91 ++++------------------------- include/uapi/drm/amdgpu_drm.h | 40 +++++++------ 6 files changed, 58 insertions(+), 304 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index ef611986b2b6..73b7aad5a872 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -32,6 +32,7 @@ #include #include #include "amdgpu.h" +#include "cikd.h" #include @@ -135,7 +136,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, rbo = gem_to_amdgpu_bo(gobj); if (fb_tiled) - tiling_flags = AMDGPU_TILING_MACRO; + tiling_flags = AMDGPU_TILING_SET(ARRAY_MODE, GRPH_ARRAY_2D_TILED_THIN1); ret = amdgpu_bo_reserve(rbo, false); if (unlikely(ret != 0)) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index a721f5044557..b545f614628c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -459,49 +459,8 @@ int amdgpu_bo_fbdev_mmap(struct amdgpu_bo *bo, int amdgpu_bo_set_tiling_flags(struct amdgpu_bo *bo, u64 tiling_flags) { - unsigned bankw, bankh, mtaspect, tilesplit, stilesplit; - - bankw = (tiling_flags >> AMDGPU_TILING_EG_BANKW_SHIFT) & AMDGPU_TILING_EG_BANKW_MASK; - bankh = (tiling_flags >> AMDGPU_TILING_EG_BANKH_SHIFT) & AMDGPU_TILING_EG_BANKH_MASK; - mtaspect = (tiling_flags >> AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK; - tilesplit = (tiling_flags >> AMDGPU_TILING_EG_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_TILE_SPLIT_MASK; - stilesplit = (tiling_flags >> AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_MASK; - switch (bankw) { - case 0: - case 1: - case 2: - case 4: - case 8: - break; - default: + if (AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT) > 6) return -EINVAL; - } - switch (bankh) { - case 0: - case 1: - case 2: - case 4: - case 8: - break; - default: - return -EINVAL; - } - switch (mtaspect) { - case 0: - case 1: - case 2: - case 4: - case 8: - break; - default: - return -EINVAL; - } - if (tilesplit > 6) { - return -EINVAL; - } - if (stilesplit > 6) { - return -EINVAL; - } bo->tiling_flags = tiling_flags; return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index d412291ed70e..37b96236fe2c 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2008,61 +2008,6 @@ static void dce_v10_0_grph_enable(struct drm_crtc *crtc, bool enable) WREG32(mmGRPH_ENABLE + amdgpu_crtc->crtc_offset, 0); } -static void dce_v10_0_tiling_fields(uint64_t tiling_flags, unsigned *bankw, - unsigned *bankh, unsigned *mtaspect, - unsigned *tile_split) -{ - *bankw = (tiling_flags >> AMDGPU_TILING_EG_BANKW_SHIFT) & AMDGPU_TILING_EG_BANKW_MASK; - *bankh = (tiling_flags >> AMDGPU_TILING_EG_BANKH_SHIFT) & AMDGPU_TILING_EG_BANKH_MASK; - *mtaspect = (tiling_flags >> AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK; - *tile_split = (tiling_flags >> AMDGPU_TILING_EG_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_TILE_SPLIT_MASK; - switch (*bankw) { - default: - case 1: - *bankw = ADDR_SURF_BANK_WIDTH_1; - break; - case 2: - *bankw = ADDR_SURF_BANK_WIDTH_2; - break; - case 4: - *bankw = ADDR_SURF_BANK_WIDTH_4; - break; - case 8: - *bankw = ADDR_SURF_BANK_WIDTH_8; - break; - } - switch (*bankh) { - default: - case 1: - *bankh = ADDR_SURF_BANK_HEIGHT_1; - break; - case 2: - *bankh = ADDR_SURF_BANK_HEIGHT_2; - break; - case 4: - *bankh = ADDR_SURF_BANK_HEIGHT_4; - break; - case 8: - *bankh = ADDR_SURF_BANK_HEIGHT_8; - break; - } - switch (*mtaspect) { - default: - case 1: - *mtaspect = ADDR_SURF_MACRO_ASPECT_1; - break; - case 2: - *mtaspect = ADDR_SURF_MACRO_ASPECT_2; - break; - case 4: - *mtaspect = ADDR_SURF_MACRO_ASPECT_4; - break; - case 8: - *mtaspect = ADDR_SURF_MACRO_ASPECT_8; - break; - } -} - static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y, int atomic) @@ -2076,10 +2021,8 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, struct amdgpu_bo *rbo; uint64_t fb_location, tiling_flags; uint32_t fb_format, fb_pitch_pixels; - unsigned bankw, bankh, mtaspect, tile_split; u32 fb_swap = REG_SET_FIELD(0, GRPH_SWAP_CNTL, GRPH_ENDIAN_SWAP, ENDIAN_NONE); - /* XXX change to VI */ - u32 pipe_config = (adev->gfx.config.tile_mode_array[10] >> 6) & 0x1f; + u32 pipe_config; u32 tmp, viewport_w, viewport_h; int r; bool bypass_lut = false; @@ -2121,6 +2064,8 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, amdgpu_bo_get_tiling_flags(rbo, &tiling_flags); amdgpu_bo_unreserve(rbo); + pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); + switch (target_fb->pixel_format) { case DRM_FORMAT_C8: fb_format = REG_SET_FIELD(0, GRPH_CONTROL, GRPH_DEPTH, 0); @@ -2198,27 +2143,15 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, return -EINVAL; } - if (tiling_flags & AMDGPU_TILING_MACRO) { - unsigned tileb, index, num_banks, tile_split_bytes; - - dce_v10_0_tiling_fields(tiling_flags, &bankw, &bankh, &mtaspect, &tile_split); - /* Set NUM_BANKS. */ - /* Calculate the macrotile mode index. */ - tile_split_bytes = 64 << tile_split; - tileb = 8 * 8 * target_fb->bits_per_pixel / 8; - tileb = min(tile_split_bytes, tileb); + if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_2D_TILED_THIN1) { + unsigned bankw, bankh, mtaspect, tile_split, num_banks; - for (index = 0; tileb > 64; index++) { - tileb >>= 1; - } - - if (index >= 16) { - DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", - target_fb->bits_per_pixel, tile_split); - return -EINVAL; - } + bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH); + bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT); + mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT); + tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT); + num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS); - num_banks = (adev->gfx.config.macrotile_mode_array[index] >> 6) & 0x3; fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_NUM_BANKS, num_banks); fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_ARRAY_MODE, ARRAY_2D_TILED_THIN1); @@ -2230,14 +2163,11 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, mtaspect); fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_MICRO_TILE_MODE, ADDR_SURF_MICRO_TILING_DISPLAY); - } else if (tiling_flags & AMDGPU_TILING_MICRO) { + } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_1D_TILED_THIN1) { fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_ARRAY_MODE, ARRAY_1D_TILED_THIN1); } - /* Read the pipe config from the 2D TILED SCANOUT mode. - * It should be the same for the other modes too, but not all - * modes set the pipe config field. */ fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_PIPE_CONFIG, pipe_config); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 55fef15a4fcf..04a5d4cd75b6 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2006,61 +2006,6 @@ static void dce_v11_0_grph_enable(struct drm_crtc *crtc, bool enable) WREG32(mmGRPH_ENABLE + amdgpu_crtc->crtc_offset, 0); } -static void dce_v11_0_tiling_fields(uint64_t tiling_flags, unsigned *bankw, - unsigned *bankh, unsigned *mtaspect, - unsigned *tile_split) -{ - *bankw = (tiling_flags >> AMDGPU_TILING_EG_BANKW_SHIFT) & AMDGPU_TILING_EG_BANKW_MASK; - *bankh = (tiling_flags >> AMDGPU_TILING_EG_BANKH_SHIFT) & AMDGPU_TILING_EG_BANKH_MASK; - *mtaspect = (tiling_flags >> AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK; - *tile_split = (tiling_flags >> AMDGPU_TILING_EG_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_TILE_SPLIT_MASK; - switch (*bankw) { - default: - case 1: - *bankw = ADDR_SURF_BANK_WIDTH_1; - break; - case 2: - *bankw = ADDR_SURF_BANK_WIDTH_2; - break; - case 4: - *bankw = ADDR_SURF_BANK_WIDTH_4; - break; - case 8: - *bankw = ADDR_SURF_BANK_WIDTH_8; - break; - } - switch (*bankh) { - default: - case 1: - *bankh = ADDR_SURF_BANK_HEIGHT_1; - break; - case 2: - *bankh = ADDR_SURF_BANK_HEIGHT_2; - break; - case 4: - *bankh = ADDR_SURF_BANK_HEIGHT_4; - break; - case 8: - *bankh = ADDR_SURF_BANK_HEIGHT_8; - break; - } - switch (*mtaspect) { - default: - case 1: - *mtaspect = ADDR_SURF_MACRO_ASPECT_1; - break; - case 2: - *mtaspect = ADDR_SURF_MACRO_ASPECT_2; - break; - case 4: - *mtaspect = ADDR_SURF_MACRO_ASPECT_4; - break; - case 8: - *mtaspect = ADDR_SURF_MACRO_ASPECT_8; - break; - } -} - static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y, int atomic) @@ -2074,10 +2019,8 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, struct amdgpu_bo *rbo; uint64_t fb_location, tiling_flags; uint32_t fb_format, fb_pitch_pixels; - unsigned bankw, bankh, mtaspect, tile_split; u32 fb_swap = REG_SET_FIELD(0, GRPH_SWAP_CNTL, GRPH_ENDIAN_SWAP, ENDIAN_NONE); - /* XXX change to VI */ - u32 pipe_config = (adev->gfx.config.tile_mode_array[10] >> 6) & 0x1f; + u32 pipe_config; u32 tmp, viewport_w, viewport_h; int r; bool bypass_lut = false; @@ -2119,6 +2062,8 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, amdgpu_bo_get_tiling_flags(rbo, &tiling_flags); amdgpu_bo_unreserve(rbo); + pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); + switch (target_fb->pixel_format) { case DRM_FORMAT_C8: fb_format = REG_SET_FIELD(0, GRPH_CONTROL, GRPH_DEPTH, 0); @@ -2196,28 +2141,15 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, return -EINVAL; } - if (tiling_flags & AMDGPU_TILING_MACRO) { - unsigned tileb, index, num_banks, tile_split_bytes; - - dce_v11_0_tiling_fields(tiling_flags, &bankw, &bankh, &mtaspect, &tile_split); - /* Set NUM_BANKS. */ - /* Calculate the macrotile mode index. */ - tile_split_bytes = 64 << tile_split; - tileb = 8 * 8 * target_fb->bits_per_pixel / 8; - tileb = min(tile_split_bytes, tileb); + if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_2D_TILED_THIN1) { + unsigned bankw, bankh, mtaspect, tile_split, num_banks; - for (index = 0; tileb > 64; index++) { - tileb >>= 1; - } - - if (index >= 16) { - DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", - target_fb->bits_per_pixel, tile_split); - return -EINVAL; - } + bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH); + bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT); + mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT); + tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT); + num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS); - /* XXX fix me for VI */ - num_banks = (adev->gfx.config.macrotile_mode_array[index] >> 6) & 0x3; fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_NUM_BANKS, num_banks); fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_ARRAY_MODE, ARRAY_2D_TILED_THIN1); @@ -2229,14 +2161,11 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, mtaspect); fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_MICRO_TILE_MODE, ADDR_SURF_MICRO_TILING_DISPLAY); - } else if (tiling_flags & AMDGPU_TILING_MICRO) { + } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_1D_TILED_THIN1) { fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_ARRAY_MODE, ARRAY_1D_TILED_THIN1); } - /* Read the pipe config from the 2D TILED SCANOUT mode. - * It should be the same for the other modes too, but not all - * modes set the pipe config field. */ fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_PIPE_CONFIG, pipe_config); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index c1bc6935c88e..9f2ff8d374f3 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -1976,61 +1976,6 @@ static void dce_v8_0_grph_enable(struct drm_crtc *crtc, bool enable) WREG32(mmGRPH_ENABLE + amdgpu_crtc->crtc_offset, 0); } -static void dce_v8_0_tiling_fields(uint64_t tiling_flags, unsigned *bankw, - unsigned *bankh, unsigned *mtaspect, - unsigned *tile_split) -{ - *bankw = (tiling_flags >> AMDGPU_TILING_EG_BANKW_SHIFT) & AMDGPU_TILING_EG_BANKW_MASK; - *bankh = (tiling_flags >> AMDGPU_TILING_EG_BANKH_SHIFT) & AMDGPU_TILING_EG_BANKH_MASK; - *mtaspect = (tiling_flags >> AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK; - *tile_split = (tiling_flags >> AMDGPU_TILING_EG_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_TILE_SPLIT_MASK; - switch (*bankw) { - default: - case 1: - *bankw = ADDR_SURF_BANK_WIDTH_1; - break; - case 2: - *bankw = ADDR_SURF_BANK_WIDTH_2; - break; - case 4: - *bankw = ADDR_SURF_BANK_WIDTH_4; - break; - case 8: - *bankw = ADDR_SURF_BANK_WIDTH_8; - break; - } - switch (*bankh) { - default: - case 1: - *bankh = ADDR_SURF_BANK_HEIGHT_1; - break; - case 2: - *bankh = ADDR_SURF_BANK_HEIGHT_2; - break; - case 4: - *bankh = ADDR_SURF_BANK_HEIGHT_4; - break; - case 8: - *bankh = ADDR_SURF_BANK_HEIGHT_8; - break; - } - switch (*mtaspect) { - default: - case 1: - *mtaspect = ADDR_SURF_MACRO_TILE_ASPECT_1; - break; - case 2: - *mtaspect = ADDR_SURF_MACRO_TILE_ASPECT_2; - break; - case 4: - *mtaspect = ADDR_SURF_MACRO_TILE_ASPECT_4; - break; - case 8: - *mtaspect = ADDR_SURF_MACRO_TILE_ASPECT_8; - break; - } -} - static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y, int atomic) @@ -2044,9 +1989,8 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, struct amdgpu_bo *rbo; uint64_t fb_location, tiling_flags; uint32_t fb_format, fb_pitch_pixels; - unsigned bankw, bankh, mtaspect, tile_split; u32 fb_swap = (GRPH_ENDIAN_NONE << GRPH_SWAP_CNTL__GRPH_ENDIAN_SWAP__SHIFT); - u32 pipe_config = (adev->gfx.config.tile_mode_array[10] >> 6) & 0x1f; + u32 pipe_config; u32 tmp, viewport_w, viewport_h; int r; bool bypass_lut = false; @@ -2088,6 +2032,8 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, amdgpu_bo_get_tiling_flags(rbo, &tiling_flags); amdgpu_bo_unreserve(rbo); + pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); + switch (target_fb->pixel_format) { case DRM_FORMAT_C8: fb_format = ((GRPH_DEPTH_8BPP << GRPH_CONTROL__GRPH_DEPTH__SHIFT) | @@ -2158,27 +2104,15 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, return -EINVAL; } - if (tiling_flags & AMDGPU_TILING_MACRO) { - unsigned tileb, index, num_banks, tile_split_bytes; - - dce_v8_0_tiling_fields(tiling_flags, &bankw, &bankh, &mtaspect, &tile_split); - /* Set NUM_BANKS. */ - /* Calculate the macrotile mode index. */ - tile_split_bytes = 64 << tile_split; - tileb = 8 * 8 * target_fb->bits_per_pixel / 8; - tileb = min(tile_split_bytes, tileb); + if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_2D_TILED_THIN1) { + unsigned bankw, bankh, mtaspect, tile_split, num_banks; - for (index = 0; tileb > 64; index++) { - tileb >>= 1; - } - - if (index >= 16) { - DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", - target_fb->bits_per_pixel, tile_split); - return -EINVAL; - } + bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH); + bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT); + mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT); + tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT); + num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS); - num_banks = (adev->gfx.config.macrotile_mode_array[index] >> 6) & 0x3; fb_format |= (num_banks << GRPH_CONTROL__GRPH_NUM_BANKS__SHIFT); fb_format |= (GRPH_ARRAY_2D_TILED_THIN1 << GRPH_CONTROL__GRPH_ARRAY_MODE__SHIFT); fb_format |= (tile_split << GRPH_CONTROL__GRPH_TILE_SPLIT__SHIFT); @@ -2186,13 +2120,10 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, fb_format |= (bankh << GRPH_CONTROL__GRPH_BANK_HEIGHT__SHIFT); fb_format |= (mtaspect << GRPH_CONTROL__GRPH_MACRO_TILE_ASPECT__SHIFT); fb_format |= (DISPLAY_MICRO_TILING << GRPH_CONTROL__GRPH_MICRO_TILE_MODE__SHIFT); - } else if (tiling_flags & AMDGPU_TILING_MICRO) { + } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_1D_TILED_THIN1) { fb_format |= (GRPH_ARRAY_1D_TILED_THIN1 << GRPH_CONTROL__GRPH_ARRAY_MODE__SHIFT); } - /* Read the pipe config from the 2D TILED SCANOUT mode. - * It should be the same for the other modes too, but not all - * modes set the pipe config field. */ fb_format |= (pipe_config << GRPH_CONTROL__GRPH_PIPE_CONFIG__SHIFT); dce_v8_0_vga_enable(crtc, false); diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 46580e950036..d9b9b6f8de2b 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -199,24 +199,28 @@ struct drm_amdgpu_gem_userptr { uint32_t handle; }; -#define AMDGPU_TILING_MACRO 0x1 -#define AMDGPU_TILING_MICRO 0x2 -#define AMDGPU_TILING_SWAP_16BIT 0x4 -#define AMDGPU_TILING_R600_NO_SCANOUT AMDGPU_TILING_SWAP_16BIT -#define AMDGPU_TILING_SWAP_32BIT 0x8 -/* this object requires a surface when mapped - i.e. front buffer */ -#define AMDGPU_TILING_SURFACE 0x10 -#define AMDGPU_TILING_MICRO_SQUARE 0x20 -#define AMDGPU_TILING_EG_BANKW_SHIFT 8 -#define AMDGPU_TILING_EG_BANKW_MASK 0xf -#define AMDGPU_TILING_EG_BANKH_SHIFT 12 -#define AMDGPU_TILING_EG_BANKH_MASK 0xf -#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 -#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf -#define AMDGPU_TILING_EG_TILE_SPLIT_SHIFT 24 -#define AMDGPU_TILING_EG_TILE_SPLIT_MASK 0xf -#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 -#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf +/* same meaning as the GB_TILE_MODE and GL_MACRO_TILE_MODE fields */ +#define AMDGPU_TILING_ARRAY_MODE_SHIFT 0 +#define AMDGPU_TILING_ARRAY_MODE_MASK 0xf +#define AMDGPU_TILING_PIPE_CONFIG_SHIFT 4 +#define AMDGPU_TILING_PIPE_CONFIG_MASK 0x1f +#define AMDGPU_TILING_TILE_SPLIT_SHIFT 9 +#define AMDGPU_TILING_TILE_SPLIT_MASK 0x7 +#define AMDGPU_TILING_MICRO_TILE_MODE_SHIFT 12 +#define AMDGPU_TILING_MICRO_TILE_MODE_MASK 0x7 +#define AMDGPU_TILING_BANK_WIDTH_SHIFT 15 +#define AMDGPU_TILING_BANK_WIDTH_MASK 0x3 +#define AMDGPU_TILING_BANK_HEIGHT_SHIFT 17 +#define AMDGPU_TILING_BANK_HEIGHT_MASK 0x3 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_SHIFT 19 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_MASK 0x3 +#define AMDGPU_TILING_NUM_BANKS_SHIFT 21 +#define AMDGPU_TILING_NUM_BANKS_MASK 0x3 + +#define AMDGPU_TILING_SET(field, value) \ + (((value) & AMDGPU_TILING_##field##_MASK) << AMDGPU_TILING_##field##_SHIFT) +#define AMDGPU_TILING_GET(value, field) \ + (((value) >> AMDGPU_TILING_##field##_SHIFT) & AMDGPU_TILING_##field##_MASK) #define AMDGPU_GEM_METADATA_OP_SET_METADATA 1 #define AMDGPU_GEM_METADATA_OP_GET_METADATA 2 -- cgit v1.2.3 From dcc357e63727b63995dd869f015a748c9235eb42 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 19 May 2015 16:08:02 +0200 Subject: drm/amdgpu: drop allocation flag masks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not needed any more. Signed-off-by: Christian König Reviewed-by: Alex Deucher Reviewed-by: Monk Liu --- include/uapi/drm/amdgpu_drm.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index d9b9b6f8de2b..64a07ac3b4b9 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -67,8 +67,6 @@ #define AMDGPU_GEM_DOMAIN_GWS 0x10 #define AMDGPU_GEM_DOMAIN_OA 0x20 -#define AMDGPU_GEM_DOMAIN_MASK 0x3F - /* Flag that CPU access will be required for the case of VRAM domain */ #define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) /* Flag that CPU access will not work, this VRAM domain is invisible */ @@ -76,12 +74,6 @@ /* Flag that USWC attributes should be used for GTT */ #define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2) -/* Flag mask for GTT domain_flags */ -#define AMDGPU_GEM_CREATE_CPU_GTT_MASK \ - (AMDGPU_GEM_CREATE_CPU_GTT_USWC | \ - AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | \ - AMDGPU_GEM_CREATE_NO_CPU_ACCESS) - struct drm_amdgpu_gem_create_in { /** the requested memory size */ uint64_t bo_size; -- cgit v1.2.3 From d8f65a2376268dfb2963152754d41208dc43d906 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Wed, 27 May 2015 14:30:38 +0200 Subject: drm/amdgpu: rename GEM_OP_SET_INITIAL_DOMAIN -> GEM_OP_SET_PLACEMENT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Olšák Reviewed-by: Christian König Reviewed-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 2 +- include/uapi/drm/amdgpu_drm.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index c90b74da0027..ad5b9c676fd8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -638,7 +638,7 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, r = -EFAULT; break; } - case AMDGPU_GEM_OP_SET_INITIAL_DOMAIN: + case AMDGPU_GEM_OP_SET_PLACEMENT: if (amdgpu_ttm_tt_has_userptr(robj->tbo.ttm)) { r = -EPERM; break; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 64a07ac3b4b9..cd54891b1d5c 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -284,8 +284,8 @@ struct drm_amdgpu_gem_op { uint64_t value; /* input or return value */ }; -#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 -#define AMDGPU_GEM_OP_SET_INITIAL_DOMAIN 1 +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_PLACEMENT 1 #define AMDGPU_VA_OP_MAP 1 #define AMDGPU_VA_OP_UNMAP 2 -- cgit v1.2.3 From 32bf7106e072b59cade754062ed86023309f50d9 Mon Sep 17 00:00:00 2001 From: Ken Wang Date: Wed, 3 Jun 2015 17:36:54 +0800 Subject: drm/amdgpu add max_memory_clock for interface query (v2) Add a query for the max memory clock. v2: handle the dpm enabled case properly Signed-off-by: Ken Wang Reviewd-by: Jammy Zhou --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 8 ++++++-- include/uapi/drm/amdgpu_drm.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index b6dd3751d9a5..3c182b6c5a27 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -414,11 +414,15 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file dev_info.num_shader_arrays_per_engine = adev->gfx.config.max_sh_per_se; /* return all clocks in KHz */ dev_info.gpu_counter_freq = amdgpu_asic_get_xclk(adev) * 10; - if (adev->pm.dpm_enabled) + if (adev->pm.dpm_enabled) { dev_info.max_engine_clock = adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk * 10; - else + dev_info.max_memory_clock = + adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk * 10; + } else { dev_info.max_engine_clock = adev->pm.default_sclk * 10; + dev_info.max_memory_clock = adev->pm.default_mclk * 10; + } dev_info.enabled_rb_pipes_mask = adev->gfx.config.backend_enable_mask; dev_info.num_rb_pipes = adev->gfx.config.max_backends_per_se * adev->gfx.config.max_shader_engines; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index cd54891b1d5c..420c762f2ed7 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -553,6 +553,7 @@ struct drm_amdgpu_info_device { uint32_t num_shader_arrays_per_engine; uint32_t gpu_counter_freq; /* in KHz */ uint64_t max_engine_clock; /* in KHz */ + uint64_t max_memory_clock; /* in KHz */ /* cu information */ uint32_t cu_active_number; uint32_t cu_ao_mask; -- cgit v1.2.3 From a101a8995ab8072125d0bb4d95425c9fb37ff809 Mon Sep 17 00:00:00 2001 From: Ken Wang Date: Wed, 3 Jun 2015 17:47:54 +0800 Subject: drm/amdgpu add ce_ram_size for interface query Add a query for the CE ram size. User mode drivers will want to use this to determine how much size of the cache on the CE. Signed-off-by: Ken Wang Reviewd-by: Jammy Zhou --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 1 + drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 2 ++ drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 2 ++ include/uapi/drm/amdgpu_drm.h | 2 ++ 5 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 4300e3d4b1cd..6c99b7560a27 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1136,6 +1136,8 @@ struct amdgpu_gfx { uint32_t gfx_current_status; /* sync signal for const engine */ unsigned ce_sync_offs; + /* ce ram size*/ + unsigned ce_ram_size; }; int amdgpu_ib_get(struct amdgpu_ring *ring, struct amdgpu_vm *vm, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 3c182b6c5a27..9ede2446dcd6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -441,6 +441,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file amdgpu_asic_get_cu_info(adev, &cu_info); dev_info.cu_active_number = cu_info.number; dev_info.cu_ao_mask = cu_info.ao_cu_mask; + dev_info.ce_ram_size = adev->gfx.ce_ram_size; memcpy(&dev_info.cu_bitmap[0], &cu_info.bitmap[0], sizeof(cu_info.bitmap)); return copy_to_user(out, &dev_info, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index ed2f35de1d4f..faa39b38f0f3 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -4820,6 +4820,8 @@ static int gfx_v7_0_hw_init(void *handle) if (r) return r; + adev->gfx.ce_ram_size = 0x8000; + return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index ffdba1965029..1895de433446 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -895,6 +895,8 @@ static int gfx_v8_0_sw_init(void *handle) if (r) return r; + adev->gfx.ce_ram_size = 0x8000; + return 0; } diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 420c762f2ed7..e24cc2e318df 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -573,6 +573,8 @@ struct drm_amdgpu_info_device { /** Page table entry - fragment size */ uint32_t pte_fragment_size; uint32_t gart_page_size; + /** constant engine ram size*/ + uint32_t ce_ram_size; }; struct drm_amdgpu_info_hw_ip { -- cgit v1.2.3 From 71062f435eaf0ff7867a1188e5c7887b0a5871ff Mon Sep 17 00:00:00 2001 From: Ken Wang Date: Thu, 4 Jun 2015 21:26:57 +0800 Subject: drm/amdgpu: add ib_size/start_alignment interface query Query the IB alignment requirements from the kernel rather than hardcoding them in the user mode drivers. Signed-off-by: Ken Wang Reviewed-by: Jammy Zhou --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 14 ++++++++++++++ include/uapi/drm/amdgpu_drm.h | 4 ++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 12b756e8c6b8..f1e5d87ef1f7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -188,6 +188,8 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file struct drm_amdgpu_info_hw_ip ip = {}; enum amd_ip_block_type type; uint32_t ring_mask = 0; + uint32_t ib_start_alignment = 0; + uint32_t ib_size_alignment = 0; if (info->query_hw_ip.ip_instance >= AMDGPU_HW_IP_INSTANCE_MAX_COUNT) return -EINVAL; @@ -197,25 +199,35 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file type = AMD_IP_BLOCK_TYPE_GFX; for (i = 0; i < adev->gfx.num_gfx_rings; i++) ring_mask |= ((adev->gfx.gfx_ring[i].ready ? 1 : 0) << i); + ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; + ib_size_alignment = 8; break; case AMDGPU_HW_IP_COMPUTE: type = AMD_IP_BLOCK_TYPE_GFX; for (i = 0; i < adev->gfx.num_compute_rings; i++) ring_mask |= ((adev->gfx.compute_ring[i].ready ? 1 : 0) << i); + ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; + ib_size_alignment = 8; break; case AMDGPU_HW_IP_DMA: type = AMD_IP_BLOCK_TYPE_SDMA; ring_mask = adev->sdma[0].ring.ready ? 1 : 0; ring_mask |= ((adev->sdma[1].ring.ready ? 1 : 0) << 1); + ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; + ib_size_alignment = 1; break; case AMDGPU_HW_IP_UVD: type = AMD_IP_BLOCK_TYPE_UVD; ring_mask = adev->uvd.ring.ready ? 1 : 0; + ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; + ib_size_alignment = 8; break; case AMDGPU_HW_IP_VCE: type = AMD_IP_BLOCK_TYPE_VCE; for (i = 0; i < AMDGPU_MAX_VCE_RINGS; i++) ring_mask |= ((adev->vce.ring[i].ready ? 1 : 0) << i); + ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; + ib_size_alignment = 8; break; default: return -EINVAL; @@ -228,6 +240,8 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file ip.hw_ip_version_minor = adev->ip_blocks[i].minor; ip.capabilities_flags = 0; ip.available_rings = ring_mask; + ip.ib_start_alignment = ib_start_alignment; + ip.ib_size_alignment = ib_size_alignment; break; } } diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index e24cc2e318df..3af5bd0e23a8 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -583,6 +583,10 @@ struct drm_amdgpu_info_hw_ip { uint32_t hw_ip_version_minor; /** Capabilities */ uint64_t capabilities_flags; + /** command buffer address start alignment*/ + uint32_t ib_start_alignment; + /** command buffer size alignment*/ + uint32_t ib_size_alignment; /** Bitmask of available rings. Bit 0 means ring 0, etc. */ uint32_t available_rings; uint32_t _pad; -- cgit v1.2.3 From 81c59f54125f9ff84546b6ba26c321662562703d Mon Sep 17 00:00:00 2001 From: Ken Wang Date: Wed, 3 Jun 2015 21:02:01 +0800 Subject: drm/amdgpu: add vram_type and vram_bit_width for interface query (v2) Track the type of vram on the board and provide a query for it. User mode drivers and tools want this information for determining bandwidth information and form informational purposes. v2: fix build when CI support is not enabled Signed-off-by: Ken Wang Reviewed-by: Jammy Zhou --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 2 ++ drivers/gpu/drm/amd/amdgpu/ci_dpm.c | 12 ++++++------ drivers/gpu/drm/amd/amdgpu/cikd.h | 11 ++++++++--- drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c | 32 +++++++++++++++++++++++++------- drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c | 33 ++++++++++++++++++++++++++------- drivers/gpu/drm/amd/amdgpu/vid.h | 11 ++++++++--- include/uapi/drm/amdgpu_drm.h | 13 +++++++++++++ 8 files changed, 89 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 4bdc3265b410..149b76913091 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -767,7 +767,7 @@ struct amdgpu_mc { const struct firmware *fw; /* MC firmware */ uint32_t fw_version; struct amdgpu_irq_src vm_fault; - bool is_gddr5; + uint32_t vram_type; }; /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index f1e5d87ef1f7..5533434c7a8f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -457,6 +457,8 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file dev_info.cu_ao_mask = cu_info.ao_cu_mask; dev_info.ce_ram_size = adev->gfx.ce_ram_size; memcpy(&dev_info.cu_bitmap[0], &cu_info.bitmap[0], sizeof(cu_info.bitmap)); + dev_info.vram_type = adev->mc.vram_type; + dev_info.vram_bit_width = adev->mc.vram_width; return copy_to_user(out, &dev_info, min((size_t)size, sizeof(dev_info))) ? -EFAULT : 0; diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index b1a4fbc22e69..82e8d0730517 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -891,7 +891,7 @@ static void ci_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) static bool ci_dpm_vblank_too_short(struct amdgpu_device *adev) { u32 vblank_time = amdgpu_dpm_get_vblank_time(adev); - u32 switch_limit = adev->mc.is_gddr5 ? 450 : 300; + u32 switch_limit = adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 300; if (vblank_time < switch_limit) return true; @@ -2920,7 +2920,7 @@ static int ci_calculate_mclk_params(struct amdgpu_device *adev, mpll_ad_func_cntl &= ~MPLL_AD_FUNC_CNTL__YCLK_POST_DIV_MASK; mpll_ad_func_cntl |= (mpll_param.post_div << MPLL_AD_FUNC_CNTL__YCLK_POST_DIV__SHIFT); - if (adev->mc.is_gddr5) { + if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) { mpll_dq_func_cntl &= ~(MPLL_DQ_FUNC_CNTL__YCLK_SEL_MASK | MPLL_AD_FUNC_CNTL__YCLK_POST_DIV_MASK); mpll_dq_func_cntl |= (mpll_param.yclk_sel << MPLL_DQ_FUNC_CNTL__YCLK_SEL__SHIFT) | @@ -3043,7 +3043,7 @@ static int ci_populate_single_memory_level(struct amdgpu_device *adev, (memory_clock <= pi->mclk_strobe_mode_threshold)) memory_level->StrobeEnable = 1; - if (adev->mc.is_gddr5) { + if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) { memory_level->StrobeRatio = ci_get_mclk_frequency_ratio(memory_clock, memory_level->StrobeEnable); if (pi->mclk_edc_enable_threshold && @@ -3681,7 +3681,7 @@ static int ci_init_smc_table(struct amdgpu_device *adev) if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC; - if (adev->mc.is_gddr5) + if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5; if (ulv->supported) { @@ -4498,14 +4498,14 @@ static int ci_set_mc_special_registers(struct amdgpu_device *adev, for (k = 0; k < table->num_entries; k++) { table->mc_reg_table_entry[k].mc_data[j] = (temp_reg & 0xffff0000) | (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff); - if (!adev->mc.is_gddr5) + if (adev->mc.vram_type != AMDGPU_VRAM_TYPE_GDDR5) table->mc_reg_table_entry[k].mc_data[j] |= 0x100; } j++; if (j > SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE) return -EINVAL; - if (!adev->mc.is_gddr5) { + if (adev->mc.vram_type != AMDGPU_VRAM_TYPE_GDDR5) { table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD; table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD; for (k = 0; k < table->num_entries; k++) { diff --git a/drivers/gpu/drm/amd/amdgpu/cikd.h b/drivers/gpu/drm/amd/amdgpu/cikd.h index 11828e2cdf34..220865a44814 100644 --- a/drivers/gpu/drm/amd/amdgpu/cikd.h +++ b/drivers/gpu/drm/amd/amdgpu/cikd.h @@ -24,9 +24,14 @@ #ifndef CIK_H #define CIK_H -#define MC_SEQ_MISC0__GDDR5__SHIFT 0x1c -#define MC_SEQ_MISC0__GDDR5_MASK 0xf0000000 -#define MC_SEQ_MISC0__GDDR5_VALUE 5 +#define MC_SEQ_MISC0__MT__MASK 0xf0000000 +#define MC_SEQ_MISC0__MT__GDDR1 0x10000000 +#define MC_SEQ_MISC0__MT__DDR2 0x20000000 +#define MC_SEQ_MISC0__MT__GDDR3 0x30000000 +#define MC_SEQ_MISC0__MT__GDDR4 0x40000000 +#define MC_SEQ_MISC0__MT__GDDR5 0x50000000 +#define MC_SEQ_MISC0__MT__HBM 0x60000000 +#define MC_SEQ_MISC0__MT__DDR3 0xB0000000 #define CP_ME_TABLE_SIZE 96 diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 01cd6b207d26..ae37fce36520 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -812,6 +812,28 @@ static void gmc_v7_0_enable_hdp_ls(struct amdgpu_device *adev, WREG32(mmHDP_MEM_POWER_LS, data); } +static int gmc_v7_0_convert_vram_type(int mc_seq_vram_type) +{ + switch (mc_seq_vram_type) { + case MC_SEQ_MISC0__MT__GDDR1: + return AMDGPU_VRAM_TYPE_GDDR1; + case MC_SEQ_MISC0__MT__DDR2: + return AMDGPU_VRAM_TYPE_DDR2; + case MC_SEQ_MISC0__MT__GDDR3: + return AMDGPU_VRAM_TYPE_GDDR3; + case MC_SEQ_MISC0__MT__GDDR4: + return AMDGPU_VRAM_TYPE_GDDR4; + case MC_SEQ_MISC0__MT__GDDR5: + return AMDGPU_VRAM_TYPE_GDDR5; + case MC_SEQ_MISC0__MT__HBM: + return AMDGPU_VRAM_TYPE_HBM; + case MC_SEQ_MISC0__MT__DDR3: + return AMDGPU_VRAM_TYPE_DDR3; + default: + return AMDGPU_VRAM_TYPE_UNKNOWN; + } +} + static int gmc_v7_0_early_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; @@ -820,15 +842,11 @@ static int gmc_v7_0_early_init(void *handle) gmc_v7_0_set_irq_funcs(adev); if (adev->flags & AMDGPU_IS_APU) { - adev->mc.is_gddr5 = false; + adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN; } else { u32 tmp = RREG32(mmMC_SEQ_MISC0); - - if (((tmp & MC_SEQ_MISC0__GDDR5_MASK) >> - MC_SEQ_MISC0__GDDR5__SHIFT) == MC_SEQ_MISC0__GDDR5_VALUE) - adev->mc.is_gddr5 = true; - else - adev->mc.is_gddr5 = false; + tmp &= MC_SEQ_MISC0__MT__MASK; + adev->mc.vram_type = gmc_v7_0_convert_vram_type(tmp); } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 675483a612c2..6206fcd39df9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -38,6 +38,7 @@ #include "vid.h" #include "vi.h" + static void gmc_v8_0_set_gart_funcs(struct amdgpu_device *adev); static void gmc_v8_0_set_irq_funcs(struct amdgpu_device *adev); @@ -786,6 +787,28 @@ static void gmc_v8_0_vm_decode_fault(struct amdgpu_device *adev, "write" : "read", block, mc_client, mc_id); } +static int gmc_v8_0_convert_vram_type(int mc_seq_vram_type) +{ + switch (mc_seq_vram_type) { + case MC_SEQ_MISC0__MT__GDDR1: + return AMDGPU_VRAM_TYPE_GDDR1; + case MC_SEQ_MISC0__MT__DDR2: + return AMDGPU_VRAM_TYPE_DDR2; + case MC_SEQ_MISC0__MT__GDDR3: + return AMDGPU_VRAM_TYPE_GDDR3; + case MC_SEQ_MISC0__MT__GDDR4: + return AMDGPU_VRAM_TYPE_GDDR4; + case MC_SEQ_MISC0__MT__GDDR5: + return AMDGPU_VRAM_TYPE_GDDR5; + case MC_SEQ_MISC0__MT__HBM: + return AMDGPU_VRAM_TYPE_HBM; + case MC_SEQ_MISC0__MT__DDR3: + return AMDGPU_VRAM_TYPE_DDR3; + default: + return AMDGPU_VRAM_TYPE_UNKNOWN; + } +} + static int gmc_v8_0_early_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; @@ -794,15 +817,11 @@ static int gmc_v8_0_early_init(void *handle) gmc_v8_0_set_irq_funcs(adev); if (adev->flags & AMDGPU_IS_APU) { - adev->mc.is_gddr5 = false; + adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN; } else { u32 tmp = RREG32(mmMC_SEQ_MISC0); - - if (((tmp & MC_SEQ_MISC0__GDDR5_MASK) >> - MC_SEQ_MISC0__GDDR5__SHIFT) == MC_SEQ_MISC0__GDDR5_VALUE) - adev->mc.is_gddr5 = true; - else - adev->mc.is_gddr5 = false; + tmp &= MC_SEQ_MISC0__MT__MASK; + adev->mc.vram_type = gmc_v8_0_convert_vram_type(tmp); } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/vid.h b/drivers/gpu/drm/amd/amdgpu/vid.h index 385267c31d11..31bb89452e12 100644 --- a/drivers/gpu/drm/amd/amdgpu/vid.h +++ b/drivers/gpu/drm/amd/amdgpu/vid.h @@ -68,9 +68,14 @@ #define RB_BITMAP_WIDTH_PER_SH 2 -#define MC_SEQ_MISC0__GDDR5__SHIFT 0x1c -#define MC_SEQ_MISC0__GDDR5_MASK 0xf0000000 -#define MC_SEQ_MISC0__GDDR5_VALUE 5 +#define MC_SEQ_MISC0__MT__MASK 0xf0000000 +#define MC_SEQ_MISC0__MT__GDDR1 0x10000000 +#define MC_SEQ_MISC0__MT__DDR2 0x20000000 +#define MC_SEQ_MISC0__MT__GDDR3 0x30000000 +#define MC_SEQ_MISC0__MT__GDDR4 0x40000000 +#define MC_SEQ_MISC0__MT__GDDR5 0x50000000 +#define MC_SEQ_MISC0__MT__HBM 0x60000000 +#define MC_SEQ_MISC0__MT__DDR3 0xB0000000 /* * PM4 diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 3af5bd0e23a8..c90f4f0d059e 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -540,6 +540,15 @@ struct drm_amdgpu_info_firmware { uint32_t feature; }; +#define AMDGPU_VRAM_TYPE_UNKNOWN 0 +#define AMDGPU_VRAM_TYPE_GDDR1 1 +#define AMDGPU_VRAM_TYPE_DDR2 2 +#define AMDGPU_VRAM_TYPE_GDDR3 3 +#define AMDGPU_VRAM_TYPE_GDDR4 4 +#define AMDGPU_VRAM_TYPE_GDDR5 5 +#define AMDGPU_VRAM_TYPE_HBM 6 +#define AMDGPU_VRAM_TYPE_DDR3 7 + struct drm_amdgpu_info_device { /** PCI Device ID */ uint32_t device_id; @@ -575,6 +584,10 @@ struct drm_amdgpu_info_device { uint32_t gart_page_size; /** constant engine ram size*/ uint32_t ce_ram_size; + /** video memory type infro*/ + uint32_t vram_type; + /** video memory bit width*/ + uint32_t vram_bit_width; }; struct drm_amdgpu_info_hw_ip { -- cgit v1.2.3 From 3ccec53c294cbec2af44b6b24f70349637c45428 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Tue, 2 Jun 2015 17:44:49 +0200 Subject: drm/amdgpu: only support IBs in the buffer list (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_cs_find_mapping doesn't work without all buffers being validated, so the TTM validation must be done first. v2: only use amdgpu_cs_find_mapping for UVD/VCE VM emulation Signed-off-by: Marek Olšák Reviewed-by: Christian König --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 - drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 95 +++++++++------------------------- include/uapi/drm/amdgpu_drm.h | 6 +-- 3 files changed, 25 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 149b76913091..c33c1af36fa2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1191,7 +1191,6 @@ struct amdgpu_cs_parser { struct amdgpu_cs_chunk *chunks; /* relocations */ struct amdgpu_bo_list_entry *vm_bos; - struct amdgpu_bo_list_entry *ib_bos; struct list_head validated; struct amdgpu_ib *ibs; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index fefa48a59a7d..f6b224a69b3a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -230,11 +230,6 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) goto out; } - p->ib_bos = kcalloc(p->num_ibs, sizeof(struct amdgpu_bo_list_entry), - GFP_KERNEL); - if (!p->ib_bos) - r = -ENOMEM; - out: kfree(chunk_array); return r; @@ -373,13 +368,6 @@ static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p) p->vm_bos = amdgpu_vm_get_bos(p->adev, &fpriv->vm, &p->validated); - for (i = 0; i < p->num_ibs; i++) { - if (!p->ib_bos[i].robj) - continue; - - list_add(&p->ib_bos[i].tv.head, &p->validated); - } - if (need_mmap_lock) down_read(¤t->mm->mmap_sem); @@ -457,15 +445,9 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bo for (i = 0; i < parser->nchunks; i++) drm_free_large(parser->chunks[i].kdata); kfree(parser->chunks); - for (i = 0; i < parser->num_ibs; i++) { - struct amdgpu_bo *bo = parser->ib_bos[i].robj; + for (i = 0; i < parser->num_ibs; i++) amdgpu_ib_free(parser->adev, &parser->ibs[i]); - - if (bo) - drm_gem_object_unreference_unlocked(&bo->gem_base); - } kfree(parser->ibs); - kfree(parser->ib_bos); if (parser->uf.bo) drm_gem_object_unreference_unlocked(&parser->uf.bo->gem_base); } @@ -505,21 +487,6 @@ static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p, } } - for (i = 0; i < p->num_ibs; i++) { - bo = p->ib_bos[i].robj; - if (!bo) - continue; - - bo_va = p->ib_bos[i].bo_va; - if (!bo_va) - continue; - - r = amdgpu_vm_bo_update(adev, bo_va, &bo->tbo.mem); - if (r) - return r; - - amdgpu_sync_fence(&p->ibs[0].sync, bo_va->last_pt_update); - } return amdgpu_vm_clear_invalids(adev, vm, &p->ibs[0].sync); } @@ -581,11 +548,7 @@ static int amdgpu_cs_ib_fill(struct amdgpu_device *adev, struct amdgpu_cs_chunk *chunk; struct amdgpu_ib *ib; struct drm_amdgpu_cs_chunk_ib *chunk_ib; - struct amdgpu_bo_list_entry *ib_bo; struct amdgpu_ring *ring; - struct drm_gem_object *gobj; - struct amdgpu_bo *aobj; - void *kptr; chunk = &parser->chunks[i]; ib = &parser->ibs[j]; @@ -594,66 +557,49 @@ static int amdgpu_cs_ib_fill(struct amdgpu_device *adev, if (chunk->chunk_id != AMDGPU_CHUNK_ID_IB) continue; - gobj = drm_gem_object_lookup(adev->ddev, parser->filp, chunk_ib->handle); - if (gobj == NULL) - return -ENOENT; - aobj = gem_to_amdgpu_bo(gobj); - r = amdgpu_cs_get_ring(adev, chunk_ib->ip_type, chunk_ib->ip_instance, chunk_ib->ring, &ring); - if (r) { - drm_gem_object_unreference_unlocked(gobj); + if (r) return r; - } if (ring->funcs->parse_cs) { - r = amdgpu_bo_reserve(aobj, false); - if (r) { - drm_gem_object_unreference_unlocked(gobj); - return r; + struct amdgpu_bo *aobj = NULL; + void *kptr; + + amdgpu_cs_find_mapping(parser, chunk_ib->va_start, &aobj); + if (!aobj) { + DRM_ERROR("IB va_start is invalid\n"); + return -EINVAL; } + /* the IB should be reserved at this point */ r = amdgpu_bo_kmap(aobj, &kptr); if (r) { - amdgpu_bo_unreserve(aobj); - drm_gem_object_unreference_unlocked(gobj); return r; } r = amdgpu_ib_get(ring, NULL, chunk_ib->ib_bytes, ib); if (r) { DRM_ERROR("Failed to get ib !\n"); - amdgpu_bo_unreserve(aobj); - drm_gem_object_unreference_unlocked(gobj); return r; } memcpy(ib->ptr, kptr, chunk_ib->ib_bytes); amdgpu_bo_kunmap(aobj); - amdgpu_bo_unreserve(aobj); } else { r = amdgpu_ib_get(ring, vm, 0, ib); if (r) { DRM_ERROR("Failed to get ib !\n"); - drm_gem_object_unreference_unlocked(gobj); return r; } ib->gpu_addr = chunk_ib->va_start; } - ib->length_dw = chunk_ib->ib_bytes / 4; + ib->length_dw = chunk_ib->ib_bytes / 4; ib->flags = chunk_ib->flags; ib->ctx = parser->ctx; - - ib_bo = &parser->ib_bos[j]; - ib_bo->robj = aobj; - ib_bo->prefered_domains = aobj->initial_domain; - ib_bo->allowed_domains = aobj->initial_domain; - ib_bo->priority = 0; - ib_bo->tv.bo = &aobj->tbo; - ib_bo->tv.shared = true; j++; } @@ -702,6 +648,7 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) union drm_amdgpu_cs *cs = data; struct amdgpu_cs_parser parser; int r, i; + bool reserved_buffers = false; down_read(&adev->exclusive_lock); if (!adev->accel_working) { @@ -721,15 +668,21 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return r; } - r = amdgpu_cs_ib_fill(adev, &parser); - if (!r) { - r = amdgpu_cs_parser_relocs(&parser); - if (r && r != -ERESTARTSYS) - DRM_ERROR("Failed to parse relocation %d!\n", r); + r = amdgpu_cs_parser_relocs(&parser); + if (r) { + if (r != -ERESTARTSYS) { + if (r == -ENOMEM) + DRM_ERROR("Not enough memory for command submission!\n"); + else + DRM_ERROR("Failed to process the buffer list %d!\n", r); + } + } else { + reserved_buffers = true; + r = amdgpu_cs_ib_fill(adev, &parser); } if (r) { - amdgpu_cs_parser_fini(&parser, r, false); + amdgpu_cs_parser_fini(&parser, r, reserved_buffers); up_read(&adev->exclusive_lock); r = amdgpu_cs_handle_lockup(adev, r); return r; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index c90f4f0d059e..780a5815fb12 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -378,11 +378,7 @@ union drm_amdgpu_cs { #define AMDGPU_IB_FLAG_PREAMBLE (1<<2) struct drm_amdgpu_cs_chunk_ib { - /** - * Handle of GEM object to be used as IB or 0 if it is already in - * residency list. - */ - uint32_t handle; + uint32_t _pad; uint32_t flags; /* IB Flags */ uint64_t va_start; /* Virtual address to begin IB execution */ uint32_t ib_bytes; /* Size of submission */ -- cgit v1.2.3 From ae45577324d1f749c907840247d443696ac3bc7a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 5 Jun 2015 12:31:12 +1000 Subject: virtgpu: include linux/types.h to avoid warning. Signed-off-by: Dave Airlie --- include/uapi/linux/virtio_gpu.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h index 571c4cbd3f93..478be5270e26 100644 --- a/include/uapi/linux/virtio_gpu.h +++ b/include/uapi/linux/virtio_gpu.h @@ -38,6 +38,8 @@ #ifndef VIRTIO_GPU_HW_H #define VIRTIO_GPU_HW_H +#include + enum virtio_gpu_ctrl_type { VIRTIO_GPU_UNDEFINED = 0, -- cgit v1.2.3 From f3f375cd4e411b66511178be9a1dd0256ae41e77 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 14 Apr 2015 15:15:34 +0200 Subject: drm/bridge: Remove stale ptn3460.h include This header file declares prototypes of functions that are no longer used. Remove this file and all references to it. Signed-off-by: Thierry Reding --- drivers/gpu/drm/bridge/ptn3460.c | 2 -- drivers/gpu/drm/exynos/exynos_dp_core.c | 1 - include/drm/bridge/ptn3460.h | 45 --------------------------------- 3 files changed, 48 deletions(-) delete mode 100644 include/drm/bridge/ptn3460.h (limited to 'include') diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index b1e0f0b07833..0e081564e70d 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -24,8 +24,6 @@ #include -#include "bridge/ptn3460.h" - #include "drm_crtc.h" #include "drm_crtc_helper.h" #include "drm_edid.h" diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 1dbfba58f909..b178f530391f 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -29,7 +29,6 @@ #include #include #include -#include #include "exynos_dp_core.h" #include "exynos_drm_fimd.h" diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h deleted file mode 100644 index b11f8e17e72f..000000000000 --- a/include/drm/bridge/ptn3460.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2013 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _DRM_BRIDGE_PTN3460_H_ -#define _DRM_BRIDGE_PTN3460_H_ - -struct drm_device; -struct drm_bridge; -struct drm_encoder; -struct i2c_client; -struct device_node; - -#if defined(CONFIG_DRM_PTN3460) || defined(CONFIG_DRM_PTN3460_MODULE) - -int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, - struct i2c_client *client, struct device_node *node); - -void ptn3460_destroy(struct drm_bridge *bridge); - -#else - -static inline int ptn3460_init(struct drm_device *dev, - struct drm_encoder *encoder, struct i2c_client *client, - struct device_node *node) -{ - return 0; -} - -static inline void ptn3460_destroy(struct drm_bridge *bridge) -{ -} - -#endif - -#endif -- cgit v1.2.3 From cab6d57c09ece2ceb03602dd44ea2f4ce9333ec9 Mon Sep 17 00:00:00 2001 From: Jammy Zhou Date: Sat, 6 Jun 2015 04:49:22 +0800 Subject: drm/amdgpu: remove unused AMDGPU_IB_FLAG_GDS Signed-off-by: Jammy Zhou Reviewed-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 780a5815fb12..3b911b604fdf 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -371,11 +371,8 @@ union drm_amdgpu_cs { /* This IB should be submitted to CE */ #define AMDGPU_IB_FLAG_CE (1<<0) -/* GDS is used by this IB */ -#define AMDGPU_IB_FLAG_GDS (1<<1) - /* CE Preamble */ -#define AMDGPU_IB_FLAG_PREAMBLE (1<<2) +#define AMDGPU_IB_FLAG_PREAMBLE (1<<1) struct drm_amdgpu_cs_chunk_ib { uint32_t _pad; @@ -580,7 +577,7 @@ struct drm_amdgpu_info_device { uint32_t gart_page_size; /** constant engine ram size*/ uint32_t ce_ram_size; - /** video memory type infro*/ + /** video memory type info*/ uint32_t vram_type; /** video memory bit width*/ uint32_t vram_bit_width; -- cgit v1.2.3 From 34b5f6a6d6d0e482c7ce498f60bce261e533821e Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 8 Jun 2015 15:03:00 +0200 Subject: drm/amdgpu: cleanup VA IOCTL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unnecessary returned status and make the IOCTL write only. Signed-off-by: Christian König Reviewed-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 64 +++++++++++---------------------- include/uapi/drm/amdgpu_drm.h | 18 ++-------- 2 files changed, 23 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index e8409fea4bf1..0ec222295fee 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -505,7 +505,7 @@ error_free: int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { - union drm_amdgpu_gem_va *args = data; + struct drm_amdgpu_gem_va *args = data; struct drm_gem_object *gobj; struct amdgpu_device *adev = dev->dev_private; struct amdgpu_fpriv *fpriv = filp->driver_priv; @@ -514,95 +514,73 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, uint32_t invalid_flags, va_flags = 0; int r = 0; - if (!adev->vm_manager.enabled) { - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; + if (!adev->vm_manager.enabled) return -ENOTTY; - } - if (args->in.va_address < AMDGPU_VA_RESERVED_SIZE) { + if (args->va_address < AMDGPU_VA_RESERVED_SIZE) { dev_err(&dev->pdev->dev, "va_address 0x%lX is in reserved area 0x%X\n", - (unsigned long)args->in.va_address, + (unsigned long)args->va_address, AMDGPU_VA_RESERVED_SIZE); - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; return -EINVAL; } invalid_flags = ~(AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE | AMDGPU_VM_PAGE_EXECUTABLE); - if ((args->in.flags & invalid_flags)) { + if ((args->flags & invalid_flags)) { dev_err(&dev->pdev->dev, "invalid flags 0x%08X vs 0x%08X\n", - args->in.flags, invalid_flags); - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; + args->flags, invalid_flags); return -EINVAL; } - switch (args->in.operation) { + switch (args->operation) { case AMDGPU_VA_OP_MAP: case AMDGPU_VA_OP_UNMAP: break; default: dev_err(&dev->pdev->dev, "unsupported operation %d\n", - args->in.operation); - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; + args->operation); return -EINVAL; } - gobj = drm_gem_object_lookup(dev, filp, args->in.handle); - if (gobj == NULL) { - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) return -ENOENT; - } + rbo = gem_to_amdgpu_bo(gobj); r = amdgpu_bo_reserve(rbo, false); if (r) { - if (r != -ERESTARTSYS) { - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; - } drm_gem_object_unreference_unlocked(gobj); return r; } + bo_va = amdgpu_vm_bo_find(&fpriv->vm, rbo); if (!bo_va) { - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; - drm_gem_object_unreference_unlocked(gobj); + amdgpu_bo_unreserve(rbo); return -ENOENT; } - switch (args->in.operation) { + switch (args->operation) { case AMDGPU_VA_OP_MAP: - if (args->in.flags & AMDGPU_VM_PAGE_READABLE) + if (args->flags & AMDGPU_VM_PAGE_READABLE) va_flags |= AMDGPU_PTE_READABLE; - if (args->in.flags & AMDGPU_VM_PAGE_WRITEABLE) + if (args->flags & AMDGPU_VM_PAGE_WRITEABLE) va_flags |= AMDGPU_PTE_WRITEABLE; - if (args->in.flags & AMDGPU_VM_PAGE_EXECUTABLE) + if (args->flags & AMDGPU_VM_PAGE_EXECUTABLE) va_flags |= AMDGPU_PTE_EXECUTABLE; - r = amdgpu_vm_bo_map(adev, bo_va, args->in.va_address, - args->in.offset_in_bo, args->in.map_size, + r = amdgpu_vm_bo_map(adev, bo_va, args->va_address, + args->offset_in_bo, args->map_size, va_flags); break; case AMDGPU_VA_OP_UNMAP: - r = amdgpu_vm_bo_unmap(adev, bo_va, args->in.va_address); + r = amdgpu_vm_bo_unmap(adev, bo_va, args->va_address); break; default: break; } - if (!r) { + if (!r) amdgpu_gem_va_update_vm(adev, bo_va); - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_OK; - } else { - memset(args, 0, sizeof(*args)); - args->out.result = AMDGPU_VA_RESULT_ERROR; - } drm_gem_object_unreference_unlocked(gobj); return r; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 3b911b604fdf..4c465e10a5a6 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -55,7 +55,7 @@ #define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info) #define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata) #define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle) -#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, union drm_amdgpu_gem_va) +#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, struct drm_amdgpu_gem_va) #define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs) #define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op) #define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) @@ -290,10 +290,6 @@ struct drm_amdgpu_gem_op { #define AMDGPU_VA_OP_MAP 1 #define AMDGPU_VA_OP_UNMAP 2 -#define AMDGPU_VA_RESULT_OK 0 -#define AMDGPU_VA_RESULT_ERROR 1 -#define AMDGPU_VA_RESULT_VA_INVALID_ALIGNMENT 2 - /* Mapping flags */ /* readable mapping */ #define AMDGPU_VM_PAGE_READABLE (1 << 1) @@ -302,7 +298,7 @@ struct drm_amdgpu_gem_op { /* executable mapping, new for VI */ #define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) -struct drm_amdgpu_gem_va_in { +struct drm_amdgpu_gem_va { /* GEM object handle */ uint32_t handle; uint32_t _pad; @@ -319,16 +315,6 @@ struct drm_amdgpu_gem_va_in { uint64_t map_size; }; -struct drm_amdgpu_gem_va_out { - uint32_t result; - uint32_t _pad; -}; - -union drm_amdgpu_gem_va { - struct drm_amdgpu_gem_va_in in; - struct drm_amdgpu_gem_va_out out; -}; - #define AMDGPU_HW_IP_GFX 0 #define AMDGPU_HW_IP_COMPUTE 1 #define AMDGPU_HW_IP_DMA 2 -- cgit v1.2.3 From 692a59e696afe1a4e777d0e4359325336ab0ad89 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 9 Jun 2015 15:21:35 +0200 Subject: drm/amdgpu: remove AMDGPU_CTX_OP_STATE_RUNNING MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not used. Signed-off-by: Christian König Reviewed-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 4c465e10a5a6..a82c0601a294 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -139,8 +139,6 @@ union drm_amdgpu_bo_list { #define AMDGPU_CTX_OP_FREE_CTX 2 #define AMDGPU_CTX_OP_QUERY_STATE 3 -#define AMDGPU_CTX_OP_STATE_RUNNING 1 - /* GPU reset status */ #define AMDGPU_CTX_NO_RESET 0 #define AMDGPU_CTX_GUILTY_RESET 1 /* this the context caused it */ -- cgit v1.2.3 From 675da0ddd6fefa5500488a5a3d500aaaefa95e5d Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 9 Jun 2015 15:54:37 +0200 Subject: drm/amdgpu: cleanup UAPI comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change. Signed-off-by: Christian König Reviewed-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 119 +++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index a82c0601a294..d3f4832db289 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -141,12 +141,17 @@ union drm_amdgpu_bo_list { /* GPU reset status */ #define AMDGPU_CTX_NO_RESET 0 -#define AMDGPU_CTX_GUILTY_RESET 1 /* this the context caused it */ -#define AMDGPU_CTX_INNOCENT_RESET 2 /* some other context caused it */ -#define AMDGPU_CTX_UNKNOWN_RESET 3 /* unknown cause */ +/* this the context caused it */ +#define AMDGPU_CTX_GUILTY_RESET 1 +/* some other context caused it */ +#define AMDGPU_CTX_INNOCENT_RESET 2 +/* unknown cause */ +#define AMDGPU_CTX_UNKNOWN_RESET 3 struct drm_amdgpu_ctx_in { + /** AMDGPU_CTX_OP_* */ uint32_t op; + /** For future use, no flags defined so far */ uint32_t flags; uint32_t ctx_id; uint32_t _pad; @@ -159,6 +164,7 @@ union drm_amdgpu_ctx_out { } alloc; struct { + /** For future use, no flags defined so far */ uint64_t flags; /** Number of resets caused by this context so far. */ uint32_t hangs; @@ -185,7 +191,9 @@ union drm_amdgpu_ctx { struct drm_amdgpu_gem_userptr { uint64_t addr; uint64_t size; + /* AMDGPU_GEM_USERPTR_* */ uint32_t flags; + /* Resulting GEM handle */ uint32_t handle; }; @@ -217,23 +225,29 @@ struct drm_amdgpu_gem_userptr { /** The same structure is shared for input/output */ struct drm_amdgpu_gem_metadata { - uint32_t handle; /* GEM Object handle */ - uint32_t op; /** Do we want get or set metadata */ + /** GEM Object handle */ + uint32_t handle; + /** Do we want get or set metadata */ + uint32_t op; struct { + /** For future use, no flags defined so far */ uint64_t flags; - uint64_t tiling_info; /* family specific tiling info */ + /** family specific tiling info */ + uint64_t tiling_info; uint32_t data_size_bytes; uint32_t data[64]; } data; }; struct drm_amdgpu_gem_mmap_in { - uint32_t handle; /** the GEM object handle */ + /** the GEM object handle */ + uint32_t handle; uint32_t _pad; }; struct drm_amdgpu_gem_mmap_out { - uint64_t addr_ptr; /** mmap offset from the vma offset manager */ + /** mmap offset from the vma offset manager */ + uint64_t addr_ptr; }; union drm_amdgpu_gem_mmap { @@ -242,14 +256,19 @@ union drm_amdgpu_gem_mmap { }; struct drm_amdgpu_gem_wait_idle_in { - uint32_t handle; /* GEM object handle */ + /** GEM object handle */ + uint32_t handle; + /** For future use, no flags defined so far */ uint32_t flags; - uint64_t timeout; /* Timeout to wait. If 0 then returned immediately with the status */ + /** Absolute timeout to wait */ + uint64_t timeout; }; struct drm_amdgpu_gem_wait_idle_out { - uint32_t status; /* BO status: 0 - BO is idle, 1 - BO is busy */ - uint32_t domain; /* Returned current memory domain */ + /** BO status: 0 - BO is idle, 1 - BO is busy */ + uint32_t status; + /** Returned current memory domain */ + uint32_t domain; }; union drm_amdgpu_gem_wait_idle { @@ -258,7 +277,9 @@ union drm_amdgpu_gem_wait_idle { }; struct drm_amdgpu_wait_cs_in { + /** Command submission handle */ uint64_t handle; + /** Absolute timeout to wait */ uint64_t timeout; uint32_t ip_type; uint32_t ip_instance; @@ -267,6 +288,7 @@ struct drm_amdgpu_wait_cs_in { }; struct drm_amdgpu_wait_cs_out { + /** CS status: 0 - CS completed, 1 - CS still busy */ uint64_t status; }; @@ -275,16 +297,19 @@ union drm_amdgpu_wait_cs { struct drm_amdgpu_wait_cs_out out; }; +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_PLACEMENT 1 + /* Sets or returns a value associated with a buffer. */ struct drm_amdgpu_gem_op { - uint32_t handle; /* buffer */ - uint32_t op; /* AMDGPU_GEM_OP_* */ - uint64_t value; /* input or return value */ + /** GEM object handle */ + uint32_t handle; + /** AMDGPU_GEM_OP_* */ + uint32_t op; + /** Input or return value */ + uint64_t value; }; -#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 -#define AMDGPU_GEM_OP_SET_PLACEMENT 1 - #define AMDGPU_VA_OP_MAP 1 #define AMDGPU_VA_OP_UNMAP 2 @@ -297,19 +322,18 @@ struct drm_amdgpu_gem_op { #define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) struct drm_amdgpu_gem_va { - /* GEM object handle */ + /** GEM object handle */ uint32_t handle; uint32_t _pad; - /* map or unmap*/ + /** AMDGPU_VA_OP_* */ uint32_t operation; - /* specify mapping flags */ + /** AMDGPU_VM_PAGE_* */ uint32_t flags; - /* va address to assign . Must be correctly aligned.*/ + /** va address to assign . Must be correctly aligned.*/ uint64_t va_address; - /* Specify offset inside of BO to assign. Must be correctly aligned.*/ + /** Specify offset inside of BO to assign. Must be correctly aligned.*/ uint64_t offset_in_bo; - /* Specify mapping size. If 0 and offset is 0 then map the whole BO.*/ - /* Must be correctly aligned. */ + /** Specify mapping size. Must be correctly aligned. */ uint64_t map_size; }; @@ -324,6 +348,7 @@ struct drm_amdgpu_gem_va { #define AMDGPU_CHUNK_ID_IB 0x01 #define AMDGPU_CHUNK_ID_FENCE 0x02 + struct drm_amdgpu_cs_chunk { uint32_t chunk_id; uint32_t length_dw; @@ -337,7 +362,7 @@ struct drm_amdgpu_cs_in { uint32_t bo_list_handle; uint32_t num_chunks; uint32_t _pad; - /* this points to uint64_t * which point to cs chunks */ + /** this points to uint64_t * which point to cs chunks */ uint64_t chunks; }; @@ -346,8 +371,8 @@ struct drm_amdgpu_cs_out { }; union drm_amdgpu_cs { - struct drm_amdgpu_cs_in in; - struct drm_amdgpu_cs_out out; + struct drm_amdgpu_cs_in in; + struct drm_amdgpu_cs_out out; }; /* Specify flags to be used for IB */ @@ -360,12 +385,18 @@ union drm_amdgpu_cs { struct drm_amdgpu_cs_chunk_ib { uint32_t _pad; - uint32_t flags; /* IB Flags */ - uint64_t va_start; /* Virtual address to begin IB execution */ - uint32_t ib_bytes; /* Size of submission */ - uint32_t ip_type; /* HW IP to submit to */ - uint32_t ip_instance; /* HW IP index of the same type to submit to */ - uint32_t ring; /* Ring index to submit to */ + /** AMDGPU_IB_FLAG_* */ + uint32_t flags; + /** Virtual address to begin IB execution */ + uint64_t va_start; + /** Size of submission */ + uint32_t ib_bytes; + /** HW IP to submit to */ + uint32_t ip_type; + /** HW IP index of the same type to submit to */ + uint32_t ip_instance; + /** Ring index to submit to */ + uint32_t ring; }; struct drm_amdgpu_cs_chunk_fence { @@ -460,23 +491,28 @@ struct drm_amdgpu_info { /** AMDGPU_HW_IP_* */ uint32_t type; /** - * Index of the IP if there are more IPs of the same type. - * Ignored by AMDGPU_INFO_HW_IP_COUNT. + * Index of the IP if there are more IPs of the same + * type. Ignored by AMDGPU_INFO_HW_IP_COUNT. */ uint32_t ip_instance; } query_hw_ip; struct { uint32_t dword_offset; - uint32_t count; /* number of registers to read */ + /** number of registers to read */ + uint32_t count; uint32_t instance; + /** For future use, no flags defined so far */ uint32_t flags; } read_mmr_reg; struct { /** AMDGPU_INFO_FW_* */ uint32_t fw_type; - /** Index of the IP if there are more IPs of the same type. */ + /** + * Index of the IP if there are more IPs of + * the same type. + */ uint32_t ip_instance; /** * Index of the engine. Whether this is used depends @@ -537,9 +573,10 @@ struct drm_amdgpu_info_device { uint32_t family; uint32_t num_shader_engines; uint32_t num_shader_arrays_per_engine; - uint32_t gpu_counter_freq; /* in KHz */ - uint64_t max_engine_clock; /* in KHz */ - uint64_t max_memory_clock; /* in KHz */ + /* in KHz */ + uint32_t gpu_counter_freq; + uint64_t max_engine_clock; + uint64_t max_memory_clock; /* cu information */ uint32_t cu_active_number; uint32_t cu_ao_mask; -- cgit v1.2.3 From 570655b09b065d2fff1b8ab9bdb8308f4c5a05a3 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 30 Jan 2015 20:18:11 +0530 Subject: drm/msm/mdp4: Support NV12MT format in mdp4 Using fb modifier flag, support NV12MT format in MDP4. v2: - rework the modifier's description [Daniel Vetter's comment] - drop .set_mode_config() callback [Rob Clark's comment] v3: - change VENDOR's name and restrict usage to NV12 [pointed by Daniel] Signed-off-by: Rob Clark --- drivers/gpu/drm/drm_crtc.c | 18 ++++++++++++++++++ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 2 ++ drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 22 ++++++++++++++++++++++ include/uapi/drm/drm_fourcc.h | 15 +++++++++++++++ 4 files changed, 57 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 77f87b23a6e7..b69ed97d447c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3255,6 +3255,24 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) r->modifier[i], i); return -EINVAL; } + + /* modifier specific checks: */ + switch (r->modifier[i]) { + case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: + /* NOTE: the pitch restriction may be lifted later if it turns + * out that no hw has this restriction: + */ + if (r->pixel_format != DRM_FORMAT_NV12 || + width % 128 || height % 32 || + r->pitches[i] % 128) { + DRM_DEBUG_KMS("bad modifier data for plane %d\n", i); + return -EINVAL; + } + break; + + default: + break; + } } for (i = num_planes; i < 4; i++) { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index d847b9436194..88a75cbc8f71 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -119,6 +119,8 @@ static int mdp4_hw_init(struct msm_kms *kms) if (mdp4_kms->rev > 1) mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1); + dev->mode_config.allow_fb_modifiers = true; + out: pm_runtime_put_sync(dev->dev); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index dbc068988377..0d1dbb737933 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -33,6 +33,21 @@ struct mdp4_plane { }; #define to_mdp4_plane(x) container_of(x, struct mdp4_plane, base) +/* MDP format helper functions */ +static inline +enum mdp4_frame_format mdp4_get_frame_format(struct drm_framebuffer *fb) +{ + bool is_tile = false; + + if (fb->modifier[1] == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE) + is_tile = true; + + if (fb->pixel_format == DRM_FORMAT_NV12 && is_tile) + return FRAME_TILE_YCBCR_420; + + return FRAME_LINEAR; +} + static void mdp4_plane_set_scanout(struct drm_plane *plane, struct drm_framebuffer *fb); static int mdp4_plane_mode_set(struct drm_plane *plane, @@ -205,6 +220,7 @@ static int mdp4_plane_mode_set(struct drm_plane *plane, uint32_t op_mode = 0; uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT; uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT; + enum mdp4_frame_format frame_type = mdp4_get_frame_format(fb); if (!(crtc && fb)) { DBG("%s: disabled!", mdp4_plane->name); @@ -304,6 +320,7 @@ static int mdp4_plane_mode_set(struct drm_plane *plane, MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | MDP4_PIPE_SRC_FORMAT_FETCH_PLANES(format->fetch_type) | MDP4_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample) | + MDP4_PIPE_SRC_FORMAT_FRAME_FORMAT(frame_type) | COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT)); mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe), @@ -324,6 +341,11 @@ static int mdp4_plane_mode_set(struct drm_plane *plane, mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step); mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step); + if (frame_type != FRAME_LINEAR) + mdp4_write(mdp4_kms, REG_MDP4_PIPE_SSTILE_FRAME_SIZE(pipe), + MDP4_PIPE_SSTILE_FRAME_SIZE_WIDTH(src_w) | + MDP4_PIPE_SSTILE_FRAME_SIZE_HEIGHT(src_h)); + return 0; } diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 07735822a28f..2f295cde657e 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -207,4 +207,19 @@ */ #define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) +/* + * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks + * + * Macroblocks are laid in a Z-shape, and each pixel data is following the + * standard NV12 style. + * As for NV12, an image is the result of two frame buffers: one for Y, + * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). + * Alignment requirements are (for each buffer): + * - multiple of 128 pixels for the width + * - multiple of 32 pixels for the height + * + * For more information: see http://linuxtv.org/downloads/v4l-dvb-apis/re32.html + */ +#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) + #endif /* DRM_FOURCC_H */ -- cgit v1.2.3 From 7f8fc88613d6310993e8d7d827e0b956b3a744fa Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Sat, 30 May 2015 17:38:08 +0200 Subject: drm/msm: use __s32, __s64, __u32 and __u64 from linux/types.h for uabi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes userspace compilation errors like: error: unknown type name ‘uint32_t’ Signed-off-by: Mikko Rapeli Signed-off-by: Rob Clark --- include/uapi/drm/msm_drm.h | 76 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 0664c31f010c..75a232b9a970 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -23,7 +23,7 @@ /* Please note that modifications to all structs defined here are * subject to backwards-compatibility constraints: - * 1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit + * 1) Do not use pointers, use __u64 instead for 32 bit / 64 bit * user/kernel compatibility * 2) Keep fields aligned to their size * 3) Because of how drm_ioctl() works, we can add new fields at @@ -44,8 +44,8 @@ * same as 'struct timespec' but 32/64b ABI safe. */ struct drm_msm_timespec { - int64_t tv_sec; /* seconds */ - int64_t tv_nsec; /* nanoseconds */ + __s64 tv_sec; /* seconds */ + __s64 tv_nsec; /* nanoseconds */ }; #define MSM_PARAM_GPU_ID 0x01 @@ -53,9 +53,9 @@ struct drm_msm_timespec { #define MSM_PARAM_CHIP_ID 0x03 struct drm_msm_param { - uint32_t pipe; /* in, MSM_PIPE_x */ - uint32_t param; /* in, MSM_PARAM_x */ - uint64_t value; /* out (get_param) or in (set_param) */ + __u32 pipe; /* in, MSM_PIPE_x */ + __u32 param; /* in, MSM_PARAM_x */ + __u64 value; /* out (get_param) or in (set_param) */ }; /* @@ -77,15 +77,15 @@ struct drm_msm_param { MSM_BO_UNCACHED) struct drm_msm_gem_new { - uint64_t size; /* in */ - uint32_t flags; /* in, mask of MSM_BO_x */ - uint32_t handle; /* out */ + __u64 size; /* in */ + __u32 flags; /* in, mask of MSM_BO_x */ + __u32 handle; /* out */ }; struct drm_msm_gem_info { - uint32_t handle; /* in */ - uint32_t pad; - uint64_t offset; /* out, offset to pass to mmap() */ + __u32 handle; /* in */ + __u32 pad; + __u64 offset; /* out, offset to pass to mmap() */ }; #define MSM_PREP_READ 0x01 @@ -95,13 +95,13 @@ struct drm_msm_gem_info { #define MSM_PREP_FLAGS (MSM_PREP_READ | MSM_PREP_WRITE | MSM_PREP_NOSYNC) struct drm_msm_gem_cpu_prep { - uint32_t handle; /* in */ - uint32_t op; /* in, mask of MSM_PREP_x */ + __u32 handle; /* in */ + __u32 op; /* in, mask of MSM_PREP_x */ struct drm_msm_timespec timeout; /* in */ }; struct drm_msm_gem_cpu_fini { - uint32_t handle; /* in */ + __u32 handle; /* in */ }; /* @@ -120,11 +120,11 @@ struct drm_msm_gem_cpu_fini { * otherwise EINVAL. */ struct drm_msm_gem_submit_reloc { - uint32_t submit_offset; /* in, offset from submit_bo */ - uint32_t or; /* in, value OR'd with result */ - int32_t shift; /* in, amount of left shift (can be negative) */ - uint32_t reloc_idx; /* in, index of reloc_bo buffer */ - uint64_t reloc_offset; /* in, offset from start of reloc_bo */ + __u32 submit_offset; /* in, offset from submit_bo */ + __u32 or; /* in, value OR'd with result */ + __s32 shift; /* in, amount of left shift (can be negative) */ + __u32 reloc_idx; /* in, index of reloc_bo buffer */ + __u64 reloc_offset; /* in, offset from start of reloc_bo */ }; /* submit-types: @@ -139,13 +139,13 @@ struct drm_msm_gem_submit_reloc { #define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002 #define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003 struct drm_msm_gem_submit_cmd { - uint32_t type; /* in, one of MSM_SUBMIT_CMD_x */ - uint32_t submit_idx; /* in, index of submit_bo cmdstream buffer */ - uint32_t submit_offset; /* in, offset into submit_bo */ - uint32_t size; /* in, cmdstream size */ - uint32_t pad; - uint32_t nr_relocs; /* in, number of submit_reloc's */ - uint64_t __user relocs; /* in, ptr to array of submit_reloc's */ + __u32 type; /* in, one of MSM_SUBMIT_CMD_x */ + __u32 submit_idx; /* in, index of submit_bo cmdstream buffer */ + __u32 submit_offset; /* in, offset into submit_bo */ + __u32 size; /* in, cmdstream size */ + __u32 pad; + __u32 nr_relocs; /* in, number of submit_reloc's */ + __u64 __user relocs; /* in, ptr to array of submit_reloc's */ }; /* Each buffer referenced elsewhere in the cmdstream submit (ie. the @@ -165,9 +165,9 @@ struct drm_msm_gem_submit_cmd { #define MSM_SUBMIT_BO_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE) struct drm_msm_gem_submit_bo { - uint32_t flags; /* in, mask of MSM_SUBMIT_BO_x */ - uint32_t handle; /* in, GEM handle */ - uint64_t presumed; /* in/out, presumed buffer address */ + __u32 flags; /* in, mask of MSM_SUBMIT_BO_x */ + __u32 handle; /* in, GEM handle */ + __u64 presumed; /* in/out, presumed buffer address */ }; /* Each cmdstream submit consists of a table of buffers involved, and @@ -175,12 +175,12 @@ struct drm_msm_gem_submit_bo { * (context-restore), and IB buffers needed for per tile/bin draw cmds. */ struct drm_msm_gem_submit { - uint32_t pipe; /* in, MSM_PIPE_x */ - uint32_t fence; /* out */ - uint32_t nr_bos; /* in, number of submit_bo's */ - uint32_t nr_cmds; /* in, number of submit_cmd's */ - uint64_t __user bos; /* in, ptr to array of submit_bo's */ - uint64_t __user cmds; /* in, ptr to array of submit_cmd's */ + __u32 pipe; /* in, MSM_PIPE_x */ + __u32 fence; /* out */ + __u32 nr_bos; /* in, number of submit_bo's */ + __u32 nr_cmds; /* in, number of submit_cmd's */ + __u64 __user bos; /* in, ptr to array of submit_bo's */ + __u64 __user cmds; /* in, ptr to array of submit_cmd's */ }; /* The normal way to synchronize with the GPU is just to CPU_PREP on @@ -191,8 +191,8 @@ struct drm_msm_gem_submit { * APIs without requiring a dummy bo to synchronize on. */ struct drm_msm_wait_fence { - uint32_t fence; /* in */ - uint32_t pad; + __u32 fence; /* in */ + __u32 pad; struct drm_msm_timespec timeout; /* in */ }; -- cgit v1.2.3 From 2465ff6217f1b63e194cfd57018fa42abe7fcdf0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Jun 2015 09:58:55 +0200 Subject: drm/atomic: Extract needs_modeset function We use the same check already in the atomic core, so might as well make this official. And it's also reused in e.g. i915. Motivated by Maarten's idea to extract a connector_changed state out of mode_changed. Cc: Maarten Lankhorst Reviewed-By: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 3 +-- drivers/gpu/drm/drm_atomic_helper.c | 16 +++++----------- include/drm/drm_atomic.h | 6 ++++++ 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c7e59b074e62..f6f2fb58eb37 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1216,8 +1216,7 @@ int drm_atomic_check_only(struct drm_atomic_state *state) if (!state->allow_modeset) { for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (crtc_state->mode_changed || - crtc_state->active_changed) { + if (drm_atomic_crtc_needs_modeset(crtc_state)) { DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n", crtc->base.id); return -EINVAL; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 536ae4da4665..b06a607db6c2 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -331,12 +331,6 @@ mode_fixup(struct drm_atomic_state *state) return 0; } -static bool -needs_modeset(struct drm_crtc_state *state) -{ - return state->mode_changed || state->active_changed; -} - /** * drm_atomic_helper_check_modeset - validate state object for modeset changes * @dev: DRM device @@ -414,7 +408,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, crtc_state->active_changed = true; } - if (!needs_modeset(crtc_state)) + if (!drm_atomic_crtc_needs_modeset(crtc_state)) continue; DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n", @@ -564,7 +558,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)]; if (!old_crtc_state->active || - !needs_modeset(old_conn_state->crtc->state)) + !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) continue; encoder = old_conn_state->best_encoder; @@ -601,7 +595,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) const struct drm_crtc_helper_funcs *funcs; /* Shut down everything that needs a full modeset. */ - if (!needs_modeset(crtc->state)) + if (!drm_atomic_crtc_needs_modeset(crtc->state)) continue; if (!old_crtc_state->active) @@ -792,7 +786,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, const struct drm_crtc_helper_funcs *funcs; /* Need to filter out CRTCs where only planes change. */ - if (!needs_modeset(crtc->state)) + if (!drm_atomic_crtc_needs_modeset(crtc->state)) continue; if (!crtc->state->active) @@ -819,7 +813,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, continue; if (!connector->state->crtc->state->active || - !needs_modeset(connector->state->crtc->state)) + !drm_atomic_crtc_needs_modeset(connector->state->crtc->state)) continue; encoder = connector->state->best_encoder; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 1bbfedf466b9..8a3a913320eb 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -163,5 +163,11 @@ int __must_check drm_atomic_async_commit(struct drm_atomic_state *state); (plane_state) = (state)->plane_states[__i], 1); \ (__i)++) \ if (plane_state) +static inline bool +drm_atomic_crtc_needs_modeset(struct drm_crtc_state *state) +{ + return state->mode_changed || state->active_changed; +} + #endif /* DRM_ATOMIC_H_ */ -- cgit v1.2.3 From c8466a9166b00ecb0c6f768baf70636fe15f63ef Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 12 Jun 2015 21:59:00 +0900 Subject: drm/exynos: add Exynos5433 decon driver DECON(Display and Enhancement Controller) is new IP replacing FIMD in Exynos5433. This patch adds Exynos5433 decon driver. Signed-off-by: Joonyoung Shim Signed-off-by: Hyungwon Hwang Signed-off-by: Inki Dae --- .../devicetree/bindings/video/exynos5433-decon.txt | 65 ++ drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 660 +++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 + include/video/exynos5433_decon.h | 165 ++++++ 6 files changed, 898 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/exynos5433-decon.txt create mode 100644 drivers/gpu/drm/exynos/exynos5433_drm_decon.c create mode 100644 include/video/exynos5433_decon.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/video/exynos5433-decon.txt b/Documentation/devicetree/bindings/video/exynos5433-decon.txt new file mode 100644 index 000000000000..377afbf5122a --- /dev/null +++ b/Documentation/devicetree/bindings/video/exynos5433-decon.txt @@ -0,0 +1,65 @@ +Device-Tree bindings for Samsung Exynos SoC display controller (DECON) + +DECON (Display and Enhancement Controller) is the Display Controller for the +Exynos series of SoCs which transfers the image data from a video memory +buffer to an external LCD interface. + +Required properties: +- compatible: value should be "samsung,exynos5433-decon"; +- reg: physical base address and length of the DECON registers set. +- interrupts: should contain a list of all DECON IP block interrupts in the + order: VSYNC, LCD_SYSTEM. The interrupt specifier format + depends on the interrupt controller used. +- interrupt-names: should contain the interrupt names: "vsync", "lcd_sys" + in the same order as they were listed in the interrupts + property. +- clocks: must include clock specifiers corresponding to entries in the + clock-names property. +- clock-names: list of clock names sorted in the same order as the clocks + property. Must contain "aclk_decon", "aclk_smmu_decon0x", + "aclk_xiu_decon0x", "pclk_smmu_decon0x", clk_decon_vclk", + "sclk_decon_eclk" +- ports: contains a port which is connected to mic node. address-cells and + size-cells must 1 and 0, respectively. +- port: contains an endpoint node which is connected to the endpoint in the mic + node. The reg value muset be 0. +- i80-if-timings: specify whether the panel which is connected to decon uses + i80 lcd interface or mipi video interface. This node contains + no timing information as that of fimd does. Because there is + no register in decon to specify i80 interface timing value, + it is not needed, but make it remain to use same kind of node + in fimd and exynos7 decon. + +Example: +SoC specific DT entry: +decon: decon@13800000 { + compatible = "samsung,exynos5433-decon"; + reg = <0x13800000 0x2104>; + clocks = <&cmu_disp CLK_ACLK_DECON>, <&cmu_disp CLK_ACLK_SMMU_DECON0X>, + <&cmu_disp CLK_ACLK_XIU_DECON0X>, + <&cmu_disp CLK_PCLK_SMMU_DECON0X>, + <&cmu_disp CLK_SCLK_DECON_VCLK>, + <&cmu_disp CLK_SCLK_DECON_ECLK>; + clock-names = "aclk_decon", "aclk_smmu_decon0x", "aclk_xiu_decon0x", + "pclk_smmu_decon0x", "sclk_decon_vclk", "sclk_decon_eclk"; + interrupt-names = "vsync", "lcd_sys"; + interrupts = <0 202 0>, <0 203 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + decon_to_mic: endpoint { + remote-endpoint = <&mic_to_decon>; + }; + }; + }; +}; + +Board specific DT entry: +&decon { + i80-if-timings { + }; +}; diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index ddb7c8a26bfc..51a4eb564497 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -24,6 +24,12 @@ config DRM_EXYNOS_FIMD help Choose this option if you want to use Exynos FIMD for DRM. +config DRM_EXYNOS5433_DECON + bool "Exynos5433 DRM DECON" + depends on DRM_EXYNOS + help + Choose this option if you want to use Exynos5433 DECON for DRM. + config DRM_EXYNOS7_DECON bool "Exynos7 DRM DECON" depends on DRM_EXYNOS && !FB_S3C diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index cc90679cfc06..fbd084d3f658 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -10,6 +10,7 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \ exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o +exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON) += exynos5433_drm_decon.o exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON) += exynos7_drm_decon.o exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c new file mode 100644 index 000000000000..8b1225f245fc --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -0,0 +1,660 @@ +/* drivers/gpu/drm/exynos5433_drm_decon.c + * + * Copyright (C) 2015 Samsung Electronics Co.Ltd + * Authors: + * Joonyoung Shim + * Hyungwon Hwang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundationr + */ + +#include +#include +#include +#include +#include + +#include