Skip to content

Commit

Permalink
drm/msm: DT support for 8960/8064 (v3)
Browse files Browse the repository at this point in the history
Now that we (almost) have enough dependencies in place (MMCC, RPM, etc),
add necessary DT support so that we can use drm/msm on upstream kernel.

v2: update for review comments
v3: rebase on component helper changes

Signed-off-by: Rob Clark <robdclark@gmail.com>
  • Loading branch information
Rob Clark committed Aug 4, 2014
1 parent 8f67da3 commit 41e6977
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 42 deletions.
52 changes: 52 additions & 0 deletions Documentation/devicetree/bindings/drm/msm/gpu.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Qualcomm adreno/snapdragon GPU

Required properties:
- compatible: "qcom,adreno-3xx"
- reg: Physical base address and length of the controller's registers.
- interrupts: The interrupt signal from the gpu.
- clocks: device clocks
See ../clocks/clock-bindings.txt for details.
- clock-names: the following clocks are required:
* "core_clk"
* "iface_clk"
* "mem_iface_clk"
- qcom,chipid: gpu chip-id. Note this may become optional for future
devices if we can reliably read the chipid from hw
- qcom,gpu-pwrlevels: list of operating points
- compatible: "qcom,gpu-pwrlevels"
- for each qcom,gpu-pwrlevel:
- qcom,gpu-freq: requested gpu clock speed
- NOTE: downstream android driver defines additional parameters to
configure memory bandwidth scaling per OPP.

Example:

/ {
...

gpu: qcom,kgsl-3d0@4300000 {
compatible = "qcom,adreno-3xx";
reg = <0x04300000 0x20000>;
reg-names = "kgsl_3d0_reg_memory";
interrupts = <GIC_SPI 80 0>;
interrupt-names = "kgsl_3d0_irq";
clock-names =
"core_clk",
"iface_clk",
"mem_iface_clk";
clocks =
<&mmcc GFX3D_CLK>,
<&mmcc GFX3D_AHB_CLK>,
<&mmcc MMSS_IMEM_AHB_CLK>;
qcom,chipid = <0x03020100>;
qcom,gpu-pwrlevels {
compatible = "qcom,gpu-pwrlevels";
qcom,gpu-pwrlevel@0 {
qcom,gpu-freq = <450000000>;
};
qcom,gpu-pwrlevel@1 {
qcom,gpu-freq = <27000000>;
};
};
};
};
46 changes: 46 additions & 0 deletions Documentation/devicetree/bindings/drm/msm/hdmi.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Qualcomm adreno/snapdragon hdmi output

Required properties:
- compatible: one of the following
* "qcom,hdmi-tx-8660"
* "qcom,hdmi-tx-8960"
- reg: Physical base address and length of the controller's registers
- reg-names: "core_physical"
- interrupts: The interrupt signal from the hdmi block.
- clocks: device clocks
See ../clocks/clock-bindings.txt for details.
- qcom,hdmi-tx-ddc-clk-gpio: ddc clk pin
- qcom,hdmi-tx-ddc-data-gpio: ddc data pin
- qcom,hdmi-tx-hpd-gpio: hpd pin
- core-vdda-supply: phandle to supply regulator
- hdmi-mux-supply: phandle to mux regulator

Optional properties:
- qcom,hdmi-tx-mux-en-gpio: hdmi mux enable pin
- qcom,hdmi-tx-mux-sel-gpio: hdmi mux select pin

Example:

/ {
...

hdmi: qcom,hdmi-tx-8960@4a00000 {
compatible = "qcom,hdmi-tx-8960";
reg-names = "core_physical";
reg = <0x04a00000 0x1000>;
interrupts = <GIC_SPI 79 0>;
clock-names =
"core_clk",
"master_iface_clk",
"slave_iface_clk";
clocks =
<&mmcc HDMI_APP_CLK>,
<&mmcc HDMI_M_AHB_CLK>,
<&mmcc HDMI_S_AHB_CLK>;
qcom,hdmi-tx-ddc-clk = <&msmgpio 70 GPIO_ACTIVE_HIGH>;
qcom,hdmi-tx-ddc-data = <&msmgpio 71 GPIO_ACTIVE_HIGH>;
qcom,hdmi-tx-hpd = <&msmgpio 72 GPIO_ACTIVE_HIGH>;
core-vdda-supply = <&pm8921_hdmi_mvs>;
hdmi-mux-supply = <&ext_3p3v>;
};
};
48 changes: 48 additions & 0 deletions Documentation/devicetree/bindings/drm/msm/mdp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Qualcomm adreno/snapdragon display controller

Required properties:
- compatible:
* "qcom,mdp" - mdp4
- reg: Physical base address and length of the controller's registers.
- interrupts: The interrupt signal from the display controller.
- connectors: array of phandles for output device(s)
- clocks: device clocks
See ../clocks/clock-bindings.txt for details.
- clock-names: the following clocks are required:
* "core_clk"
* "iface_clk"
* "lut_clk"
* "src_clk"
* "hdmi_clk"
* "mpd_clk"

Optional properties:
- gpus: phandle for gpu device

Example:

/ {
...

mdp: qcom,mdp@5100000 {
compatible = "qcom,mdp";
reg = <0x05100000 0xf0000>;
interrupts = <GIC_SPI 75 0>;
connectors = <&hdmi>;
gpus = <&gpu>;
clock-names =
"core_clk",
"iface_clk",
"lut_clk",
"src_clk",
"hdmi_clk",
"mdp_clk";
clocks =
<&mmcc MDP_SRC>,
<&mmcc MDP_AHB_CLK>,
<&mmcc MDP_LUT_CLK>,
<&mmcc TV_SRC>,
<&mmcc HDMI_TV_CLK>,
<&mmcc MDP_TV_CLK>;
};
};
2 changes: 2 additions & 0 deletions drivers/gpu/drm/msm/adreno/a3xx_gpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ static int a3xx_remove(struct platform_device *pdev)
}

static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,adreno-3xx" },
/* for backwards compat w/ downstream kgsl DT files: */
{ .compatible = "qcom,kgsl-3d0" },
{}
};
Expand Down
68 changes: 45 additions & 23 deletions drivers/gpu/drm/msm/hdmi/hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
for (i = 0; i < config->hpd_reg_cnt; i++) {
struct regulator *reg;

reg = devm_regulator_get(&pdev->dev, config->hpd_reg_names[i]);
reg = devm_regulator_get_exclusive(&pdev->dev,
config->hpd_reg_names[i]);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n",
Expand All @@ -138,7 +139,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
for (i = 0; i < config->pwr_reg_cnt; i++) {
struct regulator *reg;

reg = devm_regulator_get(&pdev->dev, config->pwr_reg_names[i]);
reg = devm_regulator_get_exclusive(&pdev->dev,
config->pwr_reg_names[i]);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n",
Expand Down Expand Up @@ -266,37 +268,55 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
int gpio = of_get_named_gpio(of_node, name, 0);
if (gpio < 0) {
dev_err(dev, "failed to get gpio: %s (%d)\n",
name, gpio);
gpio = -1;
char name2[32];
snprintf(name2, sizeof(name2), "%s-gpio", name);
gpio = of_get_named_gpio(of_node, name2, 0);
if (gpio < 0) {
dev_err(dev, "failed to get gpio: %s (%d)\n",
name, gpio);
gpio = -1;
}
}
return gpio;
}

/* TODO actually use DT.. */
static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"};
static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"};
static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"};
static unsigned long hpd_clk_freq[] = {0, 19200000, 0};
static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8074")) {
static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"};
static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"};
static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"};
static unsigned long hpd_clk_freq[] = {0, 19200000, 0};
static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
config.phy_init = hdmi_phy_8x74_init;
config.hpd_reg_names = hpd_reg_names;
config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
config.pwr_reg_names = pwr_reg_names;
config.pwr_reg_cnt = ARRAY_SIZE(pwr_reg_names);
config.hpd_clk_names = hpd_clk_names;
config.hpd_freq = hpd_clk_freq;
config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names);
config.pwr_clk_names = pwr_clk_names;
config.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names);
config.shared_irq = true;
} else if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8960")) {
static const char *hpd_clk_names[] = {"core_clk", "master_iface_clk", "slave_iface_clk"};
static const char *hpd_reg_names[] = {"core-vdda", "hdmi-mux"};
config.phy_init = hdmi_phy_8960_init;
config.hpd_reg_names = hpd_reg_names;
config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
config.hpd_clk_names = hpd_clk_names;
config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names);
} else if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8660")) {
config.phy_init = hdmi_phy_8x60_init;
} else {
dev_err(dev, "unknown phy: %s\n", of_node->name);
}

config.phy_init = hdmi_phy_8x74_init;
config.mmio_name = "core_physical";
config.hpd_reg_names = hpd_reg_names;
config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
config.pwr_reg_names = pwr_reg_names;
config.pwr_reg_cnt = ARRAY_SIZE(pwr_reg_names);
config.hpd_clk_names = hpd_clk_names;
config.hpd_freq = hpd_clk_freq;
config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names);
config.pwr_clk_names = pwr_clk_names;
config.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names);
config.ddc_clk_gpio = get_gpio("qcom,hdmi-tx-ddc-clk");
config.ddc_data_gpio = get_gpio("qcom,hdmi-tx-ddc-data");
config.hpd_gpio = get_gpio("qcom,hdmi-tx-hpd");
config.mux_en_gpio = get_gpio("qcom,hdmi-tx-mux-en");
config.mux_sel_gpio = get_gpio("qcom,hdmi-tx-mux-sel");
config.shared_irq = true;

#else
static const char *hpd_clk_names[] = {
Expand Down Expand Up @@ -373,7 +393,9 @@ static int hdmi_dev_remove(struct platform_device *pdev)
}

static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,hdmi-tx" },
{ .compatible = "qcom,hdmi-tx-8074" },
{ .compatible = "qcom,hdmi-tx-8960" },
{ .compatible = "qcom,hdmi-tx-8660" },
{}
};

Expand Down
10 changes: 7 additions & 3 deletions drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,15 +294,17 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
goto fail;
}

mdp4_kms->dsi_pll_vdda = devm_regulator_get(&pdev->dev, "dsi_pll_vdda");
mdp4_kms->dsi_pll_vdda =
devm_regulator_get_optional(&pdev->dev, "dsi_pll_vdda");
if (IS_ERR(mdp4_kms->dsi_pll_vdda))
mdp4_kms->dsi_pll_vdda = NULL;

mdp4_kms->dsi_pll_vddio = devm_regulator_get(&pdev->dev, "dsi_pll_vddio");
mdp4_kms->dsi_pll_vddio =
devm_regulator_get_optional(&pdev->dev, "dsi_pll_vddio");
if (IS_ERR(mdp4_kms->dsi_pll_vddio))
mdp4_kms->dsi_pll_vddio = NULL;

mdp4_kms->vdd = devm_regulator_get(&pdev->dev, "vdd");
mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd");
if (IS_ERR(mdp4_kms->vdd))
mdp4_kms->vdd = NULL;

Expand Down Expand Up @@ -406,6 +408,8 @@ static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
static struct mdp4_platform_config config = {};
#ifdef CONFIG_OF
/* TODO */
config.max_clk = 266667000;
config.iommu = iommu_domain_alloc(&platform_bus_type);
#else
if (cpu_is_apq8064())
config.max_clk = 266667000;
Expand Down
39 changes: 23 additions & 16 deletions drivers/gpu/drm/msm/msm_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,25 @@ static int compare_of(struct device *dev, void *data)
{
return dev->of_node == data;
}

static int add_components(struct device *dev, struct component_match **matchptr,
const char *name)
{
struct device_node *np = dev->of_node;
unsigned i;

for (i = 0; ; i++) {
struct device_node *node;

node = of_parse_phandle(np, name, i);
if (!node)
break;

component_match_add(dev, matchptr, compare_of, node);
}

return 0;
}
#else
static int compare_dev(struct device *dev, void *data)
{
Expand Down Expand Up @@ -935,21 +954,8 @@ static int msm_pdev_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
#ifdef CONFIG_OF
/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx
* (or probably any other).. so probably some room for some helpers
*/
struct device_node *np = pdev->dev.of_node;
unsigned i;

for (i = 0; ; i++) {
struct device_node *node;

node = of_parse_phandle(np, "connectors", i);
if (!node)
break;

component_match_add(&pdev->dev, &match, compare_of, node);
}
add_components(&pdev->dev, &match, "connectors");
add_components(&pdev->dev, &match, "gpus");
#else
/* For non-DT case, it kinda sucks. We don't actually have a way
* to know whether or not we are waiting for certain devices (or if
Expand Down Expand Up @@ -995,7 +1001,8 @@ static const struct platform_device_id msm_id[] = {
};

static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,mdss_mdp" },
{ .compatible = "qcom,mdp" }, /* mdp4 */
{ .compatible = "qcom,mdss_mdp" }, /* mdp5 */
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
Expand Down

0 comments on commit 41e6977

Please sign in to comment.