Skip to content

Commit

Permalink
drm/msm: convert to drm_bridge
Browse files Browse the repository at this point in the history
Drop the msm_connector base class, and special calls to base class
methods from the encoder, and use instead drm_bridge.  This allows for a
cleaner division between the hdmi (and in future dsi) blocks, from the
mdp block.

Signed-off-by: Rob Clark <robdclark@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
  • Loading branch information
Rob Clark authored and Dave Airlie committed Sep 2, 2013
1 parent 3b336ec commit a3376e3
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 260 deletions.
2 changes: 1 addition & 1 deletion drivers/gpu/drm/msm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ msm-y := \
adreno/adreno_gpu.o \
adreno/a3xx_gpu.o \
hdmi/hdmi.o \
hdmi/hdmi_bridge.o \
hdmi/hdmi_connector.o \
hdmi/hdmi_i2c.o \
hdmi/hdmi_phy_8960.o \
Expand All @@ -17,7 +18,6 @@ msm-y := \
mdp4/mdp4_irq.o \
mdp4/mdp4_kms.o \
mdp4/mdp4_plane.o \
msm_connector.o \
msm_drv.o \
msm_fb.o \
msm_gem.o \
Expand Down
49 changes: 43 additions & 6 deletions drivers/gpu/drm/msm/hdmi/hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ static irqreturn_t hdmi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}

void hdmi_destroy(struct hdmi *hdmi)
void hdmi_destroy(struct kref *kref)
{
struct hdmi *hdmi = container_of(kref, struct hdmi, refcount);
struct hdmi_phy *phy = hdmi->phy;

if (phy)
Expand All @@ -70,9 +71,10 @@ void hdmi_destroy(struct hdmi *hdmi)
}

/* initialize connector */
int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
struct drm_connector *connector)
int hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
{
struct hdmi *hdmi = NULL;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = hdmi_pdev;
struct hdmi_platform_config *config;
int ret;
Expand All @@ -85,11 +87,19 @@ int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,

config = pdev->dev.platform_data;

hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
if (!hdmi) {
ret = -ENOMEM;
goto fail;
}

kref_init(&hdmi->refcount);

get_device(&pdev->dev);

hdmi->dev = dev;
hdmi->pdev = pdev;
hdmi->connector = connector;
hdmi->encoder = encoder;

/* not sure about which phy maps to which msm.. probably I miss some */
if (config->phy_init)
Expand Down Expand Up @@ -152,6 +162,22 @@ int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
goto fail;
}

hdmi->bridge = hdmi_bridge_init(hdmi);
if (IS_ERR(hdmi->bridge)) {
ret = PTR_ERR(hdmi->bridge);
dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret);
hdmi->bridge = NULL;
goto fail;
}

hdmi->connector = hdmi_connector_init(hdmi);
if (IS_ERR(hdmi->connector)) {
ret = PTR_ERR(hdmi->connector);
dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret);
hdmi->connector = NULL;
goto fail;
}

hdmi->irq = platform_get_irq(pdev, 0);
if (hdmi->irq < 0) {
ret = hdmi->irq;
Expand All @@ -168,11 +194,22 @@ int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
goto fail;
}

encoder->bridge = hdmi->bridge;

priv->bridges[priv->num_bridges++] = hdmi->bridge;
priv->connectors[priv->num_connectors++] = hdmi->connector;

return 0;

fail:
if (hdmi)
hdmi_destroy(hdmi);
if (hdmi) {
/* bridge/connector are normally destroyed by drm: */
if (hdmi->bridge)
hdmi->bridge->funcs->destroy(hdmi->bridge);
if (hdmi->connector)
hdmi->connector->funcs->destroy(hdmi->connector);
hdmi_destroy(&hdmi->refcount);
}

return ret;
}
Expand Down
31 changes: 25 additions & 6 deletions drivers/gpu/drm/msm/hdmi/hdmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
struct hdmi_phy;

struct hdmi {
struct kref refcount;

struct drm_device *dev;
struct platform_device *pdev;

Expand All @@ -45,6 +47,10 @@ struct hdmi {
struct hdmi_phy *phy;
struct i2c_adapter *i2c;
struct drm_connector *connector;
struct drm_bridge *bridge;

/* the encoder we are hooked to (outside of hdmi block) */
struct drm_encoder *encoder;

bool hdmi_mode; /* are we in hdmi mode? */

Expand All @@ -58,9 +64,7 @@ struct hdmi_platform_config {
};

void hdmi_set_mode(struct hdmi *hdmi, bool power_on);
void hdmi_destroy(struct hdmi *hdmi);
int hdmi_init(struct hdmi *hdmi, struct drm_device *dev,
struct drm_connector *connector);
void hdmi_destroy(struct kref *kref);

static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data)
{
Expand All @@ -72,6 +76,17 @@ static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg)
return msm_readl(hdmi->mmio + reg);
}

