18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
48c2ecf20Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <drm/drm.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "rockchip_drm_drv.h"
188c2ecf20Sopenharmony_ci#include "rockchip_drm_fb.h"
198c2ecf20Sopenharmony_ci#include "rockchip_drm_gem.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
228c2ecf20Sopenharmony_ci	.destroy       = drm_gem_fb_destroy,
238c2ecf20Sopenharmony_ci	.create_handle = drm_gem_fb_create_handle,
248c2ecf20Sopenharmony_ci	.dirty	       = drm_atomic_helper_dirtyfb,
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct drm_framebuffer *
288c2ecf20Sopenharmony_cirockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
298c2ecf20Sopenharmony_ci		  struct drm_gem_object **obj, unsigned int num_planes)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb;
328c2ecf20Sopenharmony_ci	int ret;
338c2ecf20Sopenharmony_ci	int i;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
368c2ecf20Sopenharmony_ci	if (!fb)
378c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	for (i = 0; i < num_planes; i++)
428c2ecf20Sopenharmony_ci		fb->obj[i] = obj[i];
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	ret = drm_framebuffer_init(dev, fb, &rockchip_drm_fb_funcs);
458c2ecf20Sopenharmony_ci	if (ret) {
468c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(dev->dev,
478c2ecf20Sopenharmony_ci			      "Failed to initialize framebuffer: %d\n",
488c2ecf20Sopenharmony_ci			      ret);
498c2ecf20Sopenharmony_ci		kfree(fb);
508c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return fb;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
578c2ecf20Sopenharmony_ci	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic struct drm_framebuffer *
618c2ecf20Sopenharmony_cirockchip_fb_create(struct drm_device *dev, struct drm_file *file,
628c2ecf20Sopenharmony_ci		   const struct drm_mode_fb_cmd2 *mode_cmd)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct drm_afbc_framebuffer *afbc_fb;
658c2ecf20Sopenharmony_ci	const struct drm_format_info *info;
668c2ecf20Sopenharmony_ci	int ret;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	info = drm_get_format_info(dev, mode_cmd);
698c2ecf20Sopenharmony_ci	if (!info)
708c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
738c2ecf20Sopenharmony_ci	if (!afbc_fb)
748c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd,
778c2ecf20Sopenharmony_ci					 &rockchip_drm_fb_funcs);
788c2ecf20Sopenharmony_ci	if (ret) {
798c2ecf20Sopenharmony_ci		kfree(afbc_fb);
808c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (drm_is_afbc(mode_cmd->modifier[0])) {
848c2ecf20Sopenharmony_ci		int ret, i;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
878c2ecf20Sopenharmony_ci		if (ret) {
888c2ecf20Sopenharmony_ci			struct drm_gem_object **obj = afbc_fb->base.obj;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci			for (i = 0; i < info->num_planes; ++i)
918c2ecf20Sopenharmony_ci				drm_gem_object_put(obj[i]);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci			kfree(afbc_fb);
948c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return &afbc_fb->base;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
1028c2ecf20Sopenharmony_ci	.fb_create = rockchip_fb_create,
1038c2ecf20Sopenharmony_ci	.output_poll_changed = drm_fb_helper_output_poll_changed,
1048c2ecf20Sopenharmony_ci	.atomic_check = drm_atomic_helper_check,
1058c2ecf20Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistruct drm_framebuffer *
1098c2ecf20Sopenharmony_cirockchip_drm_framebuffer_init(struct drm_device *dev,
1108c2ecf20Sopenharmony_ci			      const struct drm_mode_fb_cmd2 *mode_cmd,
1118c2ecf20Sopenharmony_ci			      struct drm_gem_object *obj)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
1168c2ecf20Sopenharmony_ci	if (IS_ERR(fb))
1178c2ecf20Sopenharmony_ci		return ERR_CAST(fb);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return fb;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_civoid rockchip_drm_mode_config_init(struct drm_device *dev)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	dev->mode_config.min_width = 0;
1258c2ecf20Sopenharmony_ci	dev->mode_config.min_height = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/*
1288c2ecf20Sopenharmony_ci	 * set max width and height as default value(4096x4096).
1298c2ecf20Sopenharmony_ci	 * this value would be used to check framebuffer size limitation
1308c2ecf20Sopenharmony_ci	 * at drm_mode_addfb().
1318c2ecf20Sopenharmony_ci	 */
1328c2ecf20Sopenharmony_ci	dev->mode_config.max_width = 4096;
1338c2ecf20Sopenharmony_ci	dev->mode_config.max_height = 4096;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
1368c2ecf20Sopenharmony_ci	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
1378c2ecf20Sopenharmony_ci}
138