629 lines
15 KiB
C
629 lines
15 KiB
C
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (c) 2019 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <linux/gfp.h>
|
||
|
#include <linux/kmemleak.h>
|
||
|
#include <drm/drmP.h>
|
||
|
#include <drm/drm_crtc_helper.h>
|
||
|
#include <drm/drm_fb_helper.h>
|
||
|
#include <drm/drm_gem.h>
|
||
|
#include <drm/drm_crtc.h>
|
||
|
#include <drm/drm_atomic_helper.h>
|
||
|
|
||
|
#include "mtk_drm_drv.h"
|
||
|
#include "mtk_drm_fb.h"
|
||
|
#include "mtk_drm_gem.h"
|
||
|
#include "mtk_drm_fbdev.h"
|
||
|
#include "mtk_drm_assert.h"
|
||
|
#include "mtk_log.h"
|
||
|
#include "mtk_drm_crtc.h"
|
||
|
#include "mtk_drm_helper.h"
|
||
|
#include "mtk_log.h"
|
||
|
#include "mtk_drm_mmp.h"
|
||
|
|
||
|
#include <linux/kref.h>
|
||
|
|
||
|
#define to_drm_private(x) container_of(x, struct mtk_drm_private, fb_helper)
|
||
|
#define ALIGN_TO_32(x) ALIGN_TO(x, 32)
|
||
|
|
||
|
struct fb_info *debug_info;
|
||
|
|
||
|
static inline int try_use_fb_buf(struct drm_device *dev)
|
||
|
{
|
||
|
struct mtk_drm_private *priv = dev->dev_private;
|
||
|
|
||
|
if (priv == NULL) {
|
||
|
DDPPR_ERR("%s: priv is NULL\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return kref_get_unless_zero(&priv->kref_fb_buf);
|
||
|
}
|
||
|
|
||
|
unsigned int mtk_drm_fb_fm_auto_test(struct fb_info *info)
|
||
|
{
|
||
|
struct drm_fb_helper *fb_helper = info->par;
|
||
|
struct drm_device *drm_dev = fb_helper->dev;
|
||
|
struct drm_crtc *crtc;
|
||
|
struct mtk_drm_crtc *mtk_crtc;
|
||
|
struct mtk_drm_private *private;
|
||
|
int ret = 0;
|
||
|
|
||
|
/* this debug cmd only for crtc0 */
|
||
|
crtc = list_first_entry(&(drm_dev)->mode_config.crtc_list,
|
||
|
typeof(*crtc), head);
|
||
|
if (!crtc) {
|
||
|
DDPPR_ERR("find crtc fail\n");
|
||
|
return -1;
|
||
|
}
|
||
|
mtk_crtc = to_mtk_crtc(crtc);
|
||
|
|
||
|
DDP_MUTEX_LOCK(&mtk_crtc->lock, __func__, __LINE__);
|
||
|
|
||
|
if (!mtk_crtc->enabled || mtk_crtc->ddp_mode == DDP_NO_USE) {
|
||
|
DDPINFO("crtc 0 is already sleep, skip\n");
|
||
|
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
private = drm_dev->dev_private;
|
||
|
if (mtk_drm_helper_get_opt(private->helper_opt,
|
||
|
MTK_DRM_OPT_IDLE_MGR)) {
|
||
|
mtk_drm_set_idlemgr(crtc, 0, 0);
|
||
|
}
|
||
|
|
||
|
ret = mtk_crtc_lcm_ATA(crtc);
|
||
|
|
||
|
if (mtk_drm_helper_get_opt(private->helper_opt,
|
||
|
MTK_DRM_OPT_IDLE_MGR))
|
||
|
mtk_drm_set_idlemgr(crtc, 1, 0);
|
||
|
|
||
|
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
|
||
|
|
||
|
if (ret == 0)
|
||
|
DDPPR_ERR("ATA LCM failed\n");
|
||
|
else
|
||
|
DDPPR_ERR("ATA LCM passed\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int mtk_drm_fb_ioctl(struct fb_info *info, unsigned int cmd,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
switch (cmd) {
|
||
|
case MTKFB_FACTORY_AUTO_TEST:
|
||
|
{
|
||
|
unsigned int result = 0;
|
||
|
void __user *argp = (void __user *)arg;
|
||
|
|
||
|
DDPMSG("factory mode: lcm auto test\n");
|
||
|
result = mtk_drm_fb_fm_auto_test(info);
|
||
|
return copy_to_user(argp, &result, sizeof(result)) ?
|
||
|
-EFAULT : 0;
|
||
|
}
|
||
|
default:
|
||
|
DDPINFO("%s: Not support:info=0x%p, cmd=0x%08x, arg=0x%08lx\n",
|
||
|
__func__, info, (unsigned int)cmd, arg);
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mtk_drm_fb_pan_display(struct fb_var_screeninfo *var,
|
||
|
struct fb_info *info)
|
||
|
{
|
||
|
struct drm_fb_helper *fb_helper = info->par;
|
||
|
struct drm_device *drm_dev = fb_helper->dev;
|
||
|
struct drm_crtc *crtc;
|
||
|
struct mtk_drm_crtc *mtk_crtc;
|
||
|
struct cmdq_pkt *cmdq_handle;
|
||
|
int ret;
|
||
|
|
||
|
ret = drm_fb_helper_pan_display(var, info);
|
||
|
|
||
|
crtc = list_first_entry(&(drm_dev)->mode_config.crtc_list,
|
||
|
typeof(*crtc), head);
|
||
|
|
||
|
if (!crtc) {
|
||
|
DDPPR_ERR("find crtc fail\n");
|
||
|
return ret;
|
||
|
}
|
||
|
mtk_crtc = to_mtk_crtc(crtc);
|
||
|
DDP_MUTEX_LOCK(&mtk_crtc->lock, __func__, __LINE__);
|
||
|
|
||
|
mtk_crtc_pkt_create(&cmdq_handle, &mtk_crtc->base,
|
||
|
mtk_crtc->gce_obj.client[CLIENT_CFG]);
|
||
|
|
||
|
if (mtk_crtc_with_sub_path(crtc, mtk_crtc->ddp_mode))
|
||
|
mtk_crtc_wait_frame_done(mtk_crtc, cmdq_handle,
|
||
|
DDP_SECOND_PATH, 0);
|
||
|
else
|
||
|
mtk_crtc_wait_frame_done(mtk_crtc, cmdq_handle,
|
||
|
DDP_FIRST_PATH, 0);
|
||
|
|
||
|
cmdq_pkt_flush(cmdq_handle);
|
||
|
cmdq_pkt_destroy(cmdq_handle);
|
||
|
|
||
|
DDP_MUTEX_UNLOCK(&mtk_crtc->lock, __func__, __LINE__);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* used when early porting, test pan display*/
|
||
|
|
||
|
void disp_get_fb_address(unsigned long *fbVirAddr)
|
||
|
{
|
||
|
*fbVirAddr = (unsigned long)debug_info->screen_base;
|
||
|
pr_info(
|
||
|
"%s fbdev->fb_va_base = 0x%p\n",
|
||
|
__func__, debug_info->screen_base);
|
||
|
}
|
||
|
|
||
|
int pan_display_test(int frame_num, int bpp)
|
||
|
{
|
||
|
int i, j;
|
||
|
int Bpp = bpp / 8;
|
||
|
unsigned char *fb_va;
|
||
|
unsigned int fb_size;
|
||
|
int w, h, fb_h;
|
||
|
int yoffset_max;
|
||
|
int yoffset;
|
||
|
|
||
|
debug_info->var.yoffset = 0;
|
||
|
disp_get_fb_address((unsigned long *)&fb_va);
|
||
|
if (!fb_va)
|
||
|
return 0;
|
||
|
|
||
|
if (!mtk_crtc_frame_buffer_existed())
|
||
|
return 0;
|
||
|
|
||
|
fb_size = debug_info->fix.smem_len;
|
||
|
w = debug_info->var.xres;
|
||
|
h = debug_info->var.yres;
|
||
|
fb_h = fb_size / (ALIGN_TO(w, 32) * Bpp) - 10;
|
||
|
|
||
|
pr_info("%s: frame_num=%d,bpp=%d, w=%d,h=%d,fb_h=%d\n",
|
||
|
__func__, frame_num, bpp, w, h, fb_h);
|
||
|
|
||
|
for (i = 0; i < fb_h; i++)
|
||
|
for (j = 0; j < w; j++) {
|
||
|
int x = (i * ALIGN_TO(w, 32) + j) * Bpp;
|
||
|
|
||
|
fb_va[x++] = (i + j) % 256;
|
||
|
fb_va[x++] = (i + j) % 256;
|
||
|
fb_va[x++] = (i + j) % 256;
|
||
|
if (Bpp == 4)
|
||
|
fb_va[x++] = 255;
|
||
|
}
|
||
|
|
||
|
debug_info->var.bits_per_pixel = bpp;
|
||
|
|
||
|
yoffset_max = fb_h - h;
|
||
|
yoffset = 0;
|
||
|
for (i = 0; i < frame_num; i++, yoffset += 10) {
|
||
|
|
||
|
if (yoffset >= yoffset_max)
|
||
|
yoffset = 0;
|
||
|
|
||
|
debug_info->var.xoffset = 0;
|
||
|
debug_info->var.yoffset = yoffset;
|
||
|
mtk_drm_fb_pan_display(&debug_info->var, debug_info);
|
||
|
}
|
||
|
|
||
|
DDPMSG("%s, %d--\n", __func__, __LINE__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define MTK_LEGACY_FB_MAP
|
||
|
#ifndef MTK_LEGACY_FB_MAP
|
||
|
|
||
|
static int mtk_drm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||
|
{
|
||
|
struct drm_fb_helper *helper = info->par;
|
||
|
struct mtk_drm_private *private = helper->dev->dev_private;
|
||
|
|
||
|
debug_info = info;
|
||
|
return mtk_drm_gem_mmap_buf(private->fbdev_bo, vma);
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
static void mtk_drm_fbdev_vm_open(struct vm_area_struct *vma)
|
||
|
{
|
||
|
struct fb_info *info = vma->vm_file->private_data;
|
||
|
struct drm_fb_helper *fb_helper = info->par;
|
||
|
struct drm_device *drm_dev = fb_helper->dev;
|
||
|
struct mtk_drm_private *priv = drm_dev->dev_private;
|
||
|
|
||
|
if (priv == NULL) {
|
||
|
DDPPR_ERR("%s: priv is NULL\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
kref_get(&priv->kref_fb_buf);
|
||
|
}
|
||
|
|
||
|
static void mtk_drm_fbdev_vm_close(struct vm_area_struct *vma)
|
||
|
{
|
||
|
struct fb_info *info = vma->vm_file->private_data;
|
||
|
struct drm_fb_helper *fb_helper = info->par;
|
||
|
struct drm_device *drm_dev = fb_helper->dev;
|
||
|
|
||
|
DDPMSG("%s: munmap done\n", __func__);
|
||
|
try_free_fb_buf(drm_dev);
|
||
|
}
|
||
|
|
||
|
static int mtk_drm_fbdev_vm_split(struct vm_area_struct *area, unsigned long addr)
|
||
|
{
|
||
|
/* don't support split */
|
||
|
DDPPR_ERR("split not support\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
static const struct vm_operations_struct mtk_drm_fbdev_vm_ops = {
|
||
|
.split = mtk_drm_fbdev_vm_split,
|
||
|
.close = mtk_drm_fbdev_vm_close,
|
||
|
.open = mtk_drm_fbdev_vm_open,
|
||
|
};
|
||
|
|
||
|
static int mtk_drm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||
|
{
|
||
|
/* copy from fbmem.c */
|
||
|
|
||
|
unsigned long mmio_pgoff;
|
||
|
unsigned long start;
|
||
|
u32 len;
|
||
|
int ret;
|
||
|
|
||
|
struct drm_fb_helper *fb_helper = info->par;
|
||
|
struct drm_device *drm_dev = fb_helper->dev;
|
||
|
|
||
|
if (!try_use_fb_buf(drm_dev)) {
|
||
|
DDPPR_ERR("%s: fb has been freed!\n", __func__);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Ugh. This can be either the frame buffer mapping, or
|
||
|
* if pgoff points past it, the mmio mapping.
|
||
|
*/
|
||
|
start = info->fix.smem_start;
|
||
|
len = info->fix.smem_len;
|
||
|
mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
|
||
|
if (vma->vm_pgoff >= mmio_pgoff) {
|
||
|
if (info->var.accel_flags) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
vma->vm_pgoff -= mmio_pgoff;
|
||
|
start = info->fix.mmio_start;
|
||
|
len = info->fix.mmio_len;
|
||
|
}
|
||
|
|
||
|
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
||
|
/*
|
||
|
* The framebuffer needs to be accessed decrypted, be sure
|
||
|
* SME protection is removed
|
||
|
*/
|
||
|
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
||
|
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||
|
|
||
|
ret = vm_iomap_memory(vma, start, len);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
/* blow is our action */
|
||
|
vma->vm_ops = &mtk_drm_fbdev_vm_ops;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static struct fb_ops mtk_fbdev_ops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.fb_fillrect = drm_fb_helper_cfb_fillrect,
|
||
|
.fb_copyarea = drm_fb_helper_cfb_copyarea,
|
||
|
.fb_imageblit = drm_fb_helper_cfb_imageblit,
|
||
|
.fb_check_var = drm_fb_helper_check_var,
|
||
|
.fb_set_par = drm_fb_helper_set_par,
|
||
|
.fb_blank = drm_fb_helper_blank,
|
||
|
.fb_pan_display = mtk_drm_fb_pan_display,
|
||
|
.fb_setcmap = drm_fb_helper_setcmap,
|
||
|
.fb_mmap = mtk_drm_fbdev_mmap,
|
||
|
.fb_ioctl = mtk_drm_fb_ioctl,
|
||
|
};
|
||
|
|
||
|
bool mtk_drm_lcm_is_connect(void)
|
||
|
{
|
||
|
struct device_node *chosen_node;
|
||
|
|
||
|
chosen_node = of_find_node_by_path("/chosen");
|
||
|
if (chosen_node) {
|
||
|
struct tag_videolfb *videolfb_tag = NULL;
|
||
|
unsigned long size = 0;
|
||
|
|
||
|
videolfb_tag = (struct tag_videolfb *)of_get_property(
|
||
|
chosen_node,
|
||
|
"atag,videolfb", (int *)&size);
|
||
|
if (videolfb_tag)
|
||
|
return videolfb_tag->islcmfound;
|
||
|
|
||
|
DDPINFO("[DT][videolfb] videolfb_tag not found\n");
|
||
|
} else {
|
||
|
DDPINFO("[DT][videolfb] of_chosen not found\n");
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int _parse_tag_videolfb(unsigned int *vramsize, phys_addr_t *fb_base,
|
||
|
unsigned int *fps)
|
||
|
{
|
||
|
#ifdef CONFIG_MTK_DISP_NO_LK
|
||
|
return -1;
|
||
|
#else
|
||
|
struct device_node *chosen_node;
|
||
|
|
||
|
*fps = 6000;
|
||
|
chosen_node = of_find_node_by_path("/chosen");
|
||
|
if (chosen_node) {
|
||
|
struct tag_videolfb *videolfb_tag = NULL;
|
||
|
unsigned long size = 0;
|
||
|
|
||
|
videolfb_tag = (struct tag_videolfb *)of_get_property(
|
||
|
chosen_node, "atag,videolfb", (int *)&size);
|
||
|
if (videolfb_tag) {
|
||
|
*vramsize = videolfb_tag->vram;
|
||
|
*fb_base = videolfb_tag->fb_base;
|
||
|
*fps = videolfb_tag->fps;
|
||
|
if (*fps == 0)
|
||
|
*fps = 6000;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DDPINFO("[DT][videolfb] videolfb_tag not found\n");
|
||
|
goto found;
|
||
|
} else {
|
||
|
DDPINFO("[DT][videolfb] of_chosen not found\n");
|
||
|
}
|
||
|
return -1;
|
||
|
|
||
|
found:
|
||
|
DDPINFO("[DT][videolfb] fb_base = 0x%lx\n", (unsigned long)*fb_base);
|
||
|
DDPINFO("[DT][videolfb] vram = 0x%x (%d)\n", *vramsize,
|
||
|
*vramsize);
|
||
|
DDPINFO("[DT][videolfb] fps = %d\n", *fps);
|
||
|
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void free_fb_buf(struct kref *kref)
|
||
|
{
|
||
|
unsigned long va_start = 0;
|
||
|
unsigned long va_end = 0;
|
||
|
phys_addr_t fb_base;
|
||
|
unsigned int vramsize, fps;
|
||
|
|
||
|
_parse_tag_videolfb(&vramsize, &fb_base, &fps);
|
||
|
|
||
|
if (!fb_base) {
|
||
|
DDPPR_ERR("%s:get fb pa error\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
va_start = (unsigned long)__va(fb_base);
|
||
|
va_end = (unsigned long)__va(fb_base + (unsigned long)vramsize);
|
||
|
if (va_start)
|
||
|
free_reserved_area((void *)va_start,
|
||
|
(void *)va_end, 0xff, "fbmem");
|
||
|
else
|
||
|
DDPINFO("%s:va invalid\n", __func__);
|
||
|
}
|
||
|
|
||
|
int try_free_fb_buf(struct drm_device *dev)
|
||
|
{
|
||
|
struct mtk_drm_private *priv = dev->dev_private;
|
||
|
|
||
|
DDPFUNC();
|
||
|
|
||
|
if (priv == NULL) {
|
||
|
DDPPR_ERR("%s: priv is NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
return kref_put(&priv->kref_fb_buf, free_fb_buf);
|
||
|
}
|
||
|
|
||
|
static int mtk_fbdev_probe(struct drm_fb_helper *helper,
|
||
|
struct drm_fb_helper_surface_size *sizes)
|
||
|
{
|
||
|
struct drm_device *dev = helper->dev;
|
||
|
struct mtk_drm_private *private = helper->dev->dev_private;
|
||
|
struct drm_mode_fb_cmd2 mode = {0};
|
||
|
struct mtk_drm_gem_obj *mtk_gem;
|
||
|
struct fb_info *info;
|
||
|
struct drm_framebuffer *fb;
|
||
|
unsigned int bytes_per_pixel, vramsize = 0, fps = 0;
|
||
|
size_t size;
|
||
|
int err;
|
||
|
phys_addr_t fb_base = 0;
|
||
|
|
||
|
DDPMSG("%s+\n", __func__);
|
||
|
bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
|
||
|
mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||
|
sizes->surface_depth);
|
||
|
|
||
|
if (_parse_tag_videolfb(&vramsize, &fb_base, &fps) < 0) {
|
||
|
DDPINFO("[DT][videolfb] fb_base = 0x%lx\n",
|
||
|
(unsigned long)fb_base);
|
||
|
DDPINFO("[DT][videolfb] vram = 0x%x (%d)\n",
|
||
|
vramsize, vramsize);
|
||
|
DDPINFO("[DT][videolfb] fps = %d\n", fps);
|
||
|
|
||
|
mode.width = sizes->surface_width;
|
||
|
mode.height = sizes->surface_height;
|
||
|
mode.pitches[0] = sizes->surface_width * bytes_per_pixel;
|
||
|
size = mode.pitches[0] * mode.height;
|
||
|
mtk_gem = mtk_drm_gem_create(dev, size, true);
|
||
|
if (IS_ERR(mtk_gem))
|
||
|
return PTR_ERR(mtk_gem);
|
||
|
} else {
|
||
|
mode.width = ALIGN_TO_32(sizes->surface_width);
|
||
|
/* LK pre-allocate triple buffer */
|
||
|
mode.height = ALIGN_TO_32(sizes->surface_height) * 3;
|
||
|
mode.pitches[0] =
|
||
|
ALIGN_TO_32(sizes->surface_width) * bytes_per_pixel;
|
||
|
size = mode.pitches[0] * mode.height;
|
||
|
mtk_gem = mtk_drm_fb_gem_insert(dev, size, fb_base, vramsize);
|
||
|
if (IS_ERR(mtk_gem))
|
||
|
return PTR_ERR(mtk_gem);
|
||
|
kmemleak_ignore(mtk_gem);
|
||
|
}
|
||
|
|
||
|
private->fbdev_bo = &mtk_gem->base;
|
||
|
|
||
|
info = drm_fb_helper_alloc_fbi(helper);
|
||
|
if (IS_ERR(info)) {
|
||
|
err = PTR_ERR(info);
|
||
|
dev_err(dev->dev, "failed to allocate framebuffer info, %d\n",
|
||
|
err);
|
||
|
goto err_gem_free_object;
|
||
|
}
|
||
|
|
||
|
fb = mtk_drm_framebuffer_create(dev, &mode, private->fbdev_bo);
|
||
|
if (IS_ERR(fb)) {
|
||
|
err = PTR_ERR(fb);
|
||
|
dev_err(dev->dev, "failed to allocate DRM framebuffer, %d\n",
|
||
|
err);
|
||
|
goto err_release_fbi;
|
||
|
}
|
||
|
helper->fb = fb;
|
||
|
|
||
|
info->par = helper;
|
||
|
info->flags = FBINFO_FLAG_DEFAULT;
|
||
|
info->fbops = &mtk_fbdev_ops;
|
||
|
|
||
|
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
|
||
|
drm_fb_helper_fill_var(info, helper, sizes->fb_width, sizes->fb_height);
|
||
|
|
||
|
dev->mode_config.fb_base = fb_base;
|
||
|
// not need to read and write for fbdev
|
||
|
info->screen_base = NULL;//mtk_gem->kvaddr;
|
||
|
info->screen_size = 0;//size;
|
||
|
info->fix.smem_len = size;
|
||
|
info->fix.smem_start = fb_base;
|
||
|
debug_info = info;
|
||
|
|
||
|
#if !defined(CONFIG_DRM_MTK_DISABLE_AEE_LAYER)
|
||
|
mtk_drm_assert_fb_init(dev,
|
||
|
sizes->surface_width, sizes->surface_height);
|
||
|
#endif
|
||
|
|
||
|
DRM_DEBUG_KMS("FB [%ux%u]-%u size=%zd\n", fb->width,
|
||
|
fb->height, fb->format->depth, size);
|
||
|
|
||
|
info->skip_vt_switch = true;
|
||
|
|
||
|
DDPMSG("%s-\n", __func__);
|
||
|
return 0;
|
||
|
|
||
|
err_release_fbi:
|
||
|
err_gem_free_object:
|
||
|
mtk_drm_gem_free_object(&mtk_gem->base);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static const struct drm_fb_helper_funcs mtk_drm_fb_helper_funcs = {
|
||
|
.fb_probe = mtk_fbdev_probe,
|
||
|
};
|
||
|
|
||
|
static int mtk_drm_fb_add_one_connector(struct drm_device *dev,
|
||
|
struct drm_fb_helper *helper)
|
||
|
{
|
||
|
struct drm_connector *connector;
|
||
|
struct drm_connector_list_iter conn_iter;
|
||
|
struct drm_encoder *encoder;
|
||
|
const struct drm_connector_helper_funcs *helper_private;
|
||
|
int ret = 0;
|
||
|
|
||
|
drm_connector_list_iter_begin(dev, &conn_iter);
|
||
|
drm_for_each_connector_iter(connector, &conn_iter) {
|
||
|
helper_private = connector->helper_private;
|
||
|
if (helper_private->best_encoder)
|
||
|
encoder = helper_private->best_encoder(connector);
|
||
|
else
|
||
|
encoder = drm_atomic_helper_best_encoder(connector);
|
||
|
if (encoder && (encoder->possible_crtcs & 0x1)) {
|
||
|
ret = drm_fb_helper_add_one_connector(
|
||
|
helper, connector);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
drm_connector_list_iter_end(&conn_iter);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int mtk_fbdev_init(struct drm_device *dev)
|
||
|
{
|
||
|
struct mtk_drm_private *priv = dev->dev_private;
|
||
|
struct drm_fb_helper *helper = &priv->fb_helper;
|
||
|
int ret;
|
||
|
|
||
|
DDPMSG("%s+\n", __func__);
|
||
|
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector
|
||
|
|| (priv == NULL))
|
||
|
return -EINVAL;
|
||
|
|
||
|
kref_init(&priv->kref_fb_buf);
|
||
|
|
||
|
drm_fb_helper_prepare(dev, helper, &mtk_drm_fb_helper_funcs);
|
||
|
|
||
|
ret = drm_fb_helper_init(dev, helper, 1);
|
||
|
if (ret) {
|
||
|
dev_err(dev->dev, "failed to initialize DRM FB helper, %d\n",
|
||
|
ret);
|
||
|
goto fini;
|
||
|
}
|
||
|
|
||
|
ret = mtk_drm_fb_add_one_connector(dev, helper);
|
||
|
if (ret) {
|
||
|
dev_err(dev->dev, "failed to add connectors, %d\n", ret);
|
||
|
goto fini;
|
||
|
}
|
||
|
|
||
|
ret = drm_fb_helper_initial_config(helper, 32);
|
||
|
if (ret) {
|
||
|
dev_err(dev->dev, "failed to set initial configuration, %d\n",
|
||
|
ret);
|
||
|
goto fini;
|
||
|
}
|
||
|
DDPMSG("%s-\n", __func__);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
fini:
|
||
|
drm_fb_helper_fini(helper);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void mtk_fbdev_fini(struct drm_device *dev)
|
||
|
{
|
||
|
struct mtk_drm_private *priv = dev->dev_private;
|
||
|
struct drm_fb_helper *helper = &priv->fb_helper;
|
||
|
|
||
|
drm_fb_helper_unregister_fbi(helper);
|
||
|
|
||
|
if (helper->fb) {
|
||
|
drm_framebuffer_unregister_private(helper->fb);
|
||
|
drm_framebuffer_remove(helper->fb);
|
||
|
}
|
||
|
|
||
|
drm_fb_helper_fini(helper);
|
||
|
}
|