hibmc_drm_fbdev.c 5.9 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-or-later
2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* Hisilicon Hibmc SoC drm driver
 *
 * Based on the bochs drm driver.
 *
 * Copyright (c) 2016 Huawei Limited.
 *
 * Author:
 *	Rongrong Zou <zourongrong@huawei.com>
 *	Rongrong Zou <zourongrong@gmail.com>
 *	Jianhua Li <lijianhua@huawei.com>
 */

#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
S
Sam Ravnborg 已提交
16 17
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_vram_helper.h>
18
#include <drm/drm_probe_helper.h>
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

#include "hibmc_drm_drv.h"

static int hibmcfb_create_object(
				struct hibmc_drm_private *priv,
				const struct drm_mode_fb_cmd2 *mode_cmd,
				struct drm_gem_object **gobj_p)
{
	struct drm_gem_object *gobj;
	struct drm_device *dev = priv->dev;
	u32 size;
	int ret = 0;

	size = mode_cmd->pitches[0] * mode_cmd->height;
	ret = hibmc_gem_create(dev, size, true, &gobj);
	if (ret)
		return ret;

	*gobj_p = gobj;
	return ret;
}

static struct fb_ops hibmc_drm_fb_ops = {
	.owner = THIS_MODULE,
	.fb_check_var = drm_fb_helper_check_var,
	.fb_set_par = drm_fb_helper_set_par,
	.fb_fillrect = drm_fb_helper_sys_fillrect,
	.fb_copyarea = drm_fb_helper_sys_copyarea,
	.fb_imageblit = drm_fb_helper_sys_imageblit,
	.fb_pan_display = drm_fb_helper_pan_display,
	.fb_blank = drm_fb_helper_blank,
	.fb_setcmap = drm_fb_helper_setcmap,
};

static int hibmc_drm_fb_create(struct drm_fb_helper *helper,
			       struct drm_fb_helper_surface_size *sizes)
{
	struct hibmc_fbdev *hi_fbdev =
		container_of(helper, struct hibmc_fbdev, helper);
	struct hibmc_drm_private *priv = helper->dev->dev_private;
	struct fb_info *info;
	struct drm_mode_fb_cmd2 mode_cmd;
	struct drm_gem_object *gobj = NULL;
	int ret = 0;
	size_t size;
	unsigned int bytes_per_pixel;
65 66
	struct drm_gem_vram_object *gbo = NULL;
	void *base;
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

	DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n",
			 sizes->surface_width, sizes->surface_height,
			 sizes->surface_bpp);

	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);

	mode_cmd.width = sizes->surface_width;
	mode_cmd.height = sizes->surface_height;
	mode_cmd.pitches[0] = mode_cmd.width * bytes_per_pixel;
	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
							  sizes->surface_depth);

	size = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height);

	ret = hibmcfb_create_object(priv, &mode_cmd, &gobj);
	if (ret) {
		DRM_ERROR("failed to create fbcon backing object: %d\n", ret);
		return -ENOMEM;
	}

88
	gbo = drm_gem_vram_of_gem(gobj);
89

90
	ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM);
91 92
	if (ret) {
		DRM_ERROR("failed to pin fbcon: %d\n", ret);
93
		goto out_unref_gem;
94 95
	}

96 97 98
	base = drm_gem_vram_kmap(gbo, true, NULL);
	if (IS_ERR(base)) {
		ret = PTR_ERR(base);
99 100 101 102 103 104 105 106 107 108 109 110 111
		DRM_ERROR("failed to kmap fbcon: %d\n", ret);
		goto out_unpin_bo;
	}

	info = drm_fb_helper_alloc_fbi(helper);
	if (IS_ERR(info)) {
		ret = PTR_ERR(info);
		DRM_ERROR("failed to allocate fbi: %d\n", ret);
		goto out_release_fbi;
	}

	hi_fbdev->fb = hibmc_framebuffer_init(priv->dev, &mode_cmd, gobj);
	if (IS_ERR(hi_fbdev->fb)) {
112
		ret = PTR_ERR(hi_fbdev->fb);
113
		hi_fbdev->fb = NULL;
114 115 116 117 118 119 120 121 122
		DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
		goto out_release_fbi;
	}

	priv->fbdev->size = size;
	hi_fbdev->helper.fb = &hi_fbdev->fb->fb;

	info->fbops = &hibmc_drm_fb_ops;

123
	drm_fb_helper_fill_info(info, &priv->fbdev->helper, sizes);
124

125
	info->screen_base = base;
126 127
	info->screen_size = size;

128
	info->fix.smem_start = gbo->bo.mem.bus.offset + gbo->bo.mem.bus.base;
129 130 131 132
	info->fix.smem_len = size;
	return 0;

out_release_fbi:
133
	drm_gem_vram_kunmap(gbo);
134
out_unpin_bo:
135
	drm_gem_vram_unpin(gbo);
136
out_unref_gem:
137
	drm_gem_object_put_unlocked(gobj);
138 139 140 141 142 143 144 145 146 147 148 149 150 151

	return ret;
}

static void hibmc_fbdev_destroy(struct hibmc_fbdev *fbdev)
{
	struct hibmc_framebuffer *gfb = fbdev->fb;
	struct drm_fb_helper *fbh = &fbdev->helper;

	drm_fb_helper_unregister_fbi(fbh);

	drm_fb_helper_fini(fbh);

	if (gfb)
152
		drm_framebuffer_put(&gfb->fb);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
}

static const struct drm_fb_helper_funcs hibmc_fbdev_helper_funcs = {
	.fb_probe = hibmc_drm_fb_create,
};

int hibmc_fbdev_init(struct hibmc_drm_private *priv)
{
	int ret;
	struct fb_var_screeninfo *var;
	struct fb_fix_screeninfo *fix;
	struct hibmc_fbdev *hifbdev;

	hifbdev = devm_kzalloc(priv->dev->dev, sizeof(*hifbdev), GFP_KERNEL);
	if (!hifbdev) {
		DRM_ERROR("failed to allocate hibmc_fbdev\n");
		return -ENOMEM;
	}

	priv->fbdev = hifbdev;
	drm_fb_helper_prepare(priv->dev, &hifbdev->helper,
			      &hibmc_fbdev_helper_funcs);

	/* Now just one crtc and one channel */
177
	ret = drm_fb_helper_init(priv->dev, &hifbdev->helper, 1);
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	if (ret) {
		DRM_ERROR("failed to initialize fb helper: %d\n", ret);
		return ret;
	}

	ret = drm_fb_helper_single_add_all_connectors(&hifbdev->helper);
	if (ret) {
		DRM_ERROR("failed to add all connectors: %d\n", ret);
		goto fini;
	}

	ret = drm_fb_helper_initial_config(&hifbdev->helper, 16);
	if (ret) {
		DRM_ERROR("failed to setup initial conn config: %d\n", ret);
		goto fini;
	}

	var = &hifbdev->helper.fbdev->var;
	fix = &hifbdev->helper.fbdev->fix;

	DRM_DEBUG_DRIVER("Member of info->var is :\n"
			 "xres=%d\n"
			 "yres=%d\n"
			 "xres_virtual=%d\n"
			 "yres_virtual=%d\n"
			 "xoffset=%d\n"
			 "yoffset=%d\n"
			 "bits_per_pixel=%d\n"
			 "...\n", var->xres, var->yres, var->xres_virtual,
			 var->yres_virtual, var->xoffset, var->yoffset,
			 var->bits_per_pixel);
	DRM_DEBUG_DRIVER("Member of info->fix is :\n"
			 "smem_start=%lx\n"
			 "smem_len=%d\n"
			 "type=%d\n"
			 "type_aux=%d\n"
			 "visual=%d\n"
			 "xpanstep=%d\n"
			 "ypanstep=%d\n"
			 "ywrapstep=%d\n"
			 "line_length=%d\n"
			 "accel=%d\n"
			 "capabilities=%d\n"
			 "...\n", fix->smem_start, fix->smem_len, fix->type,
			 fix->type_aux, fix->visual, fix->xpanstep,
			 fix->ypanstep, fix->ywrapstep, fix->line_length,
			 fix->accel, fix->capabilities);

	return 0;

fini:
	drm_fb_helper_fini(&hifbdev->helper);
	return ret;
}

void hibmc_fbdev_fini(struct hibmc_drm_private *priv)
{
	if (!priv->fbdev)
		return;

	hibmc_fbdev_destroy(priv->fbdev);
	priv->fbdev = NULL;
}