summaryrefslogtreecommitdiff
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.c202
1 files changed, 186 insertions, 16 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 7cf6fa86a67e..ef4754f1519b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -42,30 +42,70 @@
* @drm_encoder: encoder object.
* @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.
*/
struct exynos_drm_encoder {
struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager;
+ int dpms;
};
-static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+static void exynos_drm_display_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);
- DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
-
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
- struct exynos_drm_display *display = manager->display;
+ struct exynos_drm_display_ops *display_ops =
+ manager->display_ops;
- if (display && display->power_on)
- display->power_on(manager->dev, mode);
+ 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);
}
}
}
+static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+
+ DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
+
+ if (exynos_encoder->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+
+ mutex_lock(&dev->struct_mutex);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (manager_ops && manager_ops->apply)
+ manager_ops->apply(manager->dev);
+ exynos_drm_display_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_encoder->dpms = mode;
+ break;
+ default:
+ DRM_ERROR("unspecified mode %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+}
+
static bool
exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
@@ -116,15 +156,11 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
-
- if (overlay_ops && overlay_ops->commit)
- overlay_ops->commit(manager->dev);
}
static struct drm_crtc *
@@ -152,7 +188,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
exynos_encoder->manager->pipe = -1;
drm_encoder_cleanup(encoder);
- encoder->dev->mode_config.num_encoder--;
kfree(exynos_encoder);
}
@@ -160,6 +195,40 @@ static struct drm_encoder_funcs exynos_encoder_funcs = {
.destroy = exynos_drm_encoder_destroy,
};
+static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
+{
+ struct drm_encoder *clone;
+ struct drm_device *dev = encoder->dev;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display_ops *display_ops =
+ exynos_encoder->manager->display_ops;
+ unsigned int clone_mask = 0;
+ int cnt = 0;
+
+ list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+ switch (display_ops->type) {
+ case EXYNOS_DISPLAY_TYPE_LCD:
+ case EXYNOS_DISPLAY_TYPE_HDMI:
+ clone_mask |= (1 << (cnt++));
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return clone_mask;
+}
+
+void exynos_drm_encoder_setup(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ encoder->possible_clones = exynos_drm_encoder_clones(encoder);
+}
+
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *manager,
@@ -182,6 +251,7 @@ exynos_drm_encoder_create(struct drm_device *dev,
return NULL;
}
+ exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
exynos_encoder->manager = manager;
encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
@@ -208,10 +278,23 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
{
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
+ struct exynos_drm_private *private = dev->dev_private;
+ struct exynos_drm_manager *manager;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (encoder->crtc != crtc)
- continue;
+ /*
+ * if crtc is detached from encoder, check pipe,
+ * otherwise check crtc attached to encoder
+ */
+ if (!encoder->crtc) {
+ manager = to_exynos_encoder(encoder)->manager;
+ if (manager->pipe < 0 ||
+ private->crtc[manager->pipe] != crtc)
+ continue;
+ } else {
+ if (encoder->crtc != crtc)
+ continue;
+ }
fn(encoder, data);
}
@@ -245,13 +328,83 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
manager_ops->disable_vblank(manager->dev);
}
-void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
+ void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ int zpos = DEFAULT_ZPOS;
+
+ if (data)
+ zpos = *(int *)data;
- overlay_ops->commit(manager->dev);
+ if (overlay_ops && overlay_ops->commit)
+ overlay_ops->commit(manager->dev, zpos);
+}
+
+void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ int crtc = *(int *)data;
+ int zpos = DEFAULT_ZPOS;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * when crtc is detached from encoder, this pipe is used
+ * to select manager operation
+ */
+ manager->pipe = crtc;
+
+ exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
+}
+
+void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ int mode = *(int *)data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_drm_encoder_dpms(encoder, mode);
+
+ exynos_encoder->dpms = mode;
+}
+
+void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
+{
+ struct drm_device *dev = encoder->dev;
+ 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;
+ struct drm_connector *connector;
+ int mode = *(int *)data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (manager_ops && manager_ops->dpms)
+ manager_ops->dpms(manager->dev, mode);
+
+ /*
+ * set current dpms mode to the connector connected to
+ * current encoder. connector->dpms would be checked
+ * at drm_helper_connector_dpms()
+ */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder == encoder)
+ connector->dpms = mode;
+
+ /*
+ * if this condition is ok then it means that the crtc is already
+ * detached from encoder and last function for detaching is properly
+ * done, so clear pipe from manager to prevent repeated call.
+ */
+ if (mode > DRM_MODE_DPMS_ON) {
+ if (!encoder->crtc)
+ manager->pipe = -1;
+ }
}
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
@@ -261,7 +414,24 @@ void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct exynos_drm_overlay *overlay = data;
- overlay_ops->mode_set(manager->dev, overlay);
+ if (overlay_ops && overlay_ops->mode_set)
+ overlay_ops->mode_set(manager->dev, overlay);
+}
+
+void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
+{
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ int zpos = DEFAULT_ZPOS;
+
+ DRM_DEBUG_KMS("\n");
+
+ if (data)
+ zpos = *(int *)data;
+
+ if (overlay_ops && overlay_ops->disable)
+ overlay_ops->disable(manager->dev, zpos);
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");