Commit 17a8e03e authored by Boris Brezillon's avatar Boris Brezillon

drm: atmel-hlcdc: rework the output code to support drm bridges

The current output code only supports connection to drm panels.
First simplify the drm panel code, and then add support for external drm
bridges.
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
Tested-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
parent aca63b76
...@@ -551,7 +551,7 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) ...@@ -551,7 +551,7 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
ret = atmel_hlcdc_create_outputs(dev); ret = atmel_hlcdc_create_outputs(dev);
if (ret) { if (ret) {
dev_err(dev->dev, "failed to create panel: %d\n", ret); dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret);
return ret; return ret;
} }
......
...@@ -34,11 +34,13 @@ ...@@ -34,11 +34,13 @@
* @connector: DRM connector * @connector: DRM connector
* @encoder: DRM encoder * @encoder: DRM encoder
* @dc: pointer to the atmel_hlcdc_dc structure * @dc: pointer to the atmel_hlcdc_dc structure
* @panel: panel connected on the RGB output
*/ */
struct atmel_hlcdc_rgb_output { struct atmel_hlcdc_rgb_output {
struct drm_connector connector; struct drm_connector connector;
struct drm_encoder encoder; struct drm_encoder encoder;
struct atmel_hlcdc_dc *dc; struct atmel_hlcdc_dc *dc;
struct drm_panel *panel;
}; };
static inline struct atmel_hlcdc_rgb_output * static inline struct atmel_hlcdc_rgb_output *
...@@ -54,46 +56,31 @@ drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder) ...@@ -54,46 +56,31 @@ drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
} }
/** static void atmel_hlcdc_rgb_encoder_enable(struct drm_encoder *encoder)
* Atmel HLCDC Panel device structure
*
* This structure is specialization of the slave device structure to
* interface with drm panels.
*
* @base: base slave device fields
* @panel: drm panel attached to this slave device
*/
struct atmel_hlcdc_panel {
struct atmel_hlcdc_rgb_output base;
struct drm_panel *panel;
};
static inline struct atmel_hlcdc_panel *
atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output *output)
{
return container_of(output, struct atmel_hlcdc_panel, base);
}
static void atmel_hlcdc_panel_encoder_enable(struct drm_encoder *encoder)
{ {
struct atmel_hlcdc_rgb_output *rgb = struct atmel_hlcdc_rgb_output *rgb =
drm_encoder_to_atmel_hlcdc_rgb_output(encoder); drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
drm_panel_enable(panel->panel); if (rgb->panel) {
drm_panel_prepare(rgb->panel);
drm_panel_enable(rgb->panel);
}
} }
static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder *encoder) static void atmel_hlcdc_rgb_encoder_disable(struct drm_encoder *encoder)
{ {
struct atmel_hlcdc_rgb_output *rgb = struct atmel_hlcdc_rgb_output *rgb =
drm_encoder_to_atmel_hlcdc_rgb_output(encoder); drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
drm_panel_disable(panel->panel); if (rgb->panel) {
drm_panel_disable(rgb->panel);
drm_panel_unprepare(rgb->panel);
}
} }
static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
.disable = atmel_hlcdc_panel_encoder_disable, .disable = atmel_hlcdc_rgb_encoder_disable,
.enable = atmel_hlcdc_panel_encoder_enable, .enable = atmel_hlcdc_rgb_encoder_enable,
}; };
static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder) static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
...@@ -110,9 +97,11 @@ static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector) ...@@ -110,9 +97,11 @@ static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector)
{ {
struct atmel_hlcdc_rgb_output *rgb = struct atmel_hlcdc_rgb_output *rgb =
drm_connector_to_atmel_hlcdc_rgb_output(connector); drm_connector_to_atmel_hlcdc_rgb_output(connector);
struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
return panel->panel->funcs->get_modes(panel->panel); if (rgb->panel)
return rgb->panel->funcs->get_modes(rgb->panel);
return 0;
} }
static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
...@@ -144,7 +133,13 @@ static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helpe ...@@ -144,7 +133,13 @@ static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helpe
static enum drm_connector_status static enum drm_connector_status
atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force) atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force)
{ {
return connector_status_connected; struct atmel_hlcdc_rgb_output *rgb =
drm_connector_to_atmel_hlcdc_rgb_output(connector);
if (rgb->panel)
return connector_status_connected;
return connector_status_disconnected;
} }
static void static void
...@@ -152,9 +147,10 @@ atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector) ...@@ -152,9 +147,10 @@ atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector)
{ {
struct atmel_hlcdc_rgb_output *rgb = struct atmel_hlcdc_rgb_output *rgb =
drm_connector_to_atmel_hlcdc_rgb_output(connector); drm_connector_to_atmel_hlcdc_rgb_output(connector);
struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
drm_panel_detach(panel->panel); if (rgb->panel)
drm_panel_detach(rgb->panel);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
} }
...@@ -168,87 +164,122 @@ static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = { ...@@ -168,87 +164,122 @@ static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
static int atmel_hlcdc_create_panel_output(struct drm_device *dev, static int atmel_hlcdc_check_endpoint(struct drm_device *dev,
struct of_endpoint *ep) const struct of_endpoint *ep)
{ {
struct atmel_hlcdc_dc *dc = dev->dev_private;
struct device_node *np; struct device_node *np;
struct drm_panel *p = NULL; void *obj;
struct atmel_hlcdc_panel *panel;
int ret;
np = of_graph_get_remote_port_parent(ep->local_node); np = of_graph_get_remote_port_parent(ep->local_node);
if (!np)
return -EINVAL;
p = of_drm_find_panel(np); obj = of_drm_find_panel(np);
if (!obj)
obj = of_drm_find_bridge(np);
of_node_put(np); of_node_put(np);
if (!p) return obj ? 0 : -EPROBE_DEFER;
return -EPROBE_DEFER; }
panel = devm_kzalloc(dev->dev, sizeof(*panel), GFP_KERNEL); static int atmel_hlcdc_attach_endpoint(struct drm_device *dev,
if (!panel) const struct of_endpoint *ep)
return -EINVAL; {
struct atmel_hlcdc_dc *dc = dev->dev_private;
struct atmel_hlcdc_rgb_output *output;
struct device_node *np;
struct drm_panel *panel;
struct drm_bridge *bridge;
int ret;
output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
if (!output)
return -EINVAL;
panel->base.dc = dc; output->dc = dc;
drm_encoder_helper_add(&panel->base.encoder, drm_encoder_helper_add(&output->encoder,
&atmel_hlcdc_panel_encoder_helper_funcs); &atmel_hlcdc_panel_encoder_helper_funcs);
ret = drm_encoder_init(dev, &panel->base.encoder, ret = drm_encoder_init(dev, &output->encoder,
&atmel_hlcdc_panel_encoder_funcs, &atmel_hlcdc_panel_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL); DRM_MODE_ENCODER_NONE, NULL);
if (ret) if (ret)
return ret; return ret;
panel->base.connector.dpms = DRM_MODE_DPMS_OFF; output->encoder.possible_crtcs = 0x1;
panel->base.connector.polled = DRM_CONNECTOR_POLL_CONNECT;
drm_connector_helper_add(&panel->base.connector, np = of_graph_get_remote_port_parent(ep->local_node);
&atmel_hlcdc_panel_connector_helper_funcs);
ret = drm_connector_init(dev, &panel->base.connector,
&atmel_hlcdc_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
if (ret)
goto err_encoder_cleanup;
drm_mode_connector_attach_encoder(&panel->base.connector, ret = -EPROBE_DEFER;
&panel->base.encoder);
panel->base.encoder.possible_crtcs = 0x1;
drm_panel_attach(p, &panel->base.connector); panel = of_drm_find_panel(np);
panel->panel = p; if (panel) {
of_node_put(np);
output->connector.dpms = DRM_MODE_DPMS_OFF;
output->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
drm_connector_helper_add(&output->connector,
&atmel_hlcdc_panel_connector_helper_funcs);
ret = drm_connector_init(dev, &output->connector,
&atmel_hlcdc_panel_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
if (ret)
goto err_encoder_cleanup;
return 0; drm_mode_connector_attach_encoder(&output->connector,
&output->encoder);
ret = drm_panel_attach(panel, &output->connector);
if (ret) {
drm_connector_cleanup(&output->connector);
goto err_encoder_cleanup;
}
output->panel = panel;
return 0;
}
bridge = of_drm_find_bridge(np);
of_node_put(np);
if (bridge) {
output->encoder.bridge = bridge;
bridge->encoder = &output->encoder;
ret = drm_bridge_attach(dev, bridge);
if (!ret)
return 0;
}
err_encoder_cleanup: err_encoder_cleanup:
drm_encoder_cleanup(&panel->base.encoder); drm_encoder_cleanup(&output->encoder);
return ret; return ret;
} }
int atmel_hlcdc_create_outputs(struct drm_device *dev) int atmel_hlcdc_create_outputs(struct drm_device *dev)
{ {
struct device_node *port_np, *np; struct device_node *ep_np = NULL;
struct of_endpoint ep; struct of_endpoint ep;
int ret; int ret;
port_np = of_get_child_by_name(dev->dev->of_node, "port"); for_each_endpoint_of_node(dev->dev->of_node, ep_np) {
if (!port_np) ret = of_graph_parse_endpoint(ep_np, &ep);
return -EINVAL; if (!ret)
ret = atmel_hlcdc_check_endpoint(dev, &ep);
np = of_get_child_by_name(port_np, "endpoint"); of_node_put(ep_np);
of_node_put(port_np); if (ret)
return ret;
}
if (!np) for_each_endpoint_of_node(dev->dev->of_node, ep_np) {
return -EINVAL; ret = of_graph_parse_endpoint(ep_np, &ep);
if (!ret)
ret = atmel_hlcdc_attach_endpoint(dev, &ep);
ret = of_graph_parse_endpoint(np, &ep); of_node_put(ep_np);
of_node_put(port_np); if (ret)
return ret;
}
if (ret) return 0;
return ret;
/* We currently only support panel output */
return atmel_hlcdc_create_panel_output(dev, &ep);
} }
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