[1/3] drm/arcpgu: Make ARC PGU usable on simulation platforms
diff mbox

Message ID 1465210585-13284-2-git-send-email-abrodkin@synopsys.com
State New
Headers show

Commit Message

Alexey Brodkin June 6, 2016, 10:56 a.m. UTC
From: Ruud Derwig <rderwig@synopsys.com>

Initially ARC PGU required real encoder/trnasmitter to exist.
That was fine for real HW such as ARC SDP boards.

But on some simulaiton platroms like ARC VDK or nSIM OSCI we have model
of the same ARC PGU and ability to output video data in a virtual LCD.

To make ARC PGU driver usable in those virtual platforms we need to istantiate
virtual encoder instead of a real one because in the model's virtual LCD
we're rendering whatever appears in frame-buffer memory.

Signed-off-by: Ruud Derwig <rderwig@synopsys.com>
Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
---
 drivers/gpu/drm/arc/Makefile     |   2 +-
 drivers/gpu/drm/arc/arcpgu.h     |   1 +
 drivers/gpu/drm/arc/arcpgu_drv.c |  15 ++--
 drivers/gpu/drm/arc/arcpgu_sim.c | 177 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 187 insertions(+), 8 deletions(-)
 create mode 100644 drivers/gpu/drm/arc/arcpgu_sim.c

Comments

