Linux Audio

Check our new training course

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