From: CK Hu <ck.hu@xxxxxxxxxxxx> Add Mediatek legacy framebuffer support. Signed-off-by: CK Hu <ck.hu@xxxxxxxxxxxx> Signed-off-by: YT Shen <yt.shen@xxxxxxxxxxxx> --- drivers/gpu/drm/mediatek/Kconfig | 12 +++ drivers/gpu/drm/mediatek/mtk_drm_drv.c | 13 +++ drivers/gpu/drm/mediatek/mtk_drm_fb.c | 192 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_fb.h | 1 + 4 files changed, 218 insertions(+) diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig index 5343cf1..fa581fb 100644 --- a/drivers/gpu/drm/mediatek/Kconfig +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -14,3 +14,15 @@ config DRM_MEDIATEK This driver provides kernel mode setting and buffer management to userspace. +config DRM_MEDIATEK_FBDEV + bool "Enable legacy fbdev support for Mediatek DRM" + depends on DRM_MEDIATEK + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_FB_HELPER + help + Choose this option if you have a need for the legacy + fbdev support. Note that this support also provides + the Linux console on top of the Mediatek DRM mode + setting driver. diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index fc071fe..b67c582 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -46,6 +46,9 @@ static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = { .fb_create = mtk_drm_mode_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = mtk_atomic_commit, +#ifdef CONFIG_DRM_MEDIATEK_FBDEV + .output_poll_changed = mtk_drm_mode_output_poll_changed, +#endif }; static const enum mtk_ddp_comp_type mtk_ddp_main[] = { @@ -140,6 +143,12 @@ static int mtk_drm_kms_init(struct drm_device *dev) drm_kms_helper_poll_init(dev); drm_mode_config_reset(dev); +#ifdef CONFIG_DRM_MEDIATEK_FBDEV + err = mtk_fbdev_create(dev); + if (err) + goto err_larb_get; +#endif + return 0; err_larb_get: @@ -160,6 +169,10 @@ static void mtk_drm_kms_deinit(struct drm_device *dev) { drm_kms_helper_poll_fini(dev); +#ifdef CONFIG_DRM_MEDIATEK_FBDEV + mtk_fbdev_destroy(dev); +#endif + drm_vblank_cleanup(dev); drm_mode_config_cleanup(dev); } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c index dfa931b..9295ad3 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_fb.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -102,6 +102,198 @@ static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev, return mtk_fb; } +#ifdef CONFIG_DRM_MEDIATEK_FBDEV +static int mtk_drm_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct drm_fb_helper *helper = info->par; + struct mtk_drm_fb *mtk_fb = to_mtk_fb(helper->fb); + + return mtk_drm_gem_mmap_buf(mtk_fb->gem_obj[0], vma); +} + +static struct fb_ops mtk_fb_ops = { + .owner = THIS_MODULE, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_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 = drm_fb_helper_pan_display, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_mmap = mtk_drm_fb_mmap, +}; + +static int mtk_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *dev = helper->dev; + struct drm_mode_fb_cmd2 mode = { 0 }; + struct mtk_drm_fb *mtk_fb; + struct mtk_drm_gem_obj *mtk_gem; + struct drm_gem_object *gem; + struct fb_info *info; + struct drm_framebuffer *fb; + unsigned long offset; + size_t size; + int err; + + mode.width = sizes->surface_width; + mode.height = sizes->surface_height; + mode.pitches[0] = sizes->surface_width * ((sizes->surface_bpp + 7) / 8); + mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + mode.height = mode.height;/* << 1; for fb use? */ + size = mode.pitches[0] * mode.height; + dev_info(dev->dev, "mtk_fbdev_probe %dx%d bpp %d pitch %d size %zu\n", + mode.width, mode.height, sizes->surface_bpp, mode.pitches[0], + size); + + mtk_gem = mtk_drm_gem_create(dev, size, true); + if (IS_ERR(mtk_gem)) { + err = PTR_ERR(mtk_gem); + goto fini; + } + + gem = &mtk_gem->base; + + mtk_fb = mtk_drm_framebuffer_init(dev, &mode, &gem); + if (IS_ERR(mtk_fb)) { + dev_err(dev->dev, "failed to allocate DRM framebuffer\n"); + err = PTR_ERR(mtk_fb); + goto free; + } + fb = &mtk_fb->base; + + info = framebuffer_alloc(0, dev->dev); + if (!info) { + dev_err(dev->dev, "failed to allocate framebuffer info\n"); + err = PTR_ERR(info); + goto release; + } + + helper->fb = fb; + helper->fbdev = info; + + info->par = helper; + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &mtk_fb_ops; + + err = fb_alloc_cmap(&info->cmap, 256, 0); + if (err < 0) { + dev_err(dev->dev, "failed to allocate color map: %d\n", err); + goto destroy; + } + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, helper, fb->width, fb->height); + + offset = info->var.xoffset * (fb->bits_per_pixel + 7) / 8; + offset += info->var.yoffset * fb->pitches[0]; + + strcpy(info->fix.id, "mtk"); + /* dev->mode_config.fb_base = (resource_size_t)bo->paddr; */ + info->var.yres = info->var.yres_virtual;/* >> 1; for fb use? */ + info->fix.smem_start = mtk_gem->dma_addr + offset; + info->fix.smem_len = size; + info->screen_base = mtk_gem->kvaddr + offset; + info->screen_size = size; + + return 0; + +destroy: + drm_framebuffer_unregister_private(fb); + mtk_drm_fb_destroy(fb); +release: + framebuffer_release(info); +free: + mtk_drm_gem_free_object(&mtk_gem->base); +fini: + dev_err(dev->dev, "mtk_fbdev_probe fail\n"); + return err; +} + +static const struct drm_fb_helper_funcs mtk_drm_fb_helper_funcs = { + .fb_probe = mtk_fbdev_probe, +}; + +int mtk_fbdev_create(struct drm_device *dev) +{ + struct mtk_drm_private *priv = dev->dev_private; + struct drm_fb_helper *fbdev; + int ret; + + fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) + return -ENOMEM; + + drm_fb_helper_prepare(dev, fbdev, &mtk_drm_fb_helper_funcs); + + ret = drm_fb_helper_init(dev, fbdev, dev->mode_config.num_crtc, + dev->mode_config.num_connector); + if (ret) { + dev_err(dev->dev, "failed to initialize DRM FB helper\n"); + goto fini; + } + + ret = drm_fb_helper_single_add_all_connectors(fbdev); + if (ret) { + dev_err(dev->dev, "failed to add connectors\n"); + goto fini; + } + + ret = drm_fb_helper_initial_config(fbdev, FBDEV_BPP); + if (ret) { + dev_err(dev->dev, "failed to set initial configuration\n"); + goto fini; + } + priv->fb_helper = fbdev; + + return 0; + +fini: + drm_fb_helper_fini(fbdev); + + return ret; +} + +void mtk_fbdev_destroy(struct drm_device *dev) +{ + struct mtk_drm_private *priv = dev->dev_private; + struct drm_fb_helper *helper = priv->fb_helper; + struct fb_info *info = helper->fbdev; + + if (info) { + int err; + + err = unregister_framebuffer(info); + if (err < 0) + DRM_DEBUG_KMS("failed to unregister framebuffer\n"); + + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + + framebuffer_release(info); + } + + if (helper->fb) { + drm_framebuffer_unregister_private(helper->fb); + mtk_drm_fb_destroy(helper->fb); + } + + drm_fb_helper_fini(helper); +} + +void mtk_drm_mode_output_poll_changed(struct drm_device *dev) +{ + struct mtk_drm_private *priv = dev->dev_private; + + if (priv->fb_helper) + drm_fb_helper_hotplug_event(priv->fb_helper); +} +#endif + struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, struct drm_file *file, struct drm_mode_fb_cmd2 *cmd) diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h index 9ce7307..acbfecf 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_fb.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h @@ -15,6 +15,7 @@ #define MTK_DRM_FB_H #define MAX_FB_OBJ 3 +#define FBDEV_BPP 16 struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb, unsigned int plane); -- 2.5.1 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html