diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_kms.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 166 |
1 files changed, 143 insertions, 23 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8b14dfd513a..37d40545ed7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | |||
@@ -105,12 +105,17 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, | |||
105 | struct vmw_dma_buffer *dmabuf = NULL; | 105 | struct vmw_dma_buffer *dmabuf = NULL; |
106 | int ret; | 106 | int ret; |
107 | 107 | ||
108 | /* A lot of the code assumes this */ | ||
109 | if (handle && (width != 64 || height != 64)) | ||
110 | return -EINVAL; | ||
111 | |||
108 | if (handle) { | 112 | if (handle) { |
109 | ret = vmw_user_surface_lookup_handle(dev_priv, tfile, | 113 | ret = vmw_user_surface_lookup_handle(dev_priv, tfile, |
110 | handle, &surface); | 114 | handle, &surface); |
111 | if (!ret) { | 115 | if (!ret) { |
112 | if (!surface->snooper.image) { | 116 | if (!surface->snooper.image) { |
113 | DRM_ERROR("surface not suitable for cursor\n"); | 117 | DRM_ERROR("surface not suitable for cursor\n"); |
118 | vmw_surface_unreference(&surface); | ||
114 | return -EINVAL; | 119 | return -EINVAL; |
115 | } | 120 | } |
116 | } else { | 121 | } else { |
@@ -176,7 +181,9 @@ err_unreserve: | |||
176 | return 0; | 181 | return 0; |
177 | } | 182 | } |
178 | 183 | ||
179 | vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y); | 184 | vmw_cursor_update_position(dev_priv, true, |
185 | du->cursor_x + du->hotspot_x, | ||
186 | du->cursor_y + du->hotspot_y); | ||
180 | 187 | ||
181 | return 0; | 188 | return 0; |
182 | } | 189 | } |
@@ -191,7 +198,8 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) | |||
191 | du->cursor_y = y + crtc->y; | 198 | du->cursor_y = y + crtc->y; |
192 | 199 | ||
193 | vmw_cursor_update_position(dev_priv, shown, | 200 | vmw_cursor_update_position(dev_priv, shown, |
194 | du->cursor_x, du->cursor_y); | 201 | du->cursor_x + du->hotspot_x, |
202 | du->cursor_y + du->hotspot_y); | ||
195 | 203 | ||
196 | return 0; | 204 | return 0; |
197 | } | 205 | } |
@@ -212,7 +220,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, | |||
212 | SVGA3dCmdHeader header; | 220 | SVGA3dCmdHeader header; |
213 | SVGA3dCmdSurfaceDMA dma; | 221 | SVGA3dCmdSurfaceDMA dma; |
214 | } *cmd; | 222 | } *cmd; |
215 | int ret; | 223 | int i, ret; |
216 | 224 | ||
217 | cmd = container_of(header, struct vmw_dma_cmd, header); | 225 | cmd = container_of(header, struct vmw_dma_cmd, header); |
218 | 226 | ||
@@ -234,16 +242,19 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, | |||
234 | box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / | 242 | box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / |
235 | sizeof(SVGA3dCopyBox); | 243 | sizeof(SVGA3dCopyBox); |
236 | 244 | ||
237 | if (cmd->dma.guest.pitch != (64 * 4) || | 245 | if (cmd->dma.guest.ptr.offset % PAGE_SIZE || |
238 | cmd->dma.guest.ptr.offset % PAGE_SIZE || | ||
239 | box->x != 0 || box->y != 0 || box->z != 0 || | 246 | box->x != 0 || box->y != 0 || box->z != 0 || |
240 | box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || | 247 | box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || |
241 | box->w != 64 || box->h != 64 || box->d != 1 || | 248 | box->d != 1 || box_count != 1) { |
242 | box_count != 1) { | ||
243 | /* TODO handle none page aligned offsets */ | 249 | /* TODO handle none page aligned offsets */ |
244 | /* TODO handle partial uploads and pitch != 256 */ | 250 | /* TODO handle more dst & src != 0 */ |
245 | /* TODO handle more then one copy (size != 64) */ | 251 | /* TODO handle more then one copy */ |
246 | DRM_ERROR("lazy programmer, can't handle weird stuff\n"); | 252 | DRM_ERROR("Cant snoop dma request for cursor!\n"); |
253 | DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n", | ||
254 | box->srcx, box->srcy, box->srcz, | ||
255 | box->x, box->y, box->z, | ||
256 | box->w, box->h, box->d, box_count, | ||
257 | cmd->dma.guest.ptr.offset); | ||
247 | return; | 258 | return; |
248 | } | 259 | } |
249 | 260 | ||
@@ -262,7 +273,16 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, | |||
262 | 273 | ||
263 | virtual = ttm_kmap_obj_virtual(&map, &dummy); | 274 | virtual = ttm_kmap_obj_virtual(&map, &dummy); |
264 | 275 | ||
265 | memcpy(srf->snooper.image, virtual, 64*64*4); | 276 | if (box->w == 64 && cmd->dma.guest.pitch == 64*4) { |
277 | memcpy(srf->snooper.image, virtual, 64*64*4); | ||
278 | } else { | ||
279 | /* Image is unsigned pointer. */ | ||
280 | for (i = 0; i < box->h; i++) | ||
281 | memcpy(srf->snooper.image + i * 64, | ||
282 | virtual + i * cmd->dma.guest.pitch, | ||
283 | box->w * 4); | ||
284 | } | ||
285 | |||
266 | srf->snooper.age++; | 286 | srf->snooper.age++; |
267 | 287 | ||
268 | /* we can't call this function from this function since execbuf has | 288 | /* we can't call this function from this function since execbuf has |
@@ -394,8 +414,9 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv, | |||
394 | top = clips->y1; | 414 | top = clips->y1; |
395 | bottom = clips->y2; | 415 | bottom = clips->y2; |
396 | 416 | ||
397 | clips_ptr = clips; | 417 | /* skip the first clip rect */ |
398 | for (i = 1; i < num_clips; i++, clips_ptr += inc) { | 418 | for (i = 1, clips_ptr = clips + inc; |
419 | i < num_clips; i++, clips_ptr += inc) { | ||
399 | left = min_t(int, left, (int)clips_ptr->x1); | 420 | left = min_t(int, left, (int)clips_ptr->x1); |
400 | right = max_t(int, right, (int)clips_ptr->x2); | 421 | right = max_t(int, right, (int)clips_ptr->x2); |
401 | top = min_t(int, top, (int)clips_ptr->y1); | 422 | top = min_t(int, top, (int)clips_ptr->y1); |
@@ -994,7 +1015,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, | |||
994 | required_size = mode_cmd->pitch * mode_cmd->height; | 1015 | required_size = mode_cmd->pitch * mode_cmd->height; |
995 | if (unlikely(required_size > (u64) dev_priv->vram_size)) { | 1016 | if (unlikely(required_size > (u64) dev_priv->vram_size)) { |
996 | DRM_ERROR("VRAM size is too small for requested mode.\n"); | 1017 | DRM_ERROR("VRAM size is too small for requested mode.\n"); |
997 | return NULL; | 1018 | return ERR_PTR(-ENOMEM); |
998 | } | 1019 | } |
999 | 1020 | ||
1000 | /* | 1021 | /* |
@@ -1307,7 +1328,10 @@ int vmw_kms_close(struct vmw_private *dev_priv) | |||
1307 | * drm_encoder_cleanup which takes the lock we deadlock. | 1328 | * drm_encoder_cleanup which takes the lock we deadlock. |
1308 | */ | 1329 | */ |
1309 | drm_mode_config_cleanup(dev_priv->dev); | 1330 | drm_mode_config_cleanup(dev_priv->dev); |
1310 | vmw_kms_close_legacy_display_system(dev_priv); | 1331 | if (dev_priv->sou_priv) |
1332 | vmw_kms_close_screen_object_display(dev_priv); | ||
1333 | else | ||
1334 | vmw_kms_close_legacy_display_system(dev_priv); | ||
1311 | return 0; | 1335 | return 0; |
1312 | } | 1336 | } |
1313 | 1337 | ||
@@ -1517,6 +1541,8 @@ int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, | |||
1517 | du->pref_width = rects[du->unit].w; | 1541 | du->pref_width = rects[du->unit].w; |
1518 | du->pref_height = rects[du->unit].h; | 1542 | du->pref_height = rects[du->unit].h; |
1519 | du->pref_active = true; | 1543 | du->pref_active = true; |
1544 | du->gui_x = rects[du->unit].x; | ||
1545 | du->gui_y = rects[du->unit].y; | ||
1520 | } else { | 1546 | } else { |
1521 | du->pref_width = 800; | 1547 | du->pref_width = 800; |
1522 | du->pref_height = 600; | 1548 | du->pref_height = 600; |
@@ -1572,12 +1598,14 @@ vmw_du_connector_detect(struct drm_connector *connector, bool force) | |||
1572 | uint32_t num_displays; | 1598 | uint32_t num_displays; |
1573 | struct drm_device *dev = connector->dev; | 1599 | struct drm_device *dev = connector->dev; |
1574 | struct vmw_private *dev_priv = vmw_priv(dev); | 1600 | struct vmw_private *dev_priv = vmw_priv(dev); |
1601 | struct vmw_display_unit *du = vmw_connector_to_du(connector); | ||
1575 | 1602 | ||
1576 | mutex_lock(&dev_priv->hw_mutex); | 1603 | mutex_lock(&dev_priv->hw_mutex); |
1577 | num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); | 1604 | num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); |
1578 | mutex_unlock(&dev_priv->hw_mutex); | 1605 | mutex_unlock(&dev_priv->hw_mutex); |
1579 | 1606 | ||
1580 | return ((vmw_connector_to_du(connector)->unit < num_displays) ? | 1607 | return ((vmw_connector_to_du(connector)->unit < num_displays && |
1608 | du->pref_active) ? | ||
1581 | connector_status_connected : connector_status_disconnected); | 1609 | connector_status_connected : connector_status_disconnected); |
1582 | } | 1610 | } |
1583 | 1611 | ||
@@ -1658,6 +1686,28 @@ static struct drm_display_mode vmw_kms_connector_builtin[] = { | |||
1658 | { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, | 1686 | { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, |
1659 | }; | 1687 | }; |
1660 | 1688 | ||
1689 | /** | ||
1690 | * vmw_guess_mode_timing - Provide fake timings for a | ||
1691 | * 60Hz vrefresh mode. | ||
1692 | * | ||
1693 | * @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay | ||
1694 | * members filled in. | ||
1695 | */ | ||
1696 | static void vmw_guess_mode_timing(struct drm_display_mode *mode) | ||
1697 | { | ||
1698 | mode->hsync_start = mode->hdisplay + 50; | ||
1699 | mode->hsync_end = mode->hsync_start + 50; | ||
1700 | mode->htotal = mode->hsync_end + 50; | ||
1701 | |||
1702 | mode->vsync_start = mode->vdisplay + 50; | ||
1703 | mode->vsync_end = mode->vsync_start + 50; | ||
1704 | mode->vtotal = mode->vsync_end + 50; | ||
1705 | |||
1706 | mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6; | ||
1707 | mode->vrefresh = drm_mode_vrefresh(mode); | ||
1708 | } | ||
1709 | |||
1710 | |||
1661 | int vmw_du_connector_fill_modes(struct drm_connector *connector, | 1711 | int vmw_du_connector_fill_modes(struct drm_connector *connector, |
1662 | uint32_t max_width, uint32_t max_height) | 1712 | uint32_t max_width, uint32_t max_height) |
1663 | { | 1713 | { |
@@ -1680,18 +1730,23 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, | |||
1680 | return 0; | 1730 | return 0; |
1681 | mode->hdisplay = du->pref_width; | 1731 | mode->hdisplay = du->pref_width; |
1682 | mode->vdisplay = du->pref_height; | 1732 | mode->vdisplay = du->pref_height; |
1683 | mode->vrefresh = drm_mode_vrefresh(mode); | 1733 | vmw_guess_mode_timing(mode); |
1734 | |||
1684 | if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, | 1735 | if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, |
1685 | mode->vdisplay)) { | 1736 | mode->vdisplay)) { |
1686 | drm_mode_probed_add(connector, mode); | 1737 | drm_mode_probed_add(connector, mode); |
1738 | } else { | ||
1739 | drm_mode_destroy(dev, mode); | ||
1740 | mode = NULL; | ||
1741 | } | ||
1687 | 1742 | ||
1688 | if (du->pref_mode) { | 1743 | if (du->pref_mode) { |
1689 | list_del_init(&du->pref_mode->head); | 1744 | list_del_init(&du->pref_mode->head); |
1690 | drm_mode_destroy(dev, du->pref_mode); | 1745 | drm_mode_destroy(dev, du->pref_mode); |
1691 | } | ||
1692 | |||
1693 | du->pref_mode = mode; | ||
1694 | } | 1746 | } |
1747 | |||
1748 | /* mode might be null here, this is intended */ | ||
1749 | du->pref_mode = mode; | ||
1695 | } | 1750 | } |
1696 | 1751 | ||
1697 | for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { | 1752 | for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { |
@@ -1712,6 +1767,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, | |||
1712 | drm_mode_probed_add(connector, mode); | 1767 | drm_mode_probed_add(connector, mode); |
1713 | } | 1768 | } |
1714 | 1769 | ||
1770 | /* Move the prefered mode first, help apps pick the right mode. */ | ||
1771 | if (du->pref_mode) | ||
1772 | list_move(&du->pref_mode->head, &connector->probed_modes); | ||
1773 | |||
1715 | drm_mode_connector_list_update(connector); | 1774 | drm_mode_connector_list_update(connector); |
1716 | 1775 | ||
1717 | return 1; | 1776 | return 1; |
@@ -1723,3 +1782,64 @@ int vmw_du_connector_set_property(struct drm_connector *connector, | |||
1723 | { | 1782 | { |
1724 | return 0; | 1783 | return 0; |
1725 | } | 1784 | } |
1785 | |||
1786 | |||
1787 | int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, | ||
1788 | struct drm_file *file_priv) | ||
1789 | { | ||
1790 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
1791 | struct drm_vmw_update_layout_arg *arg = | ||
1792 | (struct drm_vmw_update_layout_arg *)data; | ||
1793 | struct vmw_master *vmaster = vmw_master(file_priv->master); | ||
1794 | void __user *user_rects; | ||
1795 | struct drm_vmw_rect *rects; | ||
1796 | unsigned rects_size; | ||
1797 | int ret; | ||
1798 | int i; | ||
1799 | struct drm_mode_config *mode_config = &dev->mode_config; | ||
1800 | |||
1801 | ret = ttm_read_lock(&vmaster->lock, true); | ||
1802 | if (unlikely(ret != 0)) | ||
1803 | return ret; | ||
1804 | |||
1805 | if (!arg->num_outputs) { | ||
1806 | struct drm_vmw_rect def_rect = {0, 0, 800, 600}; | ||
1807 | vmw_du_update_layout(dev_priv, 1, &def_rect); | ||
1808 | goto out_unlock; | ||
1809 | } | ||
1810 | |||
1811 | rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); | ||
1812 | rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), | ||
1813 | GFP_KERNEL); | ||
1814 | if (unlikely(!rects)) { | ||
1815 | ret = -ENOMEM; | ||
1816 | goto out_unlock; | ||
1817 | } | ||
1818 | |||
1819 | user_rects = (void __user *)(unsigned long)arg->rects; | ||
1820 | ret = copy_from_user(rects, user_rects, rects_size); | ||
1821 | if (unlikely(ret != 0)) { | ||
1822 | DRM_ERROR("Failed to get rects.\n"); | ||
1823 | ret = -EFAULT; | ||
1824 | goto out_free; | ||
1825 | } | ||
1826 | |||
1827 | for (i = 0; i < arg->num_outputs; ++i) { | ||
1828 | if (rects[i].x < 0 || | ||
1829 | rects[i].y < 0 || | ||
1830 | rects[i].x + rects[i].w > mode_config->max_width || | ||
1831 | rects[i].y + rects[i].h > mode_config->max_height) { | ||
1832 | DRM_ERROR("Invalid GUI layout.\n"); | ||
1833 | ret = -EINVAL; | ||
1834 | goto out_free; | ||
1835 | } | ||
1836 | } | ||
1837 | |||
1838 | vmw_du_update_layout(dev_priv, arg->num_outputs, rects); | ||
1839 | |||
1840 | out_free: | ||
1841 | kfree(rects); | ||
1842 | out_unlock: | ||
1843 | ttm_read_unlock(&vmaster->lock); | ||
1844 | return ret; | ||
1845 | } | ||