From patchwork Thu Mar 26 23:25:16 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hai Li X-Patchwork-Id: 6102641 Return-Path: X-Original-To: patchwork-linux-arm-msm@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 76833BF90F for ; Thu, 26 Mar 2015 23:26:26 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0910020443 for ; Thu, 26 Mar 2015 23:26:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 987B72042C for ; Thu, 26 Mar 2015 23:26:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753620AbbCZX0O (ORCPT ); Thu, 26 Mar 2015 19:26:14 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:59670 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753515AbbCZX0K (ORCPT ); Thu, 26 Mar 2015 19:26:10 -0400 Received: from smtp.codeaurora.org (localhost [127.0.0.1]) by smtp.codeaurora.org (Postfix) with ESMTP id 8FAC51413AF; Thu, 26 Mar 2015 23:26:09 +0000 (UTC) Received: by smtp.codeaurora.org (Postfix, from userid 486) id 812DC1413F5; Thu, 26 Mar 2015 23:26:09 +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=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from yyzubuntu32.qualcomm.com (rrcs-67-52-130-30.west.biz.rr.com [67.52.130.30]) (using TLSv1.1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) (Authenticated sender: hali@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 248741413AF; Thu, 26 Mar 2015 23:26:02 +0000 (UTC) From: Hai Li To: dri-devel@lists.freedesktop.org Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, robdclark@gmail.com, Hai Li Subject: [PATCH v2 3/4] drm/msm: Initial add DSI connector support Date: Thu, 26 Mar 2015 19:25:16 -0400 Message-Id: <1427412317-17741-4-git-send-email-hali@codeaurora.org> X-Mailer: git-send-email 1.8.2.1 In-Reply-To: <1427412317-17741-1-git-send-email-hali@codeaurora.org> References: <1427412317-17741-1-git-send-email-hali@codeaurora.org> X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This change adds the DSI connector support in msm drm driver. v1: Initial change v2: - Address comments from Archit + minor clean-ups - Rebase to not depend on msm_drm_sub_dev change [Rob's comment] Signed-off-by: Hai Li --- drivers/gpu/drm/msm/Kconfig | 11 + drivers/gpu/drm/msm/Makefile | 4 + drivers/gpu/drm/msm/dsi/dsi.c | 212 ++++ drivers/gpu/drm/msm/dsi/dsi.h | 117 ++ drivers/gpu/drm/msm/dsi/dsi_host.c | 1992 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/dsi/dsi_manager.c | 699 ++++++++++++ drivers/gpu/drm/msm/dsi/dsi_phy.c | 352 ++++++ drivers/gpu/drm/msm/msm_drv.h | 29 + 8 files changed, 3416 insertions(+) create mode 100644 drivers/gpu/drm/msm/dsi/dsi.c create mode 100644 drivers/gpu/drm/msm/dsi/dsi.h create mode 100644 drivers/gpu/drm/msm/dsi/dsi_host.c create mode 100644 drivers/gpu/drm/msm/dsi/dsi_manager.c create mode 100644 drivers/gpu/drm/msm/dsi/dsi_phy.c diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 1e6a907..5ba5631 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -35,3 +35,14 @@ config DRM_MSM_REGISTER_LOGGING Compile in support for logging register reads/writes in a format that can be parsed by envytools demsm tool. If enabled, register logging can be switched on via msm.reglog=y module param. + +config DRM_MSM_DSI + bool "Enable DSI support in MSM DRM driver" + depends on DRM_MSM + select DRM_PANEL + select DRM_MIPI_DSI + default y + help + Choose this option if you have a need for MIPI DSI connector + support. + diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 674a132..5c144cc 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -50,5 +50,9 @@ msm-y := \ msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o +msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ + dsi/dsi_host.o \ + dsi/dsi_manager.o \ + dsi/dsi_phy.o obj-$(CONFIG_DRM_MSM) += msm.o diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c new file mode 100644 index 0000000..28d1f95 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015, 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 "dsi.h" + +struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi) +{ + if (!msm_dsi || !msm_dsi->panel) + return NULL; + + return (msm_dsi->panel_flags & MIPI_DSI_MODE_VIDEO) ? + msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID] : + msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID]; +} + +static void dsi_destroy(struct msm_dsi *msm_dsi) +{ + if (!msm_dsi) + return; + + msm_dsi_manager_unregister(msm_dsi); + if (msm_dsi->host) { + msm_dsi_host_destroy(msm_dsi->host); + msm_dsi->host = NULL; + } + + platform_set_drvdata(msm_dsi->pdev, NULL); +} + +static struct msm_dsi *dsi_init(struct platform_device *pdev) +{ + struct msm_dsi *msm_dsi = NULL; + int ret; + + if (!pdev) { + dev_err(&pdev->dev, "no dsi device\n"); + ret = -ENXIO; + goto fail; + } + + msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL); + if (!msm_dsi) { + ret = -ENOMEM; + goto fail; + } + DBG("dsi probed=%p", msm_dsi); + + msm_dsi->pdev = pdev; + platform_set_drvdata(pdev, msm_dsi); + + /* Init dsi host */ + ret = msm_dsi_host_init(msm_dsi); + if (ret) + goto fail; + + /* Register to dsi manager */ + ret = msm_dsi_manager_register(msm_dsi); + if (ret) + goto fail; + + return msm_dsi; + +fail: + if (msm_dsi) + dsi_destroy(msm_dsi); + + return ERR_PTR(ret); +} + +static int dsi_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct msm_drm_private *priv = drm->dev_private; + struct platform_device *pdev = to_platform_device(dev); + struct msm_dsi *msm_dsi; + + DBG(""); + msm_dsi = dsi_init(pdev); + if (IS_ERR(msm_dsi)) + return PTR_ERR(msm_dsi); + + priv->dsi[msm_dsi->id] = msm_dsi; + + return 0; +} + +static void dsi_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct msm_drm_private *priv = drm->dev_private; + struct msm_dsi *msm_dsi = dev_get_drvdata(dev); + int id = msm_dsi->id; + + if (priv->dsi[id]) { + dsi_destroy(msm_dsi); + priv->dsi[id] = NULL; + } +} + +static const struct component_ops dsi_ops = { + .bind = dsi_bind, + .unbind = dsi_unbind, +}; + +static int dsi_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &dsi_ops); +} + +static int dsi_dev_remove(struct platform_device *pdev) +{ + DBG(""); + component_del(&pdev->dev, &dsi_ops); + return 0; +} + +static const struct of_device_id dt_match[] = { + { .compatible = "qcom,mdss-dsi-ctrl" }, + {} +}; + +static struct platform_driver dsi_driver = { + .probe = dsi_dev_probe, + .remove = dsi_dev_remove, + .driver = { + .name = "msm_dsi", + .of_match_table = dt_match, + }, +}; + +void __init msm_dsi_register(void) +{ + DBG(""); + platform_driver_register(&dsi_driver); +} + +void __exit msm_dsi_unregister(void) +{ + DBG(""); + platform_driver_unregister(&dsi_driver); +} + +int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, + struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]) +{ + struct msm_drm_private *priv = dev->dev_private; + int ret, i; + + if (WARN_ON(!encoders[MSM_DSI_VIDEO_ENCODER_ID] || + !encoders[MSM_DSI_CMD_ENCODER_ID])) + return -EINVAL; + + msm_dsi->dev = dev; + + ret = msm_dsi_host_modeset_init(msm_dsi->host, dev); + if (ret) { + dev_err(dev->dev, "failed to modeset init host: %d\n", ret); + goto fail; + } + + msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id); + if (IS_ERR(msm_dsi->bridge)) { + ret = PTR_ERR(msm_dsi->bridge); + dev_err(dev->dev, "failed to create dsi bridge: %d\n", ret); + msm_dsi->bridge = NULL; + goto fail; + } + + msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id); + if (IS_ERR(msm_dsi->connector)) { + ret = PTR_ERR(msm_dsi->connector); + dev_err(dev->dev, "failed to create dsi connector: %d\n", ret); + msm_dsi->connector = NULL; + goto fail; + } + + for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { + encoders[i]->bridge = msm_dsi->bridge; + msm_dsi->encoders[i] = encoders[i]; + } + + priv->bridges[priv->num_bridges++] = msm_dsi->bridge; + priv->connectors[priv->num_connectors++] = msm_dsi->connector; + + return 0; +fail: + if (msm_dsi) { + /* bridge/connector are normally destroyed by drm: */ + if (msm_dsi->bridge) { + msm_dsi_manager_bridge_destroy(msm_dsi->bridge); + msm_dsi->bridge = NULL; + } + if (msm_dsi->connector) { + msm_dsi->connector->funcs->destroy(msm_dsi->connector); + msm_dsi->connector = NULL; + } + } + + return ret; +} + diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h new file mode 100644 index 0000000..10f54d4 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, 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. + */ + +#ifndef __DSI_CONNECTOR_H__ +#define __DSI_CONNECTOR_H__ + +#include + +#include "drm_crtc.h" +#include "drm_mipi_dsi.h" +#include "drm_panel.h" + +#include "msm_drv.h" + +#define DSI_0 0 +#define DSI_1 1 +#define DSI_MAX 2 + +#define DSI_CLOCK_MASTER DSI_0 +#define DSI_CLOCK_SLAVE DSI_1 + +#define DSI_LEFT DSI_0 +#define DSI_RIGHT DSI_1 + +/* According to the current drm framework sequence, take the encoder of + * DSI_1 as master encoder + */ +#define DSI_ENCODER_MASTER DSI_1 +#define DSI_ENCODER_SLAVE DSI_0 + +struct msm_dsi { + struct drm_device *dev; + struct platform_device *pdev; + + struct drm_connector *connector; + struct drm_bridge *bridge; + + struct mipi_dsi_host *host; + struct msm_dsi_phy *phy; + struct drm_panel *panel; + unsigned long panel_flags; + bool phy_enabled; + + /* the encoders we are hooked to (outside of dsi block) */ + struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]; + + int id; +}; + +/* dsi manager */ +struct drm_bridge *msm_dsi_manager_bridge_init(u8 id); +void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge); +struct drm_connector *msm_dsi_manager_connector_init(u8 id); +int msm_dsi_manager_phy_enable(int id, + const unsigned long bit_rate, const unsigned long esc_rate, + u32 *clk_pre, u32 *clk_post); +void msm_dsi_manager_phy_disable(int id); +int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg); +bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len); +int msm_dsi_manager_register(struct msm_dsi *msm_dsi); +void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); + +/* msm dsi */ +struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi); + +/* dsi host */ +int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg); +void msm_dsi_host_xfer_restore(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg); +int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg); +int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg); +void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, + u32 iova, u32 len); +int msm_dsi_host_enable(struct mipi_dsi_host *host); +int msm_dsi_host_disable(struct mipi_dsi_host *host); +int msm_dsi_host_power_on(struct mipi_dsi_host *host); +int msm_dsi_host_power_off(struct mipi_dsi_host *host); +int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, + struct drm_display_mode *mode); +struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, + unsigned long *panel_flags); +int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer); +void msm_dsi_host_unregister(struct mipi_dsi_host *host); +void msm_dsi_host_destroy(struct mipi_dsi_host *host); +int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, + struct drm_device *dev); +int msm_dsi_host_init(struct msm_dsi *msm_dsi); + +/* dsi phy */ +struct msm_dsi_phy; +enum msm_dsi_phy_type { + MSM_DSI_PHY_UNKNOWN, + MSM_DSI_PHY_28NM, + MSM_DSI_PHY_MAX +}; +struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id); +int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, + const unsigned long bit_rate, const unsigned long esc_rate); +int msm_dsi_phy_disable(struct msm_dsi_phy *phy); +void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, + u32 *clk_pre, u32 *clk_post); +#endif /* __DSI_CONNECTOR_H__ */ + diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c new file mode 100644 index 0000000..ce2d4f9 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -0,0 +1,1992 @@ +/* + * Copyright (c) 2015, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include