static inline struct hdmi * hdmi_reference(struct hdmi *hdmi)
{
kref_get(&hdmi->refcount);
return hdmi;
}

static inline void hdmi_unreference(struct hdmi *hdmi)
{
kref_put(&hdmi->refcount, hdmi_destroy);
}

/*
* The phy appears to be different, for example between 8960 and 8x60,
* so split the phy related functions out and load the correct one at
Expand All @@ -89,17 +104,21 @@ struct hdmi_phy {
const struct hdmi_phy_funcs *funcs;
};

/*
* phy can be different on different generations:
*/
struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi);
struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);

/*
* hdmi bridge:
*/

struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi);

/*
* hdmi connector:
*/

void hdmi_connector_irq(struct drm_connector *connector);
struct drm_connector *hdmi_connector_init(struct hdmi *hdmi);

/*
* i2c adapter for ddc:
Expand Down
167 changes: 167 additions & 0 deletions drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* 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_bridge {
struct drm_bridge base;

struct hdmi *hdmi;

unsigned long int pixclock;
};
#define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base)

static void hdmi_bridge_destroy(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
hdmi_unreference(hdmi_bridge->hdmi);
drm_bridge_cleanup(bridge);
kfree(hdmi_bridge);
}

static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
struct hdmi_phy *phy = hdmi->phy;

DBG("power up");
phy->funcs->powerup(phy, hdmi_bridge->pixclock);
hdmi_set_mode(hdmi, true);
}

static void hdmi_bridge_enable(struct drm_bridge *bridge)
{
}

static void hdmi_bridge_disable(struct drm_bridge *bridge)
{
}

static void hdmi_bridge_post_disable(struct drm_bridge *bridge)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
struct hdmi_phy *phy = hdmi->phy;

DBG("power down");
hdmi_set_mode(hdmi, false);
phy->funcs->powerdown(phy);
}

static void hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
int hstart, hend, vstart, vend;
uint32_t frame_ctrl;

mode = adjusted_mode;

hdmi_bridge->pixclock = mode->clock * 1000;

hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1;

hstart = mode->htotal - mode->hsync_start;
hend = mode->htotal - mode->hsync_start + mode->hdisplay;

vstart = mode->vtotal - mode->vsync_start - 1;
vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;

DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
mode->htotal, mode->vtotal, hstart, hend, vstart, vend);

hdmi_write(hdmi, REG_HDMI_TOTAL,
HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));

hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
HDMI_ACTIVE_HSYNC_START(hstart) |
HDMI_ACTIVE_HSYNC_END(hend));
hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
HDMI_ACTIVE_VSYNC_START(vstart) |
HDMI_ACTIVE_VSYNC_END(vend));

if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
} else {
hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
HDMI_VSYNC_ACTIVE_F2_START(0) |
HDMI_VSYNC_ACTIVE_F2_END(0));
}

frame_ctrl = 0;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;
DBG("frame_ctrl=%08x", frame_ctrl);
hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);

// TODO until we have audio, this might be safest:
if (hdmi->hdmi_mode)
hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
}

static const struct drm_bridge_funcs hdmi_bridge_funcs = {
.pre_enable = hdmi_bridge_pre_enable,
.enable = hdmi_bridge_enable,
.disable = hdmi_bridge_disable,
.post_disable = hdmi_bridge_post_disable,
.mode_set = hdmi_bridge_mode_set,
.destroy = hdmi_bridge_destroy,
};


/* initialize bridge */
struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi)
{
struct drm_bridge *bridge = NULL;
struct hdmi_bridge *hdmi_bridge;
int ret;

hdmi_bridge = kzalloc(sizeof(*hdmi_bridge), GFP_KERNEL);
if (!hdmi_bridge) {
ret = -ENOMEM;
goto fail;
}

hdmi_bridge->hdmi = hdmi_reference(hdmi);

bridge = &hdmi_bridge->base;

drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs);

return bridge;

fail:
if (bridge)
hdmi_bridge_destroy(bridge);

return ERR_PTR(ret);
}
Loading

0 comments on commit a3376e3

Please sign in to comment.