Daniel Vetter June 6, 2016, 2:13 p.m. UTC | #1
On Mon, Jun 06, 2016 at 01:56:23PM +0300, Alexey Brodkin wrote:
> From: Ruud Derwig <rderwig@synopsys.com>
> 
> Initially ARC PGU required real encoder/trnasmitter to exist.
> That was fine for real HW such as ARC SDP boards.
> 
> But on some simulaiton platroms like ARC VDK or nSIM OSCI we have model
> of the same ARC PGU and ability to output video data in a virtual LCD.
> 
> To make ARC PGU driver usable in those virtual platforms we need to istantiate
> virtual encoder instead of a real one because in the model's virtual LCD
> we're rendering whatever appears in frame-buffer memory.
> 
> Signed-off-by: Ruud Derwig <rderwig@synopsys.com>
> Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
> ---
>  drivers/gpu/drm/arc/Makefile     |   2 +-
>  drivers/gpu/drm/arc/arcpgu.h     |   1 +
>  drivers/gpu/drm/arc/arcpgu_drv.c |  15 ++--
>  drivers/gpu/drm/arc/arcpgu_sim.c | 177 +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 187 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/gpu/drm/arc/arcpgu_sim.c
> 
> diff --git a/drivers/gpu/drm/arc/Makefile b/drivers/gpu/drm/arc/Makefile
> index d48fda7..73de56a 100644
> --- a/drivers/gpu/drm/arc/Makefile
> +++ b/drivers/gpu/drm/arc/Makefile
> @@ -1,2 +1,2 @@
> -arcpgu-y := arcpgu_crtc.o arcpgu_hdmi.o arcpgu_drv.o
> +arcpgu-y := arcpgu_crtc.o arcpgu_hdmi.o arcpgu_sim.o arcpgu_drv.o
>  obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
> diff --git a/drivers/gpu/drm/arc/arcpgu.h b/drivers/gpu/drm/arc/arcpgu.h
> index 86574b6..329ac75 100644
> --- a/drivers/gpu/drm/arc/arcpgu.h
> +++ b/drivers/gpu/drm/arc/arcpgu.h
> @@ -43,6 +43,7 @@ static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu,
>  
>  int arc_pgu_setup_crtc(struct drm_device *dev);
>  int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np);
> +int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np);
>  struct drm_fbdev_cma *arcpgu_fbdev_cma_init(struct drm_device *dev,
>  	unsigned int preferred_bpp, unsigned int num_crtc,
>  	unsigned int max_conn_count);
> diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c
> index 69b5be0..5f1f303 100644
> --- a/drivers/gpu/drm/arc/arcpgu_drv.c
> +++ b/drivers/gpu/drm/arc/arcpgu_drv.c
> @@ -149,15 +149,16 @@ static int arcpgu_load(struct drm_device *drm)
>  
>  	/* find the encoder node and initialize it */
>  	encoder_node = of_parse_phandle(drm->dev->of_node, "encoder-slave", 0);
> -	if (!encoder_node) {
> -		dev_err(drm->dev, "failed to get an encoder slave node\n");
> -		return -ENODEV;
> +	if (encoder_node) {
> +		ret = arcpgu_drm_hdmi_init(drm, encoder_node);
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		ret = arcpgu_drm_sim_init(drm, 0);
> +		if (ret < 0)
> +			return ret;
>  	}
>  
> -	ret = arcpgu_drm_hdmi_init(drm, encoder_node);
> -	if (ret < 0)
> -		return ret;
> -
>  	drm_mode_config_reset(drm);
>  	drm_kms_helper_poll_init(drm);
>  
> diff --git a/drivers/gpu/drm/arc/arcpgu_sim.c b/drivers/gpu/drm/arc/arcpgu_sim.c
> new file mode 100644
> index 0000000..122e069
> --- /dev/null
> +++ b/drivers/gpu/drm/arc/arcpgu_sim.c
> @@ -0,0 +1,177 @@
> +/*
> + * ARC PGU DRM driver.
> + *
> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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.
> + *
> + */
> +
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_encoder_slave.h>
> +#include <drm/drm_atomic_helper.h>
> +
> +#include "arcpgu.h"
> +
> +#define XRES_DEF	640
> +#define YRES_DEF	480
> +
> +#define XRES_MAX	8192
> +#define YRES_MAX	8192
> +
> +
> +struct arcpgu_drm_connector {
> +	struct drm_connector connector;
> +	struct drm_encoder_slave *encoder_slave;
> +};
> +
> +static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
> +{
> +	int count;
> +
> +	count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
> +	drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
> +	return count;
> +}
> +
> +static struct drm_encoder *
> +arcpgu_drm_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct drm_encoder_slave *slave;
> +	struct arcpgu_drm_connector *con =
> +		container_of(connector, struct arcpgu_drm_connector, connector);
> +
> +	slave = con->encoder_slave;
> +	if (slave == NULL) {
> +		dev_err(connector->dev->dev,
> +			"connector_best_encoder: cannot find slave encoder for connector\n");
> +		return NULL;
> +	}
> +
> +	return &slave->base;
> +}
> +
> +static enum drm_connector_status
> +arcpgu_drm_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	return connector_status_connected;
> +}
> +
> +static void arcpgu_drm_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_helper_funcs
> +arcpgu_drm_connector_helper_funcs = {
> +	.get_modes = arcpgu_drm_connector_get_modes,
> +	.best_encoder = arcpgu_drm_connector_best_encoder,
> +};
> +
> +static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
> +	.dpms = drm_helper_connector_dpms,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.detect = arcpgu_drm_connector_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = arcpgu_drm_connector_destroy,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static bool sim_enc_mode_fixup(struct drm_encoder *encoder,
> +				   const struct drm_display_mode *mode,
> +				   struct drm_display_mode *adjusted_mode)
> +{
> +	return true;
> +}
> +
> +static void sim_enc_mode_set(struct drm_encoder *encoder,
> +				 struct drm_display_mode *mode,
> +				 struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static void sim_enc_enable(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void sim_enc_disable(struct drm_encoder *encoder)
> +{
> +}

There's no more need for dummy functions like these. Please also remove
them from all the other places in your driver if you have them. And if
that means you don't have a helper_funcs table any more, you don't even
need that one.
-Daniel

> +
> +static struct drm_encoder_helper_funcs arcpgu_drm_encoder_helper_funcs = {
> +	.mode_fixup = sim_enc_mode_fixup,
> +	.mode_set   = sim_enc_mode_set,
> +	.enable     = sim_enc_enable,
> +	.disable    = sim_enc_disable,
> +};
> +
> +static struct drm_encoder_funcs arcpgu_drm_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np)
> +{
> +	struct arcpgu_drm_connector *arcpgu_connector;
> +	struct drm_encoder_slave *encoder;
> +	struct drm_connector *connector;
> +	int ret;
> +
> +	encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL);
> +	if (encoder == NULL)
> +		return -ENOMEM;
> +
> +	encoder->base.possible_crtcs = 1;
> +	encoder->base.possible_clones = 0;
> +
> +	ret = drm_encoder_init(drm, &encoder->base, &arcpgu_drm_encoder_funcs,
> +			       DRM_MODE_ENCODER_VIRTUAL, NULL);
> +	if (ret)
> +		return ret;
> +
> +	drm_encoder_helper_add(&encoder->base,
> +			       &arcpgu_drm_encoder_helper_funcs);
> +
> +	arcpgu_connector = devm_kzalloc(drm->dev, sizeof(*arcpgu_connector),
> +					GFP_KERNEL);
> +	if (!arcpgu_connector) {
> +		ret = -ENOMEM;
> +		goto error_encoder_cleanup;
> +	}
> +
> +	connector = &arcpgu_connector->connector;
> +	drm_connector_helper_add(connector, &arcpgu_drm_connector_helper_funcs);
> +
> +	ret = drm_connector_init(drm, connector, &arcpgu_drm_connector_funcs,
> +			DRM_MODE_CONNECTOR_VIRTUAL);
> +	if (ret < 0) {
> +		dev_err(drm->dev, "failed to initialize drm connector\n");
> +		goto error_encoder_cleanup;
> +	}
> +
> +	ret = drm_mode_connector_attach_encoder(connector, &encoder->base);
> +	if (ret < 0) {
> +		dev_err(drm->dev, "could not attach connector to encoder\n");
> +		drm_connector_unregister(connector);
> +		goto error_connector_cleanup;
> +	}
> +
> +	arcpgu_connector->encoder_slave = encoder;
> +
> +	return 0;
> +
> +error_connector_cleanup:
> +	drm_connector_cleanup(connector);
> +
> +error_encoder_cleanup:
> +	drm_encoder_cleanup(&encoder->base);
> +	return ret;
> +}
> -- 
> 2.5.5
>

