From patchwork Wed Nov 4 11:44:59 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Zabel X-Patchwork-Id: 7548961 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 793E3BEEA4 for ; Wed, 4 Nov 2015 11:45:43 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E01AD206CC for ; Wed, 4 Nov 2015 11:45:37 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 4B191206F1 for ; Wed, 4 Nov 2015 11:45:31 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id F27EC6E756; Wed, 4 Nov 2015 03:45:24 -0800 (PST) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from metis.ext.pengutronix.de (metis.ext.4.pengutronix.de [92.198.50.35]) by gabe.freedesktop.org (Postfix) with ESMTPS id C36516E75A for ; Wed, 4 Nov 2015 03:45:22 -0800 (PST) Received: from dude.hi.4.pengutronix.de ([10.1.0.7] helo=dude.pengutronix.de.) by metis.ext.pengutronix.de with esmtp (Exim 4.80) (envelope-from ) id 1ZtwVE-0003KJ-Of; Wed, 04 Nov 2015 12:45:20 +0100 From: Philipp Zabel To: dri-devel@lists.freedesktop.org Subject: [RFC v5 02/12] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173. Date: Wed, 4 Nov 2015 12:44:59 +0100 Message-Id: <1446637509-6418-3-git-send-email-p.zabel@pengutronix.de> X-Mailer: git-send-email 2.6.1 In-Reply-To: <1446637509-6418-1-git-send-email-p.zabel@pengutronix.de> References: <1446637509-6418-1-git-send-email-p.zabel@pengutronix.de> X-SA-Exim-Connect-IP: 10.1.0.7 X-SA-Exim-Mail-From: p.zabel@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: dri-devel@lists.freedesktop.org Cc: Mark Rutland , devicetree@vger.kernel.org, Paul Bolle , YT Shen , Jitao Shi , Jie Qiu , Pawel Moll , Ian Campbell , Cawa Cheng , Rob Herring , linux-mediatek@lists.infradead.org, Kumar Gala , Matthias Brugger , kernel@pengutronix.de X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 From: CK Hu This patch adds an initial DRM driver for the Mediatek MT8173 DISP subsystem. It currently supports two fixed output streams from the OVL0/OVL1 sources to the DSI0/DPI0 sinks, respectively. Signed-off-by: CK Hu Signed-off-by: YT Shen Signed-off-by: Philipp Zabel --- Changes since v4: - Add mtk_crtc_state to keep pending state - Move drm pending vblank event into mtk_crtc_state - Make mtk_drm_crtc private - Use drm_dev_alloc and drm_dev_register directly instead of drm_platform_init - Drop unnecessary locking in mtk_drm_gem_dump_map_offset - Remove currently unused mtk_drm_gem_mmap_buf - Stop referencing plane framebuffers manually - Set RDMA FIFO output threshold depending on frame width/height/rate --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mediatek/Kconfig | 16 + drivers/gpu/drm/mediatek/Makefile | 10 + drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 590 ++++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_crtc.h | 56 +++ drivers/gpu/drm/mediatek/mtk_drm_ddp.c | 218 ++++++++++ drivers/gpu/drm/mediatek/mtk_drm_ddp.h | 39 ++ drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 424 ++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 86 ++++ drivers/gpu/drm/mediatek/mtk_drm_drv.c | 572 +++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_drv.h | 61 +++ drivers/gpu/drm/mediatek/mtk_drm_fb.c | 151 +++++++ drivers/gpu/drm/mediatek/mtk_drm_fb.h | 29 ++ drivers/gpu/drm/mediatek/mtk_drm_gem.c | 189 +++++++++ drivers/gpu/drm/mediatek/mtk_drm_gem.h | 56 +++ drivers/gpu/drm/mediatek/mtk_drm_plane.c | 167 ++++++++ drivers/gpu/drm/mediatek/mtk_drm_plane.h | 41 ++ 18 files changed, 2708 insertions(+) create mode 100644 drivers/gpu/drm/mediatek/Kconfig create mode 100644 drivers/gpu/drm/mediatek/Makefile create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 1a0a8df..9e9987b 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -264,3 +264,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 45e7719..af6b592 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..5343cf1 --- /dev/null +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -0,0 +1,16 @@ +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. + diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile new file mode 100644 index 0000000..ba6d3fc --- /dev/null +++ b/drivers/gpu/drm/mediatek/Makefile @@ -0,0 +1,10 @@ +mediatek-drm-y := mtk_drm_drv.o \ + mtk_drm_crtc.o \ + mtk_drm_ddp.o \ + mtk_drm_ddp_comp.o \ + mtk_drm_fb.o \ + mtk_drm_gem.o \ + mtk_drm_plane.o + +obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o + diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c new file mode 100644 index 0000000..4493714 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -0,0 +1,590 @@ +/* + * 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 "mtk_drm_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp.h" +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_gem.h" +#include "mtk_drm_plane.h" + +struct mtk_crtc_ddp_context; + +/* + * 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. + * @enabled: crtc enabled + * @ctx: mtk crtc context object + */ +struct mtk_drm_crtc { + struct drm_crtc base; + unsigned int pipe; + bool enabled; + struct mtk_crtc_ddp_context *ctx; + + bool do_flush; +}; + +struct mtk_crtc_ddp_context { + struct device *dev; + struct drm_device *drm_dev; + struct mtk_drm_crtc *crtc; + struct mtk_drm_plane planes[OVL_LAYER_NR]; + int pipe; + + void __iomem *config_regs; + struct device *mutex_dev; + u32 ddp_comp_nr; + struct mtk_ddp_comp *ddp_comp; +}; + +static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c) +{ + return container_of(c, struct mtk_drm_crtc, base); +} + +void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) +{ + struct drm_crtc *crtc = &mtk_crtc->base; + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); + struct drm_device *dev = mtk_crtc->base.dev; + + drm_send_vblank_event(dev, state->event->pipe, state->event); + drm_crtc_vblank_put(crtc); + state->event = NULL; +} + +static void mtk_drm_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); +} + +struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct mtk_crtc_state *crtc_state; + + crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); + if (!crtc_state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &crtc_state->base); + + crtc_state->base.crtc = crtc; + + return &crtc_state->base; +} + +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 void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); + + state->pending_width = crtc->mode.hdisplay; + state->pending_height = crtc->mode.vdisplay; + state->pending_vrefresh = crtc->mode.vrefresh; + state->pending_config = true; +} + +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe) +{ + struct mtk_drm_private *priv = drm->dev_private; + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx; + struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0]; + + if (ovl->funcs->comp_enable_vblank) + ovl->funcs->comp_enable_vblank(ovl->regs); + + return 0; +} + +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe) +{ + struct mtk_drm_private *priv = drm->dev_private; + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx; + struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0]; + + if (ovl->funcs->comp_disable_vblank) + ovl->funcs->comp_disable_vblank(ovl->regs); +} + +static void mtk_crtc_ddp_power_on(struct mtk_crtc_ddp_context *ctx) +{ + int ret; + int i; + + DRM_INFO("mtk_crtc_ddp_power_on\n"); + for (i = 0; i < ctx->ddp_comp_nr; i++) { + ret = clk_enable(ctx->ddp_comp[i].clk); + if (ret) + DRM_ERROR("clk_enable(ctx->ddp_comp_clk[%d])\n", i); + } +} + +static void mtk_crtc_ddp_power_off(struct mtk_crtc_ddp_context *ctx) +{ + int i; + + DRM_INFO("mtk_crtc_ddp_power_off\n"); + for (i = 0; i < ctx->ddp_comp_nr; i++) + clk_disable(ctx->ddp_comp[i].clk); +} + +static void mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *crtc) +{ + struct mtk_crtc_ddp_context *ctx = crtc->ctx; + struct drm_device *drm = crtc->base.dev; + unsigned int width, height, vrefresh; + int ret; + int i; + + if (ctx->crtc->base.state) { + width = crtc->base.state->adjusted_mode.hdisplay; + height = crtc->base.state->adjusted_mode.vdisplay; + vrefresh = crtc->base.state->adjusted_mode.vrefresh; + } else { + width = 1920; + height = 1080; + vrefresh = 60; + } + + DRM_INFO("mtk_crtc_ddp_hw_init\n"); + + /* disp_mtcmos */ + ret = pm_runtime_get_sync(drm->dev); + if (ret < 0) + DRM_ERROR("Failed to enable power domain: %d\n", ret); + + mtk_ddp_clock_on(ctx->mutex_dev); + mtk_crtc_ddp_power_on(ctx); + + DRM_INFO("mediatek_ddp_ddp_path_setup\n"); + for (i = 0; i < (ctx->ddp_comp_nr - 1); i++) { + mtk_ddp_add_comp_to_path(ctx->config_regs, ctx->pipe, + ctx->ddp_comp[i].funcs->comp_id, + ctx->ddp_comp[i+1].funcs->comp_id); + mtk_ddp_add_comp_to_mutex(ctx->mutex_dev, ctx->pipe, + ctx->ddp_comp[i].funcs->comp_id, + ctx->ddp_comp[i+1].funcs->comp_id); + } + + DRM_INFO("ddp_disp_path_power_on %dx%d\n", width, height); + for (i = 0; i < ctx->ddp_comp_nr; i++) { + struct mtk_ddp_comp *comp = &ctx->ddp_comp[i]; + + if (comp->funcs->comp_config) + comp->funcs->comp_config(comp->regs, width, height, + vrefresh); + + if (comp->funcs->comp_power_on) + comp->funcs->comp_power_on(comp->regs); + } +} + +static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *crtc) +{ + struct mtk_crtc_ddp_context *ctx = crtc->ctx; + struct drm_device *drm = crtc->base.dev; + struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0]; + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->base.state); + int i; + + DRM_INFO("mtk_crtc_ddp_hw_fini\n"); + for (i = 0; i < OVL_LAYER_NR; i++) { + state->pending_ovl_enable[i] = false; + ovl->funcs->comp_layer_off(ovl->regs, i); + } + mtk_crtc_ddp_power_off(ctx); + mtk_ddp_clock_off(ctx->mutex_dev); + + /* disp_mtcmos */ + pm_runtime_put_sync(drm->dev); +} + +static void mtk_drm_crtc_enable(struct drm_crtc *crtc) +{ + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); + + if (WARN_ON(mtk_crtc->enabled)) + return; + + mtk_crtc_ddp_hw_init(mtk_crtc); + + drm_crtc_vblank_on(crtc); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + mtk_crtc->enabled = true; +} + +static void mtk_drm_crtc_disable(struct drm_crtc *crtc) +{ + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); + + DRM_INFO("mtk_drm_crtc_disable %d\n", crtc->base.id); + if (WARN_ON(!mtk_crtc->enabled)) + return; + + mtk_crtc_ddp_hw_fini(mtk_crtc); + mtk_crtc->do_flush = false; + if (state->event) + mtk_drm_crtc_finish_page_flip(mtk_crtc); + drm_crtc_vblank_put(crtc); + drm_crtc_vblank_off(crtc); + + mtk_crtc->enabled = false; +} + +static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); + + if (state->base.event) { + state->base.event->pipe = drm_crtc_index(crtc); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + state->event = state->base.event; + state->base.event = NULL; + } +} + +void mtk_drm_crtc_commit(struct drm_crtc *crtc) +{ + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); + unsigned int i; + + for (i = 0; i < OVL_LAYER_NR; i++) + if (state->pending_ovl_dirty[i]) { + state->pending_ovl_config[i] = true; + state->pending_ovl_dirty[i] = false; + } +} + +static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + mtk_drm_crtc_commit(crtc); +} + +static const struct drm_crtc_funcs mtk_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .destroy = mtk_drm_crtc_destroy, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = mtk_drm_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = { + .mode_fixup = mtk_drm_crtc_mode_fixup, + .mode_set_nofb = mtk_drm_crtc_mode_set_nofb, + .enable = mtk_drm_crtc_enable, + .disable = mtk_drm_crtc_disable, + .atomic_begin = mtk_drm_crtc_atomic_begin, + .atomic_flush = mtk_drm_crtc_atomic_flush, +}; + +struct mtk_drm_crtc *mtk_drm_crtc_create(struct drm_device *drm, + struct drm_plane *primary, struct drm_plane *cursor, int pipe, + void *ctx) +{ + struct mtk_drm_private *priv = drm->dev_private; + struct mtk_drm_crtc *mtk_crtc; + int ret; + + mtk_crtc = devm_kzalloc(drm->dev, sizeof(*mtk_crtc), GFP_KERNEL); + if (!mtk_crtc) + return ERR_PTR(-ENOMEM); + + mtk_crtc->pipe = pipe; + mtk_crtc->ctx = ctx; + mtk_crtc->enabled = false; + + priv->crtc[pipe] = &mtk_crtc->base; + + ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor, + &mtk_crtc_funcs); + if (ret) + goto err_cleanup_crtc; + + drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs); + + return mtk_crtc; + +err_cleanup_crtc: + drm_crtc_cleanup(&mtk_crtc->base); + return ERR_PTR(ret); +} + +void mtk_drm_crtc_plane_config(struct drm_crtc *crtc, unsigned int idx, + bool enable, dma_addr_t addr) +{ + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); + struct mtk_crtc_ddp_context *ctx = mtk_crtc->ctx; + struct drm_plane *plane = &ctx->planes[idx].base; + struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); + unsigned int pitch, format; + int x, y; + + if (!plane->fb || !plane->state) + return; + + pitch = plane->fb->pitches[0]; + format = plane->fb->pixel_format; + + x = plane->state->crtc_x; + y = plane->state->crtc_y; + + if (x < 0) { + addr -= x * 4; + x = 0; + } + + if (y < 0) { + addr -= y * pitch; + y = 0; + } + + state->pending_ovl_enable[idx] = enable; + state->pending_ovl_addr[idx] = addr; + state->pending_ovl_pitch[idx] = pitch; + state->pending_ovl_format[idx] = format; + state->pending_ovl_x[idx] = x; + state->pending_ovl_y[idx] = y; + state->pending_ovl_size[idx] = ctx->planes[idx].disp_size; + state->pending_ovl_dirty[idx] = true; +} + +static void mtk_crtc_ddp_irq(struct mtk_crtc_ddp_context *ctx) +{ + struct mtk_drm_crtc *mtk_crtc = ctx->crtc; + struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0]; + struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state); + unsigned int i; + + if (state->pending_config) { + state->pending_config = false; + + for (i = 0; i < OVL_LAYER_NR; i++) + ovl->funcs->comp_layer_off(ovl->regs, i); + ovl->funcs->comp_config(ovl->regs, state->pending_width, + state->pending_height, + state->pending_vrefresh); + } + + for (i = 0; i < OVL_LAYER_NR; i++) { + if (state->pending_ovl_config[i]) { + if (!state->pending_ovl_enable[i]) + ovl->funcs->comp_layer_off(ovl->regs, i); + + ovl->funcs->comp_layer_config(ovl->regs, i, + state->pending_ovl_addr[i], + state->pending_ovl_pitch[i], + state->pending_ovl_format[i], + state->pending_ovl_x[i], + state->pending_ovl_y[i], + state->pending_ovl_size[i]); + + if (state->pending_ovl_enable[i]) + ovl->funcs->comp_layer_on(ovl->regs, i); + } + + state->pending_ovl_config[i] = false; + } + + drm_handle_vblank(ctx->drm_dev, ctx->pipe); +} + +static irqreturn_t mtk_crtc_ddp_irq_handler(int irq, void *dev_id) +{ + struct mtk_crtc_ddp_context *ctx = dev_id; + struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0]; + + if (ovl->funcs->comp_clear_vblank) + ovl->funcs->comp_clear_vblank(ovl->regs); + + if (ctx->pipe >= 0 && ctx->drm_dev) + mtk_crtc_ddp_irq(ctx); + + return IRQ_HANDLED; +} + +static int mtk_crtc_ddp_bind(struct device *dev, struct device *master, + void *data) +{ + struct mtk_crtc_ddp_context *ctx = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct mtk_drm_private *priv = drm_dev->dev_private; + struct device_node *node; + enum drm_plane_type type; + unsigned int zpos; + int ret; + int i; + + ctx->drm_dev = drm_dev; + ctx->pipe = priv->pipe++; + ctx->config_regs = priv->config_regs; + ctx->mutex_dev = priv->mutex_dev; + ctx->ddp_comp_nr = priv->path_len[ctx->pipe]; + ctx->ddp_comp = devm_kmalloc_array(dev, ctx->ddp_comp_nr, + sizeof(*ctx->ddp_comp), GFP_KERNEL); + + /* priv->path[ctx->pipe] starts with this OVL node */ + priv->comp_node[priv->path[ctx->pipe][0]] = dev->of_node; + + for (i = 0; i < ctx->ddp_comp_nr; i++) { + struct mtk_ddp_comp *comp = &ctx->ddp_comp[i]; + enum mtk_ddp_comp_id comp_id = priv->path[ctx->pipe][i]; + + ret = mtk_ddp_comp_init(priv->comp_node[comp_id], comp, + comp_id); + if (ret) { + dev_err(dev, "Failed to initialize component %i\n", i); + goto unprepare; + } + + if (comp->clk) { + ret = clk_prepare(comp->clk); + if (ret) { + dev_err(dev, "Failed to prepare clock %d\n", i); + ret = -EINVAL; + goto unprepare; + } + } + } + + for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) { + type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY : + (zpos == 1) ? DRM_PLANE_TYPE_CURSOR : + DRM_PLANE_TYPE_OVERLAY; + ret = mtk_plane_init(drm_dev, &ctx->planes[zpos], + 1 << ctx->pipe, type, zpos, OVL_LAYER_NR); + if (ret) + goto unprepare; + } + + ctx->crtc = mtk_drm_crtc_create(drm_dev, &ctx->planes[0].base, + &ctx->planes[1].base, ctx->pipe, ctx); + + if (IS_ERR(ctx->crtc)) { + ret = PTR_ERR(ctx->crtc); + goto unprepare; + } + + return 0; + +unprepare: + while (--i >= 0) + clk_unprepare(ctx->ddp_comp[i].clk); + of_node_put(node); + + return ret; +} + +static void mtk_crtc_ddp_unbind(struct device *dev, struct device *master, + void *data) +{ + struct mtk_crtc_ddp_context *ctx = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ctx->ddp_comp_nr; i++) + clk_unprepare(ctx->ddp_comp[i].clk); +} + +static const struct component_ops mtk_crtc_ddp_component_ops = { + .bind = mtk_crtc_ddp_bind, + .unbind = mtk_crtc_ddp_unbind, +}; + +static int mtk_crtc_ddp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_crtc_ddp_context *ctx; + int irq; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, mtk_crtc_ddp_irq_handler, + IRQF_TRIGGER_NONE, dev_name(dev), ctx); + if (ret < 0) { + dev_err(dev, "Failed to request irq %d: %d\n", irq, ret); + return -ENXIO; + } + + platform_set_drvdata(pdev, ctx); + + ret = component_add(dev, &mtk_crtc_ddp_component_ops); + if (ret) + dev_err(dev, "Failed to add component: %d\n", ret); + + return ret; +} + +static int mtk_crtc_ddp_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_crtc_ddp_component_ops); + + return 0; +} + +static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = { + { .compatible = "mediatek,mt8173-disp-ovl", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match); + +struct platform_driver mtk_disp_ovl_driver = { + .probe = mtk_crtc_ddp_probe, + .remove = mtk_crtc_ddp_remove, + .driver = { + .name = "mediatek-disp-ovl", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_ovl_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h new file mode 100644 index 0000000..b696066 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h @@ -0,0 +1,56 @@ +/* + * 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 MTK_DRM_CRTC_H +#define MTK_DRM_CRTC_H + +#include +#include "mtk_drm_plane.h" + +#define OVL_LAYER_NR 4 + +struct mtk_crtc_state { + struct drm_crtc_state base; + struct drm_pending_vblank_event *event; + + unsigned int pending_update; + bool pending_needs_vblank; + + bool pending_config; + unsigned int pending_width; + unsigned int pending_height; + unsigned int pending_vrefresh; + + bool pending_ovl_config[OVL_LAYER_NR]; + bool pending_ovl_enable[OVL_LAYER_NR]; + unsigned int pending_ovl_addr[OVL_LAYER_NR]; + unsigned int pending_ovl_pitch[OVL_LAYER_NR]; + unsigned int pending_ovl_format[OVL_LAYER_NR]; + int pending_ovl_x[OVL_LAYER_NR]; + int pending_ovl_y[OVL_LAYER_NR]; + unsigned int pending_ovl_size[OVL_LAYER_NR]; + bool pending_ovl_dirty[OVL_LAYER_NR]; +}; + +static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s) +{ + return container_of(s, struct mtk_crtc_state, base); +} + +int mtk_drm_crtc_enable_vblank(struct drm_device *drm, int pipe); +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, int pipe); +void mtk_drm_crtc_plane_config(struct drm_crtc *crtc, unsigned int idx, + bool enable, dma_addr_t addr); +void mtk_drm_crtc_commit(struct drm_crtc *crtc); + +#endif /* MTK_DRM_CRTC_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c new file mode 100644 index 0000000..9ec2960 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c @@ -0,0 +1,218 @@ +/* + * 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 "mtk_drm_crtc.h" +#include "mtk_drm_ddp.h" +#include "mtk_drm_ddp_comp.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_RDMA1_MOUT_EN 0x0c8 +#define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100 + +#define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * n) +#define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * n) +#define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * n) +#define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * n) + +#define MUTEX_MOD_OVL0 11 +#define MUTEX_MOD_OVL1 12 +#define MUTEX_MOD_RDMA0 13 +#define MUTEX_MOD_RDMA1 14 +#define MUTEX_MOD_COLOR0 18 +#define MUTEX_MOD_COLOR1 19 +#define MUTEX_MOD_AAL 20 +#define MUTEX_MOD_GAMMA 21 +#define MUTEX_MOD_UFOE 22 +#define MUTEX_MOD_PWM0 23 +#define MUTEX_MOD_OD 25 + +#define MUTEX_SOF_DSI0 1 +#define MUTEX_SOF_DPI0 3 + +#define OVL0_MOUT_EN_COLOR0 0x1 +#define OD_MOUT_EN_RDMA0 0x1 +#define UFOE_MOUT_EN_DSI0 0x1 +#define COLOR0_SEL_IN_OVL0 0x1 +#define OVL1_MOUT_EN_COLOR1 0x1 +#define GAMMA_MOUT_EN_RDMA1 0x1 +#define RDMA1_MOUT_DPI0 0x2 +#define DPI0_SEL_IN_RDMA1 0x1 +#define COLOR1_SEL_IN_OVL1 0x1 + +static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = { + [DDP_COMPONENT_AAL] = MUTEX_MOD_AAL, + [DDP_COMPONENT_COLOR0] = MUTEX_MOD_COLOR0, + [DDP_COMPONENT_COLOR1] = MUTEX_MOD_COLOR1, + [DDP_COMPONENT_GAMMA] = MUTEX_MOD_GAMMA, + [DDP_COMPONENT_OD] = MUTEX_MOD_OD, + [DDP_COMPONENT_OVL0] = MUTEX_MOD_OVL0, + [DDP_COMPONENT_OVL1] = MUTEX_MOD_OVL1, + [DDP_COMPONENT_PWM0] = MUTEX_MOD_PWM0, + [DDP_COMPONENT_RDMA0] = MUTEX_MOD_RDMA0, + [DDP_COMPONENT_RDMA1] = MUTEX_MOD_RDMA1, + [DDP_COMPONENT_UFOE] = MUTEX_MOD_UFOE, +}; + +void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next) +{ + unsigned int addr, value; + + if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { + addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN; + value = OVL0_MOUT_EN_COLOR0; + } else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) { + addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN; + value = OD_MOUT_EN_RDMA0; + } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) { + addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN; + value = UFOE_MOUT_EN_DSI0; + } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { + addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN; + value = OVL1_MOUT_EN_COLOR1; + } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) { + addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN; + value = GAMMA_MOUT_EN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { + addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN; + value = RDMA1_MOUT_DPI0; + } else { + value = 0; + } + if (value) { + unsigned int reg = readl(config_regs + addr) | value; + + writel(reg, config_regs + addr); + } + + if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { + addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN; + value = COLOR0_SEL_IN_OVL0; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { + addr = DISP_REG_CONFIG_DPI_SEL_IN; + value = DPI0_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { + addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN; + value = COLOR1_SEL_IN_OVL1; + } else { + value = 0; + } + if (value) { + unsigned int reg = readl(config_regs + addr) | value; + + writel(reg, config_regs + addr); + } +} + +void mtk_ddp_add_comp_to_mutex(struct device *dev, unsigned int pipe, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next) +{ + struct mtk_ddp *ddp = dev_get_drvdata(dev); + unsigned int reg; + + reg = readl(ddp->mutex_regs + DISP_REG_MUTEX_MOD(pipe)); + reg |= BIT(mutex_mod[cur]) | BIT(mutex_mod[next]); + writel(reg, ddp->mutex_regs + DISP_REG_MUTEX_MOD(pipe)); + + if (next == DDP_COMPONENT_DPI0) + reg = MUTEX_SOF_DPI0; + else + reg = MUTEX_SOF_DSI0; + + writel(reg, ddp->mutex_regs + DISP_REG_MUTEX_SOF(pipe)); + writel(1, ddp->mutex_regs + DISP_REG_MUTEX_EN(pipe)); +} + +void mtk_ddp_clock_on(struct device *dev) +{ + struct mtk_ddp *ddp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ddp->mutex_disp_clk); + if (ret != 0) + DRM_ERROR("clk_prepare_enable(mutex_disp_clk) error!\n"); +} + +void mtk_ddp_clock_off(struct device *dev) +{ + struct mtk_ddp *ddp = dev_get_drvdata(dev); + + clk_disable_unprepare(ddp->mutex_disp_clk); +} + +static int mtk_ddp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_ddp *ddp; + struct resource *regs; + + ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL); + if (!ddp) + return -ENOMEM; + + ddp->mutex_disp_clk = devm_clk_get(dev, NULL); + if (IS_ERR(ddp->mutex_disp_clk)) { + dev_err(dev, "Failed to get clock\n"); + return PTR_ERR(ddp->mutex_disp_clk); + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddp->mutex_regs = devm_ioremap_resource(dev, regs); + if (IS_ERR(ddp->mutex_regs)) { + dev_err(dev, "Failed to map mutex registers\n"); + return PTR_ERR(ddp->mutex_regs); + } + + platform_set_drvdata(pdev, ddp); + + return 0; +} + +static int mtk_ddp_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id ddp_driver_dt_match[] = { + { .compatible = "mediatek,mt8173-disp-mutex" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ddp_driver_dt_match); + +struct platform_driver mtk_ddp_driver = { + .probe = mtk_ddp_probe, + .remove = mtk_ddp_remove, + .driver = { + .name = "mediatek-ddp", + .owner = THIS_MODULE, + .of_match_table = ddp_driver_dt_match, + }, +}; + +module_platform_driver(mtk_ddp_driver); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h new file mode 100644 index 0000000..55c2db3 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h @@ -0,0 +1,39 @@ +/* + * 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 MTK_DRM_DDP_H +#define MTK_DRM_DDP_H + +#include "mtk_drm_ddp_comp.h" + +struct regmap; +struct device; + +struct mtk_ddp { + struct device *dev; + struct drm_device *drm_dev; + + struct clk *mutex_disp_clk; + void __iomem *mutex_regs; +}; + +void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next); +void mtk_ddp_add_comp_to_mutex(struct device *dev, unsigned int pipe, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next); +void mtk_ddp_clock_on(struct device *dev); +void mtk_ddp_clock_off(struct device *dev); + +#endif /* MTK_DRM_DDP_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c new file mode 100644 index 0000000..2f3b32b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c @@ -0,0 +1,424 @@ +/* + * 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 "mtk_drm_ddp_comp.h" + +#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_CON(n) (0x0030 + 0x20 * n) +#define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * n) +#define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * n) +#define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * n) +#define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * n) +#define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * n) +#define DISP_REG_OVL_ADDR(n) (0x0f40 + 0x20 * n) + +#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 RDMA_FIFO_UNDERFLOW_EN BIT(31) +#define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16) +#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16) + +#define DISP_OD_EN 0x0000 +#define DISP_OD_INTEN 0x0008 +#define DISP_OD_INTSTA 0x000c +#define DISP_OD_CFG 0x0020 +#define DISP_OD_SIZE 0x0030 + +#define DISP_REG_UFO_START 0x0000 + +#define DISP_COLOR_CFG_MAIN 0x0400 +#define DISP_COLOR_START 0x0c00 + +enum OVL_INPUT_FORMAT { + OVL_INFMT_RGB565 = 0, + OVL_INFMT_RGB888 = 1, + OVL_INFMT_RGBA8888 = 2, + OVL_INFMT_ARGB8888 = 3, +}; + +#define OVL_RDMA_MEM_GMC 0x40402020 +#define OVL_AEN BIT(8) +#define OVL_ALPHA 0xff + +#define OD_RELAY_MODE BIT(0) + +#define UFO_BYPASS BIT(2) + +#define COLOR_BYPASS_ALL BIT(7) +#define COLOR_SEQ_SEL BIT(13) + +static void mtk_color_start(void __iomem *color_base) +{ + writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL, + color_base + DISP_COLOR_CFG_MAIN); + writel(0x1, color_base + DISP_COLOR_START); +} + +static void mtk_od_config(void __iomem *od_base, unsigned int w, unsigned int h, + unsigned int vrefresh) +{ + writel(w << 16 | h, od_base + DISP_OD_SIZE); +} + +static void mtk_od_start(void __iomem *od_base) +{ + writel(OD_RELAY_MODE, od_base + DISP_OD_CFG); + writel(1, od_base + DISP_OD_EN); +} + +static void mtk_ovl_enable_vblank(void __iomem *disp_base) +{ + writel(0x2, disp_base + DISP_REG_OVL_INTEN); +} + +static void mtk_ovl_disable_vblank(void __iomem *disp_base) +{ + writel(0x0, disp_base + DISP_REG_OVL_INTEN); +} + +static void mtk_ovl_clear_vblank(void __iomem *disp_base) +{ + writel(0x0, disp_base + DISP_REG_OVL_INTSTA); +} + +static void mtk_ovl_start(void __iomem *ovl_base) +{ + writel(0x1, ovl_base + DISP_REG_OVL_EN); +} + +static void mtk_ovl_config(void __iomem *ovl_base, + unsigned int w, unsigned int h, unsigned int vrefresh) +{ + if (w != 0 && h != 0) + writel(h << 16 | w, ovl_base + DISP_REG_OVL_ROI_SIZE); + writel(0x0, ovl_base + DISP_REG_OVL_ROI_BGCLR); + + writel(0x1, ovl_base + DISP_REG_OVL_RST); + writel(0x0, ovl_base + DISP_REG_OVL_RST); +} + +static bool has_rb_swapped(unsigned int fmt) +{ + switch (fmt) { + case DRM_FORMAT_BGR888: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + return true; + default: + return false; + } +} + +static unsigned int ovl_fmt_convert(unsigned int fmt) +{ + switch (fmt) { + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + return OVL_INFMT_RGB888; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + return OVL_INFMT_RGB565; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + return OVL_INFMT_ARGB8888; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return OVL_INFMT_RGBA8888; + default: + DRM_ERROR("unsupport format[%08x]\n", fmt); + return -EINVAL; + } +} + +static void mtk_ovl_layer_on(void __iomem *ovl_base, unsigned int idx) +{ + unsigned int reg; + + writel(0x1, ovl_base + DISP_REG_OVL_RDMA_CTRL(idx)); + writel(OVL_RDMA_MEM_GMC, ovl_base + DISP_REG_OVL_RDMA_GMC(idx)); + + reg = readl(ovl_base + DISP_REG_OVL_SRC_CON); + reg = reg | (1 << idx); + writel(reg, ovl_base + DISP_REG_OVL_SRC_CON); +} + +static void mtk_ovl_layer_off(void __iomem *ovl_base, unsigned int idx) +{ + unsigned int reg; + + reg = readl(ovl_base + DISP_REG_OVL_SRC_CON); + reg = reg & ~(1 << idx); + writel(reg, ovl_base + DISP_REG_OVL_SRC_CON); + + writel(0x0, ovl_base + DISP_REG_OVL_RDMA_CTRL(idx)); +} + +static void mtk_ovl_layer_config(void __iomem *ovl_base, unsigned int idx, + unsigned int addr, unsigned int pitch, unsigned int fmt, + int x, int y, unsigned int size) +{ + unsigned int reg; + + reg = has_rb_swapped(fmt) << 24 | ovl_fmt_convert(fmt) << 12; + if (idx != 0) + reg |= OVL_AEN | OVL_ALPHA; + + writel(reg, ovl_base + DISP_REG_OVL_CON(idx)); + writel(pitch & 0xFFFF, ovl_base + DISP_REG_OVL_PITCH(idx)); + writel(size, ovl_base + DISP_REG_OVL_SRC_SIZE(idx)); + writel(y << 16 | x, ovl_base + DISP_REG_OVL_OFFSET(idx)); + writel(addr, ovl_base + DISP_REG_OVL_ADDR(idx)); +} + +static void mtk_rdma_start(void __iomem *rdma_base) +{ + unsigned int reg; + + writel(0x4, rdma_base + DISP_REG_RDMA_INT_ENABLE); + reg = readl(rdma_base + DISP_REG_RDMA_GLOBAL_CON); + reg |= 1; + writel(reg, rdma_base + DISP_REG_RDMA_GLOBAL_CON); +} + +static void mtk_rdma_config(void __iomem *rdma_base, + unsigned width, unsigned height, unsigned int vrefresh) +{ + unsigned int threshold; + unsigned int reg; + + reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_0); + reg = (reg & ~(0xFFF)) | (width & 0xFFF); + writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_0); + + reg = readl(rdma_base + DISP_REG_RDMA_SIZE_CON_1); + reg = (reg & ~(0xFFFFF)) | (height & 0xFFFFF); + writel(reg, rdma_base + DISP_REG_RDMA_SIZE_CON_1); + + /* + * Enable FIFO underflow since DSI and DPI can't be blocked. + * Keep the FIFO pseudo size reset default of 8 KiB. Set the + * output threshold to 6 microseconds with 7/6 overhead to + * account for blanking, and with a pixel depth of 4 bytes: + */ + threshold = width * height * vrefresh * 4 * 7 / 1000000; + reg = RDMA_FIFO_UNDERFLOW_EN | + RDMA_FIFO_PSEUDO_SIZE(SZ_8K) | + RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold); + writel(reg, rdma_base + DISP_REG_RDMA_FIFO_CON); +} + +static void mtk_ufoe_start(void __iomem *ufoe_base) +{ + writel(UFO_BYPASS, ufoe_base + DISP_REG_UFO_START); +} + +static const struct mtk_ddp_comp_funcs ddp_aal = { + .comp_id = DDP_COMPONENT_AAL, +}; + +static const struct mtk_ddp_comp_funcs ddp_color0 = { + .comp_id = DDP_COMPONENT_COLOR0, + .comp_power_on = mtk_color_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_color1 = { + .comp_id = DDP_COMPONENT_COLOR1, + .comp_power_on = mtk_color_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_dpi0 = { + .comp_id = DDP_COMPONENT_DPI0, +}; + +static const struct mtk_ddp_comp_funcs ddp_dsi0 = { + .comp_id = DDP_COMPONENT_DSI0, +}; + +static const struct mtk_ddp_comp_funcs ddp_dsi1 = { + .comp_id = DDP_COMPONENT_DSI1, +}; + +static const struct mtk_ddp_comp_funcs ddp_gamma = { + .comp_id = DDP_COMPONENT_GAMMA, +}; + +static const struct mtk_ddp_comp_funcs ddp_od = { + .comp_id = DDP_COMPONENT_OD, + .comp_config = mtk_od_config, + .comp_power_on = mtk_od_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_ovl0 = { + .comp_id = DDP_COMPONENT_OVL0, + .comp_config = mtk_ovl_config, + .comp_power_on = mtk_ovl_start, + .comp_enable_vblank = mtk_ovl_enable_vblank, + .comp_disable_vblank = mtk_ovl_disable_vblank, + .comp_clear_vblank = mtk_ovl_clear_vblank, + .comp_layer_on = mtk_ovl_layer_on, + .comp_layer_off = mtk_ovl_layer_off, + .comp_layer_config = mtk_ovl_layer_config, +}; + +static const struct mtk_ddp_comp_funcs ddp_ovl1 = { + .comp_id = DDP_COMPONENT_OVL1, + .comp_config = mtk_ovl_config, + .comp_power_on = mtk_ovl_start, + .comp_enable_vblank = mtk_ovl_enable_vblank, + .comp_disable_vblank = mtk_ovl_disable_vblank, + .comp_clear_vblank = mtk_ovl_clear_vblank, + .comp_layer_on = mtk_ovl_layer_on, + .comp_layer_off = mtk_ovl_layer_off, + .comp_layer_config = mtk_ovl_layer_config, +}; + +static const struct mtk_ddp_comp_funcs ddp_pwm0 = { + .comp_id = DDP_COMPONENT_PWM0, +}; + +static const struct mtk_ddp_comp_funcs ddp_rdma0 = { + .comp_id = DDP_COMPONENT_RDMA0, + .comp_config = mtk_rdma_config, + .comp_power_on = mtk_rdma_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_rdma1 = { + .comp_id = DDP_COMPONENT_RDMA1, + .comp_config = mtk_rdma_config, + .comp_power_on = mtk_rdma_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_rdma2 = { + .comp_id = DDP_COMPONENT_RDMA2, + .comp_config = mtk_rdma_config, + .comp_power_on = mtk_rdma_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_ufoe = { + .comp_id = DDP_COMPONENT_UFOE, + .comp_power_on = mtk_ufoe_start, +}; + +static const struct mtk_ddp_comp_funcs ddp_wdma0 = { + .comp_id = DDP_COMPONENT_WDMA0, +}; + +static const struct mtk_ddp_comp_funcs ddp_wdma1 = { + .comp_id = DDP_COMPONENT_WDMA1, +}; + +static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = { + [MTK_DISP_OVL] = "ovl", + [MTK_DISP_RDMA] = "rdma", + [MTK_DISP_WDMA] = "wdma", + [MTK_DISP_COLOR] = "color", + [MTK_DISP_AAL] = "aal", + [MTK_DISP_GAMMA] = "gamma", + [MTK_DISP_UFOE] = "ufoe", + [MTK_DSI] = "dsi", + [MTK_DPI] = "dpi", + [MTK_DISP_PWM] = "pwm", + [MTK_DISP_MUTEX] = "mutex", + [MTK_DISP_OD] = "od", +}; + +struct mtk_ddp_comp_match { + enum mtk_ddp_comp_type type; + int alias_id; + const struct mtk_ddp_comp_funcs *funcs; +}; + +static struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = { + [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, &ddp_aal }, + [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color0 }, + [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color1 }, + [DDP_COMPONENT_DPI0] = { MTK_DPI, 0, &ddp_dpi0 }, + [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, &ddp_dsi0 }, + [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, &ddp_dsi1 }, + [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, &ddp_gamma }, + [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od }, + [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, &ddp_ovl0 }, + [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, &ddp_ovl1 }, + [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, &ddp_pwm0 }, + [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, &ddp_rdma0 }, + [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, &ddp_rdma1 }, + [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, &ddp_rdma2 }, + [DDP_COMPONENT_UFOE] = { MTK_DISP_UFOE, 0, &ddp_ufoe }, + [DDP_COMPONENT_WDMA0] = { MTK_DISP_WDMA, 0, &ddp_wdma0 }, + [DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, &ddp_wdma1 }, +}; + +int mtk_ddp_comp_get_id(struct device_node *node, + enum mtk_ddp_comp_type comp_type) +{ + int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]); + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) { + if (comp_type == mtk_ddp_matches[i].type && + (id < 0 || id == mtk_ddp_matches[i].alias_id)) + return i; + } + + return -EINVAL; +} + +int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp, + enum mtk_ddp_comp_id comp_id) +{ + if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX) + return -EINVAL; + + comp->funcs = mtk_ddp_matches[comp_id].funcs; + + if (comp_id == DDP_COMPONENT_DPI0 || + comp_id == DDP_COMPONENT_DSI0 || + comp_id == DDP_COMPONENT_PWM0) { + comp->regs = NULL; + comp->clk = NULL; + comp->irq = 0; + return 0; + } + + comp->regs = of_iomap(node, 0); + comp->irq = of_irq_get(node, 0); + comp->clk = of_clk_get(node, 0); + if (IS_ERR(comp->clk)) + comp->clk = NULL; + + return 0; +} diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h new file mode 100644 index 0000000..ae3a6e8 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h @@ -0,0 +1,86 @@ +/* + * 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 MTK_DRM_DDP_COMP_H +#define MTK_DRM_DDP_COMP_H + +#include + +struct device_node; + +enum mtk_ddp_comp_type { + MTK_DISP_OVL, + MTK_DISP_RDMA, + MTK_DISP_WDMA, + MTK_DISP_COLOR, + MTK_DISP_AAL, + MTK_DISP_GAMMA, + MTK_DISP_UFOE, + MTK_DSI, + MTK_DPI, + MTK_DISP_PWM, + MTK_DISP_MUTEX, + MTK_DISP_OD, + MTK_DDP_COMP_TYPE_MAX, +}; + +enum mtk_ddp_comp_id { + DDP_COMPONENT_AAL, + DDP_COMPONENT_COLOR0, + DDP_COMPONENT_COLOR1, + DDP_COMPONENT_DPI0, + DDP_COMPONENT_DSI0, + DDP_COMPONENT_DSI1, + DDP_COMPONENT_GAMMA, + DDP_COMPONENT_OD, + DDP_COMPONENT_OVL0, + DDP_COMPONENT_OVL1, + DDP_COMPONENT_PWM0, + DDP_COMPONENT_RDMA0, + DDP_COMPONENT_RDMA1, + DDP_COMPONENT_RDMA2, + DDP_COMPONENT_UFOE, + DDP_COMPONENT_WDMA0, + DDP_COMPONENT_WDMA1, + DDP_COMPONENT_ID_MAX, +}; + +struct mtk_ddp_comp_funcs { + enum mtk_ddp_comp_id comp_id; + void (*comp_config)(void __iomem *ovl_base, + unsigned int w, unsigned int h, unsigned int vrefresh); + void (*comp_power_on)(void __iomem *ovl_base); + void (*comp_power_off)(void __iomem *ovl_base); + void (*comp_enable_vblank)(void __iomem *ovl_base); + void (*comp_disable_vblank)(void __iomem *ovl_base); + void (*comp_clear_vblank)(void __iomem *ovl_base); + void (*comp_layer_on)(void __iomem *ovl_base, unsigned int idx); + void (*comp_layer_off)(void __iomem *ovl_base, unsigned int idx); + void (*comp_layer_config)(void __iomem *ovl_base, unsigned int idx, + unsigned int addr, unsigned int pitch, unsigned int fmt, + int x, int y, unsigned int size); +}; + +struct mtk_ddp_comp { + struct clk *clk; + void __iomem *regs; + int irq; + const struct mtk_ddp_comp_funcs *funcs; +}; + +int mtk_ddp_comp_get_id(struct device_node *node, + enum mtk_ddp_comp_type comp_type); +int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp, + enum mtk_ddp_comp_id comp_id); + +#endif /* MTK_DRM_DDP_COMP_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c new file mode 100644 index 0000000..fbca99f --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -0,0 +1,572 @@ +/* + * 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 +#include + +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_drv.h" +#include "mtk_drm_fb.h" +#include "mtk_drm_gem.h" + +#define DRIVER_NAME "mediatek" +#define DRIVER_DESC "Mediatek SoC DRM" +#define DRIVER_DATE "20150513" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +static void mtk_atomic_schedule(struct mtk_drm_private *private, + struct drm_atomic_state *state) +{ + private->commit.state = state; + schedule_work(&private->commit.work); +} + +static void mtk_atomic_complete(struct mtk_drm_private *private, + struct drm_atomic_state *state) +{ + struct drm_device *drm = private->drm; + + drm_atomic_helper_commit_modeset_disables(drm, state); + drm_atomic_helper_commit_planes(drm, state); + drm_atomic_helper_commit_modeset_enables(drm, state); + drm_atomic_helper_wait_for_vblanks(drm, state); + drm_atomic_helper_cleanup_planes(drm, state); + drm_atomic_state_free(state); +} + +static void mtk_atomic_work(struct work_struct *work) +{ + struct mtk_drm_private *private = container_of(work, + struct mtk_drm_private, commit.work); + + mtk_atomic_complete(private, private->commit.state); +} + +static int mtk_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state, + bool async) +{ + struct mtk_drm_private *private = drm->dev_private; + int ret; + + ret = drm_atomic_helper_prepare_planes(drm, state); + if (ret) + return ret; + + mutex_lock(&private->commit.lock); + flush_work(&private->commit.work); + + drm_atomic_helper_swap_state(drm, state); + + if (async) + mtk_atomic_schedule(private, state); + else + mtk_atomic_complete(private, state); + + mutex_unlock(&private->commit.lock); + + return 0; +} + +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, +}; + +static const enum mtk_ddp_comp_id mtk_ddp_main[] = { + DDP_COMPONENT_OVL0, + DDP_COMPONENT_COLOR0, + DDP_COMPONENT_AAL, + DDP_COMPONENT_OD, + DDP_COMPONENT_RDMA0, + DDP_COMPONENT_UFOE, + DDP_COMPONENT_DSI0, + DDP_COMPONENT_PWM0, +}; + +static const enum mtk_ddp_comp_id mtk_ddp_ext[] = { + DDP_COMPONENT_OVL1, + DDP_COMPONENT_COLOR1, + DDP_COMPONENT_GAMMA, + DDP_COMPONENT_RDMA1, + DDP_COMPONENT_DPI0, +}; + +static int mtk_drm_kms_init(struct drm_device *drm) +{ + struct mtk_drm_private *private = drm->dev_private; + struct platform_device *pdev; + int ret; + int i; + + pdev = of_find_device_by_node(private->mutex_node); + if (!pdev) { + dev_err(drm->dev, "Waiting for disp-mutex device %s\n", + private->mutex_node->full_name); + of_node_put(private->mutex_node); + return -EPROBE_DEFER; + } + private->mutex_dev = &pdev->dev; + + for (i = 0; i < MAX_CRTC; i++) { + if (!private->larb_node[i]) + break; + + pdev = of_find_device_by_node(private->larb_node[i]); + if (!pdev) { + dev_err(drm->dev, "Waiting for larb device %s\n", + private->larb_node[i]->full_name); + return -EPROBE_DEFER; + } + private->larb_dev[i] = &pdev->dev; + } + + drm_mode_config_init(drm); + + drm->mode_config.min_width = 64; + drm->mode_config.min_height = 64; + + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + drm->mode_config.max_width = 4096; + drm->mode_config.max_height = 4096; + drm->mode_config.funcs = &mtk_drm_mode_config_funcs; + + /* + * We currently support two fixed data streams, + * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 + * and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. + */ + private->path_len[0] = ARRAY_SIZE(mtk_ddp_main); + private->path[0] = mtk_ddp_main; + private->path_len[1] = ARRAY_SIZE(mtk_ddp_ext); + private->path[1] = mtk_ddp_ext; + + ret = component_bind_all(drm->dev, drm); + if (ret) + 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. + */ + drm->irq_enabled = true; + ret = drm_vblank_init(drm, MAX_CRTC); + if (ret < 0) + goto err_unbind; + + for (i = 0; i < MAX_CRTC; i++) { + if (!private->larb_dev[i]) + break; + + ret = mtk_smi_larb_get(private->larb_dev[i]); + if (ret) { + DRM_ERROR("Failed to get larb: %d\n", ret); + goto err_larb_get; + } + } + + drm_kms_helper_poll_init(drm); + drm_mode_config_reset(drm); + + return 0; + +err_larb_get: + for (i = i - 1; i >= 0; i--) + mtk_smi_larb_put(private->larb_dev[i]); + drm_kms_helper_poll_fini(drm); + drm_vblank_cleanup(drm); +err_unbind: + component_unbind_all(drm->dev, drm); +err_crtc: + drm_mode_config_cleanup(drm); + + return ret; +} + +static void mtk_drm_kms_deinit(struct drm_device *drm) +{ + drm_kms_helper_poll_fini(drm); + + drm_vblank_cleanup(drm); + drm_mode_config_cleanup(drm); +} + +static int mtk_drm_unload(struct drm_device *drm) +{ + mtk_drm_kms_deinit(drm); + drm->dev_private = NULL; + + return 0; +} + +static const struct vm_operations_struct mtk_drm_gem_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct file_operations mtk_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 mtk_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM, + .unload = mtk_drm_unload, + .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 = &mtk_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, + + .fops = &mtk_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_bind(struct device *dev) +{ + struct mtk_drm_private *private = dev_get_drvdata(dev); + struct drm_device *drm; + int ret; + + drm = drm_dev_alloc(&mtk_drm_driver, dev); + if (!drm) + return -ENOMEM; + + drm_dev_set_unique(drm, dev_name(dev)); + + ret = drm_dev_register(drm, 0); + if (ret < 0) + goto err_free; + + drm->dev_private = private; + private->drm = drm; + + ret = mtk_drm_kms_init(drm); + if (ret < 0) + goto err_unregister; + + return 0; + +err_unregister: + drm_dev_unregister(drm); +err_free: + drm_dev_unref(drm); + return ret; +} + +static void mtk_drm_unbind(struct device *dev) +{ + struct mtk_drm_private *private = dev_get_drvdata(dev); + + drm_put_dev(private->drm); + private->drm = NULL; +} + +static const struct component_master_ops mtk_drm_ops = { + .bind = mtk_drm_bind, + .unbind = mtk_drm_unbind, +}; + +static const struct of_device_id mtk_ddp_comp_dt_ids[] = { + { .compatible = "mediatek,mt8173-disp-ovl", .data = (void *)MTK_DISP_OVL }, + { .compatible = "mediatek,mt8173-disp-rdma", .data = (void *)MTK_DISP_RDMA }, + { .compatible = "mediatek,mt8173-disp-wdma", .data = (void *)MTK_DISP_WDMA }, + { .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR }, + { .compatible = "mediatek,mt8173-disp-aal", .data = (void *)MTK_DISP_AAL}, + { .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, }, + { .compatible = "mediatek,mt8173-disp-ufoe", .data = (void *)MTK_DISP_UFOE }, + { .compatible = "mediatek,mt8173-dsi", .data = (void *)MTK_DSI }, + { .compatible = "mediatek,mt8173-dpi", .data = (void *)MTK_DPI }, + { .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX }, + { .compatible = "mediatek,mt8173-disp-od", .data = (void *)MTK_DISP_OD }, + { } +}; + +static int mtk_drm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_drm_private *private; + struct resource *mem; + struct device_node *node; + struct component_match *match = NULL; + int ret; + int num_larbs = 0; + + private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + mutex_init(&private->commit.lock); + INIT_WORK(&private->commit.work, mtk_atomic_work); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + private->config_regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(private->config_regs)) { + ret = PTR_ERR(private->config_regs); + dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n", + ret); + return ret; + } + + /* Iterate over sibling DISP function blocks */ + for_each_child_of_node(dev->of_node->parent, node) { + const struct of_device_id *of_id; + enum mtk_ddp_comp_type comp_type; + int comp_id; + + of_id = of_match_node(mtk_ddp_comp_dt_ids, node); + if (!of_id) + continue; + + comp_type = (enum mtk_ddp_comp_type)of_id->data; + + if (comp_type == MTK_DISP_MUTEX) { + private->mutex_node = of_node_get(node); + continue; + } + + if (!of_device_is_available(node)) { + dev_dbg(dev, "Skipping disabled component %s\n", + node->full_name); + continue; + } + + comp_id = mtk_ddp_comp_get_id(node, comp_type); + if (comp_id < 0) { + dev_info(dev, "Skipping unknown component %s\n", + node->full_name); + continue; + } + + /* + * Currently only the OVL, DSI, and DPI blocks have separate + * component platform drivers. + */ + if (comp_type == MTK_DISP_OVL || + comp_type == MTK_DSI || + comp_type == MTK_DPI) { + dev_info(dev, "Adding component match for %s\n", + node->full_name); + component_match_add(dev, &match, compare_of, node); + } + + private->comp_node[comp_id] = of_node_get(node); + + if (comp_type == MTK_DISP_OVL) { + struct device_node *larb_node; + + larb_node = of_parse_phandle(node, "mediatek,larb", 0); + if (larb_node && num_larbs < MAX_CRTC) + private->larb_node[num_larbs++] = larb_node; + } + } + + if (!private->mutex_node) { + dev_err(dev, "Failed to find disp-mutex node\n"); + return -ENODEV; + } + + pm_runtime_enable(dev); + + platform_set_drvdata(pdev, private); + + return component_master_add_with_match(dev, &mtk_drm_ops, match); +} + +static int mtk_drm_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &mtk_drm_ops); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_drm_sys_suspend(struct device *dev) +{ + struct mtk_drm_private *private = dev_get_drvdata(dev); + struct drm_device *drm = private->drm; + struct drm_connector *conn; + int i; + + drm_kms_helper_poll_disable(drm); + + drm_modeset_lock_all(drm); + list_for_each_entry(conn, &drm->mode_config.connector_list, head) { + int old_dpms = conn->dpms; + + if (conn->funcs->dpms) + conn->funcs->dpms(conn, DRM_MODE_DPMS_OFF); + + /* Set the old mode back to the connector for resume */ + conn->dpms = old_dpms; + } + drm_modeset_unlock_all(drm); + + for (i = 0; i < MAX_CRTC; i++) { + if (!private->larb_dev[i]) + break; + + mtk_smi_larb_put(private->larb_dev[i]); + } + + DRM_INFO("mtk_drm_sys_suspend\n"); + return 0; +} + +static int mtk_drm_sys_resume(struct device *dev) +{ + struct mtk_drm_private *private = dev_get_drvdata(dev); + struct drm_device *drm = private->drm; + struct drm_connector *conn; + int ret; + int i; + + for (i = 0; i < MAX_CRTC; i++) { + if (!private->larb_dev[i]) + break; + + ret = mtk_smi_larb_get(private->larb_dev[i]); + if (ret) { + DRM_ERROR("mtk_smi_larb_get fail %d\n", ret); + return ret; + } + } + + drm_modeset_lock_all(drm); + list_for_each_entry(conn, &drm->mode_config.connector_list, head) { + int desired_mode = conn->dpms; + + /* + * at suspend time, we save dpms to connector->dpms, + * restore the old_dpms, and at current time, the connector + * dpms status must be DRM_MODE_DPMS_OFF. + */ + conn->dpms = DRM_MODE_DPMS_OFF; + + /* + * If the connector has been disconnected during suspend, + * disconnect it from the encoder and leave it off. We'll notify + * userspace at the end. + */ + if (conn->funcs->dpms) + conn->funcs->dpms(conn, desired_mode); + } + drm_modeset_unlock_all(drm); + + drm_kms_helper_poll_enable(drm); + + DRM_INFO("mtk_drm_sys_resume\n"); + return 0; +} + +SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend, mtk_drm_sys_resume); +#endif + +static const struct of_device_id mtk_drm_of_ids[] = { + { .compatible = "mediatek,mt8173-mmsys", }, + { } +}; + +static struct platform_driver mtk_drm_platform_driver = { + .probe = mtk_drm_probe, + .remove = mtk_drm_remove, + .driver = { + .name = "mediatek-drm", + .of_match_table = mtk_drm_of_ids, +#ifdef CONFIG_PM_SLEEP + .pm = &mtk_drm_pm_ops, +#endif + }, +}; + +static int __init mtk_drm_init(void) +{ + int ret; + + ret = platform_driver_register(&mtk_drm_platform_driver); + if (ret < 0) { + pr_err("Failed to register DRM platform driver: %d\n", ret); + goto err; + } + + ret = platform_driver_register(&mtk_disp_ovl_driver); + if (ret < 0) { + pr_err("Failed to register OVL platform driver: %d\n", ret); + goto drm_err; + } + + return 0; + +drm_err: + platform_driver_unregister(&mtk_drm_platform_driver); +err: + return ret; +} + +static void __exit mtk_drm_exit(void) +{ + platform_driver_unregister(&mtk_disp_ovl_driver); + platform_driver_unregister(&mtk_drm_platform_driver); +} + +module_init(mtk_drm_init); +module_exit(mtk_drm_exit); + +MODULE_AUTHOR("YT SHEN "); +MODULE_DESCRIPTION("Mediatek SoC DRM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h new file mode 100644 index 0000000..5e5128e --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h @@ -0,0 +1,61 @@ +/* + * 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 MTK_DRM_DRV_H +#define MTK_DRM_DRV_H + +#include +#include "mtk_drm_ddp_comp.h" + +#define MAX_CRTC 2 +#define MAX_CONNECTOR 2 + +struct device; +struct device_node; +struct drm_crtc; +struct drm_device; +struct drm_fb_helper; +struct drm_property; +struct regmap; + +struct mtk_drm_private { + struct drm_fb_helper *fb_helper; + struct drm_device *drm; + + /* + * 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]; + struct drm_property *plane_zpos_property; + unsigned int pipe; + + struct device_node *larb_node[MAX_CRTC]; + struct device *larb_dev[MAX_CRTC]; + struct device_node *mutex_node; + struct device *mutex_dev; + void __iomem *config_regs; + unsigned int path_len[MAX_CRTC]; + const enum mtk_ddp_comp_id *path[MAX_CRTC]; + struct device_node *comp_node[DDP_COMPONENT_ID_MAX]; + + struct { + struct drm_atomic_state *state; + struct work_struct work; + struct mutex lock; + } commit; +}; + +extern struct platform_driver mtk_disp_ovl_driver; + +#endif /* MTK_DRM_DRV_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c new file mode 100644 index 0000000..dfa931b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -0,0 +1,151 @@ +/* + * 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 "mtk_drm_drv.h" +#include "mtk_drm_fb.h" +#include "mtk_drm_gem.h" + +/* + * mtk specific framebuffer structure. + * + * @fb: drm framebuffer object. + * @gem_obj: array of gem objects. + */ +struct mtk_drm_fb { + struct drm_framebuffer base; + struct drm_gem_object *gem_obj[MAX_FB_OBJ]; +}; + +#define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base) + +struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane) +{ + struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); + + if (plane >= MAX_FB_OBJ) + return NULL; + + return mtk_fb->gem_obj[plane]; +} + +static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); + + return drm_gem_handle_create(file_priv, mtk_fb->gem_obj[0], handle); +} + +static void mtk_drm_fb_destroy(struct drm_framebuffer *fb) +{ + unsigned int i; + struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); + struct drm_gem_object *gem; + int nr = drm_format_num_planes(fb->pixel_format); + + drm_framebuffer_cleanup(fb); + + for (i = 0; i < nr; i++) { + gem = mtk_fb->gem_obj[i]; + drm_gem_object_unreference_unlocked(gem); + } + + kfree(mtk_fb); +} + +static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = { + .create_handle = mtk_drm_fb_create_handle, + .destroy = mtk_drm_fb_destroy, +}; + +static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode, + struct drm_gem_object **obj) +{ + struct mtk_drm_fb *mtk_fb; + unsigned int i; + int ret; + + mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL); + if (!mtk_fb) + return ERR_PTR(-ENOMEM); + + drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode); + + for (i = 0; i < drm_format_num_planes(mode->pixel_format); i++) + mtk_fb->gem_obj[i] = obj[i]; + + ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs); + if (ret) { + DRM_ERROR("failed to initialize framebuffer\n"); + kfree(mtk_fb); + return ERR_PTR(ret); + } + + return mtk_fb; +} + +struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, + struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd) +{ + unsigned int hsub, vsub, i; + struct mtk_drm_fb *mtk_fb; + struct drm_gem_object *gem[MAX_FB_OBJ]; + int err; + + hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); + vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format); + for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) { + unsigned int width = cmd->width / (i ? hsub : 1); + unsigned int height = cmd->height / (i ? vsub : 1); + unsigned int size, bpp; + + gem[i] = drm_gem_object_lookup(dev, file, cmd->handles[i]); + if (!gem[i]) { + err = -ENOENT; + goto unreference; + } + + bpp = drm_format_plane_cpp(cmd->pixel_format, i); + size = (height - 1) * cmd->pitches[i] + width * bpp; + size += cmd->offsets[i]; + + if (gem[i]->size < size) { + drm_gem_object_unreference_unlocked(gem[i]); + err = -EINVAL; + goto unreference; + } + } + + mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem); + if (IS_ERR(mtk_fb)) { + err = PTR_ERR(mtk_fb); + goto unreference; + } + + return &mtk_fb->base; + +unreference: + while (i--) + drm_gem_object_unreference_unlocked(gem[i]); + + return ERR_PTR(err); +} diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h new file mode 100644 index 0000000..9ce7307 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h @@ -0,0 +1,29 @@ +/* + * 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 MTK_DRM_FB_H +#define MTK_DRM_FB_H + +#define MAX_FB_OBJ 3 + +struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane); +struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, + struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd); + +void mtk_drm_mode_output_poll_changed(struct drm_device *dev); +int mtk_fbdev_create(struct drm_device *dev); +void mtk_fbdev_destroy(struct drm_device *dev); + +#endif /* MTK_DRM_FB_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c new file mode 100644 index 0000000..34fb94c --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c @@ -0,0 +1,189 @@ +/* + * 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 "mtk_drm_gem.h" + +struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev, + unsigned long size) +{ + struct mtk_drm_gem_obj *mtk_gem_obj; + int ret; + + size = round_up(size, PAGE_SIZE); + + mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL); + if (!mtk_gem_obj) + return ERR_PTR(-ENOMEM); + + ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size); + if (ret < 0) { + DRM_ERROR("failed to initialize gem object\n"); + kfree(mtk_gem_obj); + return ERR_PTR(ret); + } + + return mtk_gem_obj; +} + +struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, + unsigned long size, bool alloc_kmap) +{ + struct mtk_drm_gem_obj *mtk_gem; + struct drm_gem_object *obj; + int ret; + + mtk_gem = mtk_drm_gem_init(dev, size); + if (IS_ERR(mtk_gem)) + return ERR_CAST(mtk_gem); + + obj = &mtk_gem->base; + + init_dma_attrs(&mtk_gem->dma_attrs); + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs); + + if (!alloc_kmap) + dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs); + + mtk_gem->cookie = dma_alloc_attrs(dev->dev, obj->size, + (dma_addr_t *)&mtk_gem->dma_addr, GFP_KERNEL, + &mtk_gem->dma_attrs); + if (!mtk_gem->cookie) { + DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size); + ret = -ENOMEM; + goto err_gem_free; + } + + if (alloc_kmap) + mtk_gem->kvaddr = mtk_gem->cookie; + + DRM_INFO("cookie = %p dma_addr = %llx\n", + mtk_gem->cookie, mtk_gem->dma_addr); + + return mtk_gem; + +err_gem_free: + drm_gem_object_release(obj); + kfree(mtk_gem); + return ERR_PTR(ret); +} + +void mtk_drm_gem_free_object(struct drm_gem_object *obj) +{ + struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); + + drm_gem_free_mmap_offset(obj); + + /* release file pointer to gem object. */ + drm_gem_object_release(obj); + + dma_free_attrs(obj->dev->dev, obj->size, mtk_gem->cookie, + mtk_gem->dma_addr, &mtk_gem->dma_attrs); + + kfree(mtk_gem); +} + +int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct mtk_drm_gem_obj *mtk_gem; + int ret; + + args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8); + args->size = args->pitch * args->height; + + mtk_gem = mtk_drm_gem_create(dev, args->size, false); + if (IS_ERR(mtk_gem)) + return PTR_ERR(mtk_gem); + + /* + * allocate a id of idr table where the obj is registered + * and handle has the id what user can see. + */ + ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle); + if (ret) + goto err_handle_create; + + /* drop reference from allocate - handle holds it now. */ + drm_gem_object_unreference_unlocked(&mtk_gem->base); + + return 0; + +err_handle_create: + mtk_drm_gem_free_object(&mtk_gem->base); + return ret; +} + +int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return -EINVAL; + } + + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto out; + + *offset = drm_vma_node_offset_addr(&obj->vma_node); + DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); + +out: + drm_gem_object_unreference_unlocked(obj); + return ret; +} + +static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma) + +{ + int ret; + struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); + struct drm_device *drm = obj->dev; + + /* + * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear + * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). + */ + vma->vm_flags &= ~VM_PFNMAP; + vma->vm_pgoff = 0; + + ret = dma_mmap_attrs(drm->dev, vma, mtk_gem->cookie, mtk_gem->dma_addr, + obj->size, &mtk_gem->dma_attrs); + if (ret) + drm_gem_vm_close(vma); + + return ret; +} + +int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_gem_object *obj; + int ret; + + ret = drm_gem_mmap(filp, vma); + if (ret) + return ret; + + obj = vma->vm_private_data; + + return mtk_drm_gem_object_mmap(obj, vma); +} diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h new file mode 100644 index 0000000..fb7953e --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h @@ -0,0 +1,56 @@ +/* + * 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 _MTK_DRM_GEM_H_ +#define _MTK_DRM_GEM_H_ + +#include + +/* + * mtk drm buffer structure. + * + * @base: a gem object. + * - a new handle to this gem object would be created + * by drm_gem_handle_create(). + * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs() + * @kvaddr: kernel virtual address of gem buffer. + * @dma_addr: dma address of gem buffer. + * @dma_attrs: dma attributes of gem buffer. + * + * P.S. this object would be transferred to user as kms_bo.handle so + * user can access the buffer through kms_bo.handle. + */ +struct mtk_drm_gem_obj { + struct drm_gem_object base; + void __iomem *cookie; + void __iomem *kvaddr; + dma_addr_t dma_addr; + struct dma_attrs dma_attrs; +}; + +#define to_mtk_gem_obj(x) container_of(x, struct mtk_drm_gem_obj, base) + +struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev, + unsigned long size); +void mtk_drm_gem_free_object(struct drm_gem_object *gem); +struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, + unsigned long size, bool alloc_kmap); +int mtk_drm_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, struct drm_mode_create_dumb *args); +int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, uint64_t *offset); +int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); +int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, + struct vm_area_struct *vma); + +#endif diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c new file mode 100644 index 0000000..3a8843c --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: 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 "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_drv.h" +#include "mtk_drm_fb.h" +#include "mtk_drm_gem.h" +#include "mtk_drm_plane.h" + +static const uint32_t formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, +}; + +static const struct drm_plane_funcs mtk_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int mtk_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_crtc_state *crtc_state; + bool visible; + int ret; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + struct drm_rect src = { + /* 16.16 fixed point */ + .x1 = state->src_x, + .y1 = state->src_y, + .x2 = state->src_x + state->src_w, + .y2 = state->src_y + state->src_h, + }; + struct drm_rect clip = { 0, }; + + if (!fb) + return 0; + + if (!mtk_fb_get_gem_obj(fb, 0)) { + DRM_DEBUG_KMS("buffer is null\n"); + return -EFAULT; + } + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + clip.x2 = crtc_state->mode.hdisplay; + clip.y2 = crtc_state->mode.vdisplay; + + ret = drm_plane_helper_check_update(plane, state->crtc, fb, + &src, &dest, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true, &visible); + if (ret) + return ret; + + return 0; +} + +static void mtk_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct drm_gem_object *gem_obj; + struct drm_crtc *crtc = state->crtc; + struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane); + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + struct drm_rect clip = { 0, }; + + if (!crtc) + return; + + clip.x2 = state->crtc->state->mode.hdisplay; + clip.y2 = state->crtc->state->mode.vdisplay; + drm_rect_intersect(&dest, &clip); + mtk_plane->disp_size = (dest.y2 - dest.y1) << 16 | (dest.x2 - dest.x1); + + plane->fb = state->fb; + + gem_obj = mtk_fb_get_gem_obj(state->fb, 0); + mtk_plane->flip_obj = to_mtk_gem_obj(gem_obj); + mtk_plane->crtc = crtc; + + if (mtk_plane->flip_obj) + mtk_drm_crtc_plane_config(crtc, mtk_plane->idx, true, + mtk_plane->flip_obj->dma_addr); +} + +static void mtk_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane); + struct drm_crtc *crtc = old_state->crtc; + + if (!crtc) + return; + + mtk_drm_crtc_plane_config(crtc, mtk_plane->idx, false, 0); + + mtk_drm_crtc_commit(crtc); +} + +static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = { + .atomic_check = mtk_plane_atomic_check, + .atomic_update = mtk_plane_atomic_update, + .atomic_disable = mtk_plane_atomic_disable, +}; + +int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, + unsigned long possible_crtcs, enum drm_plane_type type, + unsigned int zpos, unsigned int max_plane) +{ + int err; + + err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs, + &mtk_plane_funcs, formats, ARRAY_SIZE(formats), type); + + if (err) { + DRM_ERROR("failed to initialize plane\n"); + return err; + } + + drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs); + mtk_plane->idx = zpos; + + return 0; +} diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.h b/drivers/gpu/drm/mediatek/mtk_drm_plane.h new file mode 100644 index 0000000..e092c34 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: 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. + */ + +#ifndef _MTK_DRM_PLANE_H_ +#define _MTK_DRM_PLANE_H_ + +#include + +struct mtk_drm_gem_obj; + +struct mtk_drm_plane { + struct drm_plane base; + struct drm_crtc *crtc; + unsigned int idx; + unsigned int disp_size; + + struct mtk_drm_gem_obj *flip_obj; +}; + +static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane) +{ + return container_of(plane, struct mtk_drm_plane, base); +} + +void mtk_plane_finish_page_flip(struct mtk_drm_plane *mtk_plane); +int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, + unsigned long possible_crtcs, enum drm_plane_type type, + unsigned int zpos, unsigned int max_plane); + +#endif