From patchwork Wed May 13 15:23:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?Q0sgSHUgKOiDoeS/iuWFiSk=?= X-Patchwork-Id: 6398411 Return-Path: X-Original-To: patchwork-linux-mediatek@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 3115B9F374 for ; Wed, 13 May 2015 16:06:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2925D2044C for ; Wed, 13 May 2015 16:06:40 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D610D20108 for ; Wed, 13 May 2015 16:06:33 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YsZB0-0003E9-HV; Wed, 13 May 2015 16:06:30 +0000 Received: from [210.61.82.183] (helo=mailgw01.mediatek.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YsYWH-00060Z-53; Wed, 13 May 2015 15:24:43 +0000 X-Listener-Flag: 11101 Received: from mtkhts09.mediatek.inc [(172.21.101.70)] by mailgw01.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 405277825; Wed, 13 May 2015 23:23:51 +0800 Received: from mtkslt303.mediatek.inc (10.21.14.116) by mtkhts09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 14.3.181.6; Wed, 13 May 2015 23:23:49 +0800 From: CK Hu To: Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , David Airlie , Matthias Brugger Subject: [RFC][PATCH 2/2] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173. Date: Wed, 13 May 2015 23:23:46 +0800 Message-ID: <1431530626-31493-3-git-send-email-ck.hu@mediatek.com> X-Mailer: git-send-email 1.8.1.1.dirty In-Reply-To: <1431530626-31493-1-git-send-email-ck.hu@mediatek.com> References: <1431530626-31493-1-git-send-email-ck.hu@mediatek.com> MIME-Version: 1.0 X-MTK: N X-Spam-Note: CRM114 run bypassed due to message size (118893 bytes) X-Spam-Score: 1.3 (+) Cc: devicetree@vger.kernel.org, Jitao Shi , srv_heupstream@mediatek.com, Graeme Gregory , Rob Herring , Catalin Marinas , Will Deacon , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Cawa Cheng , YT Shen , CK Hu , Ashwin Chaugule , linux-mediatek@lists.infradead.org, Sascha Hauer , linux-api@vger.kernel.org, Grant Likely , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+patchwork-linux-mediatek=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch is a DRM Driver for Mediatek SoC MT8173. Now support one crtc with MIPI DSI interface. We used GEM framework for buffer management and use iommu for physically non-continuous memory. Signed-off-by: CK Hu --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mediatek/Kconfig | 28 + drivers/gpu/drm/mediatek/Makefile | 13 + drivers/gpu/drm/mediatek/mediatek_drm_crtc.c | 246 ++++ drivers/gpu/drm/mediatek/mediatek_drm_crtc.h | 80 ++ drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c | 420 +++++++ drivers/gpu/drm/mediatek/mediatek_drm_ddp.c | 202 ++++ drivers/gpu/drm/mediatek/mediatek_drm_ddp.h | 23 + drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c | 346 ++++++ drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h | 33 + drivers/gpu/drm/mediatek/mediatek_drm_drv.c | 369 ++++++ drivers/gpu/drm/mediatek/mediatek_drm_drv.h | 37 + drivers/gpu/drm/mediatek/mediatek_drm_dsi.c | 1333 +++++++++++++++++++++ drivers/gpu/drm/mediatek/mediatek_drm_dsi.h | 71 ++ drivers/gpu/drm/mediatek/mediatek_drm_fb.c | 339 ++++++ drivers/gpu/drm/mediatek/mediatek_drm_fb.h | 43 + drivers/gpu/drm/mediatek/mediatek_drm_gem.c | 315 +++++ drivers/gpu/drm/mediatek/mediatek_drm_gem.h | 94 ++ include/uapi/drm/mediatek_drm.h | 59 + 20 files changed, 4054 insertions(+) create mode 100644 drivers/gpu/drm/mediatek/Kconfig create mode 100644 drivers/gpu/drm/mediatek/Makefile create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_crtc.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_crtc.h create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp.h create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_drv.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_drv.h create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_dsi.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_dsi.h create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_fb.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_fb.h create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_gem.c create mode 100644 drivers/gpu/drm/mediatek/mediatek_drm_gem.h create mode 100644 include/uapi/drm/mediatek_drm.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 47f2ce8..441be2d 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -217,3 +217,5 @@ source "drivers/gpu/drm/sti/Kconfig" source "drivers/gpu/drm/amd/amdkfd/Kconfig" source "drivers/gpu/drm/imx/Kconfig" + +source "drivers/gpu/drm/mediatek/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7d4944e..55fe66c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ obj-$(CONFIG_DRM_IMX) += imx/ +obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig new file mode 100644 index 0000000..fa581fb --- /dev/null +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -0,0 +1,28 @@ +config DRM_MEDIATEK + tristate "DRM Support for Mediatek SoCs" + depends on DRM + depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST) + select MTK_SMI + select DRM_PANEL + select DRM_MIPI_DSI + select DRM_PANEL_SIMPLE + select DRM_KMS_HELPER + select IOMMU_DMA + help + Choose this option if you have a Mediatek SoCs. + The module will be called mediatek-drm + 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/Makefile b/drivers/gpu/drm/mediatek/Makefile new file mode 100644 index 0000000..a566a83 --- /dev/null +++ b/drivers/gpu/drm/mediatek/Makefile @@ -0,0 +1,13 @@ +mediatek-drm-objs := mediatek_drm_drv.o \ + mediatek_drm_crtc.o \ + mediatek_drm_fb.o \ + mediatek_drm_gem.o \ + mediatek_drm_dsi.o \ + mediatek_drm_ddp.o \ + mediatek_drm_ddp_comp.o \ + mediatek_drm_crtc_main.o + +obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o + +ccflags-y += \ + -Idrivers/gpu/drm diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_crtc.c b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.c new file mode 100644 index 0000000..e1437c6 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "mediatek_drm_drv.h" +#include "mediatek_drm_crtc.h" +#include "mediatek_drm_fb.h" +#include "mediatek_drm_gem.h" + + +void mtk_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) +{ + struct drm_device *dev = mtk_crtc->base.dev; + + drm_send_vblank_event(dev, mtk_crtc->event->pipe, mtk_crtc->event); + drm_crtc_vblank_put(&mtk_crtc->base); + mtk_crtc->event = NULL; +} + +static void mediatek_drm_crtc_pending_ovl_config(struct mtk_drm_crtc *mtk_crtc, + bool enable, unsigned int addr) +{ + if (mtk_crtc->ops && mtk_crtc->ops->ovl_layer_config) + mtk_crtc->ops->ovl_layer_config(mtk_crtc, enable, addr); +} + +static void mediatek_drm_crtc_pending_ovl_cursor_config( + struct mtk_drm_crtc *mtk_crtc, + bool enable, unsigned int addr) +{ + if (mtk_crtc->ops && mtk_crtc->ops->ovl_layer_config_cursor) + mtk_crtc->ops->ovl_layer_config_cursor(mtk_crtc, enable, addr); +} + +static int mtk_drm_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); + struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); + struct drm_device *dev = crtc->dev; + unsigned long flags; + bool busy; + int ret; + + spin_lock_irqsave(&dev->event_lock, flags); + busy = !!mtk_crtc->event; + if (!busy) + mtk_crtc->event = event; + spin_unlock_irqrestore(&dev->event_lock, flags); + if (busy) + return -EBUSY; + + if (fb->width != crtc->mode.hdisplay || + fb->height != crtc->mode.vdisplay) { + DRM_ERROR("mtk_drm_crtc_page_flip width/height not match !!\n"); + return -EINVAL; + } + + if (event) { + ret = drm_crtc_vblank_get(crtc); + if (ret) { + DRM_ERROR("failed to acquire vblank events\n"); + return ret; + } + } + + /* + * the values related to a buffer of the drm framebuffer + * to be applied should be set at here. because these values + * first, are set to shadow registers and then to + * real registers at vsync front porch period. + */ + crtc->primary->fb = fb; + mtk_crtc->flip_buffer = to_mtk_gem_obj(mtk_fb->gem_obj[0])->buffer; + + mediatek_drm_crtc_pending_ovl_config(mtk_crtc, true, + mtk_crtc->flip_buffer->mva_addr); + + spin_lock_irqsave(&dev->event_lock, flags); + if (mtk_crtc->event) + mtk_crtc->pending_needs_vblank = true; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return ret; +} + +static void mtk_drm_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); +} + +static void mtk_drm_crtc_prepare(struct drm_crtc *crtc) +{ + /* drm framework doesn't check NULL. */ +} + +static void mtk_drm_crtc_commit(struct drm_crtc *crtc) +{ + /* + * when set_crtc is requested from user or at booting time, + * crtc->commit would be called without dpms call so if dpms is + * no power on then crtc->dpms should be called + * with DRM_MODE_DPMS_ON for the hardware power to be on. + */ +} + +static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* drm framework doesn't check NULL */ + return true; +} + +static int mtk_drm_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); + struct drm_framebuffer *fb; + struct mtk_drm_fb *mtk_fb; + struct mtk_drm_gem_buf *buffer; + + fb = crtc->primary->fb; + mtk_fb = to_mtk_fb(fb); + + buffer = to_mtk_gem_obj(mtk_fb->gem_obj[0])->buffer; + + mediatek_drm_crtc_pending_ovl_config(mtk_crtc, true, + buffer->mva_addr); + /* + * copy the mode data adjusted by mode_fixup() into crtc->mode + * so that hardware can be seet to proper mode. + */ + memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); + + /* Take a reference to the new fb as we're using it */ + drm_framebuffer_reference(crtc->primary->fb); + + return 0; +} + +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe) +{ + struct mtk_drm_private *priv = + (struct mtk_drm_private *)drm->dev_private; + struct mtk_drm_crtc *mtk_crtc; + + if (pipe >= MAX_CRTC || pipe < 0) { + DRM_ERROR(" - %s: invalid crtc (%d)\n", __func__, pipe); + return -EINVAL; + } + + mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + + if (mtk_crtc->ops->enable_vblank) + mtk_crtc->ops->enable_vblank(mtk_crtc); + + return 0; +} + +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe) +{ + struct mtk_drm_private *priv = + (struct mtk_drm_private *)drm->dev_private; + struct mtk_drm_crtc *mtk_crtc; + + if (pipe >= MAX_CRTC || pipe < 0) + DRM_ERROR(" - %s: invalid crtc (%d)\n", __func__, pipe); + + mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + if (mtk_crtc->ops->disable_vblank) + mtk_crtc->ops->disable_vblank(mtk_crtc); +} + +static void mtk_drm_crtc_disable(struct drm_crtc *crtc) +{ + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); + + DRM_INFO("mtk_drm_crtc_disable %d\n", crtc->base.id); + + mediatek_drm_crtc_pending_ovl_config(mtk_crtc, false, 0); + mediatek_drm_crtc_pending_ovl_cursor_config(mtk_crtc, false, 0); +} + +static struct drm_crtc_funcs mediatek_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .page_flip = mtk_drm_crtc_page_flip, + .destroy = mtk_drm_crtc_destroy, +}; + +static struct drm_crtc_helper_funcs mediatek_crtc_helper_funcs = { + .prepare = mtk_drm_crtc_prepare, + .commit = mtk_drm_crtc_commit, + .mode_fixup = mtk_drm_crtc_mode_fixup, + .mode_set = mtk_drm_crtc_mode_set, + .disable = mtk_drm_crtc_disable, +}; + +struct mtk_drm_crtc *mtk_drm_crtc_create( + struct drm_device *drm_dev, int pipe, + struct mediatek_drm_crtc_ops *ops, + void *ctx) +{ + struct mtk_drm_private *priv = + (struct mtk_drm_private *)drm_dev->dev_private; + struct mtk_drm_crtc *mtk_crtc; + + mtk_crtc = devm_kzalloc(drm_dev->dev, sizeof(*mtk_crtc), GFP_KERNEL); + if (!mtk_crtc) { + DRM_ERROR("failed to allocate mtk crtc\n"); + return ERR_PTR(-ENOMEM); + } + + mtk_crtc->pipe = pipe; + mtk_crtc->ops = ops; + mtk_crtc->ctx = ctx; + + priv->crtc[pipe] = &mtk_crtc->base; + + drm_crtc_init(drm_dev, &mtk_crtc->base, &mediatek_crtc_funcs); + drm_crtc_helper_add(&mtk_crtc->base, &mediatek_crtc_helper_funcs); + + return mtk_crtc; +} + + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_crtc.h b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.h new file mode 100644 index 0000000..1732927 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_crtc.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#ifndef _MEDIATEL_DRM_CRTC_H_ +#define _MEDIATEL_DRM_CRTC_H_ + +#include "mediatek_drm_ddp.h" + + +#define MAX_FB_BUFFER 4 +#define DEFAULT_ZPOS -1 + +struct mtk_drm_crtc; +struct mediatek_drm_crtc_ops { + void (*dpms)(struct mtk_drm_crtc *crtc, int mode); + int (*enable_vblank)(struct mtk_drm_crtc *crtc); + void (*disable_vblank)(struct mtk_drm_crtc *crtc); + void (*ovl_layer_config)(struct mtk_drm_crtc *crtc, + bool enable, unsigned int addr); + void (*ovl_layer_config_cursor)(struct mtk_drm_crtc *crtc, + bool enable, unsigned int addr); +}; + +/* + * MediaTek specific crtc structure. + * + * @base: crtc object. + * @pipe: a crtc index created at load() with a new crtc object creation + * and the crtc object would be set to private->crtc array + * to get a crtc object corresponding to this pipe from private->crtc + * array when irq interrupt occurred. the reason of using this pipe is that + * drm framework doesn't support multiple irq yet. + * we can refer to the crtc to current hardware interrupt occurred through + * this pipe value. + */ +struct mtk_drm_crtc { + struct drm_crtc base; + + unsigned int pipe; + struct drm_pending_vblank_event *event; + struct mtk_drm_gem_buf *flip_buffer; + struct mediatek_drm_crtc_ops *ops; + void *ctx; + bool pending_needs_vblank; + + bool pending_ovl_config; + bool pending_ovl_enabled; + unsigned int pending_ovl_addr; + unsigned int pending_ovl_width; + unsigned int pending_ovl_height; + unsigned int pending_ovl_pitch; + unsigned int pending_ovl_format; + +}; + +#define to_mtk_crtc(x) container_of(x, struct mtk_drm_crtc, base) + +struct mtk_drm_crtc *mtk_drm_crtc_create( + struct drm_device *drm_dev, int pipe, + struct mediatek_drm_crtc_ops *ops, + void *ctx); +void mtk_drm_crtc_irq(struct mtk_drm_crtc *mtk_crtc); + +void mtk_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc); +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe); +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe); + +#endif + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c b/drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c new file mode 100644 index 0000000..4d16620 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_crtc_main.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Authors: + * YT Shen + * CK Hu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mediatek_drm_drv.h" +#include "mediatek_drm_crtc.h" +#include "mediatek_drm_gem.h" +#include "mediatek_drm_ddp_comp.h" + + +struct crtc_main_context { + struct device *dev; + struct drm_device *drm_dev; + struct mtk_drm_crtc *crtc; + int pipe; + + struct device *ddp_dev; + struct clk *ovl0_disp_clk; + struct clk *rdma0_disp_clk; + struct clk *color0_disp_clk; + struct clk *aal_disp_clk; + struct clk *ufoe_disp_clk; + struct clk *od_disp_clk; + + void __iomem *ovl0_regs; + void __iomem *rdma0_regs; + void __iomem *color0_regs; + void __iomem *aal_regs; + void __iomem *ufoe_regs; + void __iomem *od_regs; + + bool pending_ovl_config; + bool pending_ovl_enable; + unsigned int pending_ovl_addr; + unsigned int pending_ovl_width; + unsigned int pending_ovl_height; + unsigned int pending_ovl_pitch; + unsigned int pending_ovl_format; +}; + + +static int crtc_main_ctx_initialize(struct crtc_main_context *ctx, + struct drm_device *drm_dev) +{ + struct mtk_drm_private *priv; + + priv = drm_dev->dev_private; + ctx->drm_dev = drm_dev; + ctx->pipe = priv->pipe++; + + return 0; +} + +static void crtc_main_ctx_remove(struct crtc_main_context *ctx) +{ +} + +static void crtc_main_power_on(struct crtc_main_context *ctx) +{ + int ret; + + ret = clk_prepare_enable(ctx->ovl0_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(ctx->ovl0_disp_clk) error!\n"); + + ret = clk_prepare_enable(ctx->rdma0_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(ctx->rdma0_disp_clk) error!\n"); + + ret = clk_prepare_enable(ctx->color0_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(ctx->color0_disp_clk) error!\n"); + + ret = clk_prepare_enable(ctx->aal_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(ctx->aal_disp_clk) error!\n"); + + ret = clk_prepare_enable(ctx->ufoe_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(ctx->ufoe_disp_clk) error!\n"); + + ret = clk_prepare_enable(ctx->od_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(ctx->od_disp_clk) error!\n"); +} + +static void crtc_main_power_off(struct crtc_main_context *ctx) +{ + clk_disable_unprepare(ctx->ovl0_disp_clk); + + clk_disable_unprepare(ctx->rdma0_disp_clk); + + clk_disable_unprepare(ctx->color0_disp_clk); + + clk_disable_unprepare(ctx->aal_disp_clk); + + clk_disable_unprepare(ctx->ufoe_disp_clk); + + clk_disable_unprepare(ctx->od_disp_clk); +} + +static void crtc_main_dpms(struct mtk_drm_crtc *crtc, int mode) +{ + /* DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); */ + + switch (mode) { + case DRM_MODE_DPMS_ON: + crtc_main_power_on(crtc->ctx); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + crtc_main_power_off(crtc->ctx); + break; + default: + DRM_DEBUG_KMS("unspecified mode %d\n", mode); + break; + } +} + +static int crtc_main_enable_vblank(struct mtk_drm_crtc *crtc) +{ + struct crtc_main_context *ctx = (struct crtc_main_context *)crtc->ctx; + + mediatek_od_enable_vblank(ctx->od_regs); + + return 0; +} + +static void crtc_main_disable_vblank(struct mtk_drm_crtc *crtc) +{ + struct crtc_main_context *ctx = (struct crtc_main_context *)crtc->ctx; + + mediatek_od_disable_vblank(ctx->od_regs); +} + +static void crtc_main_ovl_layer_config(struct mtk_drm_crtc *crtc, + bool enable, unsigned int addr) +{ + struct crtc_main_context *ctx = (struct crtc_main_context *)crtc->ctx; + unsigned int pitch = 0; + + if (crtc->base.primary->fb && crtc->base.primary->fb->pitches[0]) + pitch = crtc->base.primary->fb->pitches[0]; + + ctx->pending_ovl_enable = enable; + if (enable) { + ctx->pending_ovl_addr = addr; + ctx->pending_ovl_width = crtc->base.mode.hdisplay; + ctx->pending_ovl_height = crtc->base.mode.vdisplay; + ctx->pending_ovl_pitch = pitch; + ctx->pending_ovl_format = crtc->base.primary->fb->pixel_format; + } + ctx->pending_ovl_config = true; +} + +static struct mediatek_drm_crtc_ops crtc_main_crtc_ops = { + .dpms = crtc_main_dpms, + .enable_vblank = crtc_main_enable_vblank, + .disable_vblank = crtc_main_disable_vblank, + .ovl_layer_config = crtc_main_ovl_layer_config, +}; + +static void crtc_main_irq(struct crtc_main_context *ctx) +{ + struct drm_device *dev = ctx->drm_dev; + struct mtk_drm_crtc *mtk_crtc = ctx->crtc; + unsigned long flags; + + if (ctx->pending_ovl_config) { + ctx->pending_ovl_config = false; + mediatek_ovl_layer_config(ctx->ovl0_regs, + ctx->pending_ovl_enable, + ctx->pending_ovl_addr, + ctx->pending_ovl_width, + ctx->pending_ovl_height, + ctx->pending_ovl_pitch, + ctx->pending_ovl_format); + } + + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + spin_lock_irqsave(&dev->event_lock, flags); + if (mtk_crtc->pending_needs_vblank) { + mtk_crtc_finish_page_flip(mtk_crtc); + mtk_crtc->pending_needs_vblank = false; + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +irqreturn_t crtc_main_irq_handler(int irq, void *dev_id) +{ + struct crtc_main_context *ctx = (struct crtc_main_context *)dev_id; + + mediatek_od_clear_vblank(ctx->od_regs); + + if (ctx->pipe < 0 || !ctx->drm_dev) + goto out; + + crtc_main_irq(ctx); +out: + return IRQ_HANDLED; +} + +static int crtc_main_bind(struct device *dev, struct device *master, void *data) +{ + struct crtc_main_context *ctx = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + int ret; + + ret = crtc_main_ctx_initialize(ctx, drm_dev); + if (ret) { + DRM_ERROR("crtc_main_ctx_initialize failed.\n"); + return ret; + } + + ctx->crtc = mtk_drm_crtc_create(drm_dev, ctx->pipe, + &crtc_main_crtc_ops, ctx); + + if (IS_ERR(ctx->crtc)) { + crtc_main_ctx_remove(ctx); + return PTR_ERR(ctx->crtc); + } + + DRM_INFO("mediatek_ddp_clock_on\n"); + mediatek_ddp_clock_on(ctx->ddp_dev); + + DRM_INFO("mediatek_ddp_main_path_setup\n"); + mediatek_ddp_main_path_setup(ctx->ddp_dev); + + DRM_INFO("main_disp_path_power_on\n"); + main_disp_path_power_on(ctx->ovl0_regs, ctx->rdma0_regs, + ctx->color0_regs, ctx->ufoe_regs, ctx->od_regs); + + return 0; + +} + +static void crtc_main_unbind(struct device *dev, struct device *master, + void *data) +{ + struct crtc_main_context *ctx = dev_get_drvdata(dev); + + crtc_main_ctx_remove(ctx); +} + +static const struct component_ops crtc_main_component_ops = { + .bind = crtc_main_bind, + .unbind = crtc_main_unbind, +}; + +static int crtc_main_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct crtc_main_context *ctx; + struct device_node *node; + struct platform_device *ddp_pdev; + struct resource *regs; + int irq; + int ret; + + if (!dev->of_node) + return -ENODEV; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + node = of_parse_phandle(dev->of_node, "ddp", 0); + if (!node) { + dev_err(dev, "crtc_main_probe: Get ddp node fail.\n"); + ret = -EINVAL; + goto err; + } + + ddp_pdev = of_find_device_by_node(node); + if (WARN_ON(!ddp_pdev)) { + dev_err(dev, "crtc_main_probe: Find ddp device fail.\n"); + ret = -EINVAL; + goto err; + } + ctx->ddp_dev = &ddp_pdev->dev; + + ctx->ovl0_disp_clk = devm_clk_get(dev, "ovl0_disp"); + if (IS_ERR(ctx->ovl0_disp_clk)) { + dev_err(dev, "crtc_main_probe: Get ovl0_disp_clk fail.\n"); + ret = PTR_ERR(ctx->ovl0_disp_clk); + goto err; + } + + ctx->rdma0_disp_clk = devm_clk_get(dev, "rdma0_disp"); + if (IS_ERR(ctx->rdma0_disp_clk)) { + dev_err(dev, "crtc_main_probe: Get rdma0_disp_clk.\n"); + ret = PTR_ERR(ctx->rdma0_disp_clk); + goto err; + } + + ctx->color0_disp_clk = devm_clk_get(dev, "color0_disp"); + if (IS_ERR(ctx->color0_disp_clk)) { + dev_err(dev, "crtc_main_probe: Get color0_disp_clk fail.\n"); + ret = PTR_ERR(ctx->color0_disp_clk); + goto err; + } + + ctx->aal_disp_clk = devm_clk_get(dev, "aal_disp"); + if (IS_ERR(ctx->aal_disp_clk)) { + dev_err(dev, "crtc_main_probe: Get aal_disp_clk fail.\n"); + ret = PTR_ERR(ctx->aal_disp_clk); + goto err; + } + + ctx->ufoe_disp_clk = devm_clk_get(dev, "ufoe_disp"); + if (IS_ERR(ctx->ufoe_disp_clk)) { + dev_err(dev, "crtc_main_probe: Get ufoe_disp_clk fail.\n"); + ret = PTR_ERR(ctx->ufoe_disp_clk); + goto err; + } + + ctx->od_disp_clk = devm_clk_get(dev, "od_disp"); + if (IS_ERR(ctx->od_disp_clk)) { + dev_err(dev, "crtc_main_probe: Get od_disp_clk fail.\n"); + ret = PTR_ERR(ctx->od_disp_clk); + goto err; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->ovl0_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ctx->ovl0_regs)) + return PTR_ERR(ctx->ovl0_regs); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ctx->rdma0_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ctx->rdma0_regs)) + return PTR_ERR(ctx->rdma0_regs); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 2); + ctx->color0_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ctx->color0_regs)) + return PTR_ERR(ctx->color0_regs); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 3); + ctx->aal_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ctx->aal_regs)) + return PTR_ERR(ctx->aal_regs); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 4); + ctx->ufoe_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ctx->ufoe_regs)) + return PTR_ERR(ctx->ufoe_regs); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 5); + ctx->od_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ctx->od_regs)) + return PTR_ERR(ctx->od_regs); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, crtc_main_irq_handler, + IRQF_TRIGGER_NONE, dev_name(dev), ctx); + if (ret < 0) { + dev_err(dev, "devm_request_irq %d fail %d\n", irq, ret); + ret = -ENXIO; + goto err; + } + + platform_set_drvdata(pdev, ctx); + + ret = component_add(&pdev->dev, &crtc_main_component_ops); + if (ret) + goto err; + + return 0; + +err: + if (node) + of_node_put(node); + + return ret; +} + +static int crtc_main_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &crtc_main_component_ops); + + return 0; +} + +static const struct of_device_id crtc_main_driver_dt_match[] = { + { .compatible = "mediatek,mt8173-crtc-main" }, + {}, +}; +MODULE_DEVICE_TABLE(of, crtc_main_driver_dt_match); + +struct platform_driver mediatek_crtc_main_driver = { + .probe = crtc_main_probe, + .remove = crtc_main_remove, + .driver = { + .name = "mediatek-crtc-main", + .owner = THIS_MODULE, + .of_match_table = crtc_main_driver_dt_match, + }, +}; + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp.c b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.c new file mode 100644 index 0000000..bb6959b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "mediatek_drm_crtc.h" +#include "mediatek_drm_ddp.h" + + +#define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0x040 +#define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN 0x044 +#define DISP_REG_CONFIG_DISP_OD_MOUT_EN 0x048 +#define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN 0x04C +#define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN 0x050 +#define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084 +#define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088 +#define DISP_REG_CONFIG_DPI_SEL_IN 0x0AC +#define DISP_REG_CONFIG_DISP_PATH1_SOUT_SEL_IN 0x0C8 +#define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100 + +#define DISP_REG_CONFIG_MUTEX_EN(n) (0x20 + 0x20 * n) +#define DISP_REG_CONFIG_MUTEX_MOD(n) (0x2C + 0x20 * n) +#define DISP_REG_CONFIG_MUTEX_SOF(n) (0x30 + 0x20 * n) + + +enum { + MUTEX_MOD_OVL0 = 11, + MUTEX_MOD_RDMA0 = 13, + MUTEX_MOD_COLOR0 = 18, + MUTEX_MOD_AAL = 20, + MUTEX_MOD_UFOE = 22, + MUTEX_MOD_PWM0 = 23, + MUTEX_MOD_OD = 25, +}; + +enum { + MUTEX_SOF_DSI0 = 1, +}; + +enum { + OVL0_MOUT_EN_COLOR0 = 0x1, +}; + +enum { + OD_MOUT_EN_RDMA0 = 0x1, +}; + +enum { + UFOE_MOUT_EN_DSI0 = 0x1, +}; + +enum { + COLOR0_SEL_IN_OVL0 = 0x1, +}; + +struct ddp_context { + struct device *dev; + struct drm_device *drm_dev; + struct mediatek_drm_crtc *crtc; + int pipe; + + struct clk *mutex_disp_clk; + + void __iomem *config_regs; + void __iomem *mutex_regs; + + bool pending_ovl_config; + bool pending_ovl_enable; + unsigned int pending_ovl_addr; + unsigned int pending_ovl_width; + unsigned int pending_ovl_height; + unsigned int pending_ovl_pitch; + unsigned int pending_ovl_format; +}; + + +static void disp_config_main_path_connection(void __iomem *disp_base) +{ + writel(OVL0_MOUT_EN_COLOR0, + disp_base + DISP_REG_CONFIG_DISP_OVL0_MOUT_EN); + writel(OD_MOUT_EN_RDMA0, disp_base + DISP_REG_CONFIG_DISP_OD_MOUT_EN); + writel(UFOE_MOUT_EN_DSI0, + disp_base + DISP_REG_CONFIG_DISP_UFOE_MOUT_EN); + writel(COLOR0_SEL_IN_OVL0, + disp_base + DISP_REG_CONFIG_DISP_COLOR0_SEL_IN); +} + +static void disp_config_main_path_mutex(void __iomem *mutex_base) +{ + unsigned int id = 0; + + writel((1 << MUTEX_MOD_OVL0 | 1 << MUTEX_MOD_RDMA0 | + 1 << MUTEX_MOD_COLOR0 | 1 << MUTEX_MOD_AAL | + 1 << MUTEX_MOD_UFOE | 1 << MUTEX_MOD_PWM0 | + 1 << MUTEX_MOD_OD), + mutex_base + DISP_REG_CONFIG_MUTEX_MOD(id)); + + writel(MUTEX_SOF_DSI0, mutex_base + DISP_REG_CONFIG_MUTEX_SOF(id)); + writel(1, mutex_base + DISP_REG_CONFIG_MUTEX_EN(id)); +} + +void mediatek_ddp_main_path_setup(struct device *dev) +{ + struct ddp_context *ddp = dev_get_drvdata(dev); + + disp_config_main_path_connection(ddp->config_regs); + disp_config_main_path_mutex(ddp->mutex_regs); +} + +void mediatek_ddp_clock_on(struct device *dev) +{ + struct ddp_context *ddp = dev_get_drvdata(dev); + int ret; + + /* disp_mtcmos */ + ret = pm_runtime_get_sync(dev); + if (ret < 0) + DRM_ERROR("failed to get_sync(%d)\n", ret); + + ret = clk_prepare_enable(ddp->mutex_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(mutex_disp_clk) error!\n"); +} + +static int ddp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ddp_context *ddp; + struct resource *regs; + int ret; + + if (!dev->of_node) + return -ENODEV; + + ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL); + if (!ddp) + return -ENOMEM; + + ddp->mutex_disp_clk = devm_clk_get(dev, "mutex_disp"); + if (IS_ERR(ddp->mutex_disp_clk)) { + dev_err(dev, "ddp_probe: Get mutex_disp_clk fail.\n"); + ret = PTR_ERR(ddp->mutex_disp_clk); + goto err; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddp->config_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ddp->config_regs)) + return PTR_ERR(ddp->config_regs); + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ddp->mutex_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ddp->mutex_regs)) + return PTR_ERR(ddp->mutex_regs); + + platform_set_drvdata(pdev, ddp); + + pm_runtime_enable(dev); + + return 0; + +err: + + return ret; +} + +static int ddp_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id ddp_driver_dt_match[] = { + { .compatible = "mediatek,mt8173-ddp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ddp_driver_dt_match); + +struct platform_driver mediatek_ddp_driver = { + .probe = ddp_probe, + .remove = ddp_remove, + .driver = { + .name = "mediatek-ddp", + .owner = THIS_MODULE, + .of_match_table = ddp_driver_dt_match, + }, +}; + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp.h b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.h new file mode 100644 index 0000000..07dd637 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MEDIATEK_DRM_DDP_H_ +#define _MEDIATEK_DRM_DDP_H_ + +void mediatek_ddp_main_path_setup(struct device *dev); + +void mediatek_ddp_clock_on(struct device *dev); + + +#endif + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c new file mode 100644 index 0000000..dd7ac83 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Authors: + * YT Shen + * CK Hu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + + +#define DISP_REG_OVL_INTEN 0x0004 +#define DISP_REG_OVL_INTSTA 0x0008 +#define DISP_REG_OVL_EN 0x000C +#define DISP_REG_OVL_RST 0x0014 +#define DISP_REG_OVL_ROI_SIZE 0x0020 +#define DISP_REG_OVL_ROI_BGCLR 0x0028 +#define DISP_REG_OVL_SRC_CON 0x002C +#define DISP_REG_OVL_L0_CON 0x0030 +#define DISP_REG_OVL_L0_SRCKEY 0x0034 +#define DISP_REG_OVL_L0_SRC_SIZE 0x0038 +#define DISP_REG_OVL_L0_OFFSET 0x003C +#define DISP_REG_OVL_L0_PITCH 0x0044 +#define DISP_REG_OVL_L1_CON 0x0050 +#define DISP_REG_OVL_L1_SRCKEY 0x0054 +#define DISP_REG_OVL_L1_SRC_SIZE 0x0058 +#define DISP_REG_OVL_L1_OFFSET 0x005C +#define DISP_REG_OVL_L1_PITCH 0x0064 +#define DISP_REG_OVL_RDMA0_CTRL 0x00C0 +#define DISP_REG_OVL_RDMA0_MEM_GMC_SETTING 0x00C8 +#define DISP_REG_OVL_RDMA1_CTRL 0x00E0 +#define DISP_REG_OVL_RDMA1_MEM_GMC_SETTING 0x00E8 +#define DISP_REG_OVL_RDMA1_FIFO_CTRL 0x00F0 +#define DISP_REG_OVL_L0_ADDR 0x0f40 +#define DISP_REG_OVL_L1_ADDR 0x0f60 + +#define DISP_REG_RDMA_INT_ENABLE 0x0000 +#define DISP_REG_RDMA_INT_STATUS 0x0004 +#define DISP_REG_RDMA_GLOBAL_CON 0x0010 +#define DISP_REG_RDMA_SIZE_CON_0 0x0014 +#define DISP_REG_RDMA_SIZE_CON_1 0x0018 +#define DISP_REG_RDMA_FIFO_CON 0x0040 + +#define DISP_OD_EN 0x000 +#define DISP_OD_INTEN 0x008 +#define DISP_OD_INTS 0x00C +#define DISP_OD_CFG 0x020 +#define DISP_OD_SIZE 0x030 + +#define DISP_REG_UFO_START 0x000 + +#define DISP_COLOR_CFG_MAIN 0x400 +#define DISP_COLOR_START 0xC00 + +enum DISPLAY_PATH { + PRIMARY_PATH = 0, + EXTERNAL_PATH = 1, +}; + +enum RDMA_MODE { + RDMA_MODE_DIRECT_LINK = 0, + RDMA_MODE_MEMORY = 1, +}; + +enum RDMA_OUTPUT_FORMAT { + RDMA_OUTPUT_FORMAT_ARGB = 0, + RDMA_OUTPUT_FORMAT_YUV444 = 1, +}; + +#define OVL_COLOR_BASE 30 +enum OVL_INPUT_FORMAT { + OVL_INFMT_RGB565 = 0, + OVL_INFMT_RGB888 = 1, + OVL_INFMT_RGBA8888 = 2, + OVL_INFMT_ARGB8888 = 3, + OVL_INFMT_UYVY = 4, + OVL_INFMT_YUYV = 5, + OVL_INFMT_UNKNOWN = 16, + + OVL_INFMT_BGR565 = OVL_INFMT_RGB565 + OVL_COLOR_BASE, + OVL_INFMT_BGR888 = OVL_INFMT_RGB888 + OVL_COLOR_BASE, + OVL_INFMT_BGRA8888 = OVL_INFMT_RGBA8888 + OVL_COLOR_BASE, + OVL_INFMT_ABGR8888 = OVL_INFMT_ARGB8888 + OVL_COLOR_BASE, +}; + +enum { + OD_RELAY_MODE = 0x1, +}; + +enum { + UFO_BYPASS = 0x4, +}; + +enum { + COLOR_BYPASS_ALL = (1UL<<7), + COLOR_SEQ_SEL = (1UL<<13), +}; + +enum { + OVL_LAYER_SRC_DRAM = 0, +}; + + +static void mediatek_ovl_start(void __iomem *ovl_base) +{ + writel(0x01, ovl_base + DISP_REG_OVL_EN); +} + +static void mediatek_ovl_roi(void __iomem *ovl_base, + unsigned int w, unsigned int h, unsigned int bg_color) +{ + writel(h << 16 | w, ovl_base + DISP_REG_OVL_ROI_SIZE); + writel(bg_color, ovl_base + DISP_REG_OVL_ROI_BGCLR); +} + +void mediatek_ovl_layer_switch(void __iomem *ovl_base, + unsigned layer, bool en) +{ + u32 reg; + + reg = readl(ovl_base + DISP_REG_OVL_SRC_CON); + if (en) + reg |= (1U<>2)<<2; + + mediatek_ovl_start(ovl_base); + mediatek_rdma_start(rdma_base); + + mediatek_ovl_roi(ovl_base, width, height, 0x00000000); + mediatek_ovl_layer_switch(ovl_base, 0, 1); + mediatek_rdma_config_direct_link(rdma_base, width, height); + mediatek_od_start(od_base, width, height); + mediatek_ufoe_start(ufoe_base); + mediatek_color_start(color_base); +} + + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h new file mode 100644 index 0000000..d3ed3e1 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_ddp_comp.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MEDIATEK_DRM_DDP_COMP_H_ +#define _MEDIATEK_DRM_DDP_COMP_H_ + + +void mediatek_od_enable_vblank(void __iomem *drm_disp_base); +void mediatek_od_disable_vblank(void __iomem *drm_disp_base); +void mediatek_od_clear_vblank(void __iomem *drm_disp_base); +void mediatek_ovl_layer_config(void __iomem *ovl_base, bool enabled, + unsigned int addr, unsigned int width, unsigned int height, + unsigned int pitch, unsigned int format); + +void main_disp_path_power_on(void __iomem *ovl_base, + void __iomem *rdma_base, void __iomem *color_base, + void __iomem *ufoe_base, void __iomem *od_base); + +void mediatek_ovl_layer_switch(void __iomem *ovl_base, + unsigned layer, bool en); + +#endif + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_drv.c b/drivers/gpu/drm/mediatek/mediatek_drm_drv.c new file mode 100644 index 0000000..dfd816f --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_drv.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: YT SHEN + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mediatek_drm_drv.h" +#include "mediatek_drm_crtc.h" +#include "mediatek_drm_fb.h" +#include "mediatek_drm_gem.h" + +#include "drm/mediatek_drm.h" + +#define DRIVER_NAME "mediatek" +#define DRIVER_DESC "Mediatek SoC DRM" +#define DRIVER_DATE "20150513" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +static struct drm_mode_config_funcs mediatek_drm_mode_config_funcs = { + .fb_create = mtk_drm_mode_fb_create, + .output_poll_changed = mtk_drm_mode_output_poll_changed, +}; + +static int mtk_drm_kms_init(struct drm_device *dev) +{ + struct device_node *node; + struct platform_device *pdev; + int err; + + drm_mode_config_init(dev); + + dev->mode_config.min_width = 640; + dev->mode_config.min_height = 480; + + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + dev->mode_config.funcs = &mediatek_drm_mode_config_funcs; + + err = component_bind_all(dev->dev, dev); + if (err) + goto err_crtc; + + /* + * We don't use the drm_irq_install() helpers provided by the DRM + * core, so we need to set this manually in order to allow the + * DRM_IOCTL_WAIT_VBLANK to operate correctly. + */ + dev->irq_enabled = true; + err = drm_vblank_init(dev, MAX_CRTC); + if (err < 0) + goto err_crtc; + + drm_kms_helper_poll_init(dev); + + node = of_parse_phandle(dev->dev->of_node, "iommus", 0); + if (!node) + return 0; + + pdev = of_find_device_by_node(node); + if (WARN_ON(!pdev)) { + of_node_put(node); + return -EINVAL; + } + err = iommu_dma_attach_device(dev->dev, + arch_get_dma_domain(&pdev->dev)); + if (err) + DRM_ERROR("iommu_dma_attach_device fail %d\n", err); + + node = of_parse_phandle(dev->dev->of_node, "larb", 0); + if (!node) + return 0; + + pdev = of_find_device_by_node(node); + if (WARN_ON(!pdev)) { + of_node_put(node); + return -EINVAL; + } + + err = mtk_smi_larb_get(&pdev->dev); + if (err) + DRM_ERROR("mtk_smi_larb_get fail %d\n", err); + + node = of_parse_phandle(dev->dev->of_node, "larb", 1); + if (!node) + return 0; + + pdev = of_find_device_by_node(node); + if (WARN_ON(!pdev)) { + of_node_put(node); + return -EINVAL; + } + + err = mtk_smi_larb_get(&pdev->dev); + if (err) + DRM_ERROR("mtk_smi_larb_get fail %d\n", err); + + mtk_fbdev_create(dev); + + return 0; +err_crtc: + drm_mode_config_cleanup(dev); + + return err; +} + +static int mtk_drm_load(struct drm_device *dev, unsigned long flags) +{ + struct mtk_drm_private *priv; + + priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev->dev_private = priv; + platform_set_drvdata(dev->platformdev, dev); + + return mtk_drm_kms_init(dev); +} + +static void mtk_drm_kms_deinit(struct drm_device *dev) +{ + drm_kms_helper_poll_fini(dev); + + mtk_fbdev_destroy(dev); + + drm_vblank_cleanup(dev); + drm_mode_config_cleanup(dev); + + pm_runtime_disable(dev->dev); +} + +static int mtk_drm_unload(struct drm_device *dev) +{ + mtk_drm_kms_deinit(dev); + dev->dev_private = NULL; + + return 0; +} + +static int mtk_drm_open(struct drm_device *drm, struct drm_file *filp) +{ + return 0; +} + +static void mediatek_drm_preclose(struct drm_device *drm, struct drm_file *file) +{ +} + +static void mediatek_drm_lastclose(struct drm_device *drm) +{ +} + +static const struct vm_operations_struct mediatek_drm_gem_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct drm_ioctl_desc mtk_ioctls[] = { + DRM_IOCTL_DEF_DRV(MTK_GEM_CREATE, mediatek_gem_create_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(MTK_GEM_MAP_OFFSET, + mediatek_gem_map_offset_ioctl, + DRM_UNLOCKED | DRM_AUTH), +}; + +static const struct file_operations mediatek_drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = mtk_drm_gem_mmap, + .poll = drm_poll, + .read = drm_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif +}; + +static struct drm_driver mediatek_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM, + .load = mtk_drm_load, + .unload = mtk_drm_unload, + .open = mtk_drm_open, + .preclose = mediatek_drm_preclose, + .lastclose = mediatek_drm_lastclose, + .set_busid = drm_platform_set_busid, + + .get_vblank_counter = drm_vblank_count, + .enable_vblank = mtk_drm_crtc_enable_vblank, + .disable_vblank = mtk_drm_crtc_disable_vblank, + + .gem_free_object = mtk_drm_gem_free_object, + .gem_vm_ops = &mediatek_drm_gem_vm_ops, + .dumb_create = mtk_drm_gem_dumb_create, + .dumb_map_offset = mtk_drm_gem_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .num_ioctls = 0, + .fops = &mediatek_drm_fops, + + .set_busid = drm_platform_set_busid, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int mtk_drm_add_components(struct device *master, struct master *m) +{ + struct device_node *np = master->of_node; + unsigned i; + int ret; + + for (i = 0; ; i++) { + struct device_node *node; + + node = of_parse_phandle(np, "connectors", i); + if (!node) + break; + + ret = component_master_add_child(m, compare_of, node); + of_node_put(node); + if (ret) { + dev_err(master, "component_master_add_child %s fail.\n", + node->full_name); + return ret; + } + } + + for (i = 0; ; i++) { + struct device_node *node; + + node = of_parse_phandle(np, "crtcs", i); + if (!node) + break; + + ret = component_master_add_child(m, compare_of, node); + of_node_put(node); + if (ret) { + dev_err(master, "component_master_add_child %s fail.\n", + node->full_name); + return ret; + } + } + + return 0; +} + +static int mtk_drm_bind(struct device *dev) +{ + return drm_platform_init(&mediatek_drm_driver, to_platform_device(dev)); +} + +static void mtk_drm_unbind(struct device *dev) +{ + drm_put_dev(platform_get_drvdata(to_platform_device(dev))); +} + +static const struct component_master_ops mtk_drm_ops = { + .add_components = mtk_drm_add_components, + .bind = mtk_drm_bind, + .unbind = mtk_drm_unbind, +}; + +static int mtk_drm_probe(struct platform_device *pdev) +{ + component_master_add(&pdev->dev, &mtk_drm_ops); + + return 0; +} + +static int mtk_drm_remove(struct platform_device *pdev) +{ + drm_put_dev(platform_get_drvdata(pdev)); + + return 0; +} + +static const struct of_device_id mediatek_drm_of_ids[] = { + { .compatible = "mediatek,mt8173-drm", }, + { } +}; + +static struct platform_driver mediatek_drm_platform_driver = { + .probe = mtk_drm_probe, + .remove = mtk_drm_remove, + .driver = { + .owner = THIS_MODULE, + .name = "mediatek-drm", + .of_match_table = mediatek_drm_of_ids, + /*.pm = &mtk_pm_ops, */ + }, + /* .id_table = mtk_drm_platform_ids, */ +}; + +static int mediatek_drm_init(void) +{ + int err; + + err = platform_driver_register(&mediatek_ddp_driver); + if (err < 0) { + DRM_DEBUG_DRIVER("register ddp driver fail.\n"); + return err; + } + + err = platform_driver_register(&mtk_dsi_driver); + if (err < 0) { + DRM_DEBUG_DRIVER("register dsi driver fail.\n"); + return err; + } + + err = platform_driver_register(&mediatek_crtc_main_driver); + if (err < 0) { + DRM_DEBUG_DRIVER("register crtc_main driver fail.\n"); + return err; + } + + err = platform_driver_register(&mediatek_drm_platform_driver); + if (err < 0) + return err; + + return 0; +} + +static void mediatek_drm_exit(void) +{ + platform_driver_unregister(&mediatek_drm_platform_driver); + platform_driver_unregister(&mtk_dsi_driver); +} + +late_initcall(mediatek_drm_init); +module_exit(mediatek_drm_exit); + +MODULE_AUTHOR("YT SHEN "); +MODULE_DESCRIPTION("Mediatek SoC DRM driver"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_drv.h b/drivers/gpu/drm/mediatek/mediatek_drm_drv.h new file mode 100644 index 0000000..10ee4c4 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_drv.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MEDIATEK_DRM_DRV_H_ +#define _MEDIATEK_DRM_DRV_H_ + +#define MAX_CRTC 2 +#define MAX_PLANE 4 + +extern struct platform_driver mediatek_ddp_driver; +extern struct platform_driver mtk_dsi_driver; +extern struct platform_driver mediatek_crtc_main_driver; + +struct mtk_drm_private { + struct drm_fb_helper *fb_helper; + + /* + * created crtc object would be contained at this array and + * this array is used to be aware of which crtc did it request vblank. + */ + struct drm_crtc *crtc[MAX_CRTC]; + unsigned int pipe; +}; + + +#endif + diff --git a/drivers/gpu/drm/mediatek/mediatek_drm_dsi.c b/drivers/gpu/drm/mediatek/mediatek_drm_dsi.c new file mode 100644 index 0000000..199ff9d --- /dev/null +++ b/drivers/gpu/drm/mediatek/mediatek_drm_dsi.c @@ -0,0 +1,1333 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include