drm/msm/hdmi: refactor bind/init
Split up hdmi_init() into hdmi_init() (done at hdmi sub-device bind/probe time) and hdmi_modeset_init() done from master driver's modeset_init(). Anything that can fail due to dependencies on other drivers which may be missing or not probed yet should go in hdmi_init(), so that devm error/cleanup paths work properly. Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
Родитель
bc00ae02e4
Коммит
067fef372c
|
@ -68,24 +68,17 @@ void hdmi_destroy(struct kref *kref)
|
||||||
platform_set_drvdata(hdmi->pdev, NULL);
|
platform_set_drvdata(hdmi->pdev, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize connector */
|
/* construct hdmi at bind/probe time, grab all the resources. If
|
||||||
struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
* we are to EPROBE_DEFER we want to do it here, rather than later
|
||||||
|
* at modeset_init() time
|
||||||
|
*/
|
||||||
|
static struct hdmi *hdmi_init(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct hdmi_platform_config *config = pdev->dev.platform_data;
|
||||||
struct hdmi *hdmi = NULL;
|
struct hdmi *hdmi = NULL;
|
||||||
struct msm_drm_private *priv = dev->dev_private;
|
|
||||||
struct platform_device *pdev = priv->hdmi_pdev;
|
|
||||||
struct hdmi_platform_config *config;
|
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (!pdev) {
|
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
||||||
dev_err(dev->dev, "no hdmi device\n");
|
|
||||||
ret = -ENXIO;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
config = pdev->dev.platform_data;
|
|
||||||
|
|
||||||
hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
|
|
||||||
if (!hdmi) {
|
if (!hdmi) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -93,12 +86,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
|
|
||||||
kref_init(&hdmi->refcount);
|
kref_init(&hdmi->refcount);
|
||||||
|
|
||||||
hdmi->dev = dev;
|
|
||||||
hdmi->pdev = pdev;
|
hdmi->pdev = pdev;
|
||||||
hdmi->config = config;
|
hdmi->config = config;
|
||||||
hdmi->encoder = encoder;
|
|
||||||
|
|
||||||
hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
|
|
||||||
|
|
||||||
/* 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 */
|
||||||
if (config->phy_init)
|
if (config->phy_init)
|
||||||
|
@ -108,7 +97,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
|
|
||||||
if (IS_ERR(hdmi->phy)) {
|
if (IS_ERR(hdmi->phy)) {
|
||||||
ret = PTR_ERR(hdmi->phy);
|
ret = PTR_ERR(hdmi->phy);
|
||||||
dev_err(dev->dev, "failed to load phy: %d\n", ret);
|
dev_err(&pdev->dev, "failed to load phy: %d\n", ret);
|
||||||
hdmi->phy = NULL;
|
hdmi->phy = NULL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +116,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
config->hpd_reg_names[i]);
|
config->hpd_reg_names[i]);
|
||||||
if (IS_ERR(reg)) {
|
if (IS_ERR(reg)) {
|
||||||
ret = PTR_ERR(reg);
|
ret = PTR_ERR(reg);
|
||||||
dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n",
|
dev_err(&pdev->dev, "failed to get hpd regulator: %s (%d)\n",
|
||||||
config->hpd_reg_names[i], ret);
|
config->hpd_reg_names[i], ret);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +132,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
config->pwr_reg_names[i]);
|
config->pwr_reg_names[i]);
|
||||||
if (IS_ERR(reg)) {
|
if (IS_ERR(reg)) {
|
||||||
ret = PTR_ERR(reg);
|
ret = PTR_ERR(reg);
|
||||||
dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n",
|
dev_err(&pdev->dev, "failed to get pwr regulator: %s (%d)\n",
|
||||||
config->pwr_reg_names[i], ret);
|
config->pwr_reg_names[i], ret);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -158,7 +147,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
|
clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
ret = PTR_ERR(clk);
|
ret = PTR_ERR(clk);
|
||||||
dev_err(dev->dev, "failed to get hpd clk: %s (%d)\n",
|
dev_err(&pdev->dev, "failed to get hpd clk: %s (%d)\n",
|
||||||
config->hpd_clk_names[i], ret);
|
config->hpd_clk_names[i], ret);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +162,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
|
clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
ret = PTR_ERR(clk);
|
ret = PTR_ERR(clk);
|
||||||
dev_err(dev->dev, "failed to get pwr clk: %s (%d)\n",
|
dev_err(&pdev->dev, "failed to get pwr clk: %s (%d)\n",
|
||||||
config->pwr_clk_names[i], ret);
|
config->pwr_clk_names[i], ret);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -184,11 +173,41 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
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);
|
||||||
dev_err(dev->dev, "failed to get i2c: %d\n", ret);
|
dev_err(&pdev->dev, "failed to get i2c: %d\n", ret);
|
||||||
hdmi->i2c = NULL;
|
hdmi->i2c = NULL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hdmi;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (hdmi)
|
||||||
|
hdmi_destroy(&hdmi->refcount);
|
||||||
|
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Second part of initialization, the drm/kms level modeset_init,
|
||||||
|
* constructs/initializes mode objects, etc, is called from master
|
||||||
|
* driver (not hdmi sub-device's probe/bind!)
|
||||||
|
*
|
||||||
|
* Any resource (regulator/clk/etc) which could be missing at boot
|
||||||
|
* should be handled in hdmi_init() so that failure happens from
|
||||||
|
* hdmi sub-device's probe.
|
||||||
|
*/
|
||||||
|
int hdmi_modeset_init(struct hdmi *hdmi,
|
||||||
|
struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
|
{
|
||||||
|
struct msm_drm_private *priv = dev->dev_private;
|
||||||
|
struct platform_device *pdev = hdmi->pdev;
|
||||||
|
struct hdmi_platform_config *config = pdev->dev.platform_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
hdmi->dev = dev;
|
||||||
|
hdmi->encoder = encoder;
|
||||||
|
|
||||||
|
hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
|
||||||
|
|
||||||
hdmi->bridge = hdmi_bridge_init(hdmi);
|
hdmi->bridge = hdmi_bridge_init(hdmi);
|
||||||
if (IS_ERR(hdmi->bridge)) {
|
if (IS_ERR(hdmi->bridge)) {
|
||||||
ret = PTR_ERR(hdmi->bridge);
|
ret = PTR_ERR(hdmi->bridge);
|
||||||
|
@ -230,19 +249,20 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, hdmi);
|
platform_set_drvdata(pdev, hdmi);
|
||||||
|
|
||||||
return hdmi;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (hdmi) {
|
/* bridge/connector are normally destroyed by drm: */
|
||||||
/* bridge/connector are normally destroyed by drm: */
|
if (hdmi->bridge) {
|
||||||
if (hdmi->bridge)
|
hdmi->bridge->funcs->destroy(hdmi->bridge);
|
||||||
hdmi->bridge->funcs->destroy(hdmi->bridge);
|
hdmi->bridge = NULL;
|
||||||
if (hdmi->connector)
|
}
|
||||||
hdmi->connector->funcs->destroy(hdmi->connector);
|
if (hdmi->connector) {
|
||||||
hdmi_destroy(&hdmi->refcount);
|
hdmi->connector->funcs->destroy(hdmi->connector);
|
||||||
|
hdmi->connector = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERR_PTR(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -251,11 +271,10 @@ fail:
|
||||||
|
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
|
||||||
static void set_hdmi_pdev(struct drm_device *dev,
|
static void set_hdmi(struct drm_device *dev, struct hdmi *hdmi)
|
||||||
struct platform_device *pdev)
|
|
||||||
{
|
{
|
||||||
struct msm_drm_private *priv = dev->dev_private;
|
struct msm_drm_private *priv = dev->dev_private;
|
||||||
priv->hdmi_pdev = pdev;
|
priv->hdmi = hdmi;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
|
@ -279,6 +298,7 @@ static int get_gpio(struct device *dev, struct device_node *of_node, const char
|
||||||
static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||||
{
|
{
|
||||||
static struct hdmi_platform_config config = {};
|
static struct hdmi_platform_config config = {};
|
||||||
|
struct hdmi *hdmi;
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
struct device_node *of_node = dev->of_node;
|
struct device_node *of_node = dev->of_node;
|
||||||
|
|
||||||
|
@ -369,14 +389,17 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
dev->platform_data = &config;
|
dev->platform_data = &config;
|
||||||
set_hdmi_pdev(dev_get_drvdata(master), to_platform_device(dev));
|
hdmi = hdmi_init(to_platform_device(dev));
|
||||||
|
if (IS_ERR(hdmi))
|
||||||
|
return PTR_ERR(hdmi);
|
||||||
|
set_hdmi(dev_get_drvdata(master), hdmi);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hdmi_unbind(struct device *dev, struct device *master,
|
static void hdmi_unbind(struct device *dev, struct device *master,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
set_hdmi_pdev(dev_get_drvdata(master), NULL);
|
set_hdmi(dev_get_drvdata(master), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct component_ops hdmi_ops = {
|
static const struct component_ops hdmi_ops = {
|
||||||
|
|
|
@ -510,7 +510,7 @@ struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
|
||||||
|
|
||||||
#ifdef CONFIG_COMMON_CLK
|
#ifdef CONFIG_COMMON_CLK
|
||||||
phy_8960->pll_hw.init = &pll_init;
|
phy_8960->pll_hw.init = &pll_init;
|
||||||
phy_8960->pll = devm_clk_register(hdmi->dev->dev, &phy_8960->pll_hw);
|
phy_8960->pll = devm_clk_register(&hdmi->pdev->dev, &phy_8960->pll_hw);
|
||||||
if (IS_ERR(phy_8960->pll)) {
|
if (IS_ERR(phy_8960->pll)) {
|
||||||
ret = PTR_ERR(phy_8960->pll);
|
ret = PTR_ERR(phy_8960->pll);
|
||||||
phy_8960->pll = NULL;
|
phy_8960->pll = NULL;
|
||||||
|
|
|
@ -228,7 +228,6 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
|
||||||
struct drm_encoder *encoder;
|
struct drm_encoder *encoder;
|
||||||
struct drm_connector *connector;
|
struct drm_connector *connector;
|
||||||
struct drm_panel *panel;
|
struct drm_panel *panel;
|
||||||
struct hdmi *hdmi;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* construct non-private planes: */
|
/* construct non-private planes: */
|
||||||
|
@ -326,11 +325,13 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
|
||||||
priv->crtcs[priv->num_crtcs++] = crtc;
|
priv->crtcs[priv->num_crtcs++] = crtc;
|
||||||
priv->encoders[priv->num_encoders++] = encoder;
|
priv->encoders[priv->num_encoders++] = encoder;
|
||||||
|
|
||||||
hdmi = hdmi_init(dev, encoder);
|
if (priv->hdmi) {
|
||||||
if (IS_ERR(hdmi)) {
|
/* Construct bridge/connector for HDMI: */
|
||||||
ret = PTR_ERR(hdmi);
|
ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
|
||||||
dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
|
if (ret) {
|
||||||
goto fail;
|
dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -82,6 +82,7 @@ irqreturn_t mdp5_irq(struct msm_kms *kms)
|
||||||
{
|
{
|
||||||
struct mdp_kms *mdp_kms = to_mdp_kms(kms);
|
struct mdp_kms *mdp_kms = to_mdp_kms(kms);
|
||||||
struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms);
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms);
|
||||||
|
struct msm_drm_private *priv = mdp5_kms->dev->dev_private;
|
||||||
uint32_t intr;
|
uint32_t intr;
|
||||||
|
|
||||||
intr = mdp5_read(mdp5_kms, REG_MDP5_HW_INTR_STATUS);
|
intr = mdp5_read(mdp5_kms, REG_MDP5_HW_INTR_STATUS);
|
||||||
|
@ -92,7 +93,7 @@ irqreturn_t mdp5_irq(struct msm_kms *kms)
|
||||||
mdp5_irq_mdp(mdp_kms);
|
mdp5_irq_mdp(mdp_kms);
|
||||||
|
|
||||||
if (intr & MDP5_HW_INTR_STATUS_INTR_HDMI)
|
if (intr & MDP5_HW_INTR_STATUS_INTR_HDMI)
|
||||||
hdmi_irq(0, mdp5_kms->hdmi);
|
hdmi_irq(0, priv->hdmi);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,11 +324,12 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
|
||||||
priv->encoders[priv->num_encoders++] = encoder;
|
priv->encoders[priv->num_encoders++] = encoder;
|
||||||
|
|
||||||
/* Construct bridge/connector for HDMI: */
|
/* Construct bridge/connector for HDMI: */
|
||||||
mdp5_kms->hdmi = hdmi_init(dev, encoder);
|
if (priv->hdmi) {
|
||||||
if (IS_ERR(mdp5_kms->hdmi)) {
|
ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
|
||||||
ret = PTR_ERR(mdp5_kms->hdmi);
|
if (ret) {
|
||||||
dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
|
dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -71,8 +71,6 @@ struct mdp5_kms {
|
||||||
struct clk *lut_clk;
|
struct clk *lut_clk;
|
||||||
struct clk *vsync_clk;
|
struct clk *vsync_clk;
|
||||||
|
|
||||||
struct hdmi *hdmi;
|
|
||||||
|
|
||||||
struct mdp_irq error_handler;
|
struct mdp_irq error_handler;
|
||||||
};
|
};
|
||||||
#define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base)
|
#define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base)
|
||||||
|
|
|
@ -75,7 +75,12 @@ struct msm_drm_private {
|
||||||
struct msm_kms *kms;
|
struct msm_kms *kms;
|
||||||
|
|
||||||
/* subordinate devices, if present: */
|
/* subordinate devices, if present: */
|
||||||
struct platform_device *hdmi_pdev, *gpu_pdev;
|
struct platform_device *gpu_pdev;
|
||||||
|
|
||||||
|
/* possibly this should be in the kms component, but it is
|
||||||
|
* shared by both mdp4 and mdp5..
|
||||||
|
*/
|
||||||
|
struct hdmi *hdmi;
|
||||||
|
|
||||||
/* when we have more than one 'msm_gpu' these need to be an array: */
|
/* when we have more than one 'msm_gpu' these need to be an array: */
|
||||||
struct msm_gpu *gpu;
|
struct msm_gpu *gpu;
|
||||||
|
@ -202,7 +207,8 @@ 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);
|
||||||
|
|
||||||
struct hdmi;
|
struct hdmi;
|
||||||
struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder);
|
int hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev,
|
||||||
|
struct drm_encoder *encoder);
|
||||||
irqreturn_t hdmi_irq(int irq, void *dev_id);
|
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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче