summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-02-23 21:35:34 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-02-23 21:35:33 -0800
commit43de29b08485a4e46139a3c825d899ce07e20b35 (patch)
treef09e610b0e8a4e18717e92a27fccd0879c619cdf /drivers/gpu
parente042032335f766c0f69c5ef9e2ba0cbb38e4f51c (diff)
parent3cc4d794524fc3c4cccf90014cfa4b47fcac321c (diff)
Merge "ARM: dts: msm: rename mdss_mdp to sde_kms and add HDMI TX device node"
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/msm/Kconfig6
-rw-r--r--drivers/gpu/drm/msm/Makefile5
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c929
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h296
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c36
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c4
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.h9
-rw-r--r--drivers/gpu/drm/msm/sde/sde_core_irq.c95
-rw-r--r--drivers/gpu/drm/msm/sde/sde_core_irq.h16
-rw-r--r--drivers/gpu/drm/msm/sde/sde_irq.c80
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c111
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.h3
13 files changed, 1497 insertions, 95 deletions
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index afd94a1e85d3..64ae44f4cbc9 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -88,3 +88,9 @@ config DRM_SDE_WB
help
Choose this option for writeback connector support.
+config DRM_SDE_HDMI
+ bool "Enable HDMI driver support in DRM SDE driver"
+ depends on DRM_MSM
+ default y
+ help
+ Choose this option if HDMI connector support is needed in SDE driver.
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index b77fdd098471..f83d32305569 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -1,4 +1,6 @@
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -Idrivers/gpu/drm/msm/dsi-staging
+ccflags-y += -Idrivers/gpu/drm/msm/hdmi
+ccflags-y += -Idrivers/gpu/drm/msm/hdmi-staging
ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi
ccflags-$(CONFIG_SYNC) += -Idrivers/staging/android
ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi
@@ -94,6 +96,9 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
dsi-staging/dsi_panel.o \
dsi-staging/dsi_display_test.o
+msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
+ hdmi-staging/sde_hdmi.o
+
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
dsi/pll/dsi_pll_28nm.o
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
new file mode 100644
index 000000000000..840202f5760c
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -0,0 +1,929 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "sde-hdmi:[%s] " fmt, __func__
+
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+
+#include "sde_kms.h"
+#include "msm_drv.h"
+#include "sde_hdmi.h"
+
+static DEFINE_MUTEX(sde_hdmi_list_lock);
+static LIST_HEAD(sde_hdmi_list);
+
+static const struct of_device_id sde_hdmi_dt_match[] = {
+ {.compatible = "qcom,hdmi-display"},
+ {}
+};
+
+static ssize_t _sde_hdmi_debugfs_dump_info_read(struct file *file,
+ char __user *buff,
+ size_t count,
+ loff_t *ppos)
+{
+ struct sde_hdmi *display = file->private_data;
+ char *buf;
+ u32 len = 0;
+
+ if (!display)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ buf = kzalloc(SZ_1K, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += snprintf(buf, SZ_4K, "name = %s\n", display->name);
+
+ if (copy_to_user(buff, buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ *ppos += len;
+
+ kfree(buf);
+ return len;
+}
+
+
+static const struct file_operations dump_info_fops = {
+ .open = simple_open,
+ .read = _sde_hdmi_debugfs_dump_info_read,
+};
+
+static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
+{
+ int rc = 0;
+ struct dentry *dir, *dump_file;
+
+ dir = debugfs_create_dir(display->name, NULL);
+ if (!dir) {
+ rc = -ENOMEM;
+ SDE_ERROR("[%s]debugfs create dir failed, rc = %d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ dump_file = debugfs_create_file("dump_info",
+ 0444,
+ dir,
+ display,
+ &dump_info_fops);
+ if (IS_ERR_OR_NULL(dump_file)) {
+ rc = PTR_ERR(dump_file);
+ SDE_ERROR("[%s]debugfs create file failed, rc=%d\n",
+ display->name, rc);
+ goto error_remove_dir;
+ }
+
+ display->root = dir;
+ return rc;
+error_remove_dir:
+ debugfs_remove(dir);
+error:
+ return rc;
+}
+
+static void _sde_hdmi_debugfs_deinit(struct sde_hdmi *display)
+{
+ debugfs_remove(display->root);
+}
+
+static void _sde_hdmi_phy_reset(struct hdmi *hdmi)
+{
+ unsigned int val;
+
+ val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
+
+ if (val & HDMI_PHY_CTRL_SW_RESET_LOW)
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val & ~HDMI_PHY_CTRL_SW_RESET);
+ else
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val | HDMI_PHY_CTRL_SW_RESET);
+
+ if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW)
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
+ else
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val | HDMI_PHY_CTRL_SW_RESET_PLL);
+
+ if (val & HDMI_PHY_CTRL_SW_RESET_LOW)
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val | HDMI_PHY_CTRL_SW_RESET);
+ else
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val & ~HDMI_PHY_CTRL_SW_RESET);
+
+ if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW)
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val | HDMI_PHY_CTRL_SW_RESET_PLL);
+ else
+ hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
+ val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
+}
+
+static int _sde_hdmi_gpio_config(struct hdmi *hdmi, bool on)
+{
+ const struct hdmi_platform_config *config = hdmi->config;
+ int ret;
+
+ if (on) {
+ if (config->ddc_clk_gpio != -1) {
+ ret = gpio_request(config->ddc_clk_gpio,
+ "HDMI_DDC_CLK");
+ if (ret) {
+ SDE_ERROR("'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_DDC_CLK", config->ddc_clk_gpio,
+ ret);
+ goto error_ddc_clk_gpio;
+ }
+ gpio_set_value_cansleep(config->ddc_clk_gpio, 1);
+ }
+
+ if (config->ddc_data_gpio != -1) {
+ ret = gpio_request(config->ddc_data_gpio,
+ "HDMI_DDC_DATA");
+ if (ret) {
+ SDE_ERROR("'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_DDC_DATA", config->ddc_data_gpio,
+ ret);
+ goto error_ddc_data_gpio;
+ }
+ gpio_set_value_cansleep(config->ddc_data_gpio, 1);
+ }
+
+ ret = gpio_request(config->hpd_gpio, "HDMI_HPD");
+ if (ret) {
+ SDE_ERROR("'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_HPD", config->hpd_gpio, ret);
+ goto error_hpd_gpio;
+ }
+ gpio_direction_output(config->hpd_gpio, 1);
+
+ if (config->mux_en_gpio != -1) {
+ ret = gpio_request(config->mux_en_gpio, "HDMI_MUX_EN");
+ if (ret) {
+ SDE_ERROR("'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_MUX_EN", config->mux_en_gpio,
+ ret);
+ goto error_en_gpio;
+ }
+ gpio_set_value_cansleep(config->mux_en_gpio, 1);
+ }
+
+ if (config->mux_sel_gpio != -1) {
+ ret = gpio_request(config->mux_sel_gpio,
+ "HDMI_MUX_SEL");
+ if (ret) {
+ SDE_ERROR("'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_MUX_SEL", config->mux_sel_gpio,
+ ret);
+ goto error_sel_gpio;
+ }
+ gpio_set_value_cansleep(config->mux_sel_gpio, 0);
+ }
+
+ if (config->mux_lpm_gpio != -1) {
+ ret = gpio_request(config->mux_lpm_gpio,
+ "HDMI_MUX_LPM");
+ if (ret) {
+ SDE_ERROR("'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_MUX_LPM",
+ config->mux_lpm_gpio, ret);
+ goto error_lpm_gpio;
+ }
+ gpio_set_value_cansleep(config->mux_lpm_gpio, 1);
+ }
+ SDE_DEBUG("gpio on");
+ } else {
+ if (config->ddc_clk_gpio != -1)
+ gpio_free(config->ddc_clk_gpio);
+
+ if (config->ddc_data_gpio != -1)
+ gpio_free(config->ddc_data_gpio);
+
+ gpio_free(config->hpd_gpio);
+
+ if (config->mux_en_gpio != -1) {
+ gpio_set_value_cansleep(config->mux_en_gpio, 0);
+ gpio_free(config->mux_en_gpio);
+ }
+
+ if (config->mux_sel_gpio != -1) {
+ gpio_set_value_cansleep(config->mux_sel_gpio, 1);
+ gpio_free(config->mux_sel_gpio);
+ }
+
+ if (config->mux_lpm_gpio != -1) {
+ gpio_set_value_cansleep(config->mux_lpm_gpio, 0);
+ gpio_free(config->mux_lpm_gpio);
+ }
+ SDE_DEBUG("gpio off");
+ }
+
+ return 0;
+
+error_lpm_gpio:
+ if (config->mux_sel_gpio != -1)
+ gpio_free(config->mux_sel_gpio);
+error_sel_gpio:
+ if (config->mux_en_gpio != -1)
+ gpio_free(config->mux_en_gpio);
+error_en_gpio:
+ gpio_free(config->hpd_gpio);
+error_hpd_gpio:
+ if (config->ddc_data_gpio != -1)
+ gpio_free(config->ddc_data_gpio);
+error_ddc_data_gpio:
+ if (config->ddc_clk_gpio != -1)
+ gpio_free(config->ddc_clk_gpio);
+error_ddc_clk_gpio:
+ return ret;
+}
+
+static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi)
+{
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+ const struct hdmi_platform_config *config = hdmi->config;
+ struct device *dev = &hdmi->pdev->dev;
+ uint32_t hpd_ctrl;
+ int i, ret;
+ unsigned long flags;
+
+ for (i = 0; i < config->hpd_reg_cnt; i++) {
+ ret = regulator_enable(hdmi->hpd_regs[i]);
+ if (ret) {
+ SDE_ERROR("failed to enable hpd regulator: %s (%d)\n",
+ config->hpd_reg_names[i], ret);
+ goto fail;
+ }
+ }
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret) {
+ SDE_ERROR("pinctrl state chg failed: %d\n", ret);
+ goto fail;
+ }
+
+ ret = _sde_hdmi_gpio_config(hdmi, true);
+ if (ret) {
+ SDE_ERROR("failed to configure GPIOs: %d\n", ret);
+ goto fail;
+ }
+
+ for (i = 0; i < config->hpd_clk_cnt; i++) {
+ if (config->hpd_freq && config->hpd_freq[i]) {
+ ret = clk_set_rate(hdmi->hpd_clks[i],
+ config->hpd_freq[i]);
+ if (ret)
+ pr_warn("failed to set clk %s (%d)\n",
+ config->hpd_clk_names[i], ret);
+ }
+
+ ret = clk_prepare_enable(hdmi->hpd_clks[i]);
+ if (ret) {
+ SDE_ERROR("failed to enable hpd clk: %s (%d)\n",
+ config->hpd_clk_names[i], ret);
+ goto fail;
+ }
+ }
+
+ hdmi_set_mode(hdmi, false);
+ _sde_hdmi_phy_reset(hdmi);
+ hdmi_set_mode(hdmi, true);
+
+ hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
+
+ /* enable HPD events: */
+ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
+ HDMI_HPD_INT_CTRL_INT_CONNECT |
+ HDMI_HPD_INT_CTRL_INT_EN);
+
+ /* set timeout to 4.1ms (max) for hardware debounce */
+ spin_lock_irqsave(&hdmi->reg_lock, flags);
+ hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
+ hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
+
+ /* Toggle HPD circuit to trigger HPD sense */
+ hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
+ ~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
+ hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
+ HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
+ spin_unlock_irqrestore(&hdmi->reg_lock, flags);
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi)
+{
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+ const struct hdmi_platform_config *config = hdmi->config;
+ struct device *dev = &hdmi->pdev->dev;
+ int i, ret = 0;
+
+ /* Disable HPD interrupt */
+ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
+
+ hdmi_set_mode(hdmi, false);
+
+ for (i = 0; i < config->hpd_clk_cnt; i++)
+ clk_disable_unprepare(hdmi->hpd_clks[i]);
+
+ ret = _sde_hdmi_gpio_config(hdmi, false);
+ if (ret)
+ pr_warn("failed to unconfigure GPIOs: %d\n", ret);
+
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret)
+ pr_warn("pinctrl state chg failed: %d\n", ret);
+
+ for (i = 0; i < config->hpd_reg_cnt; i++) {
+ ret = regulator_disable(hdmi->hpd_regs[i]);
+ if (ret)
+ pr_warn("failed to disable hpd regulator: %s (%d)\n",
+ config->hpd_reg_names[i], ret);
+ }
+}
+
+static void _sde_hdmi_hotplug_work(struct work_struct *work)
+{
+ struct sde_hdmi *sde_hdmi =
+ container_of(work, struct sde_hdmi, hpd_work);
+ struct drm_connector *connector;
+
+ if (!sde_hdmi || !sde_hdmi->ctrl.ctrl ||
+ !sde_hdmi->ctrl.ctrl->connector) {
+ SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n",
+ sde_hdmi);
+ return;
+ }
+
+ connector = sde_hdmi->ctrl.ctrl->connector;
+ drm_helper_hpd_irq_event(connector->dev);
+}
+
+static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi)
+{
+ struct hdmi *hdmi = sde_hdmi->ctrl.ctrl;
+ uint32_t hpd_int_status, hpd_int_ctrl;
+
+ /* Process HPD: */
+ hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
+ hpd_int_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
+
+ if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
+ (hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
+ bool detected = !!(hpd_int_status &
+ HDMI_HPD_INT_STATUS_CABLE_DETECTED);
+
+ /* ack & disable (temporarily) HPD events: */
+ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
+ HDMI_HPD_INT_CTRL_INT_ACK);
+
+ DRM_DEBUG("status=%04x, ctrl=%04x", hpd_int_status,
+ hpd_int_ctrl);
+
+ /* detect disconnect if we are connected or visa versa: */
+ hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
+ if (!detected)
+ hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
+ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
+
+ queue_work(hdmi->workq, &sde_hdmi->hpd_work);
+ }
+}
+
+static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id)
+{
+ struct sde_hdmi *sde_hdmi = dev_id;
+ struct hdmi *hdmi;
+
+ if (!sde_hdmi || !sde_hdmi->ctrl.ctrl) {
+ SDE_ERROR("sde_hdmi=%p or hdmi is NULL\n", sde_hdmi);
+ return IRQ_NONE;
+ }
+ hdmi = sde_hdmi->ctrl.ctrl;
+
+ /* Process HPD: */
+ _sde_hdmi_connector_irq(sde_hdmi);
+
+ /* Process DDC: */
+ hdmi_i2c_irq(hdmi->i2c);
+
+ /* Process HDCP: */
+ if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
+ hdmi_hdcp_ctrl_irq(hdmi->hdcp_ctrl);
+
+ /* TODO audio.. */
+
+ return IRQ_HANDLED;
+}
+
+int sde_hdmi_get_info(struct msm_display_info *info,
+ void *display)
+{
+ int rc = 0;
+ struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
+
+ if (!display || !info) {
+ SDE_ERROR("display=%p or info=%p is NULL\n", display, info);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hdmi_display->display_lock);
+
+ info->intf_type = DRM_MODE_CONNECTOR_HDMIA;
+ info->num_of_h_tiles = 1;
+ info->h_tile_instance[0] = 0;
+ info->is_connected = true;
+ info->capabilities = MSM_DISPLAY_CAP_HOT_PLUG | MSM_DISPLAY_CAP_EDID |
+ MSM_DISPLAY_CAP_VID_MODE;
+ info->max_width = 1920;
+ info->max_height = 1080;
+ info->compression = MSM_DISPLAY_COMPRESS_NONE;
+
+ mutex_unlock(&hdmi_display->display_lock);
+ return rc;
+}
+
+u32 sde_hdmi_get_num_of_displays(void)
+{
+ u32 count = 0;
+ struct sde_hdmi *display;
+
+ mutex_lock(&sde_hdmi_list_lock);
+
+ list_for_each_entry(display, &sde_hdmi_list, list)
+ count++;
+
+ mutex_unlock(&sde_hdmi_list_lock);
+ return count;
+}
+
+int sde_hdmi_get_displays(void **display_array, u32 max_display_count)
+{
+ struct sde_hdmi *display;
+ int i = 0;
+
+ SDE_DEBUG("\n");
+
+ if (!display_array || !max_display_count) {
+ if (!display_array)
+ SDE_ERROR("invalid param\n");
+ return 0;
+ }
+
+ mutex_lock(&sde_hdmi_list_lock);
+ list_for_each_entry(display, &sde_hdmi_list, list) {
+ if (i >= max_display_count)
+ break;
+ display_array[i++] = display;
+ }
+ mutex_unlock(&sde_hdmi_list_lock);
+
+ return i;
+}
+
+int sde_hdmi_connector_pre_deinit(struct drm_connector *connector,
+ void *display)
+{
+ struct sde_hdmi *sde_hdmi = (struct sde_hdmi *)display;
+
+ if (!sde_hdmi || !sde_hdmi->ctrl.ctrl) {
+ SDE_ERROR("sde_hdmi=%p or hdmi is NULL\n", sde_hdmi);
+ return -EINVAL;
+ }
+
+ _sde_hdmi_hdp_disable(sde_hdmi);
+
+ return 0;
+}
+
+int sde_hdmi_connector_post_init(struct drm_connector *connector,
+ void *info,
+ void *display)
+{
+ int rc = 0;
+ struct sde_hdmi *sde_hdmi = (struct sde_hdmi *)display;
+ struct hdmi *hdmi;
+
+ if (!sde_hdmi) {
+ SDE_ERROR("sde_hdmi is NULL\n");
+ return -EINVAL;
+ }
+
+ hdmi = sde_hdmi->ctrl.ctrl;
+ if (!hdmi) {
+ SDE_ERROR("hdmi is NULL\n");
+ return -EINVAL;
+ }
+
+ if (info)
+ sde_kms_info_add_keystr(info,
+ "DISPLAY_TYPE",
+ sde_hdmi->display_type);
+
+ hdmi->connector = connector;
+ INIT_WORK(&sde_hdmi->hpd_work, _sde_hdmi_hotplug_work);
+
+ /* Enable HPD detection */
+ rc = _sde_hdmi_hpd_enable(sde_hdmi);
+ if (rc)
+ SDE_ERROR("failed to enable HPD: %d\n", rc);
+
+ return rc;
+}
+
+enum drm_connector_status
+sde_hdmi_connector_detect(struct drm_connector *connector,
+ bool force,
+ void *display)
+{
+ enum drm_connector_status status = connector_status_unknown;
+ struct msm_display_info info;
+ int rc;
+
+ if (!connector || !display) {
+ SDE_ERROR("connector=%p or display=%p is NULL\n",
+ connector, display);
+ return status;
+ }
+
+ SDE_DEBUG("\n");
+
+ /* get display dsi_info */
+ memset(&info, 0x0, sizeof(info));
+ rc = sde_hdmi_get_info(&info, display);
+ if (rc) {
+ SDE_ERROR("failed to get display info, rc=%d\n", rc);
+ return connector_status_disconnected;
+ }
+
+ if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
+ status = (info.is_connected ? connector_status_connected :
+ connector_status_disconnected);
+ else
+ status = connector_status_connected;
+
+ connector->display_info.width_mm = info.width_mm;
+ connector->display_info.height_mm = info.height_mm;
+
+ return status;
+}
+
+int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
+{
+ struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
+ struct hdmi *hdmi;
+ struct edid *edid;
+ uint32_t hdmi_ctrl;
+ int ret = 0;
+
+ if (!connector || !display) {
+ SDE_ERROR("connector=%p or display=%p is NULL\n",
+ connector, display);
+ return 0;
+ }
+
+ SDE_DEBUG("\n");
+
+ hdmi = hdmi_display->ctrl.ctrl;
+ hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
+ hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
+ edid = drm_get_edid(connector, hdmi->i2c);
+
+ hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
+
+ hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);
+ drm_mode_connector_update_edid_property(connector, edid);
+
+ if (edid) {
+ ret = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ }
+
+ return ret;
+}
+
+enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ void *display)
+{
+ struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
+ struct hdmi *hdmi;
+ struct msm_drm_private *priv;
+ struct msm_kms *kms;
+ long actual, requested;
+
+ if (!connector || !display || !mode) {
+ SDE_ERROR("connector=%p or display=%p or mode=%p is NULL\n",
+ connector, display, mode);
+ return 0;
+ }
+
+ SDE_DEBUG("\n");
+
+ hdmi = hdmi_display->ctrl.ctrl;
+ priv = connector->dev->dev_private;
+ kms = priv->kms;
+ requested = 1000 * mode->clock;
+ actual = kms->funcs->round_pixclk(kms,
+ requested, hdmi->encoder);
+
+ SDE_DEBUG("requested=%ld, actual=%ld", requested, actual);
+
+ if (actual != requested)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+int sde_hdmi_dev_init(struct sde_hdmi *display)
+{
+ if (!display) {
+ SDE_ERROR("Invalid params\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int sde_hdmi_dev_deinit(struct sde_hdmi *display)
+{
+ if (!display) {
+ SDE_ERROR("Invalid params\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ int rc = 0;
+ struct sde_hdmi_ctrl *display_ctrl = NULL;
+ struct sde_hdmi *display = NULL;
+ struct drm_device *drm = NULL;
+ struct msm_drm_private *priv = NULL;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ SDE_ERROR("E\n");
+ if (!dev || !pdev || !master) {
+ pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n",
+ dev, pdev, master);
+ return -EINVAL;
+ }
+
+ drm = dev_get_drvdata(master);
+ display = platform_get_drvdata(pdev);
+ if (!drm || !display) {
+ pr_err("invalid param(s), drm %pK, display %pK\n",
+ drm, display);
+ return -EINVAL;
+ }
+
+ priv = drm->dev_private;
+ mutex_lock(&display->display_lock);
+
+ rc = _sde_hdmi_debugfs_init(display);
+ if (rc) {
+ SDE_ERROR("[%s]Debugfs init failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ display_ctrl = &display->ctrl;
+ display_ctrl->ctrl = priv->hdmi;
+ SDE_ERROR("display_ctrl->ctrl=%p\n", display_ctrl->ctrl);
+ display->drm_dev = drm;
+
+error:
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+
+static void sde_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct sde_hdmi *display = NULL;
+
+ if (!dev) {
+ SDE_ERROR("invalid params\n");
+ return;
+ }
+
+ display = platform_get_drvdata(to_platform_device(dev));
+ if (!display) {
+ SDE_ERROR("Invalid display device\n");
+ return;
+ }
+ mutex_lock(&display->display_lock);
+ (void)_sde_hdmi_debugfs_deinit(display);
+ display->drm_dev = NULL;
+ mutex_unlock(&display->display_lock);
+}
+
+static const struct component_ops sde_hdmi_comp_ops = {
+ .bind = sde_hdmi_bind,
+ .unbind = sde_hdmi_unbind,
+};
+
+static int _sde_hdmi_dev_probe(struct platform_device *pdev)
+{
+ struct sde_hdmi *display;
+ int ret = 0;
+
+ DBG("");
+ if (!pdev || !pdev->dev.of_node) {
+ SDE_ERROR("pdev not found\n");
+ return -ENODEV;
+ }
+
+ display = devm_kzalloc(&pdev->dev, sizeof(*display), GFP_KERNEL);
+ if (!display)
+ return -ENOMEM;
+
+ DBG("");
+ display->name = of_get_property(pdev->dev.of_node, "label", NULL);
+
+ display->display_type = of_get_property(pdev->dev.of_node,
+ "qcom,display-type", NULL);
+ if (!display->display_type)
+ display->display_type = "unknown";
+
+ mutex_init(&display->display_lock);
+
+ display->pdev = pdev;
+ platform_set_drvdata(pdev, display);
+ mutex_lock(&sde_hdmi_list_lock);
+ list_add(&display->list, &sde_hdmi_list);
+ mutex_unlock(&sde_hdmi_list_lock);
+ if (!sde_hdmi_dev_init(display)) {
+ ret = component_add(&pdev->dev, &sde_hdmi_comp_ops);
+ if (ret)
+ pr_err("component add failed\n");
+ }
+
+ return ret;
+}
+
+static int _sde_hdmi_dev_remove(struct platform_device *pdev)
+{
+ struct sde_hdmi *display;
+ struct sde_hdmi *pos, *tmp;
+
+ if (!pdev) {
+ SDE_ERROR("Invalid device\n");
+ return -EINVAL;
+ }
+
+ display = platform_get_drvdata(pdev);
+
+ mutex_lock(&sde_hdmi_list_lock);
+ list_for_each_entry_safe(pos, tmp, &sde_hdmi_list, list) {
+ if (pos == display) {
+ list_del(&display->list);
+ break;
+ }
+ }
+ mutex_unlock(&sde_hdmi_list_lock);
+
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, display);
+ return 0;
+}
+
+static struct platform_driver sde_hdmi_driver = {
+ .probe = _sde_hdmi_dev_probe,
+ .remove = _sde_hdmi_dev_remove,
+ .driver = {
+ .name = "sde_hdmi",
+ .of_match_table = sde_hdmi_dt_match,
+ },
+};
+
+int sde_hdmi_drm_init(struct sde_hdmi *display, struct drm_encoder *enc)
+{
+ int rc = 0;
+ struct msm_drm_private *priv = NULL;
+ struct hdmi *hdmi;
+ struct platform_device *pdev;
+
+ DBG("");
+ if (!display || !display->drm_dev || !enc) {
+ SDE_ERROR("display=%p or enc=%p or drm_dev is NULL\n",
+ display, enc);
+ return -EINVAL;
+ }
+
+ mutex_lock(&display->display_lock);
+ priv = display->drm_dev->dev_private;
+ hdmi = display->ctrl.ctrl;
+
+ if (!priv || !hdmi) {
+ SDE_ERROR("priv=%p or hdmi=%p is NULL\n",
+ priv, hdmi);
+ mutex_unlock(&display->display_lock);
+ return -EINVAL;
+ }
+
+ pdev = hdmi->pdev;
+ hdmi->dev = display->drm_dev;
+ hdmi->encoder = enc;
+
+ hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
+
+ hdmi->bridge = hdmi_bridge_init(hdmi);
+ if (IS_ERR(hdmi->bridge)) {
+ rc = PTR_ERR(hdmi->bridge);
+ SDE_ERROR("failed to create HDMI bridge: %d\n", rc);
+ hdmi->bridge = NULL;
+ goto error;
+ }
+ hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (hdmi->irq < 0) {
+ rc = hdmi->irq;
+ SDE_ERROR("failed to get irq: %d\n", rc);
+ goto error;
+ }
+
+ rc = devm_request_irq(&pdev->dev, hdmi->irq,
+ _sde_hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "sde_hdmi_isr", display);
+ if (rc < 0) {
+ SDE_ERROR("failed to request IRQ%u: %d\n",
+ hdmi->irq, rc);
+ goto error;
+ }
+
+ enc->bridge = hdmi->bridge;
+ priv->bridges[priv->num_bridges++] = hdmi->bridge;
+
+ mutex_unlock(&display->display_lock);
+ return 0;
+
+error:
+ /* bridge is normally destroyed by drm: */
+ if (hdmi->bridge) {
+ hdmi_bridge_destroy(hdmi->bridge);
+ hdmi->bridge = NULL;
+ }
+ mutex_unlock(&display->display_lock);
+ return rc;
+}
+
+int sde_hdmi_drm_deinit(struct sde_hdmi *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ SDE_ERROR("Invalid params\n");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __init sde_hdmi_register(void)
+{
+ int rc = 0;
+
+ DBG("");
+ rc = platform_driver_register(&sde_hdmi_driver);
+ return rc;
+}
+
+static void __exit sde_hdmi_unregister(void)
+{
+ platform_driver_unregister(&sde_hdmi_driver);
+}
+
+module_init(sde_hdmi_register);
+module_exit(sde_hdmi_unregister);
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
new file mode 100644
index 000000000000..9a948b0fbcf2
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2016-2017, 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 _SDE_HDMI_H_
+#define _SDE_HDMI_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "hdmi.h"
+
+/**
+ * struct sde_hdmi_info - defines hdmi display properties
+ * @display_type: Display type as defined by device tree.
+ * @is_hot_pluggable: Can panel be hot plugged.
+ * @is_connected: Is panel connected.
+ * @is_edid_supported: Does panel support reading EDID information.
+ * @width_mm: Physical width of panel in millimeters.
+ * @height_mm: Physical height of panel in millimeters.
+ */
+struct sde_hdmi_info {
+ const char *display_type;
+
+ /* HPD */
+ bool is_hot_pluggable;
+ bool is_connected;
+ bool is_edid_supported;
+
+ /* Physical properties */
+ u32 width_mm;
+ u32 height_mm;
+};
+
+/**
+ * struct sde_hdmi_ctrl - hdmi ctrl/phy information for the display
+ * @ctrl: Handle to the HDMI controller device.
+ * @ctrl_of_node: pHandle to the HDMI controller device.
+ * @hdmi_ctrl_idx: HDMI controller instance id.
+ */
+struct sde_hdmi_ctrl {
+ /* controller info */
+ struct hdmi *ctrl;
+ struct device_node *ctrl_of_node;
+ u32 hdmi_ctrl_idx;
+};
+
+/**
+ * struct sde_hdmi - hdmi display information
+ * @pdev: Pointer to platform device.
+ * @drm_dev: DRM device associated with the display.
+ * @name: Name of the display.
+ * @display_type: Display type as defined in device tree.
+ * @list: List pointer.
+ * @display_lock: Mutex for sde_hdmi interface.
+ * @ctrl: Controller information for HDMI display.
+ * @num_of_modes: Number of modes supported by display.
+ * @is_tpg_enabled: TPG state.
+ * @hpd_work: HPD work structure.
+ * @root: Debug fs root entry.
+ */
+struct sde_hdmi {
+ struct platform_device *pdev;
+ struct drm_device *drm_dev;
+
+ const char *name;
+ const char *display_type;
+ struct list_head list;
+ struct mutex display_lock;
+
+ struct sde_hdmi_ctrl ctrl;
+
+ u32 num_of_modes;
+ bool is_tpg_enabled;
+
+ struct work_struct hpd_work;
+
+ /* DEBUG FS */
+ struct dentry *root;
+};
+
+#ifdef CONFIG_DRM_SDE_HDMI
+/**
+ * sde_hdmi_get_num_of_displays() - returns number of display devices
+ * supported.
+ *
+ * Return: number of displays.
+ */
+u32 sde_hdmi_get_num_of_displays(void);
+
+/**
+ * sde_hdmi_get_displays() - returns the display list that's available.
+ * @display_array: Pointer to display list
+ * @max_display_count: Number of maximum displays in the list
+ *
+ * Return: number of available displays.
+ */
+int sde_hdmi_get_displays(void **display_array, u32 max_display_count);
+
+/**
+ * sde_hdmi_connector_pre_deinit()- perform additional deinitialization steps
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ *
+ * Return: error code
+ */
+int sde_hdmi_connector_pre_deinit(struct drm_connector *connector,
+ void *display);
+
+/**
+ * sde_hdmi_connector_post_init()- perform additional initialization steps
+ * @connector: Pointer to drm connector structure
+ * @info: Pointer to sde connector info structure
+ * @display: Pointer to private display handle
+ *
+ * Return: error code
+ */
+int sde_hdmi_connector_post_init(struct drm_connector *connector,
+ void *info,
+ void *display);
+
+/**
+ * sde_hdmi_connector_detect()- determine if connector is connected
+ * @connector: Pointer to drm connector structure
+ * @force: Force detect setting from drm framework
+ * @display: Pointer to private display handle
+ *
+ * Return: error code
+ */
+enum drm_connector_status
+sde_hdmi_connector_detect(struct drm_connector *connector,
+ bool force,
+ void *display);
+
+/**
+ * sde_hdmi_connector_get_modes - add drm modes via drm_mode_probed_add()
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+
+ * Returns: Number of modes added
+ */
+int sde_hdmi_connector_get_modes(struct drm_connector *connector,
+ void *display);
+
+/**
+ * sde_hdmi_mode_valid - determine if specified mode is valid
+ * @connector: Pointer to drm connector structure
+ * @mode: Pointer to drm mode structure
+ * @display: Pointer to private display handle
+ *
+ * Returns: Validity status for specified mode
+ */
+enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ void *display);
+
+/**
+ * sde_hdmi_dev_init() - Initializes the display device
+ * @display: Handle to the display.
+ *
+ * Initialization will acquire references to the resources required for the
+ * display hardware to function.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_dev_init(struct sde_hdmi *display);
+
+/**
+ * sde_hdmi_dev_deinit() - Desinitializes the display device
+ * @display: Handle to the display.
+ *
+ * All the resources acquired during device init will be released.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_dev_deinit(struct sde_hdmi *display);
+
+/**
+ * sde_hdmi_drm_init() - initializes DRM objects for the display device.
+ * @display: Handle to the display.
+ * @encoder: Pointer to the encoder object which is connected to the
+ * display.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_drm_init(struct sde_hdmi *display,
+ struct drm_encoder *enc);
+
+/**
+ * sde_hdmi_drm_deinit() - destroys DRM objects assosciated with the display
+ * @display: Handle to the display.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_drm_deinit(struct sde_hdmi *display);
+
+/**
+ * sde_hdmi_get_info() - returns the display properties
+ * @display: Handle to the display.
+ * @info: Pointer to the structure where info is stored.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_get_info(struct msm_display_info *info,
+ void *display);
+
+#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
+
+static inline u32 sde_hdmi_get_num_of_displays(void)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_get_displays(void **display_array,
+ u32 max_display_count)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_connector_pre_deinit(struct drm_connector *connector,
+ void *display)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_connector_post_init(struct drm_connector *connector,
+ void *info,
+ void *display)
+{
+ return 0;
+}
+
+static inline enum drm_connector_status
+sde_hdmi_connector_detect(struct drm_connector *connector,
+ bool force,
+ void *display)
+{
+ return connector_status_disconnected;
+}
+
+static inline int sde_hdmi_connector_get_modes(struct drm_connector *connector,
+ void *display)
+{
+ return 0;
+}
+
+static inline enum drm_mode_status sde_hdmi_mode_valid(
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ void *display)
+{
+ return MODE_OK;
+}
+
+static inline int sde_hdmi_dev_init(struct sde_hdmi *display)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_dev_deinit(struct sde_hdmi *display)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_drm_init(struct sde_hdmi *display,
+ struct drm_encoder *enc)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_drm_deinit(struct sde_hdmi *display)
+{
+ return 0;
+}
+
+static inline int sde_hdmi_get_info(struct msm_display_info *info,
+ void *display)
+{
+ return 0;
+}
+#endif /*#else of CONFIG_DRM_SDE_HDMI*/
+#endif /* _SDE_HDMI_H_ */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index ba5921149ac3..723ec887252b 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -234,6 +234,11 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
dev_warn(&pdev->dev, "failed to init hdcp: disabled\n");
hdmi->hdcp_ctrl = NULL;
}
+ /*making it false currently to avoid ifdefs
+ *will get rid of this flag when HDCP SW
+ *support gets added to HDMI DRM driver
+ */
+ hdmi->is_hdcp_supported = false;
return hdmi;
@@ -389,7 +394,34 @@ static struct hdmi_platform_config hdmi_tx_8996_config = {
.hpd_freq = hpd_clk_freq_8x74,
};
+/*TO DO*/
+static const char *pwr_reg_names_8x98[] = {"core-vdda", "core-vcc"};
+/*TO DO*/
+static const char *hpd_reg_names_8x98[] = {"hpd-gdsc", "hpd-5v"};
+
+static const char *pwr_clk_names_8x98[] = {"core_extp_clk",
+ "hpd_alt_iface_clk"};
+
+static const char *hpd_clk_names_8x98[] = {"hpd_iface_clk",
+ "hpd_core_clk",
+ "hpd_mdp_core_clk",
+ "mnoc_clk",
+ "hpd_misc_ahb_clk",
+ "hpd_bus_clk"};
+
+static unsigned long hpd_clk_freq_8x98[] = {0, 19200000, 0, 0, 0, 0};
+
+static struct hdmi_platform_config hdmi_tx_8998_config = {
+ .phy_init = NULL,
+ HDMI_CFG(pwr_reg, 8x98),
+ HDMI_CFG(hpd_reg, 8x98),
+ HDMI_CFG(pwr_clk, 8x98),
+ HDMI_CFG(hpd_clk, 8x98),
+ .hpd_freq = hpd_clk_freq_8x98,
+};
+
static const struct of_device_id dt_match[] = {
+ { .compatible = "qcom,hdmi-tx-8998", .data = &hdmi_tx_8998_config },
{ .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config },
{ .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config },
{ .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config },
@@ -425,7 +457,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
#ifdef CONFIG_OF
struct device_node *of_node = dev->of_node;
const struct of_device_id *match;
-
match = of_match_node(dt_match, of_node);
if (match && match->data) {
hdmi_cfg = (struct hdmi_platform_config *)match->data;
@@ -449,6 +480,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
static const char *hpd_clk_names[] = {
"core_clk", "master_iface_clk", "slave_iface_clk",
};
+
if (cpu_is_apq8064()) {
static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
config.phy_init = hdmi_phy_8960_init;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index e22ddcd51248..5ee7a0feda07 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -70,7 +70,7 @@ struct hdmi {
struct drm_encoder *encoder;
bool hdmi_mode; /* are we in hdmi mode? */
-
+ bool is_hdcp_supported;
int irq;
struct workqueue_struct *workq;
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 6e27ac09c7a4..31cf25ab5691 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -65,8 +65,12 @@ static void sde_connector_destroy(struct drm_connector *connector)
c_conn = to_sde_connector(connector);
+ if (c_conn->ops.pre_deinit)
+ c_conn->ops.pre_deinit(connector, c_conn->display);
+
if (c_conn->blob_caps)
drm_property_unreference_blob(c_conn->blob_caps);
+
msm_property_destroy(&c_conn->property_info);
drm_connector_unregister(connector);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 7d86528c81bc..3f26ee7d5965 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -44,6 +44,15 @@ struct sde_connector_ops {
void *display);
/**
+ * pre_deinit - perform additional deinitialization steps
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * Returns: Zero on success
+ */
+ int (*pre_deinit)(struct drm_connector *connector,
+ void *display);
+
+ /**
* detect - determine if connector is connected
* @connector: Pointer to drm connector structure
* @force: Force detect setting from drm framework
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index b2853e874d92..dbfc2dd11a17 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -432,6 +432,99 @@ void sde_core_irq_uninstall(struct sde_kms *sde_kms)
sde_kms->irq_obj.total_irqs = 0;
}
+static void sde_hw_irq_mask(struct irq_data *irqd)
+{
+ struct sde_kms *sde_kms;
+
+ if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
+ SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
+ return;
+ }
+ sde_kms = irq_data_get_irq_chip_data(irqd);
+
+ smp_mb__before_atomic();
+ clear_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
+ smp_mb__after_atomic();
+}
+
+static void sde_hw_irq_unmask(struct irq_data *irqd)
+{
+ struct sde_kms *sde_kms;
+
+ if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
+ SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
+ return;
+ }
+ sde_kms = irq_data_get_irq_chip_data(irqd);
+
+ smp_mb__before_atomic();
+ set_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
+ smp_mb__after_atomic();
+}
+
+static struct irq_chip sde_hw_irq_chip = {
+ .name = "sde",
+ .irq_mask = sde_hw_irq_mask,
+ .irq_unmask = sde_hw_irq_unmask,
+};
+
+static int sde_hw_irqdomain_map(struct irq_domain *domain,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ struct sde_kms *sde_kms;
+ int rc;
+
+ if (!domain || !domain->host_data) {
+ SDE_ERROR("invalid parameters domain %d\n", domain != 0);
+ return -EINVAL;
+ }
+ sde_kms = domain->host_data;
+
+ irq_set_chip_and_handler(irq, &sde_hw_irq_chip, handle_level_irq);
+ rc = irq_set_chip_data(irq, sde_kms);
+
+ return rc;
+}
+
+static struct irq_domain_ops sde_hw_irqdomain_ops = {
+ .map = sde_hw_irqdomain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+int sde_core_irq_domain_add(struct sde_kms *sde_kms)
+{
+ struct device *dev;
+ struct irq_domain *domain;
+
+ if (!sde_kms->dev || !sde_kms->dev->dev) {
+ pr_err("invalid device handles\n");
+ return -EINVAL;
+ }
+
+ dev = sde_kms->dev->dev;
+
+ domain = irq_domain_add_linear(dev->of_node, 32,
+ &sde_hw_irqdomain_ops, sde_kms);
+ if (!domain) {
+ pr_err("failed to add irq_domain\n");
+ return -EINVAL;
+ }
+
+ sde_kms->irq_controller.enabled_mask = 0;
+ sde_kms->irq_controller.domain = domain;
+
+ return 0;
+}
+
+int sde_core_irq_domain_fini(struct sde_kms *sde_kms)
+{
+ if (sde_kms->irq_controller.domain) {
+ irq_domain_remove(sde_kms->irq_controller.domain);
+ sde_kms->irq_controller.domain = NULL;
+ }
+ return 0;
+}
+
irqreturn_t sde_core_irq(struct sde_kms *sde_kms)
{
/*
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.h b/drivers/gpu/drm/msm/sde/sde_core_irq.h
index 92642e73daa8..ee1b9bd1d32b 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -38,6 +38,20 @@ int sde_core_irq_postinstall(struct sde_kms *sde_kms);
void sde_core_irq_uninstall(struct sde_kms *sde_kms);
/**
+ * sde_core_irq_domain_add - Add core IRQ domain for SDE
+ * @sde_kms: SDE handle
+ * @return: none
+ */
+int sde_core_irq_domain_add(struct sde_kms *sde_kms);
+
+/**
+ * sde_core_irq_domain_fini - uninstall core IRQ domain
+ * @sde_kms: SDE handle
+ * @return: 0 if success; error code otherwise
+ */
+int sde_core_irq_domain_fini(struct sde_kms *sde_kms);
+
+/**
* sde_core_irq - core IRQ handler
* @sde_kms: SDE handle
* @return: interrupt handling status
diff --git a/drivers/gpu/drm/msm/sde/sde_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c
index 909d6df38260..eeb7a0002eab 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -49,86 +49,14 @@ irqreturn_t sde_irq(struct msm_kms *kms)
return IRQ_HANDLED;
}
-static void sde_hw_irq_mask(struct irq_data *irqd)
-{
- struct sde_kms *sde_kms;
-
- if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
- SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
- return;
- }
- sde_kms = irq_data_get_irq_chip_data(irqd);
-
- smp_mb__before_atomic();
- clear_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
- smp_mb__after_atomic();
-}
-
-static void sde_hw_irq_unmask(struct irq_data *irqd)
-{
- struct sde_kms *sde_kms;
-
- if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
- SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
- return;
- }
- sde_kms = irq_data_get_irq_chip_data(irqd);
-
- smp_mb__before_atomic();
- set_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
- smp_mb__after_atomic();
-}
-
-static struct irq_chip sde_hw_irq_chip = {
- .name = "sde",
- .irq_mask = sde_hw_irq_mask,
- .irq_unmask = sde_hw_irq_unmask,
-};
-
-static int sde_hw_irqdomain_map(struct irq_domain *domain,
- unsigned int irq, irq_hw_number_t hwirq)
-{
- struct sde_kms *sde_kms;
- int rc;
-
- if (!domain || !domain->host_data) {
- SDE_ERROR("invalid parameters domain %d\n", domain != 0);
- return -EINVAL;
- }
- sde_kms = domain->host_data;
-
- irq_set_chip_and_handler(irq, &sde_hw_irq_chip, handle_level_irq);
- rc = irq_set_chip_data(irq, sde_kms);
-
- return rc;
-}
-
-static struct irq_domain_ops sde_hw_irqdomain_ops = {
- .map = sde_hw_irqdomain_map,
- .xlate = irq_domain_xlate_onecell,
-};
-
void sde_irq_preinstall(struct msm_kms *kms)
{
struct sde_kms *sde_kms = to_sde_kms(kms);
- struct device *dev;
- struct irq_domain *domain;
if (!sde_kms->dev || !sde_kms->dev->dev) {
pr_err("invalid device handles\n");
return;
}
- dev = sde_kms->dev->dev;
-
- domain = irq_domain_add_linear(dev->of_node, 32,
- &sde_hw_irqdomain_ops, sde_kms);
- if (!domain) {
- pr_err("failed to add irq_domain\n");
- return;
- }
-
- sde_kms->irq_controller.enabled_mask = 0;
- sde_kms->irq_controller.domain = domain;
sde_core_irq_preinstall(sde_kms);
}
@@ -158,9 +86,5 @@ void sde_irq_uninstall(struct msm_kms *kms)
}
sde_core_irq_uninstall(sde_kms);
-
- if (sde_kms->irq_controller.domain) {
- irq_domain_remove(sde_kms->irq_controller.domain);
- sde_kms->irq_controller.domain = NULL;
- }
+ sde_core_irq_domain_fini(sde_kms);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 576e7d1e7189..7c23421fb8a7 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -27,6 +27,7 @@
#include "dsi_display.h"
#include "dsi_drm.h"
#include "sde_wb.h"
+#include "sde_hdmi.h"
#include "sde_kms.h"
#include "sde_core_irq.h"
@@ -504,8 +505,30 @@ static int _sde_kms_get_displays(struct sde_kms *sde_kms)
wb_display_get_displays(sde_kms->wb_displays,
sde_kms->wb_display_count);
}
+
+ /* hdmi */
+ sde_kms->hdmi_displays = NULL;
+ sde_kms->hdmi_display_count = sde_hdmi_get_num_of_displays();
+ SDE_DEBUG("hdmi display count=%d", sde_kms->hdmi_display_count);
+ if (sde_kms->hdmi_display_count) {
+ sde_kms->hdmi_displays = kcalloc(sde_kms->hdmi_display_count,
+ sizeof(void *),
+ GFP_KERNEL);
+ if (!sde_kms->hdmi_displays) {
+ SDE_ERROR("failed to allocate hdmi displays\n");
+ goto exit_deinit_hdmi;
+ }
+ sde_kms->hdmi_display_count =
+ sde_hdmi_get_displays(sde_kms->hdmi_displays,
+ sde_kms->hdmi_display_count);
+ }
+
return 0;
+exit_deinit_hdmi:
+ sde_kms->hdmi_display_count = 0;
+ sde_kms->hdmi_displays = NULL;
+
exit_deinit_wb:
kfree(sde_kms->wb_displays);
sde_kms->wb_display_count = 0;
@@ -528,6 +551,9 @@ static void _sde_kms_release_displays(struct sde_kms *sde_kms)
SDE_ERROR("invalid sde kms\n");
return;
}
+ kfree(sde_kms->hdmi_displays);
+ sde_kms->hdmi_display_count = 0;
+ sde_kms->hdmi_displays = NULL;
kfree(sde_kms->wb_displays);
sde_kms->wb_displays = NULL;
@@ -565,6 +591,14 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.set_property = sde_wb_connector_set_property,
.get_info = sde_wb_get_info,
};
+ static const struct sde_connector_ops hdmi_ops = {
+ .pre_deinit = sde_hdmi_connector_pre_deinit,
+ .post_init = sde_hdmi_connector_post_init,
+ .detect = sde_hdmi_connector_detect,
+ .get_modes = sde_hdmi_connector_get_modes,
+ .mode_valid = sde_hdmi_mode_valid,
+ .get_info = sde_hdmi_get_info,
+ };
struct msm_display_info info;
struct drm_encoder *encoder;
void *display, *connector;
@@ -576,7 +610,10 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
return -EINVAL;
}
- max_encoders = sde_kms->dsi_display_count + sde_kms->wb_display_count;
+ max_encoders = sde_kms->dsi_display_count +
+ sde_kms->wb_display_count +
+ sde_kms->hdmi_display_count;
+
if (max_encoders > ARRAY_SIZE(priv->encoders)) {
max_encoders = ARRAY_SIZE(priv->encoders);
SDE_ERROR("capping number of displays to %d", max_encoders);
@@ -666,6 +703,54 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
}
}
+ /* hdmi */
+ for (i = 0; i < sde_kms->hdmi_display_count &&
+ priv->num_encoders < max_encoders; ++i) {
+ display = sde_kms->hdmi_displays[i];
+ encoder = NULL;
+
+ memset(&info, 0x0, sizeof(info));
+ rc = sde_hdmi_dev_init(display);
+ if (rc) {
+ SDE_ERROR("hdmi dev_init %d failed\n", i);
+ continue;
+ }
+ rc = sde_hdmi_get_info(&info, display);
+ if (rc) {
+ SDE_ERROR("hdmi get_info %d failed\n", i);
+ continue;
+ }
+
+ encoder = sde_encoder_init(dev, &info);
+ if (IS_ERR_OR_NULL(encoder)) {
+ SDE_ERROR("encoder init failed for hdmi %d\n", i);
+ continue;
+ }
+
+ rc = sde_hdmi_drm_init(display, encoder);
+ if (rc) {
+ SDE_ERROR("hdmi drm %d init failed, %d\n", i, rc);
+ sde_encoder_destroy(encoder);
+ continue;
+ }
+
+ connector = sde_connector_init(dev,
+ encoder,
+ 0,
+ display,
+ &hdmi_ops,
+ DRM_CONNECTOR_POLL_HPD,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (connector) {
+ priv->encoders[priv->num_encoders++] = encoder;
+ } else {
+ SDE_ERROR("hdmi %d connector init failed\n", i);
+ sde_hdmi_dev_deinit(display);
+ sde_hdmi_drm_deinit(display);
+ sde_encoder_destroy(encoder);
+ }
+ }
+
return 0;
}
@@ -726,6 +811,9 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
priv = dev->dev_private;
catalog = sde_kms->catalog;
+ ret = sde_core_irq_domain_add(sde_kms);
+ if (ret)
+ goto fail_irq;
/*
* Query for underlying display drivers, and create connectors,
* bridges and encoders for them.
@@ -784,6 +872,8 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
return 0;
fail:
_sde_kms_drm_obj_destroy(sde_kms);
+fail_irq:
+ sde_core_irq_domain_fini(sde_kms);
return ret;
}
@@ -1134,6 +1224,14 @@ static int sde_kms_hw_init(struct msm_kms *kms)
goto perf_err;
}
+ sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
+ if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
+ rc = PTR_ERR(sde_kms->hw_intr);
+ SDE_ERROR("hw_intr init failed: %d\n", rc);
+ sde_kms->hw_intr = NULL;
+ goto hw_intr_init_err;
+ }
+
/*
* _sde_kms_drm_obj_init should create the DRM related objects
* i.e. CRTCs, planes, encoders, connectors and so forth
@@ -1159,21 +1257,12 @@ static int sde_kms_hw_init(struct msm_kms *kms)
*/
dev->mode_config.allow_fb_modifiers = true;
- sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
- if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
- rc = PTR_ERR(sde_kms->hw_intr);
- SDE_ERROR("hw_intr init failed: %d\n", rc);
- sde_kms->hw_intr = NULL;
- goto hw_intr_init_err;
- }
-
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
return 0;
-hw_intr_init_err:
- _sde_kms_drm_obj_destroy(sde_kms);
drm_obj_init_err:
sde_core_perf_destroy(&sde_kms->perf);
+hw_intr_init_err:
perf_err:
power_error:
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index cd3722fa7df8..44f6be959ac9 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -154,8 +154,9 @@ struct sde_kms {
void **dsi_displays;
int wb_display_count;
void **wb_displays;
-
bool has_danger_ctrl;
+ void **hdmi_displays;
+ int hdmi_display_count;
};
struct vsync_info {