Hi all,
This series patches base on Tom's "Initial drm/kms driver for pl111"[1] with linaro release 13.07 and migrate the CDFv3 for evaluation. please notes that I set VGA as default output and tested on RTSM only.
[1] http://lwn.net/Articles/561344/
Cheers,
Show Liu
Show Liu (3): Add display entities and pipe link for pl111 Add display entity and set VGA output(site MB) as default add pipe link for display entity
arch/arm/boot/dts/rtsm_ve-motherboard.dtsi | 46 +++ arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts | 4 + drivers/gpu/drm/pl111/pl111_drm.h | 23 +- drivers/gpu/drm/pl111/pl111_drm_device.c | 374 ++++++++++++++++++++++-- drivers/video/vexpress-dvi.c | 94 +++++- 5 files changed, 503 insertions(+), 38 deletions(-)
--- drivers/gpu/drm/pl111/pl111_drm.h | 23 +- drivers/gpu/drm/pl111/pl111_drm_device.c | 374 ++++++++++++++++++++++++++++-- 2 files changed, 370 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index faf88cb..f81f79e 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -19,6 +19,9 @@ #ifndef _PL111_DRM_H_ #define _PL111_DRM_H_
+#include <media/media-device.h> +#include <video/display.h> + /* Defines for drm_mode_create_dumb flags settings */ #define PL111_BO_SCANOUT 0x00000001 /* scanout compatible buffer requested */
@@ -149,13 +152,17 @@ struct pl111_drm_crtc { struct drm_framebuffer *fb); };
+struct pl111_drm_encoder { + struct drm_encoder encoder; + unsigned int port; +}; + struct pl111_drm_connector { struct drm_connector connector; + struct pl111_drm_encoder *encoder; + struct media_pad *pad; };
-struct pl111_drm_encoder { - struct drm_encoder encoder; -};
struct pl111_drm_dev_private { struct pl111_drm_crtc *pl111_crtc; @@ -186,6 +193,16 @@ struct pl111_drm_dev_private {
/* Cache for flip resources used to avoid kmalloc on each page flip */ struct kmem_cache *page_flip_slab; + + struct drm_device *ddev; + + /* + *Display entities and notifier + */ + struct media_device mdev; + struct display_entity entity; + struct display_entity_notifier notifier; + };
enum pl111_cursor_size { diff --git a/drivers/gpu/drm/pl111/pl111_drm_device.c b/drivers/gpu/drm/pl111/pl111_drm_device.c index cf22ead..838506f 100644 --- a/drivers/gpu/drm/pl111/pl111_drm_device.c +++ b/drivers/gpu/drm/pl111/pl111_drm_device.c @@ -35,6 +35,25 @@
struct pl111_drm_dev_private priv;
+/* ----------------------------------------------------------------------------- + * Display Entities + */ +static int pl111_entity_set_stream(struct display_entity *ent, + unsigned int port, + enum display_entity_stream_state state) +{ + pr_info("DRM %s\n", __func__); + return 0; +} + +static const struct display_entity_video_ops pl111_entity_video_ops = { + .set_stream = pl111_entity_set_stream, +}; + +static const struct display_entity_ops pl111_entity_ops = { + .video = &pl111_entity_video_ops, +}; + #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS static void initial_kds_obtained(void *cb1, void *cb2) { @@ -94,13 +113,217 @@ struct drm_mode_config_funcs mode_config_funcs = { .fb_create = pl111_fb_create, };
+struct pl111_drm_encoder * +pl111_pad_encoder_create(struct pl111_drm_dev_private *priv, + unsigned int port, + struct media_pad *pad) +{ + struct pl111_drm_encoder *pl111_encoder; + struct device *dev = &priv->amba_dev->dev; + unsigned int encoder_type; + int ret; + + pl111_encoder = devm_kzalloc(dev, sizeof(*pl111_encoder), GFP_KERNEL); + if (pl111_encoder == NULL) + return ERR_PTR(-ENOMEM); + + pl111_encoder->port = port; + + /* Find the encoder type. */ + if (pad) { + struct display_entity *entity = to_display_entity(pad->entity); + struct display_entity_interface_params params; + + ret = display_entity_get_params(entity, pad->index, ¶ms); + if (ret < 0) { + pr_err("DRM %s: failed to retrieve display entity %s parameters\n", + __func__, + entity->name); + return ERR_PTR(ret); + } + + switch (params.type) { + case DISPLAY_ENTITY_INTERFACE_LVDS: + encoder_type = DRM_MODE_ENCODER_DAC; + break; + case DISPLAY_ENTITY_INTERFACE_VGA: + encoder_type = DRM_MODE_ENCODER_DAC; + break; + case DISPLAY_ENTITY_INTERFACE_DPI: + case DISPLAY_ENTITY_INTERFACE_DBI: + default: + encoder_type = DRM_MODE_ENCODER_NONE; + break; + } + + } else { + /* No external encoder, use the internal encoder type. */ + pr_err("DRM %s: No external encoder, use the internal encoder type.\n", + __func__); + encoder_type = DRM_MODE_ENCODER_DAC; + } + + pl111_encoder = pl111_encoder_create(priv->ddev, 1); + + return pl111_encoder; +} + +struct pl111_drm_connector * +pl111_pad_connector_create(struct pl111_drm_dev_private *priv, + struct pl111_drm_encoder *pl111_encoder, + struct media_pad *pad) +{ + struct display_entity *entity = to_display_entity(pad->entity); + struct display_entity_interface_params params; + struct pl111_drm_connector *pl111_connector; + struct drm_connector *connector; + struct device *dev = &priv->amba_dev->dev; + int connector_type; + unsigned int width; + unsigned int height; + int ret; + + pl111_connector = devm_kzalloc(dev, + sizeof(*pl111_connector), + GFP_KERNEL); + if (pl111_connector == NULL) + return ERR_PTR(-ENOMEM); + + pl111_connector->encoder = pl111_encoder; + pl111_connector->pad = pad; + connector = &pl111_connector->connector; + + ret = display_entity_get_size(entity, &width, &height); + + if (ret < 0) { + connector->display_info.width_mm = 0; + connector->display_info.height_mm = 0; + } else { + connector->display_info.width_mm = width; + connector->display_info.height_mm = height; + } + + ret = display_entity_get_params(entity, pad->index, ¶ms); + if (ret < 0) { + pr_err("failed to retrieve connector %s parameters\n", + entity->name); + return ERR_PTR(ret); + } + + switch (params.type) { + case DISPLAY_ENTITY_INTERFACE_VGA: + connector_type = DRM_MODE_CONNECTOR_VGA; + break; + case DISPLAY_ENTITY_INTERFACE_LVDS: + connector_type = DRM_MODE_CONNECTOR_LVDS; + break; + default: + connector_type = DRM_MODE_CONNECTOR_Unknown; + break; + } + + pl111_connector = pl111_connector_create(priv->ddev); + if (pl111_connector == NULL) { + pr_err("Failed to create pl111_drm_connector\n"); + ret = -ENOMEM; + } + + return pl111_connector; +} + +static int pl111_pipe_init(struct pl111_drm_dev_private *priv, + unsigned int port, + struct media_pad *remote) +{ + struct media_pad *pad_encoder = NULL; + struct media_pad *pad_connector = remote; + struct pl111_drm_encoder *pl111_encoder; + struct pl111_drm_connector *pl111_connector; + struct media_entity *entity; + int ret; + /* Start at the entity connected to the pl111 output. */ + entity = remote->entity; + + pr_info("DRM %s: starting at entity %s pad %u\n", __func__, + entity->name, remote->index); + + while (1) { + struct media_link *next = NULL; + unsigned int i; + + pr_info("DRM %s: searching for an output link on entity %s\n", + __func__, entity->name); + + for (i = 0; i < entity->num_links; ++i) { + struct media_link *link = &entity->links[i]; + + if (link->source->entity != entity) + continue; + + if (next) + return -EPIPE; + + next = link; + } + + if (next == NULL) { + pr_err(" DRM %s: not output link found\n", __func__); + break; + } + + pr_info(" DRM %s: output link %s:%u -> %s:%u found\n", __func__, + next->source->entity->name, next->source->index, + next->sink->entity->name, next->sink->index); + + pad_encoder = next->source; + pad_connector = next->sink; + entity = pad_connector->entity; + } + + pr_info(" DRM %s: encoder pad %s/%u connector pad %s/%u\n", __func__, + pad_encoder ? pad_encoder->entity->name : NULL, + pad_encoder ? pad_encoder->index : 0, + pad_connector->entity->name, pad_connector->index); + + /* Create the encoder and connector. */ + pl111_encoder = pl111_pad_encoder_create(priv, port, pad_encoder); + if (IS_ERR(pl111_encoder)) + return PTR_ERR(pl111_encoder); + + pl111_connector = pl111_pad_connector_create(priv, + pl111_encoder, + pad_connector); + + if (IS_ERR(pl111_connector)) + return PTR_ERR(pl111_connector); + + ret = drm_mode_connector_attach_encoder(&pl111_connector->connector, + &pl111_encoder->encoder); + if (ret != 0) { + pr_err("Failed to attach encoder\n"); + goto out_config; + } + + pl111_connector->connector.encoder = &pl111_encoder->encoder; + + goto finish; + +out_config: + drm_mode_config_cleanup(priv->ddev); + +finish: + DRM_DEBUG("%s returned %d\n", __func__, ret); + return ret; +} + static int pl111_modeset_init(struct drm_device *dev) { struct drm_mode_config *mode_config; struct pl111_drm_dev_private *priv = dev->dev_private; - struct pl111_drm_connector *pl111_connector; - struct pl111_drm_encoder *pl111_encoder; + int ret = 0; + unsigned int num_encoders = 0; + unsigned int i;
if (priv == NULL) return -EINVAL; @@ -122,28 +345,22 @@ static int pl111_modeset_init(struct drm_device *dev)
priv->number_crtcs = 1;
- pl111_connector = pl111_connector_create(dev); - if (pl111_connector == NULL) { - pr_err("Failed to create pl111_drm_connector\n"); - ret = -ENOMEM; - goto out_config; - } + /* Create an encoder and a connector for each output connected to + * external entities. + */ + for (i = 0; i < priv->entity.entity.num_pads; ++i) { + struct media_pad *pad;
- pl111_encoder = pl111_encoder_create(dev, 1); - if (pl111_encoder == NULL) { - pr_err("Failed to create pl111_drm_encoder\n"); - ret = -ENOMEM; - goto out_config; - } + pad = media_entity_remote_pad(&priv->entity.entity.pads[i]); + if (pad == NULL) + continue;
- ret = drm_mode_connector_attach_encoder(&pl111_connector->connector, - &pl111_encoder->encoder); - if (ret != 0) { - DRM_ERROR("Failed to attach encoder\n"); - goto out_config; - } + ret = pl111_pipe_init(priv, i, pad); + if (ret < 0) + return ret;
- pl111_connector->connector.encoder = &pl111_encoder->encoder; + num_encoders++; + }
ret = pl111_cursor_plane_init(dev, &priv->pl111_crtc->cursor, 1); if (ret != 0) { @@ -165,6 +382,74 @@ static void pl111_modeset_fini(struct drm_device *dev) drm_mode_config_cleanup(dev); }
+static int pl111_graph_link_generate(struct pl111_drm_dev_private *priv) +{ + struct display_entity *entity; + unsigned int num_pads; + int ret; + struct device *dev = &priv->amba_dev->dev; + + /* + * for CLCD default num_pads = 1 (ports = 1) + */ + num_pads = 1; + + priv->entity.dev = &priv->amba_dev->dev; + priv->entity.ops = &pl111_entity_ops; + strcpy(priv->entity.name, "CLCD(PL111)"); + + ret = display_entity_init(&priv->entity, 0, num_pads); + if (ret < 0) + return ret; + + if (dev->of_node) { + ret = display_of_entity_link_graph(dev, + &priv->notifier.done, + &priv->entity); + if (ret < 0) { + pr_err("unable to link entity graph.\n"); + return ret; + } + } else { + pr_err("Cannot find DT info.\n"); + return ret; + } + + /* Register the media device */ + priv->mdev.dev = dev; + strlcpy(priv->mdev.model, dev_name(dev), + sizeof(priv->mdev.model)); + + ret = media_device_register(&priv->mdev); + if (ret < 0) + return ret; + + list_for_each_entry(entity, &priv->notifier.done, list) { + ret = display_entity_register(&priv->mdev, entity); + if (ret < 0) + return ret; + } + + ret = display_entity_register(&priv->mdev, &priv->entity); + if (ret < 0) + return ret; + + return 0; +} + +static void pl111_graph_link_cleanup(struct pl111_drm_dev_private *priv) +{ + struct display_entity *entity; + + list_for_each_entry(entity, &priv->notifier.done, list) + display_entity_unregister(entity); + + display_entity_unregister(&priv->entity); + display_entity_cleanup(&priv->entity); + + media_device_unregister(&priv->mdev); +} + static int pl111_drm_load(struct drm_device *dev, unsigned long chipset) { int ret = 0; @@ -201,6 +486,15 @@ static int pl111_drm_load(struct drm_device *dev, unsigned long chipset)
dev->dev_private = &priv;
+ priv.ddev = dev; + + /* Generating the display entities graph link */ + ret = pl111_graph_link_generate(&priv); + if (ret < 0) { + pr_err("failed to initialize display entities\n"); + goto out_graph_callbacks; + } + ret = pl111_modeset_init(dev); if (ret != 0) { pr_err("Failed to init modeset\n"); @@ -227,6 +521,8 @@ out_modeset: pl111_modeset_fini(dev); out_slab: kmem_cache_destroy(priv.page_flip_slab); +out_graph_callbacks: + pl111_graph_link_cleanup(&priv); out_kds_callbacks: #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS kds_callback_term(&priv.kds_obtain_current_cb); @@ -301,16 +597,46 @@ static struct drm_driver driver = { .gem_prime_export = &pl111_gem_prime_export, };
-int pl111_drm_init(struct platform_device *dev) + +static int pl111_notifier_complete(struct display_entity_notifier *notifier) +{ + struct platform_device *pdev = to_platform_device(notifier->dev); + pr_info("DRM %s\n", __func__); + return drm_platform_init(&driver, pdev); +} + +int pl111_drm_init(struct platform_device *pdev) { int ret; + struct device_node *np = priv.amba_dev->dev.of_node; + pr_info("DRM %s\n", __func__); pr_info("PL111 DRM initialize, driver name: %s, version %d.%d\n", DRIVER_NAME, DRIVER_MAJOR, DRIVER_MINOR); driver.num_ioctls = 0; ret = 0; - driver.kdriver.platform_device = dev; - return drm_platform_init(&driver, dev); + driver.kdriver.platform_device = pdev; + + /* add display entity notifier function */ + + priv.notifier.dev = &pdev->dev; + priv.notifier.complete = pl111_notifier_complete; + + if (np) { + /* notify display entity */ + pr_info("DRM %s: build display entity notifier\n", __func__); + ret = display_of_entity_build_notifier(&priv.notifier, np); + } else { + pr_err("DRM %s: no Device tree info\n", __func__); + ret = -ENXIO; + } + + if (ret < 0) { + pr_err("DRM %s: initial failed\n", __func__); + return ret; + } + + return display_entity_register_notifier(&priv.notifier);
}
--- drivers/video/vexpress-dvi.c | 94 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 11 deletions(-)
diff --git a/drivers/video/vexpress-dvi.c b/drivers/video/vexpress-dvi.c index cbcb443..ca0e5bd 100644 --- a/drivers/video/vexpress-dvi.c +++ b/drivers/video/vexpress-dvi.c @@ -18,6 +18,12 @@ #include <linux/of_device.h> #include <linux/vexpress.h>
+#include <linux/platform_device.h> +#include <video/display.h> + +struct _mux_fpga { + struct display_entity entity; +};
static struct vexpress_config_func *vexpress_dvimode_func;
@@ -146,6 +152,52 @@ static int vexpress_dvi_fb_event_notify(struct notifier_block *self, return NOTIFY_OK; }
+static int mux_fpga_set_state(struct display_entity *entity, + enum display_entity_state state) +{ + struct media_pad *source; + source = media_entity_remote_pad(&entity->entity.pads[0]); + if (source == NULL) + return -EPIPE; + + switch (state) { + case DISPLAY_ENTITY_STATE_OFF: + case DISPLAY_ENTITY_STATE_STANDBY: + display_entity_set_stream(to_display_entity(source->entity), + source->index, + DISPLAY_ENTITY_STREAM_STOPPED); + break; + + case DISPLAY_ENTITY_STATE_ON: + display_entity_set_stream(to_display_entity(source->entity), + source->index, + DISPLAY_ENTITY_STREAM_CONTINUOUS); + break; + } + + return 0; +} + +static int mux_fpga_get_params(struct display_entity *entity, unsigned int port, + struct display_entity_interface_params *params) +{ + memset(params, 0, sizeof(*params)); + + /* default using VGA interface type */ + params->type = DISPLAY_ENTITY_INTERFACE_VGA; + + return 0; +} + +static const struct display_entity_control_ops mux_fpga_control_ops = { + .set_state = mux_fpga_set_state, + .get_params = mux_fpga_get_params, +}; + +static const struct display_entity_ops mux_fpga_ops = { + .ctrl = &mux_fpga_control_ops, +}; + static struct notifier_block vexpress_dvi_fb_notifier = { .notifier_call = vexpress_dvi_fb_event_notify, }; @@ -170,7 +222,9 @@ static int vexpress_dvi_probe(struct platform_device *pdev) enum vexpress_dvi_func func; const struct of_device_id *match = of_match_device(vexpress_dvi_of_match, &pdev->dev); - u32 site; + struct _mux_fpga *mux_fpga = NULL; + + int ret = 0;
if (match) func = (enum vexpress_dvi_func)match->data; @@ -182,18 +236,36 @@ static int vexpress_dvi_probe(struct platform_device *pdev) vexpress_muxfpga_func = vexpress_config_func_get_by_dev(&pdev->dev); device_create_file(&pdev->dev, &dev_attr_fb); - /* hard-coded for test DRM on RTSM - Set default site = 0 - */ - if (vexpress_dvi_fb < 0){ - /*default site = 0*/ - site = 0; - vexpress_config_write(vexpress_muxfpga_func, 0, site); - vexpress_dvi_fb = site; - } + + /* + * default using VEXPRESS_SITE_MB + */ + pr_info("Set Site MB as Default\n"); + vexpress_config_write(vexpress_muxfpga_func, 0, VEXPRESS_SITE_MB); + vexpress_dvi_fb = VEXPRESS_SITE_MB; + + /* initialize display entity */ + mux_fpga = devm_kzalloc(&pdev->dev, sizeof(*mux_fpga), GFP_KERNEL); + if (mux_fpga == NULL) + return -ENOMEM; + + mux_fpga->entity.dev = &pdev->dev; + mux_fpga->entity.ops = &mux_fpga_ops; + strlcpy(mux_fpga->entity.name, dev_name(&pdev->dev), sizeof(mux_fpga->entity.name)); + + ret = display_entity_init(&mux_fpga->entity, 1, 1); + if (ret < 0) + return ret; + + ret = display_entity_add(&mux_fpga->entity); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, mux_fpga); break; case FUNC_DVIMODE: vexpress_dvimode_func = + vexpress_config_func_get_by_dev(&pdev->dev); break; } @@ -205,7 +277,7 @@ static int vexpress_dvi_probe(struct platform_device *pdev)
vexpress_dvi_fb_select(vexpress_dvi_fb);
- return 0; + return ret; }
static const struct platform_device_id vexpress_dvi_id_table[] = {
--- arch/arm/boot/dts/rtsm_ve-motherboard.dtsi | 46 ++++++++++++++++++++++++ arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts | 4 +++ 2 files changed, 50 insertions(+)
diff --git a/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi index 6d12566..b4e032a 100644 --- a/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi +++ b/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi @@ -166,6 +166,17 @@ mode = "VGA"; use_dma = <0>; framebuffer = <0x18000000 0x00180000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + clcd_out: endpoint { + }; + }; + }; }; };
@@ -214,6 +225,41 @@ muxfpga@0 { compatible = "arm,vexpress-muxfpga"; arm,vexpress-sysreg,func = <7 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + muxfpga_in: endpoint { + remote-endpoint = <&clcd_out>; + }; + }; + port@1 { + reg = <1>; + muxfpga_out: endpoint { + remote-endpoint = <&con_vga_in>; + }; + }; + + }; + }; + + con-vga { + compatible = "con-vga"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + con_vga_in: endpoint { + remote-endpoint = <&muxfpga_out>; + }; + }; + }; };
shutdown@0 { diff --git a/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts index c59f4b5..45a07c5 100644 --- a/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts +++ b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts @@ -230,4 +230,8 @@ }; };
+&clcd_out { + remote-endpoint = <&muxfpga_in>; +}; + /include/ "clcd-panels.dtsi"
linaro-kernel@lists.linaro.org