]>
Commit | Line | Data |
---|---|---|
a61127c2 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4d8d096e AC |
2 | /************************************************************************** |
3 | * Copyright (c) 2007-2011, Intel Corporation. | |
4 | * All Rights Reserved. | |
5 | * | |
4d8d096e AC |
6 | **************************************************************************/ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/errno.h> | |
11 | #include <linux/string.h> | |
01c8f1c4 | 12 | #include <linux/pfn_t.h> |
4d8d096e AC |
13 | #include <linux/mm.h> |
14 | #include <linux/tty.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/delay.h> | |
4d8d096e AC |
17 | #include <linux/init.h> |
18 | #include <linux/console.h> | |
19 | ||
20 | #include <drm/drmP.h> | |
21 | #include <drm/drm.h> | |
22 | #include <drm/drm_crtc.h> | |
a9a644ac | 23 | #include <drm/drm_fb_helper.h> |
bc61c975 | 24 | #include <drm/drm_gem_framebuffer_helper.h> |
4d8d096e AC |
25 | |
26 | #include "psb_drv.h" | |
27 | #include "psb_intel_reg.h" | |
28 | #include "psb_intel_drv.h" | |
29 | #include "framebuffer.h" | |
a6ba582d | 30 | #include "gtt.h" |
4d8d096e | 31 | |
4d8d096e | 32 | static const struct drm_framebuffer_funcs psb_fb_funcs = { |
bc61c975 DS |
33 | .destroy = drm_gem_fb_destroy, |
34 | .create_handle = drm_gem_fb_create_handle, | |
4d8d096e AC |
35 | }; |
36 | ||
37 | #define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) | |
38 | ||
39 | static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, | |
40 | unsigned blue, unsigned transp, | |
41 | struct fb_info *info) | |
42 | { | |
43 | struct psb_fbdev *fbdev = info->par; | |
44 | struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb; | |
45 | uint32_t v; | |
46 | ||
47 | if (!fb) | |
48 | return -ENOMEM; | |
49 | ||
50 | if (regno > 255) | |
51 | return 1; | |
52 | ||
53 | red = CMAP_TOHW(red, info->var.red.length); | |
54 | blue = CMAP_TOHW(blue, info->var.blue.length); | |
55 | green = CMAP_TOHW(green, info->var.green.length); | |
56 | transp = CMAP_TOHW(transp, info->var.transp.length); | |
57 | ||
58 | v = (red << info->var.red.offset) | | |
59 | (green << info->var.green.offset) | | |
60 | (blue << info->var.blue.offset) | | |
61 | (transp << info->var.transp.offset); | |
62 | ||
63 | if (regno < 16) { | |
272725c7 | 64 | switch (fb->format->cpp[0] * 8) { |
4d8d096e AC |
65 | case 16: |
66 | ((uint32_t *) info->pseudo_palette)[regno] = v; | |
67 | break; | |
68 | case 24: | |
69 | case 32: | |
70 | ((uint32_t *) info->pseudo_palette)[regno] = v; | |
71 | break; | |
72 | } | |
73 | } | |
74 | ||
75 | return 0; | |
76 | } | |
77 | ||
a6ba582d AC |
78 | static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) |
79 | { | |
80 | struct psb_fbdev *fbdev = info->par; | |
81 | struct psb_framebuffer *psbfb = &fbdev->pfb; | |
82 | struct drm_device *dev = psbfb->base.dev; | |
bc61c975 | 83 | struct gtt_range *gtt = to_gtt_range(psbfb->base.obj[0]); |
a6ba582d AC |
84 | |
85 | /* | |
86 | * We have to poke our nose in here. The core fb code assumes | |
87 | * panning is part of the hardware that can be invoked before | |
88 | * the actual fb is mapped. In our case that isn't quite true. | |
89 | */ | |
bc61c975 | 90 | if (gtt->npage) { |
a6ba582d AC |
91 | /* GTT roll shifts in 4K pages, we need to shift the right |
92 | number of pages */ | |
93 | int pages = info->fix.line_length >> 12; | |
bc61c975 | 94 | psb_gtt_roll(dev, gtt, var->yoffset * pages); |
a6ba582d AC |
95 | } |
96 | return 0; | |
97 | } | |
4d8d096e | 98 | |
0edf6813 | 99 | static vm_fault_t psbfb_vm_fault(struct vm_fault *vmf) |
4d8d096e | 100 | { |
11bac800 | 101 | struct vm_area_struct *vma = vmf->vma; |
4d8d096e AC |
102 | struct psb_framebuffer *psbfb = vma->vm_private_data; |
103 | struct drm_device *dev = psbfb->base.dev; | |
104 | struct drm_psb_private *dev_priv = dev->dev_private; | |
bc61c975 | 105 | struct gtt_range *gtt = to_gtt_range(psbfb->base.obj[0]); |
4d8d096e AC |
106 | int page_num; |
107 | int i; | |
108 | unsigned long address; | |
0edf6813 | 109 | vm_fault_t ret = VM_FAULT_SIGBUS; |
4d8d096e | 110 | unsigned long pfn; |
61bb3fea | 111 | unsigned long phys_addr = (unsigned long)dev_priv->stolen_base + |
bc61c975 | 112 | gtt->offset; |
4d8d096e | 113 | |
024b6a63 | 114 | page_num = vma_pages(vma); |
1a29d85e | 115 | address = vmf->address - (vmf->pgoff << PAGE_SHIFT); |
4d8d096e AC |
116 | |
117 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | |
118 | ||
119 | for (i = 0; i < page_num; i++) { | |
120 | pfn = (phys_addr >> PAGE_SHIFT); | |
121 | ||
0edf6813 | 122 | ret = vmf_insert_mixed(vma, address, |
01c8f1c4 | 123 | __pfn_to_pfn_t(pfn, PFN_DEV)); |
0edf6813 | 124 | if (unlikely(ret & VM_FAULT_ERROR)) |
4d8d096e | 125 | break; |
4d8d096e AC |
126 | address += PAGE_SIZE; |
127 | phys_addr += PAGE_SIZE; | |
128 | } | |
0edf6813 | 129 | return ret; |
4d8d096e AC |
130 | } |
131 | ||
132 | static void psbfb_vm_open(struct vm_area_struct *vma) | |
133 | { | |
134 | } | |
135 | ||
136 | static void psbfb_vm_close(struct vm_area_struct *vma) | |
137 | { | |
138 | } | |
139 | ||
78b68556 | 140 | static const struct vm_operations_struct psbfb_vm_ops = { |
4d8d096e AC |
141 | .fault = psbfb_vm_fault, |
142 | .open = psbfb_vm_open, | |
143 | .close = psbfb_vm_close | |
144 | }; | |
145 | ||
146 | static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma) | |
147 | { | |
148 | struct psb_fbdev *fbdev = info->par; | |
149 | struct psb_framebuffer *psbfb = &fbdev->pfb; | |
150 | ||
151 | if (vma->vm_pgoff != 0) | |
152 | return -EINVAL; | |
153 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | |
154 | return -EINVAL; | |
155 | ||
156 | if (!psbfb->addr_space) | |
157 | psbfb->addr_space = vma->vm_file->f_mapping; | |
158 | /* | |
159 | * If this is a GEM object then info->screen_base is the virtual | |
160 | * kernel remapping of the object. FIXME: Review if this is | |
161 | * suitable for our mmap work | |
162 | */ | |
163 | vma->vm_ops = &psbfb_vm_ops; | |
164 | vma->vm_private_data = (void *)psbfb; | |
314e51b9 | 165 | vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP; |
4d8d096e AC |
166 | return 0; |
167 | } | |
168 | ||
4d8d096e AC |
169 | static struct fb_ops psbfb_ops = { |
170 | .owner = THIS_MODULE, | |
3da6c2f3 | 171 | DRM_FB_HELPER_DEFAULT_OPS, |
4d8d096e | 172 | .fb_setcolreg = psbfb_setcolreg, |
546187c8 | 173 | .fb_fillrect = drm_fb_helper_cfb_fillrect, |
4d8d096e | 174 | .fb_copyarea = psbfb_copyarea, |
546187c8 | 175 | .fb_imageblit = drm_fb_helper_cfb_imageblit, |
4d8d096e AC |
176 | .fb_mmap = psbfb_mmap, |
177 | .fb_sync = psbfb_sync, | |
4d8d096e AC |
178 | }; |
179 | ||
a6ba582d AC |
180 | static struct fb_ops psbfb_roll_ops = { |
181 | .owner = THIS_MODULE, | |
3da6c2f3 | 182 | DRM_FB_HELPER_DEFAULT_OPS, |
a6ba582d | 183 | .fb_setcolreg = psbfb_setcolreg, |
546187c8 AT |
184 | .fb_fillrect = drm_fb_helper_cfb_fillrect, |
185 | .fb_copyarea = drm_fb_helper_cfb_copyarea, | |
186 | .fb_imageblit = drm_fb_helper_cfb_imageblit, | |
a6ba582d AC |
187 | .fb_pan_display = psbfb_pan, |
188 | .fb_mmap = psbfb_mmap, | |
a6ba582d AC |
189 | }; |
190 | ||
4d8d096e AC |
191 | static struct fb_ops psbfb_unaccel_ops = { |
192 | .owner = THIS_MODULE, | |
3da6c2f3 | 193 | DRM_FB_HELPER_DEFAULT_OPS, |
4d8d096e | 194 | .fb_setcolreg = psbfb_setcolreg, |
546187c8 AT |
195 | .fb_fillrect = drm_fb_helper_cfb_fillrect, |
196 | .fb_copyarea = drm_fb_helper_cfb_copyarea, | |
197 | .fb_imageblit = drm_fb_helper_cfb_imageblit, | |
4d8d096e | 198 | .fb_mmap = psbfb_mmap, |
4d8d096e AC |
199 | }; |
200 | ||
201 | /** | |
202 | * psb_framebuffer_init - initialize a framebuffer | |
203 | * @dev: our DRM device | |
204 | * @fb: framebuffer to set up | |
205 | * @mode_cmd: mode description | |
206 | * @gt: backing object | |
207 | * | |
208 | * Configure and fill in the boilerplate for our frame buffer. Return | |
209 | * 0 on success or an error code if we fail. | |
210 | */ | |
211 | static int psb_framebuffer_init(struct drm_device *dev, | |
212 | struct psb_framebuffer *fb, | |
1eb83451 | 213 | const struct drm_mode_fb_cmd2 *mode_cmd, |
4d8d096e AC |
214 | struct gtt_range *gt) |
215 | { | |
e0f9a4ab | 216 | const struct drm_format_info *info; |
4d8d096e AC |
217 | int ret; |
218 | ||
e0f9a4ab LP |
219 | /* |
220 | * Reject unknown formats, YUV formats, and formats with more than | |
221 | * 4 bytes per pixel. | |
222 | */ | |
223 | info = drm_format_info(mode_cmd->pixel_format); | |
224 | if (!info || !info->depth || info->cpp[0] > 4) | |
225 | return -EINVAL; | |
a9a644ac DA |
226 | |
227 | if (mode_cmd->pitches[0] & 63) | |
4d8d096e | 228 | return -EINVAL; |
e0f9a4ab | 229 | |
a3f913ca | 230 | drm_helper_mode_fill_fb_struct(dev, &fb->base, mode_cmd); |
bc61c975 | 231 | fb->base.obj[0] = >->gem; |
4d8d096e AC |
232 | ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); |
233 | if (ret) { | |
234 | dev_err(dev->dev, "framebuffer init failed: %d\n", ret); | |
235 | return ret; | |
236 | } | |
4d8d096e AC |
237 | return 0; |
238 | } | |
239 | ||
240 | /** | |
241 | * psb_framebuffer_create - create a framebuffer backed by gt | |
242 | * @dev: our DRM device | |
243 | * @mode_cmd: the description of the requested mode | |
244 | * @gt: the backing object | |
245 | * | |
246 | * Create a framebuffer object backed by the gt, and fill in the | |
247 | * boilerplate required | |
248 | * | |
249 | * TODO: review object references | |
250 | */ | |
251 | ||
252 | static struct drm_framebuffer *psb_framebuffer_create | |
253 | (struct drm_device *dev, | |
1eb83451 | 254 | const struct drm_mode_fb_cmd2 *mode_cmd, |
4d8d096e AC |
255 | struct gtt_range *gt) |
256 | { | |
257 | struct psb_framebuffer *fb; | |
258 | int ret; | |
259 | ||
260 | fb = kzalloc(sizeof(*fb), GFP_KERNEL); | |
261 | if (!fb) | |
262 | return ERR_PTR(-ENOMEM); | |
263 | ||
264 | ret = psb_framebuffer_init(dev, fb, mode_cmd, gt); | |
265 | if (ret) { | |
266 | kfree(fb); | |
267 | return ERR_PTR(ret); | |
268 | } | |
269 | return &fb->base; | |
270 | } | |
271 | ||
272 | /** | |
273 | * psbfb_alloc - allocate frame buffer memory | |
274 | * @dev: the DRM device | |
275 | * @aligned_size: space needed | |
276 | * | |
277 | * Allocate the frame buffer. In the usual case we get a GTT range that | |
278 | * is stolen memory backed and life is simple. If there isn't sufficient | |
dffc9ceb AC |
279 | * we fail as we don't have the virtual mapping space to really vmap it |
280 | * and the kernel console code can't handle non linear framebuffers. | |
4d8d096e | 281 | * |
dffc9ceb | 282 | * Re-address this as and if the framebuffer layer grows this ability. |
4d8d096e AC |
283 | */ |
284 | static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) | |
285 | { | |
286 | struct gtt_range *backing; | |
287 | /* Begin by trying to use stolen memory backing */ | |
c269c685 | 288 | backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1, PAGE_SIZE); |
4d8d096e | 289 | if (backing) { |
89c8233f DH |
290 | drm_gem_private_object_init(dev, &backing->gem, aligned_size); |
291 | return backing; | |
4d8d096e | 292 | } |
dffc9ceb | 293 | return NULL; |
4d8d096e AC |
294 | } |
295 | ||
296 | /** | |
297 | * psbfb_create - create a framebuffer | |
298 | * @fbdev: the framebuffer device | |
299 | * @sizes: specification of the layout | |
300 | * | |
301 | * Create a framebuffer to the specifications provided | |
302 | */ | |
303 | static int psbfb_create(struct psb_fbdev *fbdev, | |
304 | struct drm_fb_helper_surface_size *sizes) | |
305 | { | |
306 | struct drm_device *dev = fbdev->psb_fb_helper.dev; | |
307 | struct drm_psb_private *dev_priv = dev->dev_private; | |
308 | struct fb_info *info; | |
309 | struct drm_framebuffer *fb; | |
310 | struct psb_framebuffer *psbfb = &fbdev->pfb; | |
a9a644ac | 311 | struct drm_mode_fb_cmd2 mode_cmd; |
4d8d096e AC |
312 | int size; |
313 | int ret; | |
314 | struct gtt_range *backing; | |
a9a644ac | 315 | u32 bpp, depth; |
1b223c9e AC |
316 | int gtt_roll = 0; |
317 | int pitch_lines = 0; | |
4d8d096e AC |
318 | |
319 | mode_cmd.width = sizes->surface_width; | |
320 | mode_cmd.height = sizes->surface_height; | |
a9a644ac | 321 | bpp = sizes->surface_bpp; |
6aa1ead1 | 322 | depth = sizes->surface_depth; |
4d8d096e AC |
323 | |
324 | /* No 24bit packed */ | |
a9a644ac DA |
325 | if (bpp == 24) |
326 | bpp = 32; | |
4d8d096e | 327 | |
1b223c9e AC |
328 | do { |
329 | /* | |
330 | * Acceleration via the GTT requires pitch to be | |
331 | * power of two aligned. Preferably page but less | |
332 | * is ok with some fonts | |
333 | */ | |
334 | mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096 >> pitch_lines); | |
1b223c9e AC |
335 | |
336 | size = mode_cmd.pitches[0] * mode_cmd.height; | |
337 | size = ALIGN(size, PAGE_SIZE); | |
338 | ||
339 | /* Allocate the fb in the GTT with stolen page backing */ | |
340 | backing = psbfb_alloc(dev, size); | |
341 | ||
342 | if (pitch_lines) | |
343 | pitch_lines *= 2; | |
344 | else | |
345 | pitch_lines = 1; | |
346 | gtt_roll++; | |
347 | } while (backing == NULL && pitch_lines <= 16); | |
4d8d096e | 348 | |
1b223c9e AC |
349 | /* The final pitch we accepted if we succeeded */ |
350 | pitch_lines /= 2; | |
4d8d096e | 351 | |
a6ba582d AC |
352 | if (backing == NULL) { |
353 | /* | |
354 | * We couldn't get the space we wanted, fall back to the | |
355 | * display engine requirement instead. The HW requires | |
356 | * the pitch to be 64 byte aligned | |
a6ba582d AC |
357 | */ |
358 | ||
359 | gtt_roll = 0; /* Don't use GTT accelerated scrolling */ | |
1b223c9e | 360 | pitch_lines = 64; |
a6ba582d AC |
361 | |
362 | mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64); | |
363 | ||
364 | size = mode_cmd.pitches[0] * mode_cmd.height; | |
365 | size = ALIGN(size, PAGE_SIZE); | |
366 | ||
367 | /* Allocate the framebuffer in the GTT with stolen page backing */ | |
368 | backing = psbfb_alloc(dev, size); | |
369 | if (backing == NULL) | |
370 | return -ENOMEM; | |
371 | } | |
4d8d096e | 372 | |
bb849779 AC |
373 | memset(dev_priv->vram_addr + backing->offset, 0, size); |
374 | ||
546187c8 AT |
375 | info = drm_fb_helper_alloc_fbi(&fbdev->psb_fb_helper); |
376 | if (IS_ERR(info)) { | |
377 | ret = PTR_ERR(info); | |
da7bdda2 | 378 | goto out; |
4d8d096e | 379 | } |
4d8d096e | 380 | |
a9a644ac DA |
381 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); |
382 | ||
4d8d096e AC |
383 | ret = psb_framebuffer_init(dev, psbfb, &mode_cmd, backing); |
384 | if (ret) | |
da7bdda2 | 385 | goto out; |
4d8d096e AC |
386 | |
387 | fb = &psbfb->base; | |
388 | psbfb->fbdev = info; | |
389 | ||
390 | fbdev->psb_fb_helper.fb = fb; | |
4d8d096e | 391 | |
1b223c9e AC |
392 | if (dev_priv->ops->accel_2d && pitch_lines > 8) /* 2D engine */ |
393 | info->fbops = &psbfb_ops; | |
394 | else if (gtt_roll) { /* GTT rolling seems best */ | |
a6ba582d AC |
395 | info->fbops = &psbfb_roll_ops; |
396 | info->flags |= FBINFO_HWACCEL_YPAN; | |
1b223c9e | 397 | } else /* Software */ |
a6ba582d | 398 | info->fbops = &psbfb_unaccel_ops; |
4d8d096e | 399 | |
4d8d096e AC |
400 | info->fix.smem_start = dev->mode_config.fb_base; |
401 | info->fix.smem_len = size; | |
a6ba582d AC |
402 | info->fix.ywrapstep = gtt_roll; |
403 | info->fix.ypanstep = 0; | |
4d8d096e | 404 | |
dffc9ceb | 405 | /* Accessed stolen memory directly */ |
37214ca0 | 406 | info->screen_base = dev_priv->vram_addr + backing->offset; |
4d8d096e AC |
407 | info->screen_size = size; |
408 | ||
409 | if (dev_priv->gtt.stolen_size) { | |
4d8d096e AC |
410 | info->apertures->ranges[0].base = dev->mode_config.fb_base; |
411 | info->apertures->ranges[0].size = dev_priv->gtt.stolen_size; | |
412 | } | |
413 | ||
dfec164e | 414 | drm_fb_helper_fill_info(info, &fbdev->psb_fb_helper, sizes); |
4d8d096e AC |
415 | |
416 | info->fix.mmio_start = pci_resource_start(dev->pdev, 0); | |
417 | info->fix.mmio_len = pci_resource_len(dev->pdev, 0); | |
418 | ||
fb2a99e1 | 419 | /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ |
4d8d096e | 420 | |
31a0685a | 421 | dev_dbg(dev->dev, "allocated %dx%d fb\n", |
4d8d096e AC |
422 | psbfb->base.width, psbfb->base.height); |
423 | ||
4d8d096e | 424 | return 0; |
da7bdda2 | 425 | out: |
4d8d096e AC |
426 | psb_gtt_free_range(dev, backing); |
427 | return ret; | |
428 | } | |
429 | ||
430 | /** | |
431 | * psb_user_framebuffer_create - create framebuffer | |
432 | * @dev: our DRM device | |
433 | * @filp: client file | |
434 | * @cmd: mode request | |
435 | * | |
436 | * Create a new framebuffer backed by a userspace GEM object | |
437 | */ | |
438 | static struct drm_framebuffer *psb_user_framebuffer_create | |
439 | (struct drm_device *dev, struct drm_file *filp, | |
1eb83451 | 440 | const struct drm_mode_fb_cmd2 *cmd) |
4d8d096e AC |
441 | { |
442 | struct gtt_range *r; | |
443 | struct drm_gem_object *obj; | |
444 | ||
445 | /* | |
446 | * Find the GEM object and thus the gtt range object that is | |
447 | * to back this space | |
448 | */ | |
a8ad0bd8 | 449 | obj = drm_gem_object_lookup(filp, cmd->handles[0]); |
4d8d096e AC |
450 | if (obj == NULL) |
451 | return ERR_PTR(-ENOENT); | |
452 | ||
453 | /* Let the core code do all the work */ | |
454 | r = container_of(obj, struct gtt_range, gem); | |
455 | return psb_framebuffer_create(dev, cmd, r); | |
456 | } | |
457 | ||
4d8d096e AC |
458 | static int psbfb_probe(struct drm_fb_helper *helper, |
459 | struct drm_fb_helper_surface_size *sizes) | |
460 | { | |
c39aa6a1 FF |
461 | struct psb_fbdev *psb_fbdev = |
462 | container_of(helper, struct psb_fbdev, psb_fb_helper); | |
3aad16d2 AC |
463 | struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; |
464 | struct drm_psb_private *dev_priv = dev->dev_private; | |
3aad16d2 | 465 | int bytespp; |
4d8d096e | 466 | |
3aad16d2 AC |
467 | bytespp = sizes->surface_bpp / 8; |
468 | if (bytespp == 3) /* no 24bit packed */ | |
469 | bytespp = 4; | |
470 | ||
471 | /* If the mode will not fit in 32bit then switch to 16bit to get | |
472 | a console on full resolution. The X mode setting server will | |
473 | allocate its own 32bit GEM framebuffer */ | |
474 | if (ALIGN(sizes->fb_width * bytespp, 64) * sizes->fb_height > | |
475 | dev_priv->vram_stolen_size) { | |
476 | sizes->surface_bpp = 16; | |
477 | sizes->surface_depth = 16; | |
478 | } | |
479 | ||
cd5428a5 | 480 | return psbfb_create(psb_fbdev, sizes); |
4d8d096e AC |
481 | } |
482 | ||
3a493879 | 483 | static const struct drm_fb_helper_funcs psb_fb_helper_funcs = { |
4d8d096e AC |
484 | .fb_probe = psbfb_probe, |
485 | }; | |
486 | ||
bc7f2b08 | 487 | static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) |
4d8d096e | 488 | { |
4d8d096e AC |
489 | struct psb_framebuffer *psbfb = &fbdev->pfb; |
490 | ||
546187c8 | 491 | drm_fb_helper_unregister_fbi(&fbdev->psb_fb_helper); |
546187c8 | 492 | |
4d8d096e | 493 | drm_fb_helper_fini(&fbdev->psb_fb_helper); |
36206361 | 494 | drm_framebuffer_unregister_private(&psbfb->base); |
4d8d096e AC |
495 | drm_framebuffer_cleanup(&psbfb->base); |
496 | ||
bc61c975 | 497 | if (psbfb->base.obj[0]) |
a63d3bd2 | 498 | drm_gem_object_put_unlocked(psbfb->base.obj[0]); |
4d8d096e AC |
499 | return 0; |
500 | } | |
501 | ||
502 | int psb_fbdev_init(struct drm_device *dev) | |
503 | { | |
504 | struct psb_fbdev *fbdev; | |
505 | struct drm_psb_private *dev_priv = dev->dev_private; | |
01934c2a | 506 | int ret; |
4d8d096e AC |
507 | |
508 | fbdev = kzalloc(sizeof(struct psb_fbdev), GFP_KERNEL); | |
509 | if (!fbdev) { | |
510 | dev_err(dev->dev, "no memory\n"); | |
511 | return -ENOMEM; | |
512 | } | |
513 | ||
514 | dev_priv->fbdev = fbdev; | |
10a23102 TR |
515 | |
516 | drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs); | |
4d8d096e | 517 | |
01934c2a | 518 | ret = drm_fb_helper_init(dev, &fbdev->psb_fb_helper, |
e4563f6b | 519 | INTELFB_CONN_LIMIT); |
01934c2a TR |
520 | if (ret) |
521 | goto free; | |
4d8d096e | 522 | |
01934c2a TR |
523 | ret = drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); |
524 | if (ret) | |
525 | goto fini; | |
76a39dbf DV |
526 | |
527 | /* disable all the possible outputs/crtcs before entering KMS mode */ | |
528 | drm_helper_disable_unused_functions(dev); | |
529 | ||
01934c2a TR |
530 | ret = drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); |
531 | if (ret) | |
532 | goto fini; | |
533 | ||
4d8d096e | 534 | return 0; |
01934c2a TR |
535 | |
536 | fini: | |
537 | drm_fb_helper_fini(&fbdev->psb_fb_helper); | |
538 | free: | |
539 | kfree(fbdev); | |
540 | return ret; | |
4d8d096e AC |
541 | } |
542 | ||
bc7f2b08 | 543 | static void psb_fbdev_fini(struct drm_device *dev) |
4d8d096e AC |
544 | { |
545 | struct drm_psb_private *dev_priv = dev->dev_private; | |
546 | ||
547 | if (!dev_priv->fbdev) | |
548 | return; | |
549 | ||
550 | psb_fbdev_destroy(dev, dev_priv->fbdev); | |
551 | kfree(dev_priv->fbdev); | |
552 | dev_priv->fbdev = NULL; | |
553 | } | |
554 | ||
4d8d096e AC |
555 | static const struct drm_mode_config_funcs psb_mode_funcs = { |
556 | .fb_create = psb_user_framebuffer_create, | |
cdc13f68 | 557 | .output_poll_changed = drm_fb_helper_output_poll_changed, |
4d8d096e AC |
558 | }; |
559 | ||
4d8d096e AC |
560 | static void psb_setup_outputs(struct drm_device *dev) |
561 | { | |
562 | struct drm_psb_private *dev_priv = dev->dev_private; | |
563 | struct drm_connector *connector; | |
564 | ||
565 | drm_mode_create_scaling_mode_property(dev); | |
4d8d096e | 566 | |
13619ce5 AC |
567 | /* It is ok for this to fail - we just don't get backlight control */ |
568 | if (!dev_priv->backlight_property) | |
569 | dev_priv->backlight_property = drm_property_create_range(dev, 0, | |
570 | "backlight", 0, 100); | |
4d8d096e AC |
571 | dev_priv->ops->output_init(dev); |
572 | ||
573 | list_for_each_entry(connector, &dev->mode_config.connector_list, | |
574 | head) { | |
367e4408 PJ |
575 | struct gma_encoder *gma_encoder = gma_attached_encoder(connector); |
576 | struct drm_encoder *encoder = &gma_encoder->base; | |
4d8d096e AC |
577 | int crtc_mask = 0, clone_mask = 0; |
578 | ||
579 | /* valid crtcs */ | |
367e4408 | 580 | switch (gma_encoder->type) { |
4d8d096e AC |
581 | case INTEL_OUTPUT_ANALOG: |
582 | crtc_mask = (1 << 0); | |
583 | clone_mask = (1 << INTEL_OUTPUT_ANALOG); | |
584 | break; | |
585 | case INTEL_OUTPUT_SDVO: | |
cf8efd3a | 586 | crtc_mask = dev_priv->ops->sdvo_mask; |
4d8d096e AC |
587 | clone_mask = (1 << INTEL_OUTPUT_SDVO); |
588 | break; | |
589 | case INTEL_OUTPUT_LVDS: | |
d235e64a | 590 | crtc_mask = dev_priv->ops->lvds_mask; |
4d8d096e AC |
591 | clone_mask = (1 << INTEL_OUTPUT_LVDS); |
592 | break; | |
593 | case INTEL_OUTPUT_MIPI: | |
594 | crtc_mask = (1 << 0); | |
595 | clone_mask = (1 << INTEL_OUTPUT_MIPI); | |
596 | break; | |
597 | case INTEL_OUTPUT_MIPI2: | |
598 | crtc_mask = (1 << 2); | |
599 | clone_mask = (1 << INTEL_OUTPUT_MIPI2); | |
600 | break; | |
601 | case INTEL_OUTPUT_HDMI: | |
d235e64a | 602 | crtc_mask = dev_priv->ops->hdmi_mask; |
4d8d096e AC |
603 | clone_mask = (1 << INTEL_OUTPUT_HDMI); |
604 | break; | |
220801bd AC |
605 | case INTEL_OUTPUT_DISPLAYPORT: |
606 | crtc_mask = (1 << 0) | (1 << 1); | |
607 | clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT); | |
608 | break; | |
d112a816 ZY |
609 | case INTEL_OUTPUT_EDP: |
610 | crtc_mask = (1 << 1); | |
611 | clone_mask = (1 << INTEL_OUTPUT_EDP); | |
4d8d096e AC |
612 | } |
613 | encoder->possible_crtcs = crtc_mask; | |
614 | encoder->possible_clones = | |
a3d5d75f | 615 | gma_connector_clones(dev, clone_mask); |
4d8d096e AC |
616 | } |
617 | } | |
618 | ||
619 | void psb_modeset_init(struct drm_device *dev) | |
620 | { | |
621 | struct drm_psb_private *dev_priv = dev->dev_private; | |
622 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; | |
623 | int i; | |
624 | ||
625 | drm_mode_config_init(dev); | |
626 | ||
627 | dev->mode_config.min_width = 0; | |
628 | dev->mode_config.min_height = 0; | |
629 | ||
e6ecefaa | 630 | dev->mode_config.funcs = &psb_mode_funcs; |
4d8d096e AC |
631 | |
632 | /* set memory base */ | |
dffc9ceb | 633 | /* Oaktrail and Poulsbo should use BAR 2*/ |
4d8d096e AC |
634 | pci_read_config_dword(dev->pdev, PSB_BSM, (u32 *) |
635 | &(dev->mode_config.fb_base)); | |
636 | ||
637 | /* num pipes is 2 for PSB but 1 for Mrst */ | |
638 | for (i = 0; i < dev_priv->num_pipe; i++) | |
639 | psb_intel_crtc_init(dev, i, mode_dev); | |
640 | ||
cbbd379a PJ |
641 | dev->mode_config.max_width = 4096; |
642 | dev->mode_config.max_height = 4096; | |
4d8d096e AC |
643 | |
644 | psb_setup_outputs(dev); | |
d235e64a AC |
645 | |
646 | if (dev_priv->ops->errata) | |
647 | dev_priv->ops->errata(dev); | |
4ab2c7f1 AC |
648 | |
649 | dev_priv->modeset = true; | |
4d8d096e AC |
650 | } |
651 | ||
652 | void psb_modeset_cleanup(struct drm_device *dev) | |
653 | { | |
4ab2c7f1 AC |
654 | struct drm_psb_private *dev_priv = dev->dev_private; |
655 | if (dev_priv->modeset) { | |
4ab2c7f1 AC |
656 | drm_kms_helper_poll_fini(dev); |
657 | psb_fbdev_fini(dev); | |
658 | drm_mode_config_cleanup(dev); | |
4ab2c7f1 | 659 | } |
4d8d096e | 660 | } |