Linux Audio

Check our new training course

Linux kernel drivers training

Mar 31-Apr 9, 2025, special US time zones
Register
Loading...
Note: File does not exist in v5.4.
  1/*
  2 * Copyright 2012 Red Hat
  3 *
  4 * This file is subject to the terms and conditions of the GNU General
  5 * Public License version 2. See the file COPYING in the main
  6 * directory of this archive for more details.
  7 *
  8 * Authors: Matthew Garrett
  9 *          Dave Airlie
 10 */
 11#include <linux/module.h>
 12#include "drmP.h"
 13#include "drm.h"
 14#include "drm_fb_helper.h"
 15
 16#include <linux/fb.h>
 17
 18#include "cirrus_drv.h"
 19
 20static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
 21			     int x, int y, int width, int height)
 22{
 23	int i;
 24	struct drm_gem_object *obj;
 25	struct cirrus_bo *bo;
 26	int src_offset, dst_offset;
 27	int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8;
 28	int ret;
 29	bool unmap = false;
 30
 31	obj = afbdev->gfb.obj;
 32	bo = gem_to_cirrus_bo(obj);
 33
 34	ret = cirrus_bo_reserve(bo, true);
 35	if (ret) {
 36		DRM_ERROR("failed to reserve fb bo\n");
 37		return;
 38	}
 39
 40	if (!bo->kmap.virtual) {
 41		ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
 42		if (ret) {
 43			DRM_ERROR("failed to kmap fb updates\n");
 44			cirrus_bo_unreserve(bo);
 45			return;
 46		}
 47		unmap = true;
 48	}
 49	for (i = y; i < y + height; i++) {
 50		/* assume equal stride for now */
 51		src_offset = dst_offset = i * afbdev->gfb.base.pitches[0] + (x * bpp);
 52		memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
 53
 54	}
 55	if (unmap)
 56		ttm_bo_kunmap(&bo->kmap);
 57
 58	cirrus_bo_unreserve(bo);
 59}
 60
 61static void cirrus_fillrect(struct fb_info *info,
 62			 const struct fb_fillrect *rect)
 63{
 64	struct cirrus_fbdev *afbdev = info->par;
 65	sys_fillrect(info, rect);
 66	cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width,
 67			 rect->height);
 68}
 69
 70static void cirrus_copyarea(struct fb_info *info,
 71			 const struct fb_copyarea *area)
 72{
 73	struct cirrus_fbdev *afbdev = info->par;
 74	sys_copyarea(info, area);
 75	cirrus_dirty_update(afbdev, area->dx, area->dy, area->width,
 76			 area->height);
 77}
 78
 79static void cirrus_imageblit(struct fb_info *info,
 80			  const struct fb_image *image)
 81{
 82	struct cirrus_fbdev *afbdev = info->par;
 83	sys_imageblit(info, image);
 84	cirrus_dirty_update(afbdev, image->dx, image->dy, image->width,
 85			 image->height);
 86}
 87
 88
 89static struct fb_ops cirrusfb_ops = {
 90	.owner = THIS_MODULE,
 91	.fb_check_var = drm_fb_helper_check_var,
 92	.fb_set_par = drm_fb_helper_set_par,
 93	.fb_fillrect = cirrus_fillrect,
 94	.fb_copyarea = cirrus_copyarea,
 95	.fb_imageblit = cirrus_imageblit,
 96	.fb_pan_display = drm_fb_helper_pan_display,
 97	.fb_blank = drm_fb_helper_blank,
 98	.fb_setcmap = drm_fb_helper_setcmap,
 99};
