Commit dada25bd authored by Rob Clark's avatar Rob Clark

drm/msm: add hdmi support for apq8x74/mdp5

The HDMI block is basically the same between older SoC's with mdp4
display controller, and newer ones with mdp5.

So mostly this consists of better abstracting out the different sets of
regulators, clks, etc.  In particular, for regulators and clks we can
split it up by what is needed for hot plug detect to work, and what is
needed to light up the display.

Also, 8x74 has a new phy.. a very simple one, but split out into a
different mmio space.  And with mdp5, the irq is shared with mdp, so we
don't directly register our own irq handler.
Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 9e0efa63
...@@ -12,6 +12,7 @@ msm-y := \ ...@@ -12,6 +12,7 @@ msm-y := \
hdmi/hdmi_i2c.o \ hdmi/hdmi_i2c.o \
hdmi/hdmi_phy_8960.o \ hdmi/hdmi_phy_8960.o \
hdmi/hdmi_phy_8x60.o \ hdmi/hdmi_phy_8x60.o \
hdmi/hdmi_phy_8x74.o \
mdp/mdp_format.o \ mdp/mdp_format.o \
mdp/mdp_kms.o \ mdp/mdp_kms.o \
mdp/mdp4/mdp4_crtc.o \ mdp/mdp4/mdp4_crtc.o \
......
...@@ -41,7 +41,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on) ...@@ -41,7 +41,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
power_on ? "Enable" : "Disable", ctrl); power_on ? "Enable" : "Disable", ctrl);
} }
static irqreturn_t hdmi_irq(int irq, void *dev_id) irqreturn_t hdmi_irq(int irq, void *dev_id)
{ {
struct hdmi *hdmi = dev_id; struct hdmi *hdmi = dev_id;
...@@ -71,13 +71,13 @@ void hdmi_destroy(struct kref *kref) ...@@ -71,13 +71,13 @@ void hdmi_destroy(struct kref *kref)
} }
/* initialize connector */ /* initialize connector */
int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
{ {
struct hdmi *hdmi = NULL; struct hdmi *hdmi = NULL;
struct msm_drm_private *priv = dev->dev_private; struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = hdmi_pdev; struct platform_device *pdev = hdmi_pdev;
struct hdmi_platform_config *config; struct hdmi_platform_config *config;
int ret; int i, ret;
if (!pdev) { if (!pdev) {
dev_err(dev->dev, "no hdmi device\n"); dev_err(dev->dev, "no hdmi device\n");
...@@ -99,6 +99,7 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) ...@@ -99,6 +99,7 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
hdmi->dev = dev; hdmi->dev = dev;
hdmi->pdev = pdev; hdmi->pdev = pdev;
hdmi->config = config;
hdmi->encoder = encoder; hdmi->encoder = encoder;
/* not sure about which phy maps to which msm.. probably I miss some */ /* not sure about which phy maps to which msm.. probably I miss some */
...@@ -114,46 +115,72 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) ...@@ -114,46 +115,72 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
goto fail; goto fail;
} }
hdmi->mmio = msm_ioremap(pdev, "hdmi_msm_hdmi_addr", "HDMI"); hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
if (IS_ERR(hdmi->mmio)) { if (IS_ERR(hdmi->mmio)) {
ret = PTR_ERR(hdmi->mmio); ret = PTR_ERR(hdmi->mmio);
goto fail; goto fail;
} }
hdmi->mvs = devm_regulator_get(&pdev->dev, "8901_hdmi_mvs"); BUG_ON(config->hpd_reg_cnt > ARRAY_SIZE(hdmi->hpd_regs));
if (IS_ERR(hdmi->mvs)) for (i = 0; i < config->hpd_reg_cnt; i++) {
hdmi->mvs = devm_regulator_get(&pdev->dev, "8921_hdmi_mvs"); struct regulator *reg;
if (IS_ERR(hdmi->mvs)) {
ret = PTR_ERR(hdmi->mvs); reg = devm_regulator_get(&pdev->dev, config->hpd_reg_names[i]);
dev_err(dev->dev, "failed to get mvs regulator: %d\n", ret); if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n",
config->hpd_reg_names[i], ret);
goto fail; goto fail;
} }
hdmi->mpp0 = devm_regulator_get(&pdev->dev, "8901_mpp0"); hdmi->hpd_regs[i] = reg;
if (IS_ERR(hdmi->mpp0)) }
hdmi->mpp0 = NULL;
BUG_ON(config->pwr_reg_cnt > ARRAY_SIZE(hdmi->pwr_regs));
for (i = 0; i < config->pwr_reg_cnt; i++) {
struct regulator *reg;
hdmi->clk = devm_clk_get(&pdev->dev, "core_clk"); reg = devm_regulator_get(&pdev->dev, config->pwr_reg_names[i]);
if (IS_ERR(hdmi->clk)) { if (IS_ERR(reg)) {
ret = PTR_ERR(hdmi->clk); ret = PTR_ERR(reg);
dev_err(dev->dev, "failed to get 'clk': %d\n", ret); dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n",
config->pwr_reg_names[i], ret);
goto fail; goto fail;
} }
hdmi->m_pclk = devm_clk_get(&pdev->dev, "master_iface_clk"); hdmi->pwr_regs[i] = reg;
if (IS_ERR(hdmi->m_pclk)) { }
ret = PTR_ERR(hdmi->m_pclk);
dev_err(dev->dev, "failed to get 'm_pclk': %d\n", ret); BUG_ON(config->hpd_clk_cnt > ARRAY_SIZE(hdmi->hpd_clks));
for (i = 0; i < config->hpd_clk_cnt; i++) {
struct clk *clk;
clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(dev->dev, "failed to get hpd clk: %s (%d)\n",
config->hpd_clk_names[i], ret);
goto fail; goto fail;
} }
hdmi->s_pclk = devm_clk_get(&pdev->dev, "slave_iface_clk"); hdmi->hpd_clks[i] = clk;
if (IS_ERR(hdmi->s_pclk)) { }
ret = PTR_ERR(hdmi->s_pclk);
dev_err(dev->dev, "failed to get 's_pclk': %d\n", ret); BUG_ON(config->pwr_clk_cnt > ARRAY_SIZE(hdmi->pwr_clks));
for (i = 0; i < config->pwr_clk_cnt; i++) {
struct clk *clk;
clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(dev->dev, "failed to get pwr clk: %s (%d)\n",
config->pwr_clk_names[i], ret);
goto fail; goto fail;
} }
hdmi->pwr_clks[i] = clk;
}
hdmi->i2c = hdmi_i2c_init(hdmi); hdmi->i2c = hdmi_i2c_init(hdmi);
if (IS_ERR(hdmi->i2c)) { if (IS_ERR(hdmi->i2c)) {
ret = PTR_ERR(hdmi->i2c); ret = PTR_ERR(hdmi->i2c);
...@@ -178,6 +205,7 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) ...@@ -178,6 +205,7 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
goto fail; goto fail;
} }
if (!config->shared_irq) {
hdmi->irq = platform_get_irq(pdev, 0); hdmi->irq = platform_get_irq(pdev, 0);
if (hdmi->irq < 0) { if (hdmi->irq < 0) {
ret = hdmi->irq; ret = hdmi->irq;
...@@ -193,13 +221,14 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) ...@@ -193,13 +221,14 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
hdmi->irq, ret); hdmi->irq, ret);
goto fail; goto fail;
} }
}
encoder->bridge = hdmi->bridge; encoder->bridge = hdmi->bridge;
priv->bridges[priv->num_bridges++] = hdmi->bridge; priv->bridges[priv->num_bridges++] = hdmi->bridge;
priv->connectors[priv->num_connectors++] = hdmi->connector; priv->connectors[priv->num_connectors++] = hdmi->connector;
return 0; return hdmi;
fail: fail:
if (hdmi) { if (hdmi) {
...@@ -211,37 +240,100 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) ...@@ -211,37 +240,100 @@ int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
hdmi_destroy(&hdmi->refcount); hdmi_destroy(&hdmi->refcount);
} }
return ret; return ERR_PTR(ret);
} }
/* /*
* The hdmi device: * The hdmi device:
*/ */
#include <linux/of_gpio.h>
static int hdmi_dev_probe(struct platform_device *pdev) static int hdmi_dev_probe(struct platform_device *pdev)
{ {
static struct hdmi_platform_config config = {}; static struct hdmi_platform_config config = {};
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* TODO */ struct device_node *of_node = pdev->dev.of_node;
int get_gpio(const char *name)
{
int gpio = of_get_named_gpio(of_node, name, 0);
if (gpio < 0) {
dev_err(&pdev->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 const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
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_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 #else
static const char *hpd_clk_names[] = {
"core_clk", "master_iface_clk", "slave_iface_clk",
};
if (cpu_is_apq8064()) { if (cpu_is_apq8064()) {
static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
config.phy_init = hdmi_phy_8960_init; config.phy_init = hdmi_phy_8960_init;
config.mmio_name = "hdmi_msm_hdmi_addr";
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);
config.ddc_clk_gpio = 70; config.ddc_clk_gpio = 70;
config.ddc_data_gpio = 71; config.ddc_data_gpio = 71;
config.hpd_gpio = 72; config.hpd_gpio = 72;
config.pmic_gpio = 13 + NR_GPIO_IRQS; config.mux_en_gpio = -1;
config.mux_sel_gpio = 13 + NR_GPIO_IRQS;
} else if (cpu_is_msm8960() || cpu_is_msm8960ab()) { } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
config.phy_init = hdmi_phy_8960_init; config.phy_init = hdmi_phy_8960_init;
config.mmio_name = "hdmi_msm_hdmi_addr";
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);
config.ddc_clk_gpio = 100; config.ddc_clk_gpio = 100;
config.ddc_data_gpio = 101; config.ddc_data_gpio = 101;
config.hpd_gpio = 102; config.hpd_gpio = 102;
config.pmic_gpio = -1; config.mux_en_gpio = -1;
config.mux_sel_gpio = -1;
} else if (cpu_is_msm8x60()) { } else if (cpu_is_msm8x60()) {
static const char *hpd_reg_names[] = {
"8901_hdmi_mvs", "8901_mpp0"
};
config.phy_init = hdmi_phy_8x60_init; config.phy_init = hdmi_phy_8x60_init;
config.mmio_name = "hdmi_msm_hdmi_addr";
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);
config.ddc_clk_gpio = 170; config.ddc_clk_gpio = 170;
config.ddc_data_gpio = 171; config.ddc_data_gpio = 171;
config.hpd_gpio = 172; config.hpd_gpio = 172;
config.pmic_gpio = -1; config.mux_en_gpio = -1;
config.mux_sel_gpio = -1;
} }
#endif #endif
pdev->dev.platform_data = &config; pdev->dev.platform_data = &config;
...@@ -255,10 +347,19 @@ static int hdmi_dev_remove(struct platform_device *pdev) ...@@ -255,10 +347,19 @@ static int hdmi_dev_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,hdmi-tx" },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
static struct platform_driver hdmi_driver = { static struct platform_driver hdmi_driver = {
.probe = hdmi_dev_probe, .probe = hdmi_dev_probe,
.remove = hdmi_dev_remove, .remove = hdmi_dev_remove,
.driver.name = "hdmi_msm", .driver = {
.name = "hdmi_msm",
.of_match_table = dt_match,
},
}; };
void __init hdmi_register(void) void __init hdmi_register(void)
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
struct hdmi_phy; struct hdmi_phy;
struct hdmi_platform_config;
struct hdmi { struct hdmi {
struct kref refcount; struct kref refcount;
...@@ -35,14 +36,14 @@ struct hdmi { ...@@ -35,14 +36,14 @@ struct hdmi {
struct drm_device *dev; struct drm_device *dev;
struct platform_device *pdev; struct platform_device *pdev;
void __iomem *mmio; const struct hdmi_platform_config *config;
struct regulator *mvs; /* HDMI_5V */ void __iomem *mmio;
struct regulator *mpp0; /* External 5V */
struct clk *clk; struct regulator *hpd_regs[2];
struct clk *m_pclk; struct regulator *pwr_regs[2];
struct clk *s_pclk; struct clk *hpd_clks[3];
struct clk *pwr_clks[2];
struct hdmi_phy *phy; struct hdmi_phy *phy;
struct i2c_adapter *i2c; struct i2c_adapter *i2c;
...@@ -60,7 +61,29 @@ struct hdmi { ...@@ -60,7 +61,29 @@ struct hdmi {
/* platform config data (ie. from DT, or pdata) */ /* platform config data (ie. from DT, or pdata) */
struct hdmi_platform_config { struct hdmi_platform_config {
struct hdmi_phy *(*phy_init)(struct hdmi *hdmi); struct hdmi_phy *(*phy_init)(struct hdmi *hdmi);
int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, pmic_gpio; const char *mmio_name;
/* regulators that need to be on for hpd: */
const char **hpd_reg_names;
int hpd_reg_cnt;
/* regulators that need to be on for screen pwr: */
const char **pwr_reg_names;
int pwr_reg_cnt;
/* clks that need to be on for hpd: */
const char **hpd_clk_names;
int hpd_clk_cnt;
/* clks that need to be on for screen pwr (ie pixel clk): */
const char **pwr_clk_names;
int pwr_clk_cnt;
/* gpio's: */
int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, mux_en_gpio, mux_sel_gpio;
/* older devices had their own irq, mdp5+ it is shared w/ mdp: */
bool shared_irq;
}; };
void hdmi_set_mode(struct hdmi *hdmi, bool power_on); void hdmi_set_mode(struct hdmi *hdmi, bool power_on);
...@@ -106,6 +129,7 @@ struct hdmi_phy { ...@@ -106,6 +129,7 @@ struct hdmi_phy {
struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi); struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi);
struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi); struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);
struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi);
/* /*
* hdmi bridge: * hdmi bridge:
......
...@@ -21,6 +21,7 @@ struct hdmi_bridge { ...@@ -21,6 +21,7 @@ struct hdmi_bridge {
struct drm_bridge base; struct drm_bridge base;
struct hdmi *hdmi; struct hdmi *hdmi;
bool power_on;
unsigned long int pixclock; unsigned long int pixclock;
}; };
...@@ -34,6 +35,65 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge) ...@@ -34,6 +35,65 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge)
kfree(hdmi_bridge); kfree(hdmi_bridge);
} }
static void power_on(struct drm_bridge *bridge)
{
struct drm_device *dev = bridge->dev;
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
const struct hdmi_platform_config *config = hdmi->config;
int i, ret;
for (i = 0; i < config->pwr_reg_cnt; i++) {
ret = regulator_enable(hdmi->pwr_regs[i]);
if (ret) {
dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n",
config->pwr_reg_names[i], ret);
}
}
if (config->pwr_clk_cnt > 0) {
DBG("pixclock: %lu", hdmi_bridge->pixclock);
ret = clk_set_rate(hdmi->pwr_clks[0], hdmi_bridge->pixclock);
if (ret) {
dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n",
config->pwr_clk_names[0], ret);
}
}
for (i = 0; i < config->pwr_clk_cnt; i++) {
ret = clk_prepare_enable(hdmi->pwr_clks[i]);
if (ret) {
dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n",
config->pwr_clk_names[i], ret);
}
}
}
static void power_off(struct drm_bridge *bridge)
{
struct drm_device *dev = bridge->dev;
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
const struct hdmi_platform_config *config = hdmi->config;
int i, ret;
/* TODO do we need to wait for final vblank somewhere before
* cutting the clocks?
*/
mdelay(16 + 4);
for (i = 0; i < config->pwr_clk_cnt; i++)
clk_disable_unprepare(hdmi->pwr_clks[i]);
for (i = 0; i < config->pwr_reg_cnt; i++) {
ret = regulator_disable(hdmi->pwr_regs[i]);
if (ret) {
dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n",
config->pwr_reg_names[i], ret);
}
}
}
static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)
{ {
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
...@@ -41,6 +101,12 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) ...@@ -41,6 +101,12 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)
struct hdmi_phy *phy = hdmi->phy; struct hdmi_phy *phy = hdmi->phy;
DBG("power up"); DBG("power up");
if (!hdmi_bridge->power_on) {
power_on(bridge);
hdmi_bridge->power_on = true;
}
phy->funcs->powerup(phy, hdmi_bridge->pixclock); phy->funcs->powerup(phy, hdmi_bridge->pixclock);
hdmi_set_mode(hdmi, true); hdmi_set_mode(hdmi, true);
} }
...@@ -62,6 +128,11 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge) ...@@ -62,6 +128,11 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge)
DBG("power down"); DBG("power down");
hdmi_set_mode(hdmi, false); hdmi_set_mode(hdmi, false);
phy->funcs->powerdown(phy); phy->funcs->powerdown(phy);
if (hdmi_bridge->power_on) {
power_off(bridge);
hdmi_bridge->power_on = false;
}
} }
static void hdmi_bridge_mode_set(struct drm_bridge *bridge, static void hdmi_bridge_mode_set(struct drm_bridge *bridge,
......
...@@ -23,14 +23,14 @@ ...@@ -23,14 +23,14 @@
struct hdmi_connector { struct hdmi_connector {
struct drm_connector base; struct drm_connector base;
struct hdmi *hdmi; struct hdmi *hdmi;
struct work_struct hpd_work;
}; };
#define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base) #define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
static int gpio_config(struct hdmi *hdmi, bool on) static int gpio_config(struct hdmi *hdmi, bool on)
{ {
struct drm_device *dev = hdmi->dev; struct drm_device *dev = hdmi->dev;
struct hdmi_platform_config *config = const struct hdmi_platform_config *config = hdmi->config;
hdmi->pdev->dev.platform_data;
int ret; int ret;
if (on) { if (on) {
...@@ -40,26 +40,43 @@ static int gpio_config(struct hdmi *hdmi, bool on) ...@@ -40,26 +40,43 @@ static int gpio_config(struct hdmi *hdmi, bool on)
"HDMI_DDC_CLK", config->ddc_clk_gpio, ret); "HDMI_DDC_CLK", config->ddc_clk_gpio, ret);
goto error1; goto error1;
} }
gpio_set_value_cansleep(config->ddc_clk_gpio, 1);
ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA"); ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA");
if (ret) { if (ret) {
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n", dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
"HDMI_DDC_DATA", config->ddc_data_gpio, ret); "HDMI_DDC_DATA", config->ddc_data_gpio, ret);
goto error2; goto error2;
} }
gpio_set_value_cansleep(config->ddc_data_gpio, 1);
ret = gpio_request(config->hpd_gpio, "HDMI_HPD"); ret = gpio_request(config->hpd_gpio, "HDMI_HPD");
if (ret) { if (ret) {
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n", dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
"HDMI_HPD", config->hpd_gpio, ret); "HDMI_HPD", config->hpd_gpio, ret);
goto error3; goto error3;
} }
if (config->pmic_gpio != -1) { gpio_direction_input(config->hpd_gpio);
ret = gpio_request(config->pmic_gpio, "PMIC_HDMI_MUX_SEL"); gpio_set_value_cansleep(config->hpd_gpio, 1);
if (config->mux_en_gpio != -1) {
ret = gpio_request(config->mux_en_gpio, "HDMI_MUX_EN");
if (ret) { if (ret) {
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n", dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
"PMIC_HDMI_MUX_SEL", config->pmic_gpio, ret); "HDMI_MUX_SEL", config->mux_en_gpio, ret);
goto error4; goto error4;
} }
gpio_set_value_cansleep(config->pmic_gpio, 0); 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) {
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
"HDMI_MUX_SEL", config->mux_sel_gpio, ret);
goto error5;
}
gpio_set_value_cansleep(config->mux_sel_gpio, 0);
} }
DBG("gpio on"); DBG("gpio on");
} else { } else {
...@@ -67,15 +84,23 @@ static int gpio_config(struct hdmi *hdmi, bool on) ...@@ -67,15 +84,23 @@ static int gpio_config(struct hdmi *hdmi, bool on)
gpio_free(config->ddc_data_gpio); gpio_free(config->ddc_data_gpio);
gpio_free(config->hpd_gpio); gpio_free(config->hpd_gpio);
if (config->pmic_gpio != -1) { if (config->mux_en_gpio != -1) {
gpio_set_value_cansleep(config->pmic_gpio, 1); gpio_set_value_cansleep(config->mux_en_gpio, 0);
gpio_free(config->pmic_gpio); 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);
} }
DBG("gpio off"); DBG("gpio off");
} }
return 0; return 0;
error5:
if (config->mux_en_gpio != -1)
gpio_free(config->mux_en_gpio);
error4: error4:
gpio_free(config->hpd_gpio); gpio_free(config->hpd_gpio);
error3: error3:
...@@ -89,10 +114,11 @@ static int gpio_config(struct hdmi *hdmi, bool on) ...@@ -89,10 +114,11 @@ static int gpio_config(struct hdmi *hdmi, bool on)
static int hpd_enable(struct hdmi_connector *hdmi_connector) static int hpd_enable(struct hdmi_connector *hdmi_connector)
{ {
struct hdmi *hdmi = hdmi_connector->hdmi; struct hdmi *hdmi = hdmi_connector->hdmi;
const struct hdmi_platform_config *config = hdmi->config;
struct drm_device *dev = hdmi_connector->base.dev; struct drm_device *dev = hdmi_connector->base.dev;
struct hdmi_phy *phy = hdmi->phy; struct hdmi_phy *phy = hdmi->phy;
uint32_t hpd_ctrl; uint32_t hpd_ctrl;
int ret; int i, ret;
ret = gpio_config(hdmi, true); ret = gpio_config(hdmi, true);
if (ret) { if (ret) {
...@@ -100,31 +126,22 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) ...@@ -100,31 +126,22 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector)
goto fail; goto fail;
} }
ret = clk_prepare_enable(hdmi->clk); for (i = 0; i < config->hpd_clk_cnt; i++) {
ret = clk_prepare_enable(hdmi->hpd_clks[i]);
if (ret) { if (ret) {
dev_err(dev->dev, "failed to enable 'clk': %d\n", ret); dev_err(dev->dev, "failed to enable hpd clk: %s (%d)\n",
config->hpd_clk_names[i], ret);
goto fail; goto fail;
} }
ret = clk_prepare_enable(hdmi->m_pclk);
if (ret) {
dev_err(dev->dev, "failed to enable 'm_pclk': %d\n", ret);
goto fail;
} }
ret = clk_prepare_enable(hdmi->s_pclk); for (i = 0; i < config->hpd_reg_cnt; i++) {
ret = regulator_enable(hdmi->hpd_regs[i]);
if (ret) { if (ret) {
dev_err(dev->dev, "failed to enable 's_pclk': %d\n", ret); dev_err(dev->dev, "failed to enable hpd regulator: %s (%d)\n",
config->hpd_reg_names[i], ret);
goto fail; goto fail;
} }
if (hdmi->mpp0)
ret = regulator_enable(hdmi->mpp0);
if (!ret)
ret = regulator_enable(hdmi->mvs);
if (ret) {
dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
goto fail;
} }
hdmi_set_mode(hdmi, false); hdmi_set_mode(hdmi, false);
...@@ -157,26 +174,26 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) ...@@ -157,26 +174,26 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector)
static int hdp_disable(struct hdmi_connector *hdmi_connector) static int hdp_disable(struct hdmi_connector *hdmi_connector)
{ {
struct hdmi *hdmi = hdmi_connector->hdmi; struct hdmi *hdmi = hdmi_connector->hdmi;
const struct hdmi_platform_config *config = hdmi->config;
struct drm_device *dev = hdmi_connector->base.dev; struct drm_device *dev = hdmi_connector->base.dev;
int ret = 0; int i, ret = 0;
/* Disable HPD interrupt */ /* Disable HPD interrupt */
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0); hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
hdmi_set_mode(hdmi, false); hdmi_set_mode(hdmi, false);
if (hdmi->mpp0) for (i = 0; i < config->hpd_reg_cnt; i++) {
ret = regulator_disable(hdmi->mpp0); ret = regulator_disable(hdmi->hpd_regs[i]);
if (!ret)
ret = regulator_disable(hdmi->mvs);
if (ret) { if (ret) {
dev_err(dev->dev, "failed to enable regulators: %d\n", ret); dev_err(dev->dev, "failed to disable hpd regulator: %s (%d)\n",
config->hpd_reg_names[i], ret);
goto fail; goto fail;
} }
}
clk_disable_unprepare(hdmi->clk); for (i = 0; i < config->hpd_clk_cnt; i++)
clk_disable_unprepare(hdmi->m_pclk); clk_disable_unprepare(hdmi->hpd_clks[i]);
clk_disable_unprepare(hdmi->s_pclk);
ret = gpio_config(hdmi, false); ret = gpio_config(hdmi, false);
if (ret) { if (ret) {
...@@ -190,9 +207,19 @@ static int hdp_disable(struct hdmi_connector *hdmi_connector) ...@@ -190,9 +207,19 @@ static int hdp_disable(struct hdmi_connector *hdmi_connector)
return ret; return ret;
} }
static void
hotplug_work(struct work_struct *work)
{
struct hdmi_connector *hdmi_connector =
container_of(work, struct hdmi_connector, hpd_work);
struct drm_connector *connector = &hdmi_connector->base;
drm_helper_hpd_irq_event(connector->dev);
}
void hdmi_connector_irq(struct drm_connector *connector) void hdmi_connector_irq(struct drm_connector *connector)
{ {
struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
struct msm_drm_private *priv = connector->dev->dev_private;
struct hdmi *hdmi = hdmi_connector->hdmi; struct hdmi *hdmi = hdmi_connector->hdmi;
uint32_t hpd_int_status, hpd_int_ctrl; uint32_t hpd_int_status, hpd_int_ctrl;
...@@ -210,13 +237,13 @@ void hdmi_connector_irq(struct drm_connector *connector) ...@@ -210,13 +237,13 @@ void hdmi_connector_irq(struct drm_connector *connector)
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK); hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK);
drm_helper_hpd_irq_event(connector->dev);
/* detect disconnect if we are connected or visa versa: */ /* detect disconnect if we are connected or visa versa: */
hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN; hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
if (!detected) if (!detected)
hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT; hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl); hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
queue_work(priv->wq, &hdmi_connector->hpd_work);
} }
} }
...@@ -225,6 +252,7 @@ static enum drm_connector_status hdmi_connector_detect( ...@@ -225,6 +252,7 @@ static enum drm_connector_status hdmi_connector_detect(
{ {
struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
struct hdmi *hdmi = hdmi_connector->hdmi; struct hdmi *hdmi = hdmi_connector->hdmi;
const struct hdmi_platform_config *config = hdmi->config;
uint32_t hpd_int_status; uint32_t hpd_int_status;
int retry = 20; int retry = 20;
...@@ -234,6 +262,14 @@ static enum drm_connector_status hdmi_connector_detect( ...@@ -234,6 +262,14 @@ static enum drm_connector_status hdmi_connector_detect(
* let that trick us into thinking the monitor is gone: * let that trick us into thinking the monitor is gone:
*/ */
while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) { while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
/* hdmi debounce logic seems to get stuck sometimes,
* read directly the gpio to get a second opinion:
*/
if (gpio_get_value(config->hpd_gpio)) {
DBG("gpio tells us we are connected!");
hpd_int_status |= HDMI_HPD_INT_STATUS_CABLE_DETECTED;
break;
}
mdelay(10); mdelay(10);
hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS); hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
DBG("status=%08x", hpd_int_status); DBG("status=%08x", hpd_int_status);
...@@ -286,6 +322,8 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector, ...@@ -286,6 +322,8 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
struct hdmi *hdmi = hdmi_connector->hdmi;
const struct hdmi_platform_config *config = hdmi->config;
struct msm_drm_private *priv = connector->dev->dev_private; struct msm_drm_private *priv = connector->dev->dev_private;
struct msm_kms *kms = priv->kms; struct msm_kms *kms = priv->kms;
long actual, requested; long actual, requested;
...@@ -294,6 +332,13 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector, ...@@ -294,6 +332,13 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector,
actual = kms->funcs->round_pixclk(kms, actual = kms->funcs->round_pixclk(kms,
requested, hdmi_connector->hdmi->encoder); requested, hdmi_connector->hdmi->encoder);
/* for mdp5/apq8074, we manage our own pixel clk (as opposed to
* mdp4/dtv stuff where pixel clk is assigned to mdp/encoder
* instead):
*/
if (config->pwr_clk_cnt > 0)
actual = clk_round_rate(hdmi->pwr_clks[0], actual);
DBG("requested=%ld, actual=%ld", requested, actual); DBG("requested=%ld, actual=%ld", requested, actual);
if (actual != requested) if (actual != requested)
...@@ -336,6 +381,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi) ...@@ -336,6 +381,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
} }
hdmi_connector->hdmi = hdmi_reference(hdmi); hdmi_connector->hdmi = hdmi_reference(hdmi);
INIT_WORK(&hdmi_connector->hpd_work, hotplug_work);
connector = &hdmi_connector->base; connector = &hdmi_connector->base;
......
/*
* 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/>.
*/
#include "hdmi.h"
struct hdmi_phy_8x74 {
struct hdmi_phy base;
struct hdmi *hdmi;
void __iomem *mmio;
};
#define to_hdmi_phy_8x74(x) container_of(x, struct hdmi_phy_8x74, base)
static void phy_write(struct hdmi_phy_8x74 *phy, u32 reg, u32 data)
{
msm_writel(data, phy->mmio + reg);
}
//static u32 phy_read(struct hdmi_phy_8x74 *phy, u32 reg)
//{
// return msm_readl(phy->mmio + reg);
//}
static void hdmi_phy_8x74_destroy(struct hdmi_phy *phy)
{
struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
kfree(phy_8x74);
}
static void hdmi_phy_8x74_reset(struct hdmi_phy *phy)
{
struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
struct hdmi *hdmi = phy_8x74->hdmi;
unsigned int val;
/* NOTE that HDMI_PHY_CTL is in core mmio, not phy mmio: */
val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
/* pull low */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val & ~HDMI_PHY_CTRL_SW_RESET);
} else {
/* pull high */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val | HDMI_PHY_CTRL_SW_RESET);
}
if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
/* pull low */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
} else {
/* pull high */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val | HDMI_PHY_CTRL_SW_RESET_PLL);
}
msleep(100);
if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
/* pull high */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val | HDMI_PHY_CTRL_SW_RESET);
} else {
/* pull low */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val & ~HDMI_PHY_CTRL_SW_RESET);
}
if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
/* pull high */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val | HDMI_PHY_CTRL_SW_RESET_PLL);
} else {
/* pull low */
hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
}
}
static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy,
unsigned long int pixclock)
{
struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG0, 0x1b);
phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG1, 0xf2);
phy_write(phy_8x74, REG_HDMI_8x74_BIST_CFG0, 0x0);
phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN0, 0x0);
phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN1, 0x0);
phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN2, 0x0);
phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN3, 0x0);
phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL1, 0x20);
}
static void hdmi_phy_8x74_powerdown(struct hdmi_phy *phy)
{
struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL0, 0x7f);
}
static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = {
.destroy = hdmi_phy_8x74_destroy,
.reset = hdmi_phy_8x74_reset,
.powerup = hdmi_phy_8x74_powerup,
.powerdown = hdmi_phy_8x74_powerdown,
};
struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi)
{
struct hdmi_phy_8x74 *phy_8x74;
struct hdmi_phy *phy = NULL;
int ret;
phy_8x74 = kzalloc(sizeof(*phy_8x74), GFP_KERNEL);
if (!phy_8x74) {
ret = -ENOMEM;
goto fail;
}
phy = &phy_8x74->base;
phy->funcs = &hdmi_phy_8x74_funcs;
phy_8x74->hdmi = hdmi;
/* for 8x74, the phy mmio is mapped separately: */
phy_8x74->mmio = msm_ioremap(hdmi->pdev,
"phy_physical", "HDMI_8x74");
if (IS_ERR(phy_8x74->mmio)) {
ret = PTR_ERR(phy_8x74->mmio);
goto fail;
}
return phy;
fail:
if (phy)
hdmi_phy_8x74_destroy(phy);
return ERR_PTR(ret);
}
...@@ -39,7 +39,7 @@ static int mdp4_hw_init(struct msm_kms *kms) ...@@ -39,7 +39,7 @@ static int mdp4_hw_init(struct msm_kms *kms)
major = FIELD(version, MDP4_VERSION_MAJOR); major = FIELD(version, MDP4_VERSION_MAJOR);
minor = FIELD(version, MDP4_VERSION_MINOR); minor = FIELD(version, MDP4_VERSION_MINOR);
DBG("found MDP version v%d.%d", major, minor); DBG("found MDP4 version v%d.%d", major, minor);
if (major != 4) { if (major != 4) {
dev_err(dev->dev, "unexpected MDP version: v%d.%d\n", dev_err(dev->dev, "unexpected MDP version: v%d.%d\n",
...@@ -195,6 +195,7 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) ...@@ -195,6 +195,7 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
struct drm_plane *plane; struct drm_plane *plane;
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct hdmi *hdmi;
int ret; int ret;
/* /*
...@@ -244,9 +245,10 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) ...@@ -244,9 +245,10 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */ encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */
priv->encoders[priv->num_encoders++] = encoder; priv->encoders[priv->num_encoders++] = encoder;
ret = hdmi_init(dev, encoder); hdmi = hdmi_init(dev, encoder);
if (ret) { if (IS_ERR(hdmi)) {
dev_err(dev->dev, "failed to initialize HDMI\n"); ret = PTR_ERR(hdmi);
dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
goto fail; goto fail;
} }
......
...@@ -190,7 +190,9 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, ...@@ -190,7 +190,9 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev); struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev);
int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder); struct hdmi;
struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder);
irqreturn_t hdmi_irq(int irq, void *dev_id);
void __init hdmi_register(void); void __init hdmi_register(void);
void __exit hdmi_unregister(void); void __exit hdmi_unregister(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment