aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/exynos/exynos_drm_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_encoder.c')
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c127
1 files changed, 107 insertions, 20 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 2c037cd7d2d4..241ad1eeec64 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -26,11 +26,12 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-#include "drmP.h"
-#include "drm_crtc_helper.h"
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
+#include "exynos_drm_connector.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
@@ -42,28 +43,27 @@
* @manager: specific encoder has its own manager to control a hardware
* appropriately and we can access a hardware drawing on this manager.
* @dpms: store the encoder dpms value.
+ * @updated: indicate whether overlay data updating is needed or not.
*/
struct exynos_drm_encoder {
+ struct drm_crtc *old_crtc;
struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager;
- int dpms;
+ int dpms;
+ bool updated;
};
-static void exynos_drm_display_power(struct drm_encoder *encoder, int mode)
+static void exynos_drm_connector_power(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
- struct exynos_drm_display_ops *display_ops =
- manager->display_ops;
-
+ if (exynos_drm_best_encoder(connector) == encoder) {
DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
connector->base.id, mode);
- if (display_ops && display_ops->power_on)
- display_ops->power_on(manager->dev, mode);
+
+ exynos_drm_display_power(connector, mode);
}
}
}
@@ -87,15 +87,18 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
switch (mode) {
case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->apply)
- manager_ops->apply(manager->dev);
- exynos_drm_display_power(encoder, mode);
+ if (!exynos_encoder->updated)
+ manager_ops->apply(manager->dev);
+
+ exynos_drm_connector_power(encoder, mode);
exynos_encoder->dpms = mode;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- exynos_drm_display_power(encoder, mode);
+ exynos_drm_connector_power(encoder, mode);
exynos_encoder->dpms = mode;
+ exynos_encoder->updated = false;
break;
default:
DRM_ERROR("unspecified mode %d\n", mode);
@@ -127,24 +130,74 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
+static void disable_plane_to_crtc(struct drm_device *dev,
+ struct drm_crtc *old_crtc,
+ struct drm_crtc *new_crtc)
+{
+ struct drm_plane *plane;
+
+ /*
+ * if old_crtc isn't same as encoder->crtc then it means that
+ * user changed crtc id to another one so the plane to old_crtc
+ * should be disabled and plane->crtc should be set to new_crtc
+ * (encoder->crtc)
+ */
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ if (plane->crtc == old_crtc) {
+ /*
+ * do not change below call order.
+ *
+ * plane->funcs->disable_plane call checks
+ * if encoder->crtc is same as plane->crtc and if same
+ * then overlay_ops->disable callback will be called
+ * to diasble current hw overlay so plane->crtc should
+ * have new_crtc because new_crtc was set to
+ * encoder->crtc in advance.
+ */
+ plane->crtc = new_crtc;
+ plane->funcs->disable_plane(plane);
+ }
+ }
+}
+
static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_manager *manager;
+ struct exynos_drm_manager_ops *manager_ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
-
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder)
+ if (connector->encoder == encoder) {
+ struct exynos_drm_encoder *exynos_encoder;
+
+ exynos_encoder = to_exynos_encoder(encoder);
+
+ if (exynos_encoder->old_crtc != encoder->crtc &&
+ exynos_encoder->old_crtc) {
+
+ /*
+ * disable a plane to old crtc and change
+ * crtc of the plane to new one.
+ */
+ disable_plane_to_crtc(dev,
+ exynos_encoder->old_crtc,
+ encoder->crtc);
+ }
+
+ manager = exynos_drm_get_manager(encoder);
+ manager_ops = manager->ops;
+
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev,
adjusted_mode);
+
+ exynos_encoder->old_crtc = encoder->crtc;
+ }
}
}
@@ -157,13 +210,36 @@ static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_manager *manager = exynos_encoder->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
+
+ /*
+ * this will avoid one issue that overlay data is updated to
+ * real hardware two times.
+ * And this variable will be used to check if the data was
+ * already updated or not by exynos_drm_encoder_dpms function.
+ */
+ exynos_encoder->updated = true;
+}
+
+static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
+{
+ struct drm_plane *plane;
+ struct drm_device *dev = encoder->dev;
+
+ exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ /* all planes connected to this encoder should be also disabled. */
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ if (plane->crtc == encoder->crtc)
+ plane->funcs->disable_plane(plane);
+ }
}
static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
@@ -172,6 +248,7 @@ static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
.mode_set = exynos_drm_encoder_mode_set,
.prepare = exynos_drm_encoder_prepare,
.commit = exynos_drm_encoder_commit,
+ .disable = exynos_drm_encoder_disable,
};
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
@@ -422,4 +499,14 @@ void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
+
+ /*
+ * wait for vblank interrupt
+ * - this makes sure that hardware overlay is disabled to avoid
+ * for the dma accesses to memory after gem buffer was released
+ * because the setting for disabling the overlay will be updated
+ * at vsync.
+ */
+ if (overlay_ops->wait_for_vblank)
+ overlay_ops->wait_for_vblank(manager->dev);
}