From patchwork Sun Jul 7 18:18:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11034197 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D2F09912 for ; Sun, 7 Jul 2019 18:23:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BEF2827F91 for ; Sun, 7 Jul 2019 18:23:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AF1042837D; Sun, 7 Jul 2019 18:23:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 2A33027F91 for ; Sun, 7 Jul 2019 18:23:28 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 21CF389ACC; Sun, 7 Jul 2019 18:23:28 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by gabe.freedesktop.org (Postfix) with ESMTPS id 7AB5A89ACC for ; Sun, 7 Jul 2019 18:23:26 +0000 (UTC) Received: from pendragon.nordic-sky.finnair.com (unknown [38.98.37.142]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 66E28CC; Sun, 7 Jul 2019 20:23:08 +0200 (CEST) From: Laurent Pinchart To: dri-devel@lists.freedesktop.org Subject: [PATCH 11/60] drm/bridge: Add driver for the TI TPD12S015 HDMI level shifter Date: Sun, 7 Jul 2019 21:18:48 +0300 Message-Id: <20190707181937.6250-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190707181937.6250-1-laurent.pinchart@ideasonboard.com> References: <20190707180852.5512-1-laurent.pinchart@ideasonboard.com> <20190707181937.6250-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 X-Mailman-Original-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1562523805; bh=fj8g4xvAoiKNgfoqVCiT1eIYuOPuPrcwaBn8icJUR7M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Pc0H4VUpo1VYMqaiwuWwsckfZ1oXyvr9lmYVCrRqcDlffsOK5e11pqcl6Q1b/VNae y4nfVEgQ6nQUPIaXcunKNcw04R7W6E4VRDFrRgxyOkSF2RFWlZZsjOWRTkDoOwo+sL qitisR+M2HyGrENgnS+kK+W49cGi3du+K3avxKMw= X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Maxime Ripard , Sebastian Reichel , Tomi Valkeinen , Sean Paul Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The TI TPD12S015 is an HDMI level shifter and ESD protector controlled through GPIOs. Add a DRM bridge driver for the device. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/bridge/Kconfig | 8 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ti-tpd12s015.c | 204 ++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 drivers/gpu/drm/bridge/ti-tpd12s015.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 295a62f65ef9..3928651a0819 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -159,6 +159,14 @@ config DRM_TI_SN65DSI86 help Texas Instruments SN65DSI86 DSI to eDP Bridge driver +config DRM_TI_TPD12S015 + tristate "TI TPD12S015 HDMI level shifter and ESD protection" + depends on OF + select DRM_KMS_HELPER + help + Texas Instruments TPD12S015 HDMI level shifter and ESD protection + driver. + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index e5987b3aaf62..ce635651e31b 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o +obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c new file mode 100644 index 000000000000..d01f0c4133a2 --- /dev/null +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TPD12S015 HDMI ESD protection & level shifter chip driver + * + * Copyright (C) 2013 Texas Instruments Incorporated + * Author: Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct tpd12s015_device { + struct drm_bridge bridge; + + struct gpio_desc *ct_cp_hpd_gpio; + struct gpio_desc *ls_oe_gpio; + struct gpio_desc *hpd_gpio; + int hpd_irq; + + struct drm_bridge *next_bridge; +}; + +static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge) +{ + return container_of(bridge, struct tpd12s015_device, bridge); +} + +static int tpd12s015_attach(struct drm_bridge *bridge, bool create_connector) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + int ret; + + if (create_connector) + return -EINVAL; + + ret = drm_bridge_attach(bridge->encoder, tpd->next_bridge, + bridge, false); + if (ret < 0) + return ret; + + gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 1); + gpiod_set_value_cansleep(tpd->ls_oe_gpio, 1); + + /* DC-DC converter needs at max 300us to get to 90% of 5V. */ + usleep_range(300, 1000); + + return 0; +} + +static void tpd12s015_detach(struct drm_bridge *bridge) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + + gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 0); + gpiod_set_value_cansleep(tpd->ls_oe_gpio, 0); +} + +static enum drm_connector_status tpd12s015_detect(struct drm_bridge *bridge) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + + if (gpiod_get_value_cansleep(tpd->hpd_gpio)) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void tpd12s015_hpd_enable(struct drm_bridge *bridge) +{ +} + +static void tpd12s015_hpd_disable(struct drm_bridge *bridge) +{ +} + +static const struct drm_bridge_funcs tpd12s015_bridge_funcs = { + .attach = tpd12s015_attach, + .detach = tpd12s015_detach, + .detect = tpd12s015_detect, + .hpd_enable = tpd12s015_hpd_enable, + .hpd_disable = tpd12s015_hpd_disable, +}; + +static irqreturn_t tpd12s015_hpd_isr(int irq, void *data) +{ + struct tpd12s015_device *tpd = data; + struct drm_bridge *bridge = &tpd->bridge; + + drm_bridge_hpd_notify(bridge, tpd12s015_detect(bridge)); + + return IRQ_HANDLED; +} + +static int tpd12s015_probe(struct platform_device *pdev) +{ + struct tpd12s015_device *tpd; + struct device_node *node; + struct gpio_desc *gpio; + int ret; + + tpd = devm_kzalloc(&pdev->dev, sizeof(*tpd), GFP_KERNEL); + if (!tpd) + return -ENOMEM; + + platform_set_drvdata(pdev, tpd); + + tpd->bridge.funcs = &tpd12s015_bridge_funcs; + tpd->bridge.of_node = pdev->dev.of_node; + tpd->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + tpd->bridge.ops = DRM_BRIDGE_OP_DETECT; + + /* Get the next bridge, connected to port@1. */ + node = of_graph_get_remote_node(pdev->dev.of_node, 1, -1); + if (!node) + return -ENODEV; + + tpd->next_bridge = of_drm_find_bridge(node); + of_node_put(node); + + if (!tpd->next_bridge) + return -EPROBE_DEFER; + + /* Get the control and HPD GPIOs. */ + gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, + GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + tpd->ct_cp_hpd_gpio = gpio; + + gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, + GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + tpd->ls_oe_gpio = gpio; + + gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_IN); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + tpd->hpd_gpio = gpio; + + /* Register the IRQ if the HPD GPIO is IRQ-capable. */ + if (tpd->hpd_gpio) + tpd->hpd_irq = gpiod_to_irq(tpd->hpd_gpio); + + if (tpd->hpd_irq) { + ret = devm_request_threaded_irq(&pdev->dev, tpd->hpd_irq, NULL, + tpd12s015_hpd_isr, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "tpd12s015 hpd", tpd); + if (ret) + return ret; + + tpd->bridge.ops |= DRM_BRIDGE_OP_HPD; + } + + /* Register the DRM bridge. */ + drm_bridge_add(&tpd->bridge); + + return 0; +} + +static int __exit tpd12s015_remove(struct platform_device *pdev) +{ + struct tpd12s015_device *tpd = platform_get_drvdata(pdev); + + drm_bridge_remove(&tpd->bridge); + + return 0; +} + +static const struct of_device_id tpd12s015_of_match[] = { + { .compatible = "ti,tpd12s015", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, tpd12s015_of_match); + +static struct platform_driver tpd12s015_driver = { + .probe = tpd12s015_probe, + .remove = __exit_p(tpd12s015_remove), + .driver = { + .name = "tpd12s015", + .of_match_table = tpd12s015_of_match, + }, +}; + +module_platform_driver(tpd12s015_driver); + +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("TPD12S015 HDMI level shifter and ESD protection driver"); +MODULE_LICENSE("GPL");