diff options
author | Dave Airlie <airlied@redhat.com> | 2016-05-20 16:41:59 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-05-20 16:41:59 -0400 |
commit | fc7fedc20b7c819b9ef003afa16a64dc9e5ac30e (patch) | |
tree | 060d0ad8c242388c88415ba5f24067f0c9822d18 | |
parent | d5fa33f28401414959b68591075c8053e8276547 (diff) | |
parent | f921791314811afa00bb7fbbd40f51bd3b8eff01 (diff) |
Merge tag 'vmwgfx-next-160520' of git://people.freedesktop.org/~thomash/linux into drm-next
Pull request of 2016-05-20
* tag 'vmwgfx-next-160520' of git://people.freedesktop.org/~thomash/linux:
drm/vmwgfx: Report vmwgfx version to vmware.log
drm/vmwgfx: Add VMWare host messaging capability
drm/vmwgfx: Kill some lockdep warnings
-rw-r--r-- | drivers/gpu/drm/vmwgfx/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 27 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_msg.c | 416 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_msg.h | 191 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 3 |
8 files changed, 653 insertions, 16 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index d281575bbe11..473d00451b0f 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile | |||
@@ -8,6 +8,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ | |||
8 | vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ | 8 | vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ |
9 | vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ | 9 | vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ |
10 | vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ | 10 | vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ |
11 | vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o | 11 | vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o |
12 | 12 | ||
13 | obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o | 13 | obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 9555e204814a..9fcd8200d485 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /************************************************************************** | 1 | /************************************************************************** |
2 | * | 2 | * |
3 | * Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA | 3 | * Copyright © 2009-2016 VMware, Inc., Palo Alto, CA., USA |
4 | * All Rights Reserved. | 4 | * All Rights Reserved. |
5 | * | 5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a | 6 | * Permission is hereby granted, free of charge, to any person obtaining a |
@@ -44,6 +44,12 @@ | |||
44 | #define VMW_MIN_INITIAL_WIDTH 800 | 44 | #define VMW_MIN_INITIAL_WIDTH 800 |
45 | #define VMW_MIN_INITIAL_HEIGHT 600 | 45 | #define VMW_MIN_INITIAL_HEIGHT 600 |
46 | 46 | ||
47 | #ifndef VMWGFX_GIT_VERSION | ||
48 | #define VMWGFX_GIT_VERSION "Unknown" | ||
49 | #endif | ||
50 | |||
51 | #define VMWGFX_REPO "In Tree" | ||
52 | |||
47 | 53 | ||
48 | /** | 54 | /** |
49 | * Fully encoded drm commands. Might move to vmw_drm.h | 55 | * Fully encoded drm commands. Might move to vmw_drm.h |
@@ -613,6 +619,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) | |||
613 | uint32_t svga_id; | 619 | uint32_t svga_id; |
614 | enum vmw_res_type i; | 620 | enum vmw_res_type i; |
615 | bool refuse_dma = false; | 621 | bool refuse_dma = false; |
622 | char host_log[100] = {0}; | ||
616 | 623 | ||
617 | dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); | 624 | dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); |
618 | if (unlikely(dev_priv == NULL)) { | 625 | if (unlikely(dev_priv == NULL)) { |
@@ -628,6 +635,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) | |||
628 | mutex_init(&dev_priv->cmdbuf_mutex); | 635 | mutex_init(&dev_priv->cmdbuf_mutex); |
629 | mutex_init(&dev_priv->release_mutex); | 636 | mutex_init(&dev_priv->release_mutex); |
630 | mutex_init(&dev_priv->binding_mutex); | 637 | mutex_init(&dev_priv->binding_mutex); |
638 | mutex_init(&dev_priv->global_kms_state_mutex); | ||
631 | rwlock_init(&dev_priv->resource_lock); | 639 | rwlock_init(&dev_priv->resource_lock); |
632 | ttm_lock_init(&dev_priv->reservation_sem); | 640 | ttm_lock_init(&dev_priv->reservation_sem); |
633 | spin_lock_init(&dev_priv->hw_lock); | 641 | spin_lock_init(&dev_priv->hw_lock); |
@@ -873,6 +881,16 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) | |||
873 | 881 | ||
874 | DRM_INFO("DX: %s\n", dev_priv->has_dx ? "yes." : "no."); | 882 | DRM_INFO("DX: %s\n", dev_priv->has_dx ? "yes." : "no."); |
875 | 883 | ||
884 | snprintf(host_log, sizeof(host_log), "vmwgfx: %s-%s", | ||
885 | VMWGFX_REPO, VMWGFX_GIT_VERSION); | ||
886 | vmw_host_log(host_log); | ||
887 | |||
888 | memset(host_log, 0, sizeof(host_log)); | ||
889 | snprintf(host_log, sizeof(host_log), "vmwgfx: Module Version: %d.%d.%d", | ||
890 | VMWGFX_DRIVER_MAJOR, VMWGFX_DRIVER_MINOR, | ||
891 | VMWGFX_DRIVER_PATCHLEVEL); | ||
892 | vmw_host_log(host_log); | ||
893 | |||
876 | if (dev_priv->enable_fb) { | 894 | if (dev_priv->enable_fb) { |
877 | vmw_fifo_resource_inc(dev_priv); | 895 | vmw_fifo_resource_inc(dev_priv); |
878 | vmw_svga_enable(dev_priv); | 896 | vmw_svga_enable(dev_priv); |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 019a6ca3e8e9..1980e2a28265 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | |||
@@ -412,6 +412,7 @@ struct vmw_private { | |||
412 | struct drm_property *implicit_placement_property; | 412 | struct drm_property *implicit_placement_property; |
413 | unsigned num_implicit; | 413 | unsigned num_implicit; |
414 | struct vmw_framebuffer *implicit_fb; | 414 | struct vmw_framebuffer *implicit_fb; |
415 | struct mutex global_kms_state_mutex; | ||
415 | 416 | ||
416 | /* | 417 | /* |
417 | * Context and surface management. | 418 | * Context and surface management. |
@@ -1234,4 +1235,10 @@ static inline void vmw_mmio_write(u32 value, u32 *addr) | |||
1234 | { | 1235 | { |
1235 | WRITE_ONCE(*addr, value); | 1236 | WRITE_ONCE(*addr, value); |
1236 | } | 1237 | } |
1238 | |||
1239 | /** | ||
1240 | * Add vmw_msg module function | ||
1241 | */ | ||
1242 | extern int vmw_host_log(const char *log); | ||
1243 | |||
1237 | #endif | 1244 | #endif |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index fc20d45e3da9..55231cce73a0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | |||
@@ -2143,13 +2143,13 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, | |||
2143 | void vmw_kms_del_active(struct vmw_private *dev_priv, | 2143 | void vmw_kms_del_active(struct vmw_private *dev_priv, |
2144 | struct vmw_display_unit *du) | 2144 | struct vmw_display_unit *du) |
2145 | { | 2145 | { |
2146 | lockdep_assert_held_once(&dev_priv->dev->mode_config.mutex); | 2146 | mutex_lock(&dev_priv->global_kms_state_mutex); |
2147 | |||
2148 | if (du->active_implicit) { | 2147 | if (du->active_implicit) { |
2149 | if (--(dev_priv->num_implicit) == 0) | 2148 | if (--(dev_priv->num_implicit) == 0) |
2150 | dev_priv->implicit_fb = NULL; | 2149 | dev_priv->implicit_fb = NULL; |
2151 | du->active_implicit = false; | 2150 | du->active_implicit = false; |
2152 | } | 2151 | } |
2152 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
2153 | } | 2153 | } |
2154 | 2154 | ||
2155 | /** | 2155 | /** |
@@ -2165,8 +2165,7 @@ void vmw_kms_add_active(struct vmw_private *dev_priv, | |||
2165 | struct vmw_display_unit *du, | 2165 | struct vmw_display_unit *du, |
2166 | struct vmw_framebuffer *vfb) | 2166 | struct vmw_framebuffer *vfb) |
2167 | { | 2167 | { |
2168 | lockdep_assert_held_once(&dev_priv->dev->mode_config.mutex); | 2168 | mutex_lock(&dev_priv->global_kms_state_mutex); |
2169 | |||
2170 | WARN_ON_ONCE(!dev_priv->num_implicit && dev_priv->implicit_fb); | 2169 | WARN_ON_ONCE(!dev_priv->num_implicit && dev_priv->implicit_fb); |
2171 | 2170 | ||
2172 | if (!du->active_implicit && du->is_implicit) { | 2171 | if (!du->active_implicit && du->is_implicit) { |
@@ -2174,6 +2173,7 @@ void vmw_kms_add_active(struct vmw_private *dev_priv, | |||
2174 | du->active_implicit = true; | 2173 | du->active_implicit = true; |
2175 | dev_priv->num_implicit++; | 2174 | dev_priv->num_implicit++; |
2176 | } | 2175 | } |
2176 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
2177 | } | 2177 | } |
2178 | 2178 | ||
2179 | /** | 2179 | /** |
@@ -2190,16 +2190,13 @@ bool vmw_kms_crtc_flippable(struct vmw_private *dev_priv, | |||
2190 | struct drm_crtc *crtc) | 2190 | struct drm_crtc *crtc) |
2191 | { | 2191 | { |
2192 | struct vmw_display_unit *du = vmw_crtc_to_du(crtc); | 2192 | struct vmw_display_unit *du = vmw_crtc_to_du(crtc); |
2193 | bool ret; | ||
2193 | 2194 | ||
2194 | lockdep_assert_held_once(&dev_priv->dev->mode_config.mutex); | 2195 | mutex_lock(&dev_priv->global_kms_state_mutex); |
2195 | 2196 | ret = !du->is_implicit || dev_priv->num_implicit == 1; | |
2196 | if (!du->is_implicit) | 2197 | mutex_unlock(&dev_priv->global_kms_state_mutex); |
2197 | return true; | ||
2198 | |||
2199 | if (dev_priv->num_implicit != 1) | ||
2200 | return false; | ||
2201 | 2198 | ||
2202 | return true; | 2199 | return ret; |
2203 | } | 2200 | } |
2204 | 2201 | ||
2205 | /** | 2202 | /** |
@@ -2214,16 +2211,18 @@ void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv, | |||
2214 | struct vmw_display_unit *du = vmw_crtc_to_du(crtc); | 2211 | struct vmw_display_unit *du = vmw_crtc_to_du(crtc); |
2215 | struct vmw_framebuffer *vfb; | 2212 | struct vmw_framebuffer *vfb; |
2216 | 2213 | ||
2217 | lockdep_assert_held_once(&dev_priv->dev->mode_config.mutex); | 2214 | mutex_lock(&dev_priv->global_kms_state_mutex); |
2218 | 2215 | ||
2219 | if (!du->is_implicit) | 2216 | if (!du->is_implicit) |
2220 | return; | 2217 | goto out_unlock; |
2221 | 2218 | ||
2222 | vfb = vmw_framebuffer_to_vfb(crtc->primary->fb); | 2219 | vfb = vmw_framebuffer_to_vfb(crtc->primary->fb); |
2223 | WARN_ON_ONCE(dev_priv->num_implicit != 1 && | 2220 | WARN_ON_ONCE(dev_priv->num_implicit != 1 && |
2224 | dev_priv->implicit_fb != vfb); | 2221 | dev_priv->implicit_fb != vfb); |
2225 | 2222 | ||
2226 | dev_priv->implicit_fb = vfb; | 2223 | dev_priv->implicit_fb = vfb; |
2224 | out_unlock: | ||
2225 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
2227 | } | 2226 | } |
2228 | 2227 | ||
2229 | /** | 2228 | /** |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c new file mode 100644 index 000000000000..6de283c8fa3e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c | |||
@@ -0,0 +1,416 @@ | |||
1 | /* | ||
2 | * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sub license, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial portions | ||
15 | * of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
20 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
23 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | |||
28 | #include <linux/slab.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <asm/hypervisor.h> | ||
32 | #include "drmP.h" | ||
33 | #include "vmwgfx_msg.h" | ||
34 | |||
35 | |||
36 | #define MESSAGE_STATUS_SUCCESS 0x0001 | ||
37 | #define MESSAGE_STATUS_DORECV 0x0002 | ||
38 | #define MESSAGE_STATUS_CPT 0x0010 | ||
39 | #define MESSAGE_STATUS_HB 0x0080 | ||
40 | |||
41 | #define RPCI_PROTOCOL_NUM 0x49435052 | ||
42 | #define GUESTMSG_FLAG_COOKIE 0x80000000 | ||
43 | |||
44 | #define RETRIES 3 | ||
45 | |||
46 | #define VMW_HYPERVISOR_MAGIC 0x564D5868 | ||
47 | #define VMW_HYPERVISOR_PORT 0x5658 | ||
48 | #define VMW_HYPERVISOR_HB_PORT 0x5659 | ||
49 | |||
50 | #define VMW_PORT_CMD_MSG 30 | ||
51 | #define VMW_PORT_CMD_HB_MSG 0 | ||
52 | #define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG) | ||
53 | #define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG) | ||
54 | #define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG) | ||
55 | #define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) | ||
56 | #define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) | ||
57 | |||
58 | #define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) | ||
59 | |||
60 | static u32 vmw_msg_enabled = 1; | ||
61 | |||
62 | enum rpc_msg_type { | ||
63 | MSG_TYPE_OPEN, | ||
64 | MSG_TYPE_SENDSIZE, | ||
65 | MSG_TYPE_SENDPAYLOAD, | ||
66 | MSG_TYPE_RECVSIZE, | ||
67 | MSG_TYPE_RECVPAYLOAD, | ||
68 | MSG_TYPE_RECVSTATUS, | ||
69 | MSG_TYPE_CLOSE, | ||
70 | }; | ||
71 | |||
72 | struct rpc_channel { | ||
73 | u16 channel_id; | ||
74 | u32 cookie_high; | ||
75 | u32 cookie_low; | ||
76 | }; | ||
77 | |||
78 | |||
79 | |||
80 | /** | ||
81 | * vmw_open_channel | ||
82 | * | ||
83 | * @channel: RPC channel | ||
84 | * @protocol: | ||
85 | * | ||
86 | * Returns: 0 on success | ||
87 | */ | ||
88 | static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol) | ||
89 | { | ||
90 | unsigned long eax, ebx, ecx, edx, si = 0, di = 0; | ||
91 | |||
92 | VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, | ||
93 | (protocol | GUESTMSG_FLAG_COOKIE), si, di, | ||
94 | VMW_HYPERVISOR_PORT, | ||
95 | VMW_HYPERVISOR_MAGIC, | ||
96 | eax, ebx, ecx, edx, si, di); | ||
97 | |||
98 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) | ||
99 | return -EINVAL; | ||
100 | |||
101 | channel->channel_id = HIGH_WORD(edx); | ||
102 | channel->cookie_high = si; | ||
103 | channel->cookie_low = di; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | |||
109 | |||
110 | /** | ||
111 | * vmw_close_channel | ||
112 | * | ||
113 | * @channel: RPC channel | ||
114 | * | ||
115 | * Returns: 0 on success | ||
116 | */ | ||
117 | static int vmw_close_channel(struct rpc_channel *channel) | ||
118 | { | ||
119 | unsigned long eax, ebx, ecx, edx, si, di; | ||
120 | |||
121 | /* Set up additional parameters */ | ||
122 | si = channel->cookie_high; | ||
123 | di = channel->cookie_low; | ||
124 | |||
125 | VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, | ||
126 | 0, si, di, | ||
127 | (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), | ||
128 | VMW_HYPERVISOR_MAGIC, | ||
129 | eax, ebx, ecx, edx, si, di); | ||
130 | |||
131 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) | ||
132 | return -EINVAL; | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | |||
138 | |||
139 | /** | ||
140 | * vmw_send_msg: Sends a message to the host | ||
141 | * | ||
142 | * @channel: RPC channel | ||
143 | * @logmsg: NULL terminated string | ||
144 | * | ||
145 | * Returns: 0 on success | ||
146 | */ | ||
147 | static int vmw_send_msg(struct rpc_channel *channel, const char *msg) | ||
148 | { | ||
149 | unsigned long eax, ebx, ecx, edx, si, di, bp; | ||
150 | size_t msg_len = strlen(msg); | ||
151 | int retries = 0; | ||
152 | |||
153 | |||
154 | while (retries < RETRIES) { | ||
155 | retries++; | ||
156 | |||
157 | /* Set up additional parameters */ | ||
158 | si = channel->cookie_high; | ||
159 | di = channel->cookie_low; | ||
160 | |||
161 | VMW_PORT(VMW_PORT_CMD_SENDSIZE, | ||
162 | msg_len, si, di, | ||
163 | VMW_HYPERVISOR_PORT | (channel->channel_id << 16), | ||
164 | VMW_HYPERVISOR_MAGIC, | ||
165 | eax, ebx, ecx, edx, si, di); | ||
166 | |||
167 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 || | ||
168 | (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) { | ||
169 | /* Expected success + high-bandwidth. Give up. */ | ||
170 | return -EINVAL; | ||
171 | } | ||
172 | |||
173 | /* Send msg */ | ||
174 | si = (uintptr_t) msg; | ||
175 | di = channel->cookie_low; | ||
176 | bp = channel->cookie_high; | ||
177 | |||
178 | VMW_PORT_HB_OUT( | ||
179 | (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, | ||
180 | msg_len, si, di, | ||
181 | VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), | ||
182 | VMW_HYPERVISOR_MAGIC, bp, | ||
183 | eax, ebx, ecx, edx, si, di); | ||
184 | |||
185 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) { | ||
186 | return 0; | ||
187 | } else if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { | ||
188 | /* A checkpoint occurred. Retry. */ | ||
189 | continue; | ||
190 | } else { | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return -EINVAL; | ||
196 | } | ||
197 | |||
198 | |||
199 | |||
200 | /** | ||
201 | * vmw_recv_msg: Receives a message from the host | ||
202 | * | ||
203 | * Note: It is the caller's responsibility to call kfree() on msg. | ||
204 | * | ||
205 | * @channel: channel opened by vmw_open_channel | ||
206 | * @msg: [OUT] message received from the host | ||
207 | * @msg_len: message length | ||
208 | */ | ||
209 | static int vmw_recv_msg(struct rpc_channel *channel, void **msg, | ||
210 | size_t *msg_len) | ||
211 | { | ||
212 | unsigned long eax, ebx, ecx, edx, si, di, bp; | ||
213 | char *reply; | ||
214 | size_t reply_len; | ||
215 | int retries = 0; | ||
216 | |||
217 | |||
218 | *msg_len = 0; | ||
219 | *msg = NULL; | ||
220 | |||
221 | while (retries < RETRIES) { | ||
222 | retries++; | ||
223 | |||
224 | /* Set up additional parameters */ | ||
225 | si = channel->cookie_high; | ||
226 | di = channel->cookie_low; | ||
227 | |||
228 | VMW_PORT(VMW_PORT_CMD_RECVSIZE, | ||
229 | 0, si, di, | ||
230 | (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), | ||
231 | VMW_HYPERVISOR_MAGIC, | ||
232 | eax, ebx, ecx, edx, si, di); | ||
233 | |||
234 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 || | ||
235 | (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) { | ||
236 | DRM_ERROR("Failed to get reply size\n"); | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | /* No reply available. This is okay. */ | ||
241 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_DORECV) == 0) | ||
242 | return 0; | ||
243 | |||
244 | reply_len = ebx; | ||
245 | reply = kzalloc(reply_len + 1, GFP_KERNEL); | ||
246 | if (reply == NULL) { | ||
247 | DRM_ERROR("Cannot allocate memory for reply\n"); | ||
248 | return -ENOMEM; | ||
249 | } | ||
250 | |||
251 | |||
252 | /* Receive buffer */ | ||
253 | si = channel->cookie_high; | ||
254 | di = (uintptr_t) reply; | ||
255 | bp = channel->cookie_low; | ||
256 | |||
257 | VMW_PORT_HB_IN( | ||
258 | (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, | ||
259 | reply_len, si, di, | ||
260 | VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), | ||
261 | VMW_HYPERVISOR_MAGIC, bp, | ||
262 | eax, ebx, ecx, edx, si, di); | ||
263 | |||
264 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { | ||
265 | kfree(reply); | ||
266 | |||
267 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { | ||
268 | /* A checkpoint occurred. Retry. */ | ||
269 | continue; | ||
270 | } | ||
271 | |||
272 | return -EINVAL; | ||
273 | } | ||
274 | |||
275 | reply[reply_len] = '\0'; | ||
276 | |||
277 | |||
278 | /* Ack buffer */ | ||
279 | si = channel->cookie_high; | ||
280 | di = channel->cookie_low; | ||
281 | |||
282 | VMW_PORT(VMW_PORT_CMD_RECVSTATUS, | ||
283 | MESSAGE_STATUS_SUCCESS, si, di, | ||
284 | (VMW_HYPERVISOR_PORT | (channel->channel_id << 16)), | ||
285 | VMW_HYPERVISOR_MAGIC, | ||
286 | eax, ebx, ecx, edx, si, di); | ||
287 | |||
288 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { | ||
289 | kfree(reply); | ||
290 | |||
291 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) { | ||
292 | /* A checkpoint occurred. Retry. */ | ||
293 | continue; | ||
294 | } | ||
295 | |||
296 | return -EINVAL; | ||
297 | } | ||
298 | |||
299 | break; | ||
300 | } | ||
301 | |||
302 | *msg_len = reply_len; | ||
303 | *msg = reply; | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | |||
309 | /** | ||
310 | * vmw_host_get_guestinfo: Gets a GuestInfo parameter | ||
311 | * | ||
312 | * Gets the value of a GuestInfo.* parameter. The value returned will be in | ||
313 | * a string, and it is up to the caller to post-process. | ||
314 | * | ||
315 | * @guest_info_param: Parameter to get, e.g. GuestInfo.svga.gl3 | ||
316 | * @buffer: if NULL, *reply_len will contain reply size. | ||
317 | * @length: size of the reply_buf. Set to size of reply upon return | ||
318 | * | ||
319 | * Returns: 0 on success | ||
320 | */ | ||
321 | int vmw_host_get_guestinfo(const char *guest_info_param, | ||
322 | char *buffer, size_t *length) | ||
323 | { | ||
324 | struct rpc_channel channel; | ||
325 | char *msg, *reply = NULL; | ||
326 | size_t msg_len, reply_len = 0; | ||
327 | int ret = 0; | ||
328 | |||
329 | |||
330 | if (!vmw_msg_enabled) | ||
331 | return -ENODEV; | ||
332 | |||
333 | if (!guest_info_param || !length) | ||
334 | return -EINVAL; | ||
335 | |||
336 | msg_len = strlen(guest_info_param) + strlen("info-get ") + 1; | ||
337 | msg = kzalloc(msg_len, GFP_KERNEL); | ||
338 | if (msg == NULL) { | ||
339 | DRM_ERROR("Cannot allocate memory to get %s", guest_info_param); | ||
340 | return -ENOMEM; | ||
341 | } | ||
342 | |||
343 | sprintf(msg, "info-get %s", guest_info_param); | ||
344 | |||
345 | if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) || | ||
346 | vmw_send_msg(&channel, msg) || | ||
347 | vmw_recv_msg(&channel, (void *) &reply, &reply_len) || | ||
348 | vmw_close_channel(&channel)) { | ||
349 | DRM_ERROR("Failed to get %s", guest_info_param); | ||
350 | |||
351 | ret = -EINVAL; | ||
352 | } | ||
353 | |||
354 | if (buffer && reply && reply_len > 0) { | ||
355 | /* Remove reply code, which are the first 2 characters of | ||
356 | * the reply | ||
357 | */ | ||
358 | reply_len = max(reply_len - 2, (size_t) 0); | ||
359 | reply_len = min(reply_len, *length); | ||
360 | |||
361 | if (reply_len > 0) | ||
362 | memcpy(buffer, reply + 2, reply_len); | ||
363 | } | ||
364 | |||
365 | *length = reply_len; | ||
366 | |||
367 | kfree(reply); | ||
368 | kfree(msg); | ||
369 | |||
370 | return ret; | ||
371 | } | ||
372 | |||
373 | |||
374 | |||
375 | /** | ||
376 | * vmw_host_log: Sends a log message to the host | ||
377 | * | ||
378 | * @log: NULL terminated string | ||
379 | * | ||
380 | * Returns: 0 on success | ||
381 | */ | ||
382 | int vmw_host_log(const char *log) | ||
383 | { | ||
384 | struct rpc_channel channel; | ||
385 | char *msg; | ||
386 | int msg_len; | ||
387 | int ret = 0; | ||
388 | |||
389 | |||
390 | if (!vmw_msg_enabled) | ||
391 | return -ENODEV; | ||
392 | |||
393 | if (!log) | ||
394 | return ret; | ||
395 | |||
396 | msg_len = strlen(log) + strlen("log ") + 1; | ||
397 | msg = kzalloc(msg_len, GFP_KERNEL); | ||
398 | if (msg == NULL) { | ||
399 | DRM_ERROR("Cannot allocate memory for log message\n"); | ||
400 | return -ENOMEM; | ||
401 | } | ||
402 | |||
403 | sprintf(msg, "log %s", log); | ||
404 | |||
405 | if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) || | ||
406 | vmw_send_msg(&channel, msg) || | ||
407 | vmw_close_channel(&channel)) { | ||
408 | DRM_ERROR("Failed to send log\n"); | ||
409 | |||
410 | ret = -EINVAL; | ||
411 | } | ||
412 | |||
413 | kfree(msg); | ||
414 | |||
415 | return ret; | ||
416 | } | ||
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.h b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.h new file mode 100644 index 000000000000..557a033fb610 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.h | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016, VMware, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
12 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
13 | * details. | ||
14 | * | ||
15 | * Based on code from vmware.c and vmmouse.c. | ||
16 | * Author: | ||
17 | * Sinclair Yeh <syeh@vmware.com> | ||
18 | */ | ||
19 | #ifndef _VMWGFX_MSG_H | ||
20 | #define _VMWGFX_MSG_H | ||
21 | |||
22 | |||
23 | /** | ||
24 | * Hypervisor-specific bi-directional communication channel. Should never | ||
25 | * execute on bare metal hardware. The caller must make sure to check for | ||
26 | * supported hypervisor before using these macros. | ||
27 | * | ||
28 | * The last two parameters are both input and output and must be initialized. | ||
29 | * | ||
30 | * @cmd: [IN] Message Cmd | ||
31 | * @in_ebx: [IN] Message Len, through EBX | ||
32 | * @in_si: [IN] Input argument through SI, set to 0 if not used | ||
33 | * @in_di: [IN] Input argument through DI, set ot 0 if not used | ||
34 | * @port_num: [IN] port number + [channel id] | ||
35 | * @magic: [IN] hypervisor magic value | ||
36 | * @eax: [OUT] value of EAX register | ||
37 | * @ebx: [OUT] e.g. status from an HB message status command | ||
38 | * @ecx: [OUT] e.g. status from a non-HB message status command | ||
39 | * @edx: [OUT] e.g. channel id | ||
40 | * @si: [OUT] | ||
41 | * @di: [OUT] | ||
42 | */ | ||
43 | #define VMW_PORT(cmd, in_ebx, in_si, in_di, \ | ||
44 | port_num, magic, \ | ||
45 | eax, ebx, ecx, edx, si, di) \ | ||
46 | ({ \ | ||
47 | asm volatile ("inl %%dx, %%eax;" : \ | ||
48 | "=a"(eax), \ | ||
49 | "=b"(ebx), \ | ||
50 | "=c"(ecx), \ | ||
51 | "=d"(edx), \ | ||
52 | "=S"(si), \ | ||
53 | "=D"(di) : \ | ||
54 | "a"(magic), \ | ||
55 | "b"(in_ebx), \ | ||
56 | "c"(cmd), \ | ||
57 | "d"(port_num), \ | ||
58 | "S"(in_si), \ | ||
59 | "D"(in_di) : \ | ||
60 | "memory"); \ | ||
61 | }) | ||
62 | |||
63 | |||
64 | /** | ||
65 | * Hypervisor-specific bi-directional communication channel. Should never | ||
66 | * execute on bare metal hardware. The caller must make sure to check for | ||
67 | * supported hypervisor before using these macros. | ||
68 | * | ||
69 | * The last 3 parameters are both input and output and must be initialized. | ||
70 | * | ||
71 | * @cmd: [IN] Message Cmd | ||
72 | * @in_ecx: [IN] Message Len, through ECX | ||
73 | * @in_si: [IN] Input argument through SI, set to 0 if not used | ||
74 | * @in_di: [IN] Input argument through DI, set to 0 if not used | ||
75 | * @port_num: [IN] port number + [channel id] | ||
76 | * @magic: [IN] hypervisor magic value | ||
77 | * @bp: [IN] | ||
78 | * @eax: [OUT] value of EAX register | ||
79 | * @ebx: [OUT] e.g. status from an HB message status command | ||
80 | * @ecx: [OUT] e.g. status from a non-HB message status command | ||
81 | * @edx: [OUT] e.g. channel id | ||
82 | * @si: [OUT] | ||
83 | * @di: [OUT] | ||
84 | */ | ||
85 | #ifdef __x86_64__ | ||
86 | |||
87 | #define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, \ | ||
88 | port_num, magic, bp, \ | ||
89 | eax, ebx, ecx, edx, si, di) \ | ||
90 | ({ \ | ||
91 | asm volatile ("push %%rbp;" \ | ||
92 | "mov %12, %%rbp;" \ | ||
93 | "rep outsb;" \ | ||
94 | "pop %%rbp;" : \ | ||
95 | "=a"(eax), \ | ||
96 | "=b"(ebx), \ | ||
97 | "=c"(ecx), \ | ||
98 | "=d"(edx), \ | ||
99 | "=S"(si), \ | ||
100 | "=D"(di) : \ | ||
101 | "a"(magic), \ | ||
102 | "b"(cmd), \ | ||
103 | "c"(in_ecx), \ | ||
104 | "d"(port_num), \ | ||
105 | "S"(in_si), \ | ||
106 | "D"(in_di), \ | ||
107 | "r"(bp) : \ | ||
108 | "memory", "cc"); \ | ||
109 | }) | ||
110 | |||
111 | |||
112 | #define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, \ | ||
113 | port_num, magic, bp, \ | ||
114 | eax, ebx, ecx, edx, si, di) \ | ||
115 | ({ \ | ||
116 | asm volatile ("push %%rbp;" \ | ||
117 | "mov %12, %%rbp;" \ | ||
118 | "rep insb;" \ | ||
119 | "pop %%rbp" : \ | ||
120 | "=a"(eax), \ | ||
121 | "=b"(ebx), \ | ||
122 | "=c"(ecx), \ | ||
123 | "=d"(edx), \ | ||
124 | "=S"(si), \ | ||
125 | "=D"(di) : \ | ||
126 | "a"(magic), \ | ||
127 | "b"(cmd), \ | ||
128 | "c"(in_ecx), \ | ||
129 | "d"(port_num), \ | ||
130 | "S"(in_si), \ | ||
131 | "D"(in_di), \ | ||
132 | "r"(bp) : \ | ||
133 | "memory", "cc"); \ | ||
134 | }) | ||
135 | |||
136 | #else | ||
137 | |||
138 | /* In the 32-bit version of this macro, we use "m" because there is no | ||
139 | * more register left for bp | ||
140 | */ | ||
141 | #define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, \ | ||
142 | port_num, magic, bp, \ | ||
143 | eax, ebx, ecx, edx, si, di) \ | ||
144 | ({ \ | ||
145 | asm volatile ("push %%ebp;" \ | ||
146 | "mov %12, %%ebp;" \ | ||
147 | "rep outsb;" \ | ||
148 | "pop %%ebp;" : \ | ||
149 | "=a"(eax), \ | ||
150 | "=b"(ebx), \ | ||
151 | "=c"(ecx), \ | ||
152 | "=d"(edx), \ | ||
153 | "=S"(si), \ | ||
154 | "=D"(di) : \ | ||
155 | "a"(magic), \ | ||
156 | "b"(cmd), \ | ||
157 | "c"(in_ecx), \ | ||
158 | "d"(port_num), \ | ||
159 | "S"(in_si), \ | ||
160 | "D"(in_di), \ | ||
161 | "m"(bp) : \ | ||
162 | "memory", "cc"); \ | ||
163 | }) | ||
164 | |||
165 | |||
166 | #define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, \ | ||
167 | port_num, magic, bp, \ | ||
168 | eax, ebx, ecx, edx, si, di) \ | ||
169 | ({ \ | ||
170 | asm volatile ("push %%ebp;" \ | ||
171 | "mov %12, %%ebp;" \ | ||
172 | "rep insb;" \ | ||
173 | "pop %%ebp" : \ | ||
174 | "=a"(eax), \ | ||
175 | "=b"(ebx), \ | ||
176 | "=c"(ecx), \ | ||
177 | "=d"(edx), \ | ||
178 | "=S"(si), \ | ||
179 | "=D"(di) : \ | ||
180 | "a"(magic), \ | ||
181 | "b"(cmd), \ | ||
182 | "c"(in_ecx), \ | ||
183 | "d"(port_num), \ | ||
184 | "S"(in_si), \ | ||
185 | "D"(in_di), \ | ||
186 | "m"(bp) : \ | ||
187 | "memory", "cc"); \ | ||
188 | }) | ||
189 | #endif /* #if __x86_64__ */ | ||
190 | |||
191 | #endif | ||
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 0ea22fd112c9..b74eae2b8594 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | |||
@@ -285,14 +285,17 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) | |||
285 | } | 285 | } |
286 | 286 | ||
287 | /* Only one active implicit frame-buffer at a time. */ | 287 | /* Only one active implicit frame-buffer at a time. */ |
288 | mutex_lock(&dev_priv->global_kms_state_mutex); | ||
288 | if (sou->base.is_implicit && | 289 | if (sou->base.is_implicit && |
289 | dev_priv->implicit_fb && vfb && | 290 | dev_priv->implicit_fb && vfb && |
290 | !(dev_priv->num_implicit == 1 && | 291 | !(dev_priv->num_implicit == 1 && |
291 | sou->base.active_implicit) && | 292 | sou->base.active_implicit) && |
292 | dev_priv->implicit_fb != vfb) { | 293 | dev_priv->implicit_fb != vfb) { |
294 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
293 | DRM_ERROR("Multiple implicit framebuffers not supported.\n"); | 295 | DRM_ERROR("Multiple implicit framebuffers not supported.\n"); |
294 | return -EINVAL; | 296 | return -EINVAL; |
295 | } | 297 | } |
298 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
296 | 299 | ||
297 | /* since they always map one to one these are safe */ | 300 | /* since they always map one to one these are safe */ |
298 | connector = &sou->base.connector; | 301 | connector = &sou->base.connector; |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index b949102ad864..9ca818fb034c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | |||
@@ -553,12 +553,15 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | |||
553 | } | 553 | } |
554 | 554 | ||
555 | /* Only one active implicit frame-buffer at a time. */ | 555 | /* Only one active implicit frame-buffer at a time. */ |
556 | mutex_lock(&dev_priv->global_kms_state_mutex); | ||
556 | if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb && | 557 | if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb && |
557 | !(dev_priv->num_implicit == 1 && stdu->base.active_implicit) | 558 | !(dev_priv->num_implicit == 1 && stdu->base.active_implicit) |
558 | && dev_priv->implicit_fb != vfb) { | 559 | && dev_priv->implicit_fb != vfb) { |
560 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
559 | DRM_ERROR("Multiple implicit framebuffers not supported.\n"); | 561 | DRM_ERROR("Multiple implicit framebuffers not supported.\n"); |
560 | return -EINVAL; | 562 | return -EINVAL; |
561 | } | 563 | } |
564 | mutex_unlock(&dev_priv->global_kms_state_mutex); | ||
562 | 565 | ||
563 | /* Since they always map one to one these are safe */ | 566 | /* Since they always map one to one these are safe */ |
564 | connector = &stdu->base.connector; | 567 | connector = &stdu->base.connector; |