Patch
diff mbox

diff --git a/drivers/gpu/drm/arc/Makefile b/drivers/gpu/drm/arc/Makefile
index d48fda7..73de56a 100644
--- a/drivers/gpu/drm/arc/Makefile
+++ b/drivers/gpu/drm/arc/Makefile
@@ -1,2 +1,2 @@ 
-arcpgu-y := arcpgu_crtc.o arcpgu_hdmi.o arcpgu_drv.o
+arcpgu-y := arcpgu_crtc.o arcpgu_hdmi.o arcpgu_sim.o arcpgu_drv.o
 obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
diff --git a/drivers/gpu/drm/arc/arcpgu.h b/drivers/gpu/drm/arc/arcpgu.h
index 86574b6..329ac75 100644
--- a/drivers/gpu/drm/arc/arcpgu.h
+++ b/drivers/gpu/drm/arc/arcpgu.h
@@ -43,6 +43,7 @@  static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu,
 
 int arc_pgu_setup_crtc(struct drm_device *dev);
 int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np);
+int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np);
 struct drm_fbdev_cma *arcpgu_fbdev_cma_init(struct drm_device *dev,
 	unsigned int preferred_bpp, unsigned int num_crtc,
 	unsigned int max_conn_count);
diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c
index 69b5be0..5f1f303 100644
--- a/drivers/gpu/drm/arc/arcpgu_drv.c
+++ b/drivers/gpu/drm/arc/arcpgu_drv.c
@@ -149,15 +149,16 @@  static int arcpgu_load(struct drm_device *drm)
 
 	/* find the encoder node and initialize it */
 	encoder_node = of_parse_phandle(drm->dev->of_node, "encoder-slave", 0);
