mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
56dc5afca1
PD#SWPL-139231 Problem: buffer of stop frame can't remove Solution: add ioctl MESON_RMFB Verify: sc2 Test: DRM-OSD-103 Change-Id: Ibd556f906bbe9d00d8f86713178f3cb57e535b8a Signed-off-by: mingyang.he <mingyang.he@amlogic.com>
196 lines
4.8 KiB
C
196 lines
4.8 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
|
|
#include "meson_fb.h"
|
|
#include "meson_vpu.h"
|
|
|
|
void am_meson_fb_destroy(struct drm_framebuffer *fb)
|
|
{
|
|
struct am_meson_fb *meson_fb = to_am_meson_fb(fb);
|
|
int i;
|
|
|
|
for (i = 0; i < AM_MESON_GEM_OBJECT_NUM && meson_fb->bufp[i]; i++)
|
|
drm_gem_object_put(&meson_fb->bufp[i]->base);
|
|
drm_framebuffer_cleanup(fb);
|
|
if (meson_fb->logo && meson_fb->logo->alloc_flag)
|
|
am_meson_free_logo_memory();
|
|
DRM_DEBUG("meson_fb=0x%p,\n", meson_fb);
|
|
kfree(meson_fb);
|
|
}
|
|
|
|
int am_meson_fb_create_handle(struct drm_framebuffer *fb,
|
|
struct drm_file *file_priv,
|
|
unsigned int *handle)
|
|
{
|
|
struct am_meson_fb *meson_fb = to_am_meson_fb(fb);
|
|
|
|
if (meson_fb->bufp[0])
|
|
return drm_gem_handle_create(file_priv,
|
|
&meson_fb->bufp[0]->base, handle);
|
|
else
|
|
return -EFAULT;
|
|
}
|
|
|
|
struct drm_framebuffer_funcs am_meson_fb_funcs = {
|
|
.create_handle = am_meson_fb_create_handle, //must for fbdev emulate
|
|
.destroy = am_meson_fb_destroy,
|
|
};
|
|
|
|
struct drm_framebuffer *
|
|
am_meson_fb_alloc(struct drm_device *dev,
|
|
struct drm_mode_fb_cmd2 *mode_cmd,
|
|
struct drm_gem_object *obj)
|
|
{
|
|
struct am_meson_fb *meson_fb;
|
|
struct am_meson_gem_object *meson_gem;
|
|
int ret = 0;
|
|
|
|
meson_fb = kzalloc(sizeof(*meson_fb), GFP_KERNEL);
|
|
if (!meson_fb)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
if (obj) {
|
|
meson_gem = container_of(obj, struct am_meson_gem_object, base);
|
|
meson_fb->bufp[0] = meson_gem;
|
|
} else {
|
|
meson_fb->bufp[0] = NULL;
|
|
}
|
|
drm_helper_mode_fill_fb_struct(dev, &meson_fb->base, mode_cmd);
|
|
|
|
ret = drm_framebuffer_init(dev, &meson_fb->base,
|
|
&am_meson_fb_funcs);
|
|
if (ret) {
|
|
dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
|
|
ret);
|
|
goto err_free_fb;
|
|
}
|
|
DRM_DEBUG("meson_fb[id:%d,ref:%d]=0x%p,meson_fb->bufp[0]=0x%p\n",
|
|
meson_fb->base.base.id,
|
|
kref_read(&meson_fb->base.base.refcount),
|
|
meson_fb, meson_fb->bufp[0]);
|
|
|
|
return &meson_fb->base;
|
|
|
|
err_free_fb:
|
|
kfree(meson_fb);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev,
|
|
struct drm_file *file_priv,
|
|
const struct drm_mode_fb_cmd2 *mode_cmd)
|
|
{
|
|
struct am_meson_fb *meson_fb = 0;
|
|
struct drm_gem_object *obj = 0;
|
|
struct am_meson_gem_object *meson_gem;
|
|
int ret, i;
|
|
|
|
meson_fb = kzalloc(sizeof(*meson_fb), GFP_KERNEL);
|
|
if (!meson_fb)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
/* support multi handle .*/
|
|
for (i = 0; i < AM_MESON_GEM_OBJECT_NUM && mode_cmd->handles[i]; i++) {
|
|
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
|
|
if (!obj) {
|
|
dev_err(dev->dev, "Failed to lookup GEM handle\n");
|
|
kfree(meson_fb);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
meson_gem = container_of(obj, struct am_meson_gem_object, base);
|
|
meson_fb->bufp[i] = meson_gem;
|
|
}
|
|
|
|
drm_helper_mode_fill_fb_struct(dev, &meson_fb->base, mode_cmd);
|
|
|
|
ret = drm_framebuffer_init(dev, &meson_fb->base, &am_meson_fb_funcs);
|
|
if (ret) {
|
|
dev_err(dev->dev,
|
|
"Failed to initialize framebuffer: %d\n",
|
|
ret);
|
|
drm_gem_object_put(obj);
|
|
kfree(meson_fb);
|
|
return ERR_PTR(ret);
|
|
}
|
|
DRM_DEBUG("meson_fb[id:%d,ref:%d]=0x%px,meson_fb->bufp[%d-0]=0x%p\n",
|
|
meson_fb->base.base.id,
|
|
kref_read(&meson_fb->base.base.refcount),
|
|
meson_fb, i, meson_fb->bufp[0]);
|
|
|
|
return &meson_fb->base;
|
|
}
|
|
|
|
struct drm_framebuffer *
|
|
am_meson_drm_framebuffer_init(struct drm_device *dev,
|
|
struct drm_mode_fb_cmd2 *mode_cmd,
|
|
struct drm_gem_object *obj)
|
|
{
|
|
struct drm_framebuffer *fb;
|
|
|
|
fb = am_meson_fb_alloc(dev, mode_cmd, obj);
|
|
if (IS_ERR(fb))
|
|
return NULL;
|
|
|
|
return fb;
|
|
}
|
|
|
|
int am_meson_mode_rmfb(struct drm_device *dev, u32 fb_id,
|
|
struct drm_file *file_priv)
|
|
{
|
|
struct drm_framebuffer *fb = NULL;
|
|
struct drm_framebuffer *fbl = NULL;
|
|
int found = 0;
|
|
|
|
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
|
return -EOPNOTSUPP;
|
|
|
|
fb = drm_framebuffer_lookup(dev, file_priv, fb_id);
|
|
if (!fb)
|
|
return -ENOENT;
|
|
|
|
mutex_lock(&file_priv->fbs_lock);
|
|
list_for_each_entry(fbl, &file_priv->fbs, filp_head)
|
|
if (fb == fbl)
|
|
found = 1;
|
|
if (!found) {
|
|
mutex_unlock(&file_priv->fbs_lock);
|
|
goto fail_unref;
|
|
}
|
|
|
|
list_del_init(&fb->filp_head);
|
|
mutex_unlock(&file_priv->fbs_lock);
|
|
|
|
/* drop the reference we picked up in framebuffer lookup */
|
|
drm_framebuffer_put(fb);
|
|
|
|
/*
|
|
* we now own the reference that was stored in the fbs list
|
|
*
|
|
* drm_framebuffer_remove may fail with -EINTR on pending signals,
|
|
* so run this in a separate stack as there's no way to correctly
|
|
* handle this after the fb is already removed from the lookup table.
|
|
*/
|
|
DRM_DEBUG("fb=%px, fbid=%d, ref=%d\n", fb,
|
|
fb->base.id, drm_framebuffer_read_refcount(fb));
|
|
|
|
drm_framebuffer_put(fb);
|
|
|
|
return 0;
|
|
|
|
fail_unref:
|
|
drm_framebuffer_put(fb);
|
|
return -ENOENT;
|
|
}
|
|
|
|
int am_meson_mode_rmfb_ioctl(struct drm_device *dev,
|
|
void *data, struct drm_file *file_priv)
|
|
{
|
|
u32 *fb_id = data;
|
|
|
|
return am_meson_mode_rmfb(dev, *fb_id, file_priv);
|
|
}
|