From patchwork Tue May 13 18:26:11 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Gaignard X-Patchwork-Id: 30113 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-yk0-f199.google.com (mail-yk0-f199.google.com [209.85.160.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 05C2120369 for ; Tue, 13 May 2014 18:27:04 +0000 (UTC) Received: by mail-yk0-f199.google.com with SMTP id 200sf1371945ykr.10 for ; Tue, 13 May 2014 11:27:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:delivered-to:from:to:subject:date :message-id:in-reply-to:references:cc:precedence:list-id :list-unsubscribe:list-archive:list-post:list-help:list-subscribe :mime-version:errors-to:sender:x-original-sender :x-original-authentication-results:mailing-list:content-type :content-transfer-encoding; bh=em0BMnhnMUCFeh+lTy4LqZMRv+WxOO7C0EIIT4PLqOE=; b=kEwQCrjs6jq8sNwfCZlZxdxw7XsvSzhtNuxxRvmv5bTiK08Ij7qMFvP9JFLMNKZ3OM sMUVK3a2f1XRYuMwr/3Kb+gIScF/2z2kdIGIDaT3Ny3tsVrZdKG2g4TPwQlYLbfl0lrI IDC8+WFazKauRiZPsSUCiVD6C3E+pxRWPsjfxPzCmZobf+snFf9tOWAdVCtN1y28alRM bzUYneCbTRsmpv7R2JSy5FH1orvORNMkQRT3L8DNSR+VLAlzT/ab5vuSTfyNHOy5BndS 4qk0kEsBtZ8p0XVFjL5D17PQvjv5ltw1HjfbRM8REp2p688gPgidCxUepQH+e0LOsXnG pUJA== X-Gm-Message-State: ALoCoQlN8pa6rn1wZeEOh5K8U0SouQ8hP12wKim4vC+703Y5Sbs1RYFqm7Wb4NRu+F7WPCndVg8c X-Received: by 10.58.22.166 with SMTP id e6mr18085608vef.6.1400005623828; Tue, 13 May 2014 11:27:03 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.91.201 with SMTP id z67ls1964890qgd.59.gmail; Tue, 13 May 2014 11:27:03 -0700 (PDT) X-Received: by 10.220.116.136 with SMTP id m8mr103250vcq.77.1400005623668; Tue, 13 May 2014 11:27:03 -0700 (PDT) Received: from mail-vc0-f170.google.com (mail-vc0-f170.google.com [209.85.220.170]) by mx.google.com with ESMTPS id o6si2785710vcz.76.2014.05.13.11.27.03 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 13 May 2014 11:27:03 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.170 as permitted sender) client-ip=209.85.220.170; Received: by mail-vc0-f170.google.com with SMTP id lf12so996885vcb.15 for ; Tue, 13 May 2014 11:27:03 -0700 (PDT) X-Received: by 10.52.11.230 with SMTP id t6mr3043548vdb.27.1400005623524; Tue, 13 May 2014 11:27:03 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.221.72 with SMTP id ib8csp172935vcb; Tue, 13 May 2014 11:27:02 -0700 (PDT) X-Received: by 10.68.113.5 with SMTP id iu5mr7106479pbb.60.1400005622658; Tue, 13 May 2014 11:27:02 -0700 (PDT) Received: from gabe.freedesktop.org (gabe.freedesktop.org. [131.252.210.177]) by mx.google.com with ESMTP id vu2si4897868pbc.493.2014.05.13.11.27.01 for ; Tue, 13 May 2014 11:27:02 -0700 (PDT) Received-SPF: none (google.com: dri-devel-bounces@lists.freedesktop.org does not designate permitted sender hosts) client-ip=131.252.210.177; Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 85E9B6EBA6; Tue, 13 May 2014 11:27:00 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-wg0-f50.google.com (mail-wg0-f50.google.com [74.125.82.50]) by gabe.freedesktop.org (Postfix) with ESMTP id C66CE6EBA1 for ; Tue, 13 May 2014 11:26:58 -0700 (PDT) Received: by mail-wg0-f50.google.com with SMTP id x12so778542wgg.21 for ; Tue, 13 May 2014 11:26:58 -0700 (PDT) X-Received: by 10.180.218.2 with SMTP id pc2mr22524227wic.19.1400005617852; Tue, 13 May 2014 11:26:57 -0700 (PDT) Received: from lmenx321.lme.st.com (lya72-2-88-175-155-153.fbx.proxad.net. [88.175.155.153]) by mx.google.com with ESMTPSA id f7sm23191631wjy.24.2014.05.13.11.26.55 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 13 May 2014 11:26:57 -0700 (PDT) From: Benjamin Gaignard To: dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org Subject: [PATCH v2 02/19] drm: sti: add VTG driver Date: Tue, 13 May 2014 20:26:11 +0200 Message-Id: <1400005588-3974-3-git-send-email-benjamin.gaignard@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1400005588-3974-1-git-send-email-benjamin.gaignard@linaro.org> References: <1400005588-3974-1-git-send-email-benjamin.gaignard@linaro.org> Cc: Fabien Dessenne , Benjamin Gaignard X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: 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-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: benjamin.gaignard@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.170 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 Video Time Generator drivers are used to synchronize the compositor and tvout hardware IPs by providing line count, sample count, synchronization signals (HSYNC, VSYNC) and top and bottom fields indication. VTG are used by pair for each data path (main or auxiliary): one for master and one for slave. Signed-off-by: Benjamin Gaignard Signed-off-by: Vincent Abriou Signed-off-by: Fabien Dessenne --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/sti/Kconfig | 11 + drivers/gpu/drm/sti/Makefile | 3 + drivers/gpu/drm/sti/sti_vtg.c | 467 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sti/sti_vtg.h | 20 ++ drivers/gpu/drm/sti/sti_vtg_utils.c | 99 ++++++++ drivers/gpu/drm/sti/sti_vtg_utils.h | 29 +++ 8 files changed, 632 insertions(+) create mode 100644 drivers/gpu/drm/sti/Kconfig create mode 100644 drivers/gpu/drm/sti/Makefile create mode 100644 drivers/gpu/drm/sti/sti_vtg.c create mode 100644 drivers/gpu/drm/sti/sti_vtg.h create mode 100644 drivers/gpu/drm/sti/sti_vtg_utils.c create mode 100644 drivers/gpu/drm/sti/sti_vtg_utils.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index d1cc2f6..0e30029 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -201,3 +201,5 @@ source "drivers/gpu/drm/tegra/Kconfig" source "drivers/gpu/drm/panel/Kconfig" source "drivers/gpu/drm/bridge/Kconfig" + +source "drivers/gpu/drm/sti/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 5e792b0..44f7b17 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_BOCHS) += bochs/ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ +obj-$(CONFIG_DRM_STI) += sti/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig new file mode 100644 index 0000000..3fff278 --- /dev/null +++ b/drivers/gpu/drm/sti/Kconfig @@ -0,0 +1,11 @@ +config DRM_STI + bool "DRM Support for STMicroelectronics SoC stiH41x Series" + depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM) + help + Choose this option to enable DRM on STM stiH41x chipset + +config VTG_STI + bool "Video Timing Generator for STMicroelectronics SoC stiH41x Series" + depends on DRM_STI + help + Choose this option to enable VTG on STM stiH41x chipset diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile new file mode 100644 index 0000000..33216e1 --- /dev/null +++ b/drivers/gpu/drm/sti/Makefile @@ -0,0 +1,3 @@ +ccflags-y := -Iinclude/drm + +obj-$(CONFIG_VTG_STI) += sti_vtg.o sti_vtg_utils.o diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c new file mode 100644 index 0000000..faf7539 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) STMicroelectronics SA 2013 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * Vincent Abriou + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include + +#include "sti_vtg_utils.h" +#include "sti_vtg.h" + +#define VTG_TYPE_MASTER 0 +#define VTG_TYPE_SLAVE_BY_EXT0 1 + +/* registers offset */ +#define VTG_MODE 0x0000 +#define VTG_CLKLN 0x0008 +#define VTG_HLFLN 0x000C +#define VTG_DRST_AUTOC 0x0010 +#define VTG_VID_TFO 0x0040 +#define VTG_VID_TFS 0x0044 +#define VTG_VID_BFO 0x0048 +#define VTG_VID_BFS 0x004C + +#define VTG_HOST_ITS 0x0078 +#define VTG_HOST_ITS_BCLR 0x007C +#define VTG_HOST_ITM_BCLR 0x0088 +#define VTG_HOST_ITM_BSET 0x008C + +#define VTG_H_HD_1 0x00C0 +#define VTG_TOP_V_VD_1 0x00C4 +#define VTG_BOT_V_VD_1 0x00C8 +#define VTG_TOP_V_HD_1 0x00CC +#define VTG_BOT_V_HD_1 0x00D0 + +#define VTG_H_HD_2 0x00E0 +#define VTG_TOP_V_VD_2 0x00E4 +#define VTG_BOT_V_VD_2 0x00E8 +#define VTG_TOP_V_HD_2 0x00EC +#define VTG_BOT_V_HD_2 0x00F0 + +#define VTG_H_HD_3 0x0100 +#define VTG_TOP_V_VD_3 0x0104 +#define VTG_BOT_V_VD_3 0x0108 +#define VTG_TOP_V_HD_3 0x010C +#define VTG_BOT_V_HD_3 0x0110 + +/* IRQ mask */ +#define VTG_IRQ_TOP_FIELD_MASK (1L << 1) +#define VTG_IRQ_BOTTOM_FIELD_MASK (1L << 0) +#define VTG_IRQ_MASK (VTG_IRQ_TOP_FIELD_MASK | \ + VTG_IRQ_BOTTOM_FIELD_MASK) + +/* Delay introduced by the AWG in nb of pixels */ +#define AWG_DELAY_HD (-9) +#define AWG_DELAY_ED (-8) +#define AWG_DELAY_SD (-7) + +static const struct of_device_id vtg_match_types[]; + +/* + * STI VTG data structure + * + * @nb_reg: number of memory resources to register + * @reg_names: names of the memory resources to register + * @regs: ioremapped registers + */ +#define MAX_MEM_REGION 2 +#define VTG_MASTER 0 +#define VTG_SLAVE 1 +struct sti_vtg_data { + int nb_reg; + char *reg_names[MAX_MEM_REGION]; + void __iomem *regs[MAX_MEM_REGION]; +}; + +/* + * STI VTG structure + * + * @dev: pointer to device driver + * @data: data associated to the device + * @irq: VTG irq + * @type: VTG type (main or aux) + * @notifier_list: notifier callback + */ +struct sti_vtg { + struct device *dev; + struct sti_vtg_data data; + int irq; + int type; + struct raw_notifier_head notifier_list; +}; + +/* + * STiH416: + * -------- + * VTG slave is connected to the VTG master by the ext0 input (ext1 input is + * left unconnected). + * + * MPE ! ! SAS + * ________ ! ! ________ + * | | ! ! ext0 | | + * | | _________! !_________ ----->| | + * | VTG | hsync | ! ! | hsync | | VTG | + * | |------------>| VTAC Tx !==>! VTAC Rx |-------- | | + * | master | vsync |_________! !_________| vsync | slave | + * | | ! ! ---->| | + * |________| ! ! ext1 |________| + * ! ! | + * ! ! synchro_irq + * */ +struct sti_vtg_data stih416_vtg_data = { + .nb_reg = 2, + .reg_names = {"master", "slave"}, +}; + +/* + * STiH407: + * -------- + * Only VTG master is used. There is no MPE and SAS domain => only one domain. + * + * MPE + * ________ + * | | + * | | + * | VTG | hsync + * | |------------> + * | master | vsync + * | | + * |________| + * | + * synchro_irq + * */ +struct sti_vtg_data stih407_vtg_data = { + .nb_reg = 1, + .reg_names = {"master"}, +}; + +static int vtg_reset(void __iomem *regs) +{ + writel(1, regs + VTG_DRST_AUTOC); + return 0; +} + +static int vtg_reg_dump(struct device *dev, void __iomem *regs) +{ + dev_dbg(dev, "regs %p\n", regs); + dev_dbg(dev, "VTG_MODE 0x%x\n", readl(regs + VTG_MODE)); + dev_dbg(dev, "VTG_CLKLN 0x%x\n", readl(regs + VTG_CLKLN)); + dev_dbg(dev, "VTG_HLFLN 0x%x\n", readl(regs + VTG_HLFLN)); + dev_dbg(dev, "VTG_VID_TFO 0x%x\n", readl(regs + VTG_VID_TFO)); + dev_dbg(dev, "VTG_VID_BFO 0x%x\n", readl(regs + VTG_VID_BFO)); + dev_dbg(dev, "VTG_VID_TFS 0x%x\n", readl(regs + VTG_VID_TFS)); + dev_dbg(dev, "VTG_VID_BFS 0x%x\n", readl(regs + VTG_VID_BFS)); + dev_dbg(dev, "VTG_H_HD_1 0x%x\n", readl(regs + VTG_H_HD_1)); + dev_dbg(dev, "VTG_TOP_V_VD_1 0x%x\n", readl(regs + VTG_TOP_V_VD_1)); + dev_dbg(dev, "VTG_BOT_V_VD_1 0x%x\n", readl(regs + VTG_BOT_V_VD_1)); + dev_dbg(dev, "VTG_TOP_V_HD_1 0x%x\n", readl(regs + VTG_TOP_V_HD_1)); + dev_dbg(dev, "VTG_BOT_V_HD_1 0x%x\n", readl(regs + VTG_BOT_V_HD_1)); + dev_dbg(dev, "VTG_H_HD_2 0x%x\n", readl(regs + VTG_H_HD_2)); + dev_dbg(dev, "VTG_TOP_V_VD_2 0x%x\n", readl(regs + VTG_TOP_V_VD_2)); + dev_dbg(dev, "VTG_BOT_V_VD_2 0x%x\n", readl(regs + VTG_BOT_V_VD_2)); + dev_dbg(dev, "VTG_TOP_V_HD_2 0x%x\n", readl(regs + VTG_TOP_V_HD_2)); + dev_dbg(dev, "VTG_BOT_V_HD_2 0x%x\n", readl(regs + VTG_BOT_V_HD_2)); + dev_dbg(dev, "VTG_H_HD_3 0x%x\n", readl(regs + VTG_H_HD_3)); + dev_dbg(dev, "VTG_TOP_V_VD_3 0x%x\n", readl(regs + VTG_TOP_V_VD_3)); + dev_dbg(dev, "VTG_BOT_V_VD_3 0x%x\n", readl(regs + VTG_BOT_V_VD_3)); + dev_dbg(dev, "VTG_TOP_V_HD_3 0x%x\n", readl(regs + VTG_TOP_V_HD_3)); + dev_dbg(dev, "VTG_BOT_V_HD_3 0x%x\n", readl(regs + VTG_BOT_V_HD_3)); + return 0; +} + +static int vtg_write_reg(void __iomem *regs, + int type, const struct drm_display_mode *mode) +{ + int fo, fs, h_hd, v_vd, v_hd; + + writel(mode->htotal, regs + VTG_CLKLN); + writel(mode->vtotal * 2, regs + VTG_HLFLN); + + fo = (mode->vtotal - mode->vsync_start + 1) << 16; + fo |= mode->htotal - mode->hsync_start; + writel(fo, regs + VTG_VID_TFO); + writel(fo, regs + VTG_VID_BFO); + + fs = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16; + fs |= mode->hdisplay + mode->htotal - mode->hsync_start; + writel(fs, regs + VTG_VID_TFS); + writel(fs, regs + VTG_VID_BFS); + + /* Prepare VTG set 1 for HDMI and VTG set 3 for HD DAC */ + h_hd = (mode->hsync_end - mode->hsync_start) << 16; + writel(h_hd, regs + VTG_H_HD_1); + + v_vd = (mode->vsync_end - mode->vsync_start + 1) << 16; + v_vd |= 1; + writel(v_vd, regs + VTG_TOP_V_VD_1); + writel(v_vd, regs + VTG_BOT_V_VD_1); + writel(0, regs + VTG_TOP_V_HD_1); + writel(0, regs + VTG_BOT_V_HD_1); + + /* Prepare VTG set 2 for for HD DCS */ + writel(h_hd, regs + VTG_H_HD_2); + writel(v_vd, regs + VTG_TOP_V_VD_2); + writel(v_vd, regs + VTG_BOT_V_VD_2); + writel(0, regs + VTG_TOP_V_HD_2); + writel(0, regs + VTG_BOT_V_HD_2); + + /* Prepare VTG set 3 for HD Analog in HD mode */ + h_hd = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16; + h_hd |= mode->htotal + AWG_DELAY_HD; + writel(h_hd, regs + VTG_H_HD_3); + + v_vd = (mode->vsync_end - mode->vsync_start) << 16; + v_vd |= mode->vtotal; + writel(v_vd, regs + VTG_TOP_V_VD_3); + writel(v_vd, regs + VTG_BOT_V_VD_3); + + v_hd = (mode->htotal + AWG_DELAY_HD) << 16; + v_hd |= mode->htotal + AWG_DELAY_HD; + writel(v_hd, regs + VTG_TOP_V_HD_3); + writel(v_hd, regs + VTG_BOT_V_HD_3); + + /* Mode */ + writel(type, regs + VTG_MODE); + return 0; +} + +static int vtg_disable_irq(void __iomem *regs) +{ + /* Clear interrupt status and mask */ + writel(0xFFFF, regs + VTG_HOST_ITS_BCLR); + writel(0xFFFF, regs + VTG_HOST_ITM_BCLR); + return 0; +} + +static int vtg_enable_irq(void __iomem *regs) +{ + vtg_disable_irq(regs); + writel(VTG_IRQ_MASK, regs + VTG_HOST_ITM_BSET); + return 0; +} + +int vtg_set_config(struct device *dev, const struct drm_display_mode *mode) +{ + struct sti_vtg *vtg = dev_get_drvdata(dev); + struct sti_vtg_data *data = &vtg->data; + + if (!vtg) + return 1; + + if (!data->regs[VTG_MASTER]) + return 1; + + /* write slave / master configuration */ + if (data->regs[VTG_SLAVE]) + vtg_write_reg(data->regs[VTG_SLAVE], + VTG_TYPE_SLAVE_BY_EXT0, mode); + + vtg_write_reg(data->regs[VTG_MASTER], VTG_TYPE_MASTER, mode); + + /* reset slave and then master */ + if (data->regs[VTG_SLAVE]) + vtg_reset(data->regs[VTG_SLAVE]); + + vtg_reset(data->regs[VTG_MASTER]); + + /* Enable irq for the vtg vblank synchro */ + if (data->regs[VTG_SLAVE]) + vtg_enable_irq(data->regs[VTG_SLAVE]); + else + vtg_enable_irq(data->regs[VTG_MASTER]); + + if (data->regs[VTG_SLAVE]) + vtg_reg_dump(dev, data->regs[VTG_SLAVE]); + + vtg_reg_dump(dev, data->regs[VTG_MASTER]); + + return 0; +} + +int vtg_register_client(struct device *dev, struct notifier_block *nb) +{ + struct sti_vtg *vtg = dev_get_drvdata(dev); + return raw_notifier_chain_register(&vtg->notifier_list, nb); +} + +int vtg_unregister_client(struct device *dev, struct notifier_block *nb) +{ + struct sti_vtg *vtg = dev_get_drvdata(dev); + return raw_notifier_chain_unregister(&vtg->notifier_list, nb); +} + +static irqreturn_t vtg_irq_thread(int irq, void *arg) +{ + struct sti_vtg *vtg = arg; + struct sti_vtg_data *data = &vtg->data; + void __iomem *regs; + u32 status; + + if (data->regs[VTG_SLAVE]) + regs = data->regs[VTG_SLAVE]; + else + regs = data->regs[VTG_MASTER]; + + status = readl(regs + VTG_HOST_ITS); + + writel(status, regs + VTG_HOST_ITS_BCLR); + /* TODO: check why this sync bus write solves the problem which + * is that without this line, the handler is sometimes called twice, + * first with status = 1 or 2, and immediately after with status=0 + */ + /* sync bus write */ + readl(regs + VTG_HOST_ITS); + + if (status & VTG_IRQ_TOP_FIELD_MASK) { + raw_notifier_call_chain(&vtg->notifier_list, + VTG_TOP_FIELD_EVENT, &vtg->type); + } else { + raw_notifier_call_chain(&vtg->notifier_list, + VTG_BOTTOM_FIELD_EVENT, &vtg->type); + } + + return IRQ_HANDLED; +} + +static const char *vtg_get_name(struct sti_vtg *vtg) +{ + if (vtg->type == VTG_MAIN) + return "main"; + else if (vtg->type == VTG_AUX) + return "aux"; + else + return "?"; +} + +static int vtg_compositor_bind(struct device *dev, struct device *master, + void *d) +{ + struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; + struct sti_vtg *vtg; + struct resource *res; + char irq_name[32]; + struct sti_vtg_data *data; + int i; + int ret; + + if (!np) + return -ENXIO; + + vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL); + if (!vtg) { + DRM_ERROR("Failed to allocate VTG context\n"); + return -ENOMEM; + } + + vtg->dev = dev; + + /* populate data structure depending on compatibility */ + BUG_ON(!of_match_node(vtg_match_types, np)->data); + + memcpy(&vtg->data, of_match_node(vtg_match_types, np)->data, + sizeof(struct sti_vtg_data)); + + data = &vtg->data; + + /* Get resources */ + for (i = 0; i < data->nb_reg; i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + data->reg_names[i]); + if (!res) { + DRM_ERROR("Invalid resource\n"); + return -ENOMEM; + } + data->regs[i] = devm_ioremap_nocache(dev, + res->start, + resource_size(res)); + if (IS_ERR(data->regs[i])) + return PTR_ERR(data->regs[i]); + } + + vtg->irq = platform_get_irq_byname(pdev, "synchro_irq"); + if (IS_ERR_VALUE(vtg->irq)) { + DRM_ERROR("Failed to get VTG interrupt\n"); + return vtg->irq; + } + + snprintf(irq_name, sizeof(irq_name), "vsync-%s", vtg_get_name(vtg)); + + RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); + + ret = devm_request_threaded_irq(dev, vtg->irq, NULL, vtg_irq_thread, + IRQF_ONESHOT, irq_name, vtg); + if (IS_ERR_VALUE(ret)) { + DRM_ERROR("Failed to register VTG interrupt\n"); + return ret; + } + + if (of_property_read_bool(np, "vtg-aux")) { + vtg->type = VTG_AUX; + vtg_aux = dev; + } else { + vtg->type = VTG_MAIN; + vtg_main = dev; + } + + platform_set_drvdata(pdev, vtg); + + DRM_INFO("%s VTG %s\n", __func__, vtg_get_name(vtg)); + + return 0; +} + +static void vtg_compositor_unbind(struct device *dev, struct device *master, + void *data) +{ + /* do nothing */ +} + +static const struct component_ops vtg_compositor_ops = { + .bind = vtg_compositor_bind, + .unbind = vtg_compositor_unbind, +}; + +static int vtg_compositor_probe(struct platform_device *pdev) +{ + DRM_INFO("%s\n", __func__); + return component_add(&pdev->dev, &vtg_compositor_ops); +} + +static int vtg_compositor_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vtg_compositor_ops); + return 0; +} + +static const struct of_device_id vtg_match_types[] = { + { + .compatible = "st,stih416-vtg", + .data = &stih416_vtg_data, + }, + { + .compatible = "st,stih407-vtg", + .data = &stih407_vtg_data, + }, + { /* end node */ } +}; +MODULE_DEVICE_TABLE(of, vtg_match_types); + +struct platform_driver sti_vtg_driver = { + .driver = { + .name = "sti-vtg", + .owner = THIS_MODULE, + .of_match_table = vtg_match_types, + }, + .probe = vtg_compositor_probe, + .remove = vtg_compositor_remove, +}; + +module_platform_driver(sti_vtg_driver); diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h new file mode 100644 index 0000000..da67eb3 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtg.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) STMicroelectronics SA 2013 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_VTG_H_ +#define _STI_VTG_H_ + +#include +#include + +extern struct device *vtg_main; +extern struct device *vtg_aux; + +int vtg_set_config(struct device *dev, const struct drm_display_mode *mode); +int vtg_register_client(struct device *dev, struct notifier_block *nb); +int vtg_unregister_client(struct device *dev, struct notifier_block *nb); + +#endif diff --git a/drivers/gpu/drm/sti/sti_vtg_utils.c b/drivers/gpu/drm/sti/sti_vtg_utils.c new file mode 100644 index 0000000..06bb4b4 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtg_utils.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) STMicroelectronics SA 2013 + * Author: Benjamin Gaignard for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include + +#include "sti_vtg_utils.h" +#include "sti_vtg.h" + +struct device *vtg_main; +struct device *vtg_aux; + +int sti_vtg_setconfig(int main_aux, const struct drm_display_mode *mode) +{ + if (main_aux == VTG_MAIN && vtg_main) + return vtg_set_config(vtg_main, mode); + + if (main_aux == VTG_AUX && vtg_aux) + return vtg_set_config(vtg_aux, mode); + + return 1; +} + +int sti_vtg_register_client(int main_aux, struct notifier_block *nb) +{ + if (main_aux == VTG_MAIN && vtg_main) + return vtg_register_client(vtg_main, nb); + + if (main_aux == VTG_AUX && vtg_aux) + return vtg_register_client(vtg_aux, nb); + + return 1; +} + +int sti_vtg_unregister_client(int main_aux, struct notifier_block *nb) +{ + if (main_aux == VTG_MAIN && vtg_main) + return vtg_unregister_client(vtg_main, nb); + + if (main_aux == VTG_AUX && vtg_aux) + return vtg_unregister_client(vtg_aux, nb); + + return 1; +} + +/* + * Active Front Sync Back Active + * Region Porch Porch Region + * <---------------><-------->0<---------><--------><-----------------> + * + * ///////////////| | ///////////////| + * /////////////// | | /////////////// | + * /////////////// |......... ..........|/////////////// | + * 0___________ x/ymin x/ymax + * + * <--[hv]display--> <--[hv]display--> + * <--[hv]sync_start---------> <--[hv]sync_start- + * <--[hv]sync_end-----------------------> <--[hv]sync_end--- + * <--[hv]total------------------------------------> <--[hv]total------ + */ + +/* + * sti_vtg_get_line_number + * + * @mode: display mode to be used + * @y: line + * + * Return the line number according to the display mode taking + * into account the Sync and Back Porch information. + * Video frame line numbers start at 1, y starts at 0. + * In interlaced modes the start line is the field line number of the odd + * field, but y is still defined as a progressive frame. + */ +u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y) +{ + int start_line = mode.vtotal - mode.vsync_start + 1; + + if (mode.flags & DRM_MODE_FLAG_INTERLACE) + start_line *= 2; + + return start_line + y; +} + +/* + * sti_vtg_get_pixel_number + * + * @mode: display mode to be used + * @x: row + * + * Return the pixel number according to the display mode taking + * into account the Sync and Back Porch information. + * Pixels are counted from 0. + */ +u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) +{ + return mode.htotal - mode.hsync_start + x; +} diff --git a/drivers/gpu/drm/sti/sti_vtg_utils.h b/drivers/gpu/drm/sti/sti_vtg_utils.h new file mode 100644 index 0000000..fea2852 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_vtg_utils.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) STMicroelectronics SA 2013 + * Authors: Benjamin Gaignard + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_VTG_UTILS_H_ +#define _STI_VTG_UTILS_H_ + +#include + +#define WAIT_NEXT_VSYNC_MS 50 /*ms*/ + +#define VTG_MAIN 0 +#define VTG_AUX 1 + +#define VTG_TOP_FIELD_EVENT 1 +#define VTG_BOTTOM_FIELD_EVENT 2 + +int sti_vtg_setconfig(int main_aux, const struct drm_display_mode *mode); +int sti_vtg_register_client(int main_aux, struct notifier_block *nb); +int sti_vtg_unregister_client(int main_aux, struct notifier_block *nb); + +u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y); +u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x); + +#endif