-	if (!encoder_node) {
-		dev_err(drm->dev, "failed to get an encoder slave node\n");
-		return -ENODEV;
+	if (encoder_node) {
+		ret = arcpgu_drm_hdmi_init(drm, encoder_node);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = arcpgu_drm_sim_init(drm, 0);
+		if (ret < 0)
+			return ret;
 	}
 
-	ret = arcpgu_drm_hdmi_init(drm, encoder_node);
-	if (ret < 0)
-		return ret;
-
 	drm_mode_config_reset(drm);
 	drm_kms_helper_poll_init(drm);
 
diff --git a/drivers/gpu/drm/arc/arcpgu_sim.c b/drivers/gpu/drm/arc/arcpgu_sim.c
new file mode 100644
index 0000000..122e069
--- /dev/null
+++ b/drivers/gpu/drm/arc/arcpgu_sim.c
@@ -0,0 +1,177 @@ 
+/*
+ * ARC PGU DRM driver.
+ *
+ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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.
+ *
+ */
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "arcpgu.h"
+
+#define XRES_DEF	640
+#define YRES_DEF	480
+
+#define XRES_MAX	8192
+#define YRES_MAX	8192
+
+
+struct arcpgu_drm_connector {
+	struct drm_connector connector;
+	struct drm_encoder_slave *encoder_slave;
+};
+
+static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
+{
+	int count;
+
+	count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
+	drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
+	return count;
+}
+
+static struct drm_encoder *
+arcpgu_drm_connector_best_encoder(struct drm_connector *connector)
+{
+	struct drm_encoder_slave *slave;
+	struct arcpgu_drm_connector *con =
+		container_of(connector, struct arcpgu_drm_connector, connector);
+
+	slave = con->encoder_slave;
+	if (slave == NULL) {
+		dev_err(connector->dev->dev,
+			"connector_best_encoder: cannot find slave encoder for connector\n");
+		return NULL;
+	}
+
+	return &slave->base;
+}
+
+static enum drm_connector_status
+arcpgu_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void arcpgu_drm_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_helper_funcs
+arcpgu_drm_connector_helper_funcs = {
+	.get_modes = arcpgu_drm_connector_get_modes,
+	.best_encoder = arcpgu_drm_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.detect = arcpgu_drm_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = arcpgu_drm_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static bool sim_enc_mode_fixup(struct drm_encoder *encoder,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void sim_enc_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void sim_enc_enable(struct drm_encoder *encoder)
+{
+}
+
+static void sim_enc_disable(struct drm_encoder *encoder)
+{
+}
+
+static struct drm_encoder_helper_funcs arcpgu_drm_encoder_helper_funcs = {
+	.mode_fixup = sim_enc_mode_fixup,
+	.mode_set   = sim_enc_mode_set,
+	.enable     = sim_enc_enable,
+	.disable    = sim_enc_disable,
+};
+
+static struct drm_encoder_funcs arcpgu_drm_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+int arcpgu_drm_sim_init(struct drm_device *drm, struct device_node *np)
+{
+	struct arcpgu_drm_connector *arcpgu_connector;
+	struct drm_encoder_slave *encoder;
+	struct drm_connector *connector;
+	int ret;
+
+	encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL);
+	if (encoder == NULL)
+		return -ENOMEM;
+
+	encoder->base.possible_crtcs = 1;
+	encoder->base.possible_clones = 0;
+
+	ret = drm_encoder_init(drm, &encoder->base, &arcpgu_drm_encoder_funcs,
+			       DRM_MODE_ENCODER_VIRTUAL, NULL);
+	if (ret)
+		return ret;
+
+	drm_encoder_helper_add(&encoder->base,
+			       &arcpgu_drm_encoder_helper_funcs);
+
+	arcpgu_connector = devm_kzalloc(drm->dev, sizeof(*arcpgu_connector),
+					GFP_KERNEL);
+	if (!arcpgu_connector) {
+		ret = -ENOMEM;
+		goto error_encoder_cleanup;
+	}
+
+	connector = &arcpgu_connector->connector;
+	drm_connector_helper_add(connector, &arcpgu_drm_connector_helper_funcs);
+
+	ret = drm_connector_init(drm, connector, &arcpgu_drm_connector_funcs,
+			DRM_MODE_CONNECTOR_VIRTUAL);
+	if (ret < 0) {
+		dev_err(drm->dev, "failed to initialize drm connector\n");
+		goto error_encoder_cleanup;
+	}
+
+	ret = drm_mode_connector_attach_encoder(connector, &encoder->base);
+	if (ret < 0) {
+		dev_err(drm->dev, "could not attach connector to encoder\n");
+		drm_connector_unregister(connector);
+		goto error_connector_cleanup;
+	}
+
+	arcpgu_connector->encoder_slave = encoder;
+
+	return 0;
+
+error_connector_cleanup:
+	drm_connector_cleanup(connector);
+
+error_encoder_cleanup:
+	drm_encoder_cleanup(&encoder->base);
+	return ret;
+}