intel_fb.c revision 296548
1/*
2 * Copyright �� 2007 David Airlie
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 *     David Airlie
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/drm2/i915/intel_fb.c 296548 2016-03-08 20:33:02Z dumbbell $");
29
30#include "opt_syscons.h"
31#include <dev/drm2/drmP.h>
32#include <dev/drm2/drm_crtc.h>
33#include <dev/drm2/drm_fb_helper.h>
34#include <dev/drm2/i915/intel_drv.h>
35#include <dev/drm2/i915/i915_drm.h>
36#include <dev/drm2/i915/i915_drv.h>
37
38#if defined(__linux__)
39static struct fb_ops intelfb_ops = {
40	.owner = THIS_MODULE,
41	.fb_check_var = drm_fb_helper_check_var,
42	.fb_set_par = drm_fb_helper_set_par,
43	.fb_fillrect = cfb_fillrect,
44	.fb_copyarea = cfb_copyarea,
45	.fb_imageblit = cfb_imageblit,
46	.fb_pan_display = drm_fb_helper_pan_display,
47	.fb_blank = drm_fb_helper_blank,
48	.fb_setcmap = drm_fb_helper_setcmap,
49	.fb_debug_enter = drm_fb_helper_debug_enter,
50	.fb_debug_leave = drm_fb_helper_debug_leave,
51};
52#endif
53
54static int intelfb_create(struct intel_fbdev *ifbdev,
55			  struct drm_fb_helper_surface_size *sizes)
56{
57	struct drm_device *dev = ifbdev->helper.dev;
58	struct drm_i915_private *dev_priv = dev->dev_private;
59	struct fb_info *info;
60	struct drm_framebuffer *fb;
61	struct drm_mode_fb_cmd2 mode_cmd = {};
62	struct drm_i915_gem_object *obj;
63	int size, ret;
64
65	/* we don't do packed 24bpp */
66	if (sizes->surface_bpp == 24)
67		sizes->surface_bpp = 32;
68
69	mode_cmd.width = sizes->surface_width;
70	mode_cmd.height = sizes->surface_height;
71
72	mode_cmd.pitches[0] = roundup2(mode_cmd.width * ((sizes->surface_bpp + 7) /
73						      8), 64);
74	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
75							  sizes->surface_depth);
76
77	size = mode_cmd.pitches[0] * mode_cmd.height;
78	size = roundup2(size, PAGE_SIZE);
79	obj = i915_gem_alloc_object(dev, size);
80	if (!obj) {
81		DRM_ERROR("failed to allocate framebuffer\n");
82		ret = -ENOMEM;
83		goto out;
84	}
85
86	DRM_LOCK(dev);
87
88	/* Flush everything out, we'll be doing GTT only from now on */
89	ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
90	if (ret) {
91		DRM_ERROR("failed to pin fb: %d\n", ret);
92		goto out_unref;
93	}
94
95	info = framebuffer_alloc();
96	if (!info) {
97		ret = -ENOMEM;
98		goto out_unpin;
99	}
100
101	info->fb_size = size;
102	info->fb_bpp = sizes->surface_bpp;
103	info->fb_pbase = dev_priv->mm.gtt_base_addr + obj->gtt_offset;
104	info->fb_vbase = (vm_offset_t)pmap_mapdev_attr(info->fb_pbase, size,
105	    PAT_WRITE_COMBINING);
106
107	ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj);
108	if (ret)
109		goto out_unpin;
110
111	fb = &ifbdev->ifb.base;
112
113	ifbdev->helper.fb = fb;
114	ifbdev->helper.fbdev = info;
115
116	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
117	drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
118
119	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
120
121	DRM_DEBUG_KMS("allocated %dx%d (s %dbits) fb: 0x%08x, bo %p\n",
122		      fb->width, fb->height, fb->depth,
123		      obj->gtt_offset, obj);
124
125
126	DRM_UNLOCK(dev);
127#ifdef __linux__
128	vga_switcheroo_client_fb_set(dev->pdev, info);
129#endif
130	return 0;
131
132out_unpin:
133	i915_gem_object_unpin(obj);
134out_unref:
135	drm_gem_object_unreference(&obj->base);
136	DRM_UNLOCK(dev);
137out:
138	return ret;
139}
140
141static int intel_fb_find_or_create_single(struct drm_fb_helper *helper,
142					  struct drm_fb_helper_surface_size *sizes)
143{
144	struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper;
145	int new_fb = 0;
146	int ret;
147
148	if (!helper->fb) {
149		ret = intelfb_create(ifbdev, sizes);
150		if (ret)
151			return ret;
152		new_fb = 1;
153	}
154	return new_fb;
155}
156
157static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
158	.gamma_set = intel_crtc_fb_gamma_set,
159	.gamma_get = intel_crtc_fb_gamma_get,
160	.fb_probe = intel_fb_find_or_create_single,
161};
162
163static void intel_fbdev_destroy(struct drm_device *dev,
164				struct intel_fbdev *ifbdev)
165{
166	struct fb_info *info;
167	struct intel_framebuffer *ifb = &ifbdev->ifb;
168
169	if (ifbdev->helper.fbdev) {
170		info = ifbdev->helper.fbdev;
171		if (info->fb_fbd_dev != NULL)
172			device_delete_child(dev->dev, info->fb_fbd_dev);
173		framebuffer_release(info);
174	}
175
176	drm_fb_helper_fini(&ifbdev->helper);
177
178	drm_framebuffer_cleanup(&ifb->base);
179	if (ifb->obj) {
180		drm_gem_object_unreference_unlocked(&ifb->obj->base);
181		ifb->obj = NULL;
182	}
183}
184
185#ifdef DEV_SC
186extern int sc_txtmouse_no_retrace_wait;
187#endif
188
189int intel_fbdev_init(struct drm_device *dev)
190{
191	struct intel_fbdev *ifbdev;
192	drm_i915_private_t *dev_priv = dev->dev_private;
193	int ret;
194
195	ifbdev = malloc(sizeof(struct intel_fbdev), DRM_MEM_KMS, M_WAITOK | M_ZERO);
196	if (!ifbdev)
197		return -ENOMEM;
198
199	dev_priv->fbdev = ifbdev;
200	ifbdev->helper.funcs = &intel_fb_helper_funcs;
201
202	ret = drm_fb_helper_init(dev, &ifbdev->helper,
203				 dev_priv->num_pipe,
204				 INTELFB_CONN_LIMIT);
205	if (ret) {
206		free(ifbdev, DRM_MEM_KMS);
207		return ret;
208	}
209
210	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
211	drm_fb_helper_initial_config(&ifbdev->helper, 32);
212#ifdef DEV_SC
213	sc_txtmouse_no_retrace_wait = 1;
214#endif
215	return 0;
216}
217
218void intel_fbdev_fini(struct drm_device *dev)
219{
220	drm_i915_private_t *dev_priv = dev->dev_private;
221	if (!dev_priv->fbdev)
222		return;
223
224	intel_fbdev_destroy(dev, dev_priv->fbdev);
225	free(dev_priv->fbdev, DRM_MEM_KMS);
226	dev_priv->fbdev = NULL;
227}
228
229void intel_fbdev_set_suspend(struct drm_device *dev, int state)
230{
231	drm_i915_private_t *dev_priv = dev->dev_private;
232	if (!dev_priv->fbdev)
233		return;
234
235#ifdef FREEBSD_WIP
236	fb_set_suspend(dev_priv->fbdev->helper.fbdev, state);
237#endif /* FREEBSD_WIP */
238}
239
240MODULE_LICENSE("GPL and additional rights");
241
242void intel_fb_output_poll_changed(struct drm_device *dev)
243{
244	drm_i915_private_t *dev_priv = dev->dev_private;
245	drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
246}
247
248void intel_fb_restore_mode(struct drm_device *dev)
249{
250	int ret;
251	drm_i915_private_t *dev_priv = dev->dev_private;
252	struct drm_mode_config *config = &dev->mode_config;
253	struct drm_plane *plane;
254
255	sx_xlock(&dev->mode_config.mutex);
256
257	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
258	if (ret)
259		DRM_DEBUG("failed to restore crtc mode\n");
260
261	/* Be sure to shut off any planes that may be active */
262	list_for_each_entry(plane, &config->plane_list, head)
263		plane->funcs->disable_plane(plane);
264
265	sx_xunlock(&dev->mode_config.mutex);
266}
267