100
101static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
102			       struct drm_mode_fb_cmd2 *mode_cmd,
103			       struct drm_gem_object **gobj_p)
104{
105	struct drm_device *dev = afbdev->helper.dev;
106	u32 bpp, depth;
107	u32 size;
108	struct drm_gem_object *gobj;
109
110	int ret = 0;
111	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
112
113	if (bpp > 24)
114		return -EINVAL;
115	size = mode_cmd->pitches[0] * mode_cmd->height;
116	ret = cirrus_gem_create(dev, size, true, &gobj);
117	if (ret)
118		return ret;
119
120	*gobj_p = gobj;
121	return ret;
122}
123
124static int cirrusfb_create(struct cirrus_fbdev *gfbdev,
125			   struct drm_fb_helper_surface_size *sizes)
126{
127	struct drm_device *dev = gfbdev->helper.dev;
128	struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
129	struct fb_info *info;
130	struct drm_framebuffer *fb;
131	struct drm_mode_fb_cmd2 mode_cmd;
132	struct device *device = &dev->pdev->dev;
133	void *sysram;
134	struct drm_gem_object *gobj = NULL;
135	struct cirrus_bo *bo = NULL;
136	int size, ret;
137
138	mode_cmd.width = sizes->surface_width;
139	mode_cmd.height = sizes->surface_height;
140	mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
141	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
142							  sizes->surface_depth);
143	size = mode_cmd.pitches[0] * mode_cmd.height;
144
145	ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj);
146	if (ret) {
147		DRM_ERROR("failed to create fbcon backing object %d\n", ret);
148		return ret;
149	}
150
151	bo = gem_to_cirrus_bo(gobj);
152
153	sysram = vmalloc(size);
154	if (!sysram)
155		return -ENOMEM;
156
157	info = framebuffer_alloc(0, device);
158	if (info == NULL)
159		return -ENOMEM;
160
161	info->par = gfbdev;
162
163	ret = cirrus_framebuffer_init(cdev->dev, &gfbdev->gfb, &mode_cmd, gobj);
164	if (ret)
165		return ret;
166
167	gfbdev->sysram = sysram;
168	gfbdev->size = size;
169
170	fb = &gfbdev->gfb.base;
171	if (!fb) {
172		DRM_INFO("fb is NULL\n");
173		return -EINVAL;
174	}
175
176	/* setup helper */
177	gfbdev->helper.fb = fb;
178	gfbdev->helper.fbdev = info;
179
180	strcpy(info->fix.id, "cirrusdrmfb");
181
182
183	info->flags = FBINFO_DEFAULT;
184	info->fbops = &cirrusfb_ops;
185
186	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
187	drm_fb_helper_fill_var(info, &gfbdev->helper, sizes->fb_width,
188			       sizes->fb_height);
189
190	/* setup aperture base/size for vesafb takeover */
191	info->apertures = alloc_apertures(1);
192	if (!info->apertures) {
193		ret = -ENOMEM;
194		goto out_iounmap;
195	}
196	info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base;
197	info->apertures->ranges[0].size = cdev->mc.vram_size;
198
199	info->screen_base = sysram;
200	info->screen_size = size;
201
202	info->fix.mmio_start = 0;
203	info->fix.mmio_len = 0;
204
205	ret = fb_alloc_cmap(&info->cmap, 256, 0);
206	if (ret) {
207		DRM_ERROR("%s: can't allocate color map\n", info->fix.id);
208		ret = -ENOMEM;
209		goto out_iounmap;
210	}
211
212	DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
213	DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start);
214	DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len);
215	DRM_INFO("fb depth is %d\n", fb->depth);
216	DRM_INFO("   pitch is %d\n", fb->pitches[0]);
217
218	return 0;
219out_iounmap:
220	return ret;
221}
222
223static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper,
224					   struct drm_fb_helper_surface_size
225					   *sizes)
226{
227	struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper;
228	int new_fb = 0;
229	int ret;
230
231	if (!helper->fb) {
232		ret = cirrusfb_create(gfbdev, sizes);
233		if (ret)
234			return ret;
235		new_fb = 1;
236	}
237	return new_fb;
238}
239
240static int cirrus_fbdev_destroy(struct drm_device *dev,
241				struct cirrus_fbdev *gfbdev)
242{
243	struct fb_info *info;
244	struct cirrus_framebuffer *gfb = &gfbdev->gfb;
245
246	if (gfbdev->helper.fbdev) {
247		info = gfbdev->helper.fbdev;
248
249		unregister_framebuffer(info);
250		if (info->cmap.len)
251			fb_dealloc_cmap(&info->cmap);
252		framebuffer_release(info);
253	}
254
255	if (gfb->obj) {
256		drm_gem_object_unreference_unlocked(gfb->obj);
257		gfb->obj = NULL;
258	}
259
260	vfree(gfbdev->sysram);
261	drm_fb_helper_fini(&gfbdev->helper);
262	drm_framebuffer_cleanup(&gfb->base);
263
264	return 0;
265}
266
267static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
268	.gamma_set = cirrus_crtc_fb_gamma_set,
269	.gamma_get = cirrus_crtc_fb_gamma_get,
270	.fb_probe = cirrus_fb_find_or_create_single,
271};
272
273int cirrus_fbdev_init(struct cirrus_device *cdev)
274{
275	struct cirrus_fbdev *gfbdev;
276	int ret;
277	int bpp_sel = 24;
278
279	/*bpp_sel = 8;*/
280	gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL);
281	if (!gfbdev)
282		return -ENOMEM;
283
284	cdev->mode_info.gfbdev = gfbdev;
285	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
286
287	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
288				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
289	if (ret) {
290		kfree(gfbdev);
291		return ret;
292	}
293	drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
294	drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel);
295
296	return 0;
297}
298
299void cirrus_fbdev_fini(struct cirrus_device *cdev)
300{
301	if (!cdev->mode_info.gfbdev)
302		return;
303
304	cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev);
305	kfree(cdev->mode_info.gfbdev);
306	cdev->mode_info.gfbdev = NULL;
307}