From patchwork Mon Feb 15 06:53:19 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Archit Taneja X-Patchwork-Id: 8310221 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 5D7BD9F6E4 for ; Mon, 15 Feb 2016 06:54:21 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 22A86204DE for ; Mon, 15 Feb 2016 06:54:20 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id C888F2051C for ; Mon, 15 Feb 2016 06:54:17 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 2A5616E5E1; Mon, 15 Feb 2016 06:54:17 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from smtp.codeaurora.org (smtp.codeaurora.org [198.145.29.96]) by gabe.freedesktop.org (Postfix) with ESMTPS id AB2066E5E1 for ; Mon, 15 Feb 2016 06:54:15 +0000 (UTC) Received: from smtp.codeaurora.org (localhost [127.0.0.1]) by smtp.codeaurora.org (Postfix) with ESMTP id 93D716121D; Mon, 15 Feb 2016 06:54:15 +0000 (UTC) Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 842676121F; Mon, 15 Feb 2016 06:54:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from localhost (unknown [202.46.23.61]) (using TLSv1.2 with cipher DHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) (Authenticated sender: architt@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 1C431611F8; Mon, 15 Feb 2016 06:54:12 +0000 (UTC) From: Archit Taneja To: robdclark@gmail.com Subject: [PATCH v2 06/13] drm/msm/hdmi: Create a separate HDMI PHY driver Date: Mon, 15 Feb 2016 12:23:19 +0530 Message-Id: <1455519206-12939-7-git-send-email-architt@codeaurora.org> X-Mailer: git-send-email 1.8.2.1 In-Reply-To: <1455519206-12939-1-git-send-email-architt@codeaurora.org> References: <1455519206-12939-1-git-send-email-architt@codeaurora.org> X-Virus-Scanned: ClamAV using ClamSMTP Cc: linux-arm-msm@vger.kernel.org, dri-devel@lists.freedesktop.org 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-Virus-Scanned: ClamAV using ClamSMTP Create a phy device that represents the TX PHY and PLL parts of the HDMI block. This makes management of PHY specific resources (regulators and clocks) much easier, and makes the PHY and PLL usable independently. It also simplifies the core HDMI driver, which currently assigns phy ops among many other things. The PHY driver implementation done here is very similar to the PHY driver we already have for DSI. Keep the old hdmi_phy_funcs ops for now. The driver will use these until the HDMI PHY/PLL register offsets aren't considered as separate domains (i.e. their offsets start from 0). Signed-off-by: Archit Taneja --- drivers/gpu/drm/msm/hdmi/Makefile | 4 +- drivers/gpu/drm/msm/hdmi/hdmi.c | 2 + drivers/gpu/drm/msm/hdmi/hdmi.h | 48 +++++++- drivers/gpu/drm/msm/hdmi/hdmi_phy.c | 188 +++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c | 18 +++ drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c | 6 + drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c | 20 ++++ 7 files changed, 280 insertions(+), 6 deletions(-) create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_phy.c diff --git a/drivers/gpu/drm/msm/hdmi/Makefile b/drivers/gpu/drm/msm/hdmi/Makefile index 3810388..aeb9329 100644 --- a/drivers/gpu/drm/msm/hdmi/Makefile +++ b/drivers/gpu/drm/msm/hdmi/Makefile @@ -1,4 +1,4 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -obj-y := hdmi.o hdmi_audio.o hdmi_bridge.o hdmi_connector.o hdmi_hdcp.o \ - hdmi_i2c.o hdmi_phy_8960.o hdmi_phy_8x60.o hdmi_phy_8x74.o +obj-y := hdmi.o hdmi_phy.o hdmi_audio.o hdmi_bridge.o hdmi_connector.o \ + hdmi_hdcp.o hdmi_i2c.o hdmi_phy_8960.o hdmi_phy_8x60.o hdmi_phy_8x74.o diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 9d65442..bc22ec4 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -626,10 +626,12 @@ static struct platform_driver hdmi_driver = { void __init hdmi_register(void) { + hdmi_phy_driver_register(); platform_driver_register(&hdmi_driver); } void __exit hdmi_unregister(void) { platform_driver_unregister(&hdmi_driver); + hdmi_phy_driver_unregister(); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 2636c78..19eb1e0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -140,25 +140,65 @@ static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg) } /* - * The phy appears to be different, for example between 8960 and 8x60, - * so split the phy related functions out and load the correct one at - * runtime: + * hdmi phy: */ - struct hdmi_phy_funcs { void (*destroy)(struct hdmi_phy *phy); void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock); void (*powerdown)(struct hdmi_phy *phy); }; +enum hdmi_phy_type { + MSM_HDMI_PHY_8x60, + MSM_HDMI_PHY_8960, + MSM_HDMI_PHY_8x74, + MSM_HDMI_PHY_MAX, +}; + +struct hdmi_phy_cfg { + enum hdmi_phy_type type; + void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock); + void (*powerdown)(struct hdmi_phy *phy); + const char * const *reg_names; + int num_regs; + const char * const *clk_names; + int num_clks; +}; + +extern const struct hdmi_phy_cfg hdmi_phy_8x60_cfg; +extern const struct hdmi_phy_cfg hdmi_phy_8960_cfg; +extern const struct hdmi_phy_cfg hdmi_phy_8x74_cfg; + struct hdmi_phy { + struct platform_device *pdev; + void __iomem *mmio; + struct hdmi_phy_cfg *cfg; const struct hdmi_phy_funcs *funcs; + struct regulator **regs; + struct clk **clks; }; struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi); struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi); struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi); +static inline void hdmi_phy_write(struct hdmi_phy *phy, u32 reg, u32 data) +{ + msm_writel(data, phy->mmio + reg); +} + +static inline u32 hdmi_phy_read(struct hdmi_phy *phy, u32 reg) +{ + return msm_readl(phy->mmio + reg); +} + +int hdmi_phy_resource_enable(struct hdmi_phy *phy); +void hdmi_phy_resource_disable(struct hdmi_phy *phy); +void hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock); +void hdmi_phy_powerdown(struct hdmi_phy *phy); +void __init hdmi_phy_driver_register(void); +void __exit hdmi_phy_driver_unregister(void); + /* * audio: */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c new file mode 100644 index 0000000..184a061 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 "hdmi.h" + +static int hdmi_phy_resource_init(struct hdmi_phy *phy) +{ + struct hdmi_phy_cfg *cfg = phy->cfg; + struct device *dev = &phy->pdev->dev; + int i, ret; + + phy->regs = devm_kzalloc(dev, sizeof(phy->regs[0]) * cfg->num_regs, + GFP_KERNEL); + if (!phy->regs) + return -ENOMEM; + + phy->clks = devm_kzalloc(dev, sizeof(phy->clks[0]) * cfg->num_clks, + GFP_KERNEL); + if (!phy->clks) + return -ENOMEM; + + for (i = 0; i < cfg->num_regs; i++) { + struct regulator *reg; + + reg = devm_regulator_get(dev, cfg->reg_names[i]); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(dev, "failed to get phy regulator: %s (%d)\n", + cfg->reg_names[i], ret); + return ret; + } + + phy->regs[i] = reg; + } + + for (i = 0; i < cfg->num_clks; i++) { + struct clk *clk; + + clk = devm_clk_get(dev, cfg->clk_names[i]); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "failed to get phy clock: %s (%d)\n", + cfg->clk_names[i], ret); + return ret; + } + + phy->clks[i] = clk; + } + + return 0; +} + +int hdmi_phy_resource_enable(struct hdmi_phy *phy) +{ + struct hdmi_phy_cfg *cfg = phy->cfg; + struct device *dev = &phy->pdev->dev; + int i, ret = 0; + + pm_runtime_get_sync(dev); + + for (i = 0; i < cfg->num_regs; i++) { + ret = regulator_enable(phy->regs[i]); + if (ret) + dev_err(dev, "failed to enable regulator: %s (%d)\n", + cfg->reg_names[i], ret); + } + + for (i = 0; i < cfg->num_clks; i++) { + ret = clk_prepare_enable(phy->clks[i]); + if (ret) + dev_err(dev, "failed to enable clock: %s (%d)\n", + cfg->clk_names[i], ret); + } + + return ret; +} + +void hdmi_phy_resource_disable(struct hdmi_phy *phy) +{ + struct hdmi_phy_cfg *cfg = phy->cfg; + struct device *dev = &phy->pdev->dev; + int i; + + for (i = cfg->num_clks - 1; i >= 0; i--) + clk_disable_unprepare(phy->clks[i]); + + for (i = cfg->num_regs - 1; i >= 0; i--) + regulator_disable(phy->regs[i]); + + pm_runtime_put_sync(dev); +} + +void hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock) +{ + if (!phy || !phy->cfg->powerup) + return; + + phy->cfg->powerup(phy, pixclock); +} + +void hdmi_phy_powerdown(struct hdmi_phy *phy) +{ + if (!phy || !phy->cfg->powerdown) + return; + + phy->cfg->powerdown(phy); +} + +static int hdmi_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hdmi_phy *phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENODEV; + + phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev); + if (!phy->cfg) + return -ENODEV; + + phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY"); + if (IS_ERR(phy->mmio)) { + dev_err(dev, "%s: failed to map phy base\n", __func__); + return -ENOMEM; + } + + phy->pdev = pdev; + + ret = hdmi_phy_resource_init(phy); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + + platform_set_drvdata(pdev, phy); + + return 0; +} + +static int hdmi_phy_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id hdmi_phy_dt_match[] = { + { .compatible = "qcom,hdmi-phy-8x60", + .data = &hdmi_phy_8x60_cfg }, + { .compatible = "qcom,hdmi-phy-8960", + .data = &hdmi_phy_8960_cfg }, + { .compatible = "qcom,hdmi-phy-8x74", + .data = &hdmi_phy_8x74_cfg }, + {} +}; + +static struct platform_driver hdmi_phy_platform_driver = { + .probe = hdmi_phy_probe, + .remove = hdmi_phy_remove, + .driver = { + .name = "msm_hdmi_phy", + .of_match_table = hdmi_phy_dt_match, + }, +}; + +void __init hdmi_phy_driver_register(void) +{ + platform_driver_register(&hdmi_phy_platform_driver); +} + +void __exit hdmi_phy_driver_unregister(void) +{ + platform_driver_unregister(&hdmi_phy_platform_driver); +} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c index 3a01cb5..cbdd700 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c @@ -464,6 +464,24 @@ static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = { .powerdown = hdmi_phy_8960_powerdown, }; +static const char * const hdmi_phy_8960_reg_names[] = { + "core-vdda", +}; + +static const char * const hdmi_phy_8960_clk_names[] = { + "slave_iface_clk", +}; + +const struct hdmi_phy_cfg hdmi_phy_8960_cfg = { + .type = MSM_HDMI_PHY_8960, + .powerup = hdmi_phy_8960_powerup, + .powerdown = hdmi_phy_8960_powerdown, + .reg_names = hdmi_phy_8960_reg_names, + .num_regs = ARRAY_SIZE(hdmi_phy_8960_reg_names), + .clk_names = hdmi_phy_8960_clk_names, + .num_clks = ARRAY_SIZE(hdmi_phy_8960_clk_names), +}; + struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi) { struct hdmi_phy_8960 *phy_8960; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c index cb01421..d529164 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c @@ -155,6 +155,12 @@ static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = { .powerdown = hdmi_phy_8x60_powerdown, }; +const struct hdmi_phy_cfg hdmi_phy_8x60_cfg = { + .type = MSM_HDMI_PHY_8x60, + .powerup = hdmi_phy_8x60_powerup, + .powerdown = hdmi_phy_8x60_powerdown, +}; + struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi) { struct hdmi_phy_8x60 *phy_8x60; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c index 56ab891..5e42d92 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c @@ -67,6 +67,26 @@ static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = { .powerdown = hdmi_phy_8x74_powerdown, }; +static const char * const hdmi_phy_8x74_reg_names[] = { + "core-vdda", + "vddio", +}; + +static const char * const hdmi_phy_8x74_clk_names[] = { + "iface_clk", + "alt_iface_clk" +}; + +const struct hdmi_phy_cfg hdmi_phy_8x74_cfg = { + .type = MSM_HDMI_PHY_8x74, + .powerup = hdmi_phy_8x74_powerup, + .powerdown = hdmi_phy_8x74_powerdown, + .reg_names = hdmi_phy_8x74_reg_names, + .num_regs = ARRAY_SIZE(hdmi_phy_8x74_reg_names), + .clk_names = hdmi_phy_8x74_clk_names, + .num_clks = ARRAY_SIZE(hdmi_phy_8x74_clk_names), +}; + struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi) { struct hdmi_phy_8x74 *phy_8x74;