mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-06 16:49:22 +00:00
drm/omap: Add support for drm_bridge
Hook up drm_bridge support in the omapdrm driver. Despite the recent extensive preparation work, this is a rather intrusive change, as the management of outputs needs to be adapted through the driver to handle both omap_dss_device and drm_bridge. Connector creation is skipped when using a drm_bridge, as the bridge creates the connector internally. This creates issues with systems that split connector operations (such as modes retrieval and hot-plug detection) across different bridges. These systems can't be supported using drm_bridge for now (their support through the omap_dss_device infrastructure is not affected), this will be fixed in subsequent changes. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com> Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
This commit is contained in:
parent
163f7a3578
commit
79107f274b
9 changed files with 144 additions and 61 deletions
|
@ -19,6 +19,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "omapdss.h"
|
||||
|
@ -156,7 +157,7 @@ struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (dssdev->id && dssdev->next)
|
||||
if (dssdev->id && (dssdev->next || dssdev->bridge))
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -184,7 +185,18 @@ int omapdss_device_connect(struct dss_device *dss,
|
|||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(dst->dev, "connect\n");
|
||||
dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
|
||||
src ? dev_name(src->dev) : "NULL",
|
||||
dst ? dev_name(dst->dev) : "NULL");
|
||||
|
||||
if (!dst) {
|
||||
/*
|
||||
* The destination is NULL when the source is connected to a
|
||||
* bridge instead of a DSS device. Stop here, we will attach the
|
||||
* bridge later when we will have a DRM encoder.
|
||||
*/
|
||||
return src && src->bridge ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
if (omapdss_device_is_connected(dst))
|
||||
return -EBUSY;
|
||||
|
@ -204,7 +216,16 @@ EXPORT_SYMBOL_GPL(omapdss_device_connect);
|
|||
void omapdss_device_disconnect(struct omap_dss_device *src,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
dev_dbg(dst->dev, "disconnect\n");
|
||||
struct dss_device *dss = src ? src->dss : dst->dss;
|
||||
|
||||
dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
|
||||
src ? dev_name(src->dev) : "NULL",
|
||||
dst ? dev_name(dst->dev) : "NULL");
|
||||
|
||||
if (!dst) {
|
||||
WARN_ON(!src->bridge);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dst->id && !omapdss_device_is_connected(dst)) {
|
||||
WARN_ON(!dst->display);
|
||||
|
|
|
@ -410,6 +410,7 @@ struct omap_dss_device {
|
|||
|
||||
struct dss_device *dss;
|
||||
struct omap_dss_device *next;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
|
|
|
@ -20,25 +20,34 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "omapdss.h"
|
||||
|
||||
int omapdss_device_init_output(struct omap_dss_device *out)
|
||||
{
|
||||
out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
|
||||
if (IS_ERR(out->next)) {
|
||||
if (PTR_ERR(out->next) != -EPROBE_DEFER)
|
||||
dev_err(out->dev, "failed to find video sink\n");
|
||||
return PTR_ERR(out->next);
|
||||
struct device_node *remote_node;
|
||||
|
||||
remote_node = of_graph_get_remote_node(out->dev->of_node, 0, 0);
|
||||
if (!remote_node) {
|
||||
dev_dbg(out->dev, "failed to find video sink\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
out->next = omapdss_find_device_by_node(remote_node);
|
||||
out->bridge = of_drm_find_bridge(remote_node);
|
||||
|
||||
of_node_put(remote_node);
|
||||
|
||||
if (out->next && out->type != out->next->type) {
|
||||
dev_err(out->dev, "output type and display type don't match\n");
|
||||
omapdss_device_put(out->next);
|
||||
out->next = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return out->next || out->bridge ? 0 : -EPROBE_DEFER;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_device_init_output);
|
||||
|
||||
|
|
|
@ -299,9 +299,16 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
|
|||
.mode_valid = omap_connector_mode_valid,
|
||||
};
|
||||
|
||||
static int omap_connector_get_type(struct omap_dss_device *display)
|
||||
static int omap_connector_get_type(struct omap_dss_device *output)
|
||||
{
|
||||
switch (display->type) {
|
||||
struct omap_dss_device *display;
|
||||
enum omap_display_type type;
|
||||
|
||||
display = omapdss_display_get(output);
|
||||
type = display->type;
|
||||
omapdss_device_put(display);
|
||||
|
||||
switch (type) {
|
||||
case OMAP_DISPLAY_TYPE_HDMI:
|
||||
return DRM_MODE_CONNECTOR_HDMIA;
|
||||
case OMAP_DISPLAY_TYPE_DVI:
|
||||
|
@ -324,14 +331,13 @@ static int omap_connector_get_type(struct omap_dss_device *display)
|
|||
/* initialize connector */
|
||||
struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||||
struct omap_dss_device *output,
|
||||
struct omap_dss_device *display,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct omap_connector *omap_connector;
|
||||
struct omap_dss_device *dssdev;
|
||||
|
||||
DBG("%s", display->name);
|
||||
DBG("%s", output->name);
|
||||
|
||||
omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
|
||||
if (!omap_connector)
|
||||
|
@ -344,7 +350,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
|
|||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_connector_init(dev, connector, &omap_connector_funcs,
|
||||
omap_connector_get_type(display));
|
||||
omap_connector_get_type(output));
|
||||
drm_connector_helper_add(connector, &omap_connector_helper_funcs);
|
||||
|
||||
/*
|
||||
|
|
|
@ -31,7 +31,6 @@ struct omap_dss_device;
|
|||
|
||||
struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||||
struct omap_dss_device *output,
|
||||
struct omap_dss_device *display,
|
||||
struct drm_encoder *encoder);
|
||||
bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
|
||||
void omap_connector_enable_hpd(struct drm_connector *connector);
|
||||
|
|
|
@ -666,7 +666,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
|||
&omap_crtc_funcs, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "%s(): could not init crtc for: %s\n",
|
||||
__func__, pipe->display->name);
|
||||
__func__, pipe->output->name);
|
||||
kfree(omap_crtc);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -140,9 +140,7 @@ static void omap_disconnect_pipelines(struct drm_device *ddev)
|
|||
omapdss_device_disconnect(NULL, pipe->output);
|
||||
|
||||
omapdss_device_put(pipe->output);
|
||||
omapdss_device_put(pipe->display);
|
||||
pipe->output = NULL;
|
||||
pipe->display = NULL;
|
||||
}
|
||||
|
||||
memset(&priv->channels, 0, sizeof(priv->channels));
|
||||
|
@ -169,7 +167,6 @@ static int omap_connect_pipelines(struct drm_device *ddev)
|
|||
|
||||
pipe = &priv->pipes[priv->num_pipes++];
|
||||
pipe->output = omapdss_device_get(output);
|
||||
pipe->display = omapdss_display_get(output);
|
||||
|
||||
if (priv->num_pipes == ARRAY_SIZE(priv->pipes)) {
|
||||
/* To balance the 'for_each_dss_output' loop */
|
||||
|
@ -207,6 +204,28 @@ static int omap_modeset_init_properties(struct drm_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int omap_display_id(struct omap_dss_device *output)
|
||||
{
|
||||
struct device_node *node = NULL;
|
||||
|
||||
if (output->next) {
|
||||
struct omap_dss_device *display;
|
||||
|
||||
display = omapdss_display_get(output);
|
||||
node = display->dev->of_node;
|
||||
omapdss_device_put(display);
|
||||
} else {
|
||||
struct drm_bridge *bridge = output->bridge;
|
||||
|
||||
while (bridge->next)
|
||||
bridge = bridge->next;
|
||||
|
||||
node = bridge->of_node;
|
||||
}
|
||||
|
||||
return node ? of_alias_get_id(node, "display") : -ENODEV;
|
||||
}
|
||||
|
||||
static int omap_modeset_init(struct drm_device *dev)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
|
@ -262,7 +281,10 @@ static int omap_modeset_init(struct drm_device *dev)
|
|||
priv->planes[priv->num_planes++] = plane;
|
||||
}
|
||||
|
||||
/* Create the encoders and get the pipelines aliases. */
|
||||
/*
|
||||
* Create the encoders, attach the bridges and get the pipeline alias
|
||||
* IDs.
|
||||
*/
|
||||
for (i = 0; i < priv->num_pipes; i++) {
|
||||
struct omap_drm_pipeline *pipe = &priv->pipes[i];
|
||||
int id;
|
||||
|
@ -271,7 +293,14 @@ static int omap_modeset_init(struct drm_device *dev)
|
|||
if (!pipe->encoder)
|
||||
return -ENOMEM;
|
||||
|
||||
id = of_alias_get_id(pipe->display->dev->of_node, "display");
|
||||
if (pipe->output->bridge) {
|
||||
ret = drm_bridge_attach(pipe->encoder,
|
||||
pipe->output->bridge, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
id = omap_display_id(pipe->output);
|
||||
pipe->alias_id = id >= 0 ? id : i;
|
||||
}
|
||||
|
||||
|
@ -297,16 +326,16 @@ static int omap_modeset_init(struct drm_device *dev)
|
|||
for (i = 0; i < priv->num_pipes; i++) {
|
||||
struct omap_drm_pipeline *pipe = &priv->pipes[i];
|
||||
struct drm_encoder *encoder = pipe->encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
connector = omap_connector_init(dev, pipe->output,
|
||||
pipe->display, encoder);
|
||||
if (!connector)
|
||||
return -ENOMEM;
|
||||
if (!pipe->output->bridge) {
|
||||
pipe->connector = omap_connector_init(dev, pipe->output,
|
||||
encoder);
|
||||
if (!pipe->connector)
|
||||
return -ENOMEM;
|
||||
|
||||
drm_connector_attach_encoder(connector, encoder);
|
||||
pipe->connector = connector;
|
||||
drm_connector_attach_encoder(pipe->connector, encoder);
|
||||
}
|
||||
|
||||
crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
|
||||
if (IS_ERR(crtc))
|
||||
|
@ -350,10 +379,12 @@ static int omap_modeset_init(struct drm_device *dev)
|
|||
static void omap_modeset_enable_external_hpd(struct drm_device *ddev)
|
||||
{
|
||||
struct omap_drm_private *priv = ddev->dev_private;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->num_pipes; i++)
|
||||
omap_connector_enable_hpd(priv->pipes[i].connector);
|
||||
for (i = 0; i < priv->num_pipes; i++) {
|
||||
if (priv->pipes[i].connector)
|
||||
omap_connector_enable_hpd(priv->pipes[i].connector);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -362,10 +393,12 @@ static void omap_modeset_enable_external_hpd(struct drm_device *ddev)
|
|||
static void omap_modeset_disable_external_hpd(struct drm_device *ddev)
|
||||
{
|
||||
struct omap_drm_private *priv = ddev->dev_private;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->num_pipes; i++)
|
||||
omap_connector_disable_hpd(priv->pipes[i].connector);
|
||||
for (i = 0; i < priv->num_pipes; i++) {
|
||||
if (priv->pipes[i].connector)
|
||||
omap_connector_disable_hpd(priv->pipes[i].connector);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -49,7 +49,6 @@ struct omap_drm_pipeline {
|
|||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
struct omap_dss_device *output;
|
||||
struct omap_dss_device *display;
|
||||
unsigned int alias_id;
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,34 @@ static const struct drm_encoder_funcs omap_encoder_funcs = {
|
|||
.destroy = omap_encoder_destroy,
|
||||
};
|
||||
|
||||
static void omap_encoder_update_videomode_flags(struct videomode *vm,
|
||||
u32 bus_flags)
|
||||
{
|
||||
if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW |
|
||||
DISPLAY_FLAGS_DE_HIGH))) {
|
||||
if (bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
vm->flags |= DISPLAY_FLAGS_DE_LOW;
|
||||
else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
|
||||
vm->flags |= DISPLAY_FLAGS_DE_HIGH;
|
||||
}
|
||||
|
||||
if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
|
||||
DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
|
||||
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
|
||||
vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
|
||||
else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
|
||||
vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
|
||||
}
|
||||
|
||||
if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
|
||||
DISPLAY_FLAGS_SYNC_NEGEDGE))) {
|
||||
if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE)
|
||||
vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
|
||||
else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE)
|
||||
vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
|
||||
}
|
||||
}
|
||||
|
||||
static void omap_encoder_hdmi_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
|
@ -87,7 +115,9 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
|
|||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
|
||||
struct omap_dss_device *output = omap_encoder->output;
|
||||
struct omap_dss_device *dssdev;
|
||||
struct drm_bridge *bridge;
|
||||
struct videomode vm = { 0 };
|
||||
|
||||
drm_display_mode_to_videomode(adjusted_mode, &vm);
|
||||
|
@ -101,44 +131,29 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
|
|||
*
|
||||
* A better solution is to use DRM's bus-flags through the whole driver.
|
||||
*/
|
||||
for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) {
|
||||
unsigned long bus_flags = dssdev->bus_flags;
|
||||
for (dssdev = output; dssdev; dssdev = dssdev->next)
|
||||
omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
|
||||
|
||||
if (!(vm.flags & (DISPLAY_FLAGS_DE_LOW |
|
||||
DISPLAY_FLAGS_DE_HIGH))) {
|
||||
if (bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
vm.flags |= DISPLAY_FLAGS_DE_LOW;
|
||||
else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
|
||||
vm.flags |= DISPLAY_FLAGS_DE_HIGH;
|
||||
}
|
||||
for (bridge = output->bridge; bridge; bridge = bridge->next) {
|
||||
u32 bus_flags;
|
||||
|
||||
if (!(vm.flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
|
||||
DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
|
||||
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
|
||||
vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
|
||||
else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
|
||||
vm.flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
|
||||
}
|
||||
if (!bridge->timings)
|
||||
continue;
|
||||
|
||||
if (!(vm.flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
|
||||
DISPLAY_FLAGS_SYNC_NEGEDGE))) {
|
||||
if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE)
|
||||
vm.flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
|
||||
else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE)
|
||||
vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
|
||||
}
|
||||
bus_flags = bridge->timings->input_bus_flags;
|
||||
omap_encoder_update_videomode_flags(&vm, bus_flags);
|
||||
}
|
||||
|
||||
/* Set timings for all devices in the display pipeline. */
|
||||
dss_mgr_set_timings(omap_encoder->output, &vm);
|
||||
dss_mgr_set_timings(output, &vm);
|
||||
|
||||
for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) {
|
||||
for (dssdev = output; dssdev; dssdev = dssdev->next) {
|
||||
if (dssdev->ops->set_timings)
|
||||
dssdev->ops->set_timings(dssdev, adjusted_mode);
|
||||
}
|
||||
|
||||
/* Set the HDMI mode and HDMI infoframe if applicable. */
|
||||
if (omap_encoder->output->type == OMAP_DISPLAY_TYPE_HDMI)
|
||||
if (output->type == OMAP_DISPLAY_TYPE_HDMI)
|
||||
omap_encoder_hdmi_mode_set(encoder, adjusted_mode);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue