Merge "ARM: dts: msm: rename mdss_mdp to sde_kms and add HDMI TX device node"
This commit is contained in:
commit
43de29b084
17 changed files with 1765 additions and 95 deletions
|
@ -3,6 +3,7 @@ Qualcomm adreno/snapdragon hdmi output
|
|||
Required properties:
|
||||
- compatible: one of the following
|
||||
* "qcom,hdmi-tx-8996"
|
||||
* "qcom,hdmi-tx-8998"
|
||||
* "qcom,hdmi-tx-8994"
|
||||
* "qcom,hdmi-tx-8084"
|
||||
* "qcom,hdmi-tx-8974"
|
||||
|
|
22
Documentation/devicetree/bindings/drm/msm/hdmi-display.txt
Normal file
22
Documentation/devicetree/bindings/drm/msm/hdmi-display.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
Qualcomm Technologies,Inc. Adreno/Snapdragon hdmi display manager
|
||||
|
||||
Required properties:
|
||||
- compatible: "qcom,hdmi-display"
|
||||
- label: label of this display manager
|
||||
|
||||
Optional properties:
|
||||
- qcom,display-type: display type of this manager. It could be "primary",
|
||||
"secondary", "tertiary", etc.
|
||||
|
||||
Example:
|
||||
|
||||
/ {
|
||||
...
|
||||
|
||||
hdmi_display: qcom,hdmi-display {
|
||||
compatible = "qcom,hdmi-display";
|
||||
label = "hdmi_display";
|
||||
qcom,display-type = "secondary";
|
||||
};
|
||||
|
||||
};
|
31
arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi
Normal file
31
arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
&soc {
|
||||
|
||||
sde_wb: qcom,wb-display@0 {
|
||||
compatible = "qcom,wb-display";
|
||||
cell-index = <0>;
|
||||
label = "wb_display";
|
||||
};
|
||||
|
||||
sde_hdmi: qcom,hdmi-display {
|
||||
compatible = "qcom,hdmi-display";
|
||||
label = "sde_hdmi";
|
||||
qcom,display-type = "secondary";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
&sde_kms {
|
||||
connectors = <&sde_hdmi_tx &sde_hdmi &sde_wb>;
|
||||
};
|
214
arch/arm/boot/dts/qcom/msm8998-sde.dtsi
Normal file
214
arch/arm/boot/dts/qcom/msm8998-sde.dtsi
Normal file
|
@ -0,0 +1,214 @@
|
|||
/* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
&soc {
|
||||
sde_kms: qcom,sde_kms@c900000 {
|
||||
compatible = "qcom,sde-kms";
|
||||
reg = <0x0c900000 0x90000>,
|
||||
<0x0c9b0000 0x1040>;
|
||||
reg-names = "mdp_phys", "vbif_phys";
|
||||
|
||||
/* clock and supply entries */
|
||||
clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
|
||||
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
|
||||
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_axi_clk>,
|
||||
<&clock_mmss clk_mdp_clk_src>,
|
||||
<&clock_mmss clk_mmss_mdss_mdp_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_vsync_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_mdp_lut_clk>;
|
||||
clock-names = "mmss_noc_axi_clk",
|
||||
"mmss_noc_ahb_clk",
|
||||
"mmss_smmu_ahb_clk",
|
||||
"mmss_smmu_axi_clk",
|
||||
"mnoc_clk", "iface_clk", "bus_clk",
|
||||
"core_clk_src", "core_clk", "vsync_clk",
|
||||
"lut_clk";
|
||||
clock-rate = <0 0 0 0 0 0 0 330000000 0 0 0 0>;
|
||||
clock-max-rate = <0 0 0 0 0 0 412500000 412500000 0 0 0 0>;
|
||||
qcom,sde-max-bw-low-kbps = <6700000>;
|
||||
qcom,sde-max-bw-high-kbps = <6700000>;
|
||||
|
||||
/* interrupt config */
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 83 0>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
iommus = <&mmss_smmu 0>;
|
||||
|
||||
/* hw blocks */
|
||||
qcom,sde-off = <0x1000>;
|
||||
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
|
||||
0x2600 0x2800>;
|
||||
qcom,sde-mixer-off = <0x45000 0x46000 0x47000
|
||||
0x48000 0x49000 0x4a000>;
|
||||
qcom,sde-dspp-off = <0x55000 0x57000>;
|
||||
qcom,sde-wb-off = <0x66000>;
|
||||
qcom,sde-wb-id = <2>;
|
||||
qcom,sde-wb-xin-id = <6>;
|
||||
qcom,sde-wb-clk-ctrl = <0x2bc 0x10>;
|
||||
qcom,sde-intf-off = <0x6b000 0x6b800
|
||||
0x6c000 0x6c800>;
|
||||
qcom,sde-intf-type = "dp", "dsi", "dsi", "hdmi";
|
||||
qcom,sde-pp-off = <0x71000 0x71800
|
||||
0x72000 0x72800>;
|
||||
qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0>;
|
||||
qcom,sde-cdm-off = <0x7a200>;
|
||||
qcom,sde-dsc-off = <0x10000 0x10000 0x0 0x0>;
|
||||
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
|
||||
|
||||
qcom,sde-sspp-type = "vig", "vig", "vig", "vig",
|
||||
"dma", "dma", "dma", "dma",
|
||||
"cursor", "cursor";
|
||||
|
||||
qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000
|
||||
0x25000 0x27000 0x29000 0x2b000
|
||||
0x35000 0x37000>;
|
||||
|
||||
qcom,sde-sspp-xin-id = <0 4 8 12 1 5 9 13 2 10>;
|
||||
|
||||
/* offsets are relative to "mdp_phys + qcom,sde-off */
|
||||
qcom,sde-sspp-clk-ctrl = <0x2ac 0x8>, <0x2b4 0x8>,
|
||||
<0x2c4 0x8>, <0x2c4 0xc>, <0x3a8 0x10>,
|
||||
<0x3b0 0x10>;
|
||||
|
||||
qcom,sde-qseed-type = "qseedv3";
|
||||
qcom,sde-mixer-linewidth = <2560>;
|
||||
qcom,sde-sspp-linewidth = <2560>;
|
||||
qcom,sde-mixer-blendstages = <0x7>;
|
||||
qcom,sde-highest-bank-bit = <0x2>;
|
||||
qcom,sde-panic-per-pipe;
|
||||
qcom,sde-has-cdp;
|
||||
qcom,sde-has-src-split;
|
||||
qcom,sde-sspp-danger-lut = <0x000f 0xffff 0x0000>;
|
||||
qcom,sde-sspp-safe-lut = <0xfffc 0xff00 0xffff>;
|
||||
|
||||
qcom,sde-vbif-off = <0>;
|
||||
qcom,sde-vbif-id = <0>;
|
||||
qcom,sde-vbif-default-ot-rd-limit = <32>;
|
||||
qcom,sde-vbif-default-ot-wr-limit = <32>;
|
||||
qcom,sde-vbif-dynamic-ot-rd-limit = <62208000 2>,
|
||||
<124416000 4>, <248832000 16>;
|
||||
qcom,sde-vbif-dynamic-ot-wr-limit = <62208000 2>,
|
||||
<124416000 4>, <248832000 16>;
|
||||
|
||||
vdd-supply = <&gdsc_mdss>;
|
||||
gdsc-mmagic-mdss-supply = <&gdsc_bimc_smmu>;
|
||||
qcom,sde-csc-type = "csc-10bit";
|
||||
|
||||
qcom,sde-sspp-vig-blocks {
|
||||
qcom,sde-vig-csc-off = <0x1a00>;
|
||||
qcom,sde-vig-qseed-off = <0xa00>;
|
||||
};
|
||||
|
||||
qcom,platform-supply-entries {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
qcom,platform-supply-entry@0 {
|
||||
reg = <0>;
|
||||
qcom,supply-name = "gdsc-mmagic-mdss";
|
||||
qcom,supply-min-voltage = <0>;
|
||||
qcom,supply-max-voltage = <0>;
|
||||
qcom,supply-enable-load = <0>;
|
||||
qcom,supply-disable-load = <0>;
|
||||
};
|
||||
|
||||
qcom,platform-supply-entry@1 {
|
||||
reg = <1>;
|
||||
qcom,supply-name = "vdd";
|
||||
qcom,supply-min-voltage = <0>;
|
||||
qcom,supply-max-voltage = <0>;
|
||||
qcom,supply-enable-load = <0>;
|
||||
qcom,supply-disable-load = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
smmu_kms_unsec: qcom,smmu_kms_unsec_cb {
|
||||
compatible = "qcom,smmu_mdp_unsec";
|
||||
iommus = <&mmss_smmu 0>;
|
||||
};
|
||||
|
||||
/* data and reg bus scale settings */
|
||||
qcom,sde-data-bus {
|
||||
qcom,msm-bus,name = "mdss_sde";
|
||||
qcom,msm-bus,num-cases = <3>;
|
||||
qcom,msm-bus,num-paths = <2>;
|
||||
qcom,msm-bus,vectors-KBps =
|
||||
<22 512 0 0>, <23 512 0 0>,
|
||||
<22 512 0 6400000>, <23 512 0 6400000>,
|
||||
<22 512 0 6400000>, <23 512 0 6400000>;
|
||||
};
|
||||
qcom,sde-reg-bus {
|
||||
qcom,msm-bus,name = "mdss_reg";
|
||||
qcom,msm-bus,num-cases = <4>;
|
||||
qcom,msm-bus,num-paths = <1>;
|
||||
qcom,msm-bus,active-only;
|
||||
qcom,msm-bus,vectors-KBps =
|
||||
<1 590 0 0>,
|
||||
<1 590 0 76800>,
|
||||
<1 590 0 160000>,
|
||||
<1 590 0 320000>;
|
||||
};
|
||||
};
|
||||
|
||||
sde_hdmi_tx: qcom,hdmi_tx_8998@c9a0000 {
|
||||
cell-index = <0>;
|
||||
compatible = "qcom,hdmi-tx-8998";
|
||||
reg = <0xc9a0000 0x50c>,
|
||||
<0x780000 0x621c>,
|
||||
<0xc9e0000 0x28>;
|
||||
reg-names = "core_physical", "qfprom_physical", "hdcp_physical";
|
||||
interrupt-parent = <&sde_kms>;
|
||||
interrupts = <8 0>;
|
||||
qcom,hdmi-tx-ddc-clk-gpio = <&tlmm 32 0>;
|
||||
qcom,hdmi-tx-ddc-data-gpio = <&tlmm 33 0>;
|
||||
qcom,hdmi-tx-hpd-gpio = <&tlmm 34 0>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&mdss_hdmi_hpd_active
|
||||
&mdss_hdmi_ddc_active
|
||||
&mdss_hdmi_cec_active>;
|
||||
pinctrl-1 = <&mdss_hdmi_hpd_suspend
|
||||
&mdss_hdmi_ddc_suspend
|
||||
&mdss_hdmi_cec_suspend>;
|
||||
hpd-gdsc-supply = <&gdsc_mdss>;
|
||||
qcom,supply-names = "hpd-gdsc";
|
||||
qcom,min-voltage-level = <0>;
|
||||
qcom,max-voltage-level = <0>;
|
||||
qcom,enable-load = <0>;
|
||||
qcom,disable-load = <0>;
|
||||
|
||||
qcom,msm_ext_disp = <&msm_ext_disp>;
|
||||
|
||||
clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_hdmi_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_mdp_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_extpclk_clk>,
|
||||
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_misc_ahb_clk>,
|
||||
<&clock_mmss clk_mmss_mdss_axi_clk>;
|
||||
clock-names = "hpd_mnoc_clk", "hpd_iface_clk",
|
||||
"hpd_core_clk", "hpd_mdp_core_clk",
|
||||
"hpd_alt_iface_clk", "core_extp_clk",
|
||||
"mnoc_clk","hpd_misc_ahb_clk",
|
||||
"hpd_bus_clk";
|
||||
|
||||
/*qcom,mdss-fb-map = <&mdss_fb2>;*/
|
||||
qcom,pluggable;
|
||||
};
|
||||
};
|
||||
#include "msm8998-sde-display.dtsi"
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
929
drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
Normal file
929
drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
Normal file
|
@ -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);
|
296
drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
Normal file
296
drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
Normal file
|
@ -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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -43,6 +43,15 @@ struct sde_connector_ops {
|
|||
void *info,
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
@ -37,6 +37,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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue