]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/exynos/exynos_drm_vidi.c
Merge branch 'x86-vdso-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / drivers / gpu / drm / exynos / exynos_drm_vidi.c
index ddaaedde173d0814b5823e628bc1f61e07eae8d8..7afead9c3f30258b7869e69e0f2015d9c2921fca 100644 (file)
@@ -28,7 +28,9 @@
 /* vidi has totally three virtual windows. */
 #define WINDOWS_NR             3
 
-#define get_vidi_context(dev)  platform_get_drvdata(to_platform_device(dev))
+#define get_vidi_mgr(dev)      platform_get_drvdata(to_platform_device(dev))
+#define ctx_from_connector(c)  container_of(c, struct vidi_context, \
+                                       connector)
 
 struct vidi_win_data {
        unsigned int            offset_x;
@@ -45,8 +47,10 @@ struct vidi_win_data {
 };
 
 struct vidi_context {
-       struct exynos_drm_subdrv        subdrv;
+       struct drm_device               *drm_dev;
        struct drm_crtc                 *crtc;
+       struct drm_encoder              *encoder;
+       struct drm_connector            connector;
        struct vidi_win_data            win_data[WINDOWS_NR];
        struct edid                     *raw_edid;
        unsigned int                    clkdiv;
@@ -58,6 +62,7 @@ struct vidi_context {
        bool                            direct_vblank;
        struct work_struct              work;
        struct mutex                    lock;
+       int                             pipe;
 };
 
 static const char fake_edid_info[] = {
@@ -85,126 +90,34 @@ static const char fake_edid_info[] = {
        0x00, 0x00, 0x00, 0x06
 };
 
-static bool vidi_display_is_connected(struct device *dev)
+static void vidi_apply(struct exynos_drm_manager *mgr)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
-
-       /*
-        * connection request would come from user side
-        * to do hotplug through specific ioctl.
-        */
-       return ctx->connected ? true : false;
-}
-
-static struct edid *vidi_get_edid(struct device *dev,
-                       struct drm_connector *connector)
-{
-       struct vidi_context *ctx = get_vidi_context(dev);
-       struct edid *edid;
-
-       /*
-        * the edid data comes from user side and it would be set
-        * to ctx->raw_edid through specific ioctl.
-        */
-       if (!ctx->raw_edid) {
-               DRM_DEBUG_KMS("raw_edid is null.\n");
-               return ERR_PTR(-EFAULT);
-       }
-
-       edid = drm_edid_duplicate(ctx->raw_edid);
-       if (!edid) {
-               DRM_DEBUG_KMS("failed to allocate edid\n");
-               return ERR_PTR(-ENOMEM);
-       }
-
-       return edid;
-}
-
-static void *vidi_get_panel(struct device *dev)
-{
-       /* TODO. */
-
-       return NULL;
-}
-
-static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode)
-{
-       /* TODO. */
-
-       return 0;
-}
-
-static int vidi_display_power_on(struct device *dev, int mode)
-{
-       /* TODO */
-
-       return 0;
-}
-
-static struct exynos_drm_display_ops vidi_display_ops = {
-       .type = EXYNOS_DISPLAY_TYPE_VIDI,
-       .is_connected = vidi_display_is_connected,
-       .get_edid = vidi_get_edid,
-       .get_panel = vidi_get_panel,
-       .check_mode = vidi_check_mode,
-       .power_on = vidi_display_power_on,
-};
-
-static void vidi_dpms(struct device *subdrv_dev, int mode)
-{
-       struct vidi_context *ctx = get_vidi_context(subdrv_dev);
-
-       DRM_DEBUG_KMS("%d\n", mode);
-
-       mutex_lock(&ctx->lock);
-
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               /* TODO. */
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               /* TODO. */
-               break;
-       default:
-               DRM_DEBUG_KMS("unspecified mode %d\n", mode);
-               break;
-       }
-
-       mutex_unlock(&ctx->lock);
-}
-
-static void vidi_apply(struct device *subdrv_dev)
-{
-       struct vidi_context *ctx = get_vidi_context(subdrv_dev);
-       struct exynos_drm_manager *mgr = ctx->subdrv.manager;
+       struct vidi_context *ctx = mgr->ctx;
        struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
-       struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
        struct vidi_win_data *win_data;
        int i;
 
        for (i = 0; i < WINDOWS_NR; i++) {
                win_data = &ctx->win_data[i];
-               if (win_data->enabled && (ovl_ops && ovl_ops->commit))
-                       ovl_ops->commit(subdrv_dev, i);
+               if (win_data->enabled && (mgr_ops && mgr_ops->win_commit))
+                       mgr_ops->win_commit(mgr, i);
        }
 
        if (mgr_ops && mgr_ops->commit)
-               mgr_ops->commit(subdrv_dev);
+               mgr_ops->commit(mgr);
 }
 
-static void vidi_commit(struct device *dev)
+static void vidi_commit(struct exynos_drm_manager *mgr)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_context *ctx = mgr->ctx;
 
        if (ctx->suspended)
                return;
 }
 
-static int vidi_enable_vblank(struct device *dev)
+static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_context *ctx = mgr->ctx;
 
        if (ctx->suspended)
                return -EPERM;
@@ -217,16 +130,16 @@ static int vidi_enable_vblank(struct device *dev)
        /*
         * in case of page flip request, vidi_finish_pageflip function
         * will not be called because direct_vblank is true and then
-        * that function will be called by overlay_ops->commit callback
+        * that function will be called by manager_ops->win_commit callback
         */
        schedule_work(&ctx->work);
 
        return 0;
 }
 
-static void vidi_disable_vblank(struct device *dev)
+static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_context *ctx = mgr->ctx;
 
        if (ctx->suspended)
                return;
@@ -235,24 +148,16 @@ static void vidi_disable_vblank(struct device *dev)
                ctx->vblank_on = false;
 }
 
-static struct exynos_drm_manager_ops vidi_manager_ops = {
-       .dpms = vidi_dpms,
-       .apply = vidi_apply,
-       .commit = vidi_commit,
-       .enable_vblank = vidi_enable_vblank,
-       .disable_vblank = vidi_disable_vblank,
-};
-
-static void vidi_win_mode_set(struct device *dev,
-                             struct exynos_drm_overlay *overlay)
+static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
+                       struct exynos_drm_overlay *overlay)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_context *ctx = mgr->ctx;
        struct vidi_win_data *win_data;
        int win;
        unsigned long offset;
 
        if (!overlay) {
-               dev_err(dev, "overlay is NULL\n");
+               DRM_ERROR("overlay is NULL\n");
                return;
        }
 
@@ -296,9 +201,9 @@ static void vidi_win_mode_set(struct device *dev,
                        overlay->fb_width, overlay->crtc_width);
 }
 
-static void vidi_win_commit(struct device *dev, int zpos)
+static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_context *ctx = mgr->ctx;
        struct vidi_win_data *win_data;
        int win = zpos;
 
@@ -321,9 +226,9 @@ static void vidi_win_commit(struct device *dev, int zpos)
                schedule_work(&ctx->work);
 }
 
-static void vidi_win_disable(struct device *dev, int zpos)
+static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_context *ctx = mgr->ctx;
        struct vidi_win_data *win_data;
        int win = zpos;
 
@@ -339,98 +244,132 @@ static void vidi_win_disable(struct device *dev, int zpos)
        /* TODO. */
 }
 
-static struct exynos_drm_overlay_ops vidi_overlay_ops = {
-       .mode_set = vidi_win_mode_set,
-       .commit = vidi_win_commit,
-       .disable = vidi_win_disable,
-};
+static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
+{
+       struct vidi_context *ctx = mgr->ctx;
 
-static struct exynos_drm_manager vidi_manager = {
-       .pipe           = -1,
-       .ops            = &vidi_manager_ops,
-       .overlay_ops    = &vidi_overlay_ops,
-       .display_ops    = &vidi_display_ops,
-};
+       DRM_DEBUG_KMS("%s\n", __FILE__);
 
-static void vidi_fake_vblank_handler(struct work_struct *work)
-{
-       struct vidi_context *ctx = container_of(work, struct vidi_context,
-                                       work);
-       struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
-       struct exynos_drm_manager *manager = subdrv->manager;
+       if (enable != false && enable != true)
+               return -EINVAL;
 
-       if (manager->pipe < 0)
-               return;
+       if (enable) {
+               ctx->suspended = false;
 
-       /* refresh rate is about 50Hz. */
-       usleep_range(16000, 20000);
+               /* if vblank was enabled status, enable it again. */
+               if (test_and_clear_bit(0, &ctx->irq_flags))
+                       vidi_enable_vblank(mgr);
+
+               vidi_apply(mgr);
+       } else {
+               ctx->suspended = true;
+       }
+
+       return 0;
+}
+
+static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
+{
+       struct vidi_context *ctx = mgr->ctx;
+
+       DRM_DEBUG_KMS("%d\n", mode);
 
        mutex_lock(&ctx->lock);
 
-       if (ctx->direct_vblank) {
-               drm_handle_vblank(subdrv->drm_dev, manager->pipe);
-               ctx->direct_vblank = false;
-               mutex_unlock(&ctx->lock);
-               return;
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               vidi_power_on(mgr, true);
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+       case DRM_MODE_DPMS_OFF:
+               vidi_power_on(mgr, false);
+               break;
+       default:
+               DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+               break;
        }
 
        mutex_unlock(&ctx->lock);
-
-       exynos_drm_crtc_finish_pageflip(subdrv->drm_dev, manager->pipe);
 }
 
-static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
+                       struct drm_device *drm_dev, int pipe)
 {
+       struct vidi_context *ctx = mgr->ctx;
+
+       DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
+
+       ctx->drm_dev = drm_dev;
+       ctx->pipe = pipe;
+
        /*
         * enable drm irq mode.
-        * - with irq_enabled = true, we can use the vblank feature.
+        * - with irq_enabled = 1, we can use the vblank feature.
         *
         * P.S. note that we wouldn't use drm irq handler but
         *      just specific driver own one instead because
         *      drm framework supports only one irq handler.
         */
-       drm_dev->irq_enabled = true;
+       drm_dev->irq_enabled = 1;
 
        /*
-        * with vblank_disable_allowed = true, vblank interrupt will be disabled
+        * with vblank_disable_allowed = 1, vblank interrupt will be disabled
         * by drm timer once a current process gives up ownership of
         * vblank event.(after drm_vblank_put function is called)
         */
-       drm_dev->vblank_disable_allowed = true;
+       drm_dev->vblank_disable_allowed = 1;
 
        return 0;
 }
 
-static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
-{
-       /* TODO. */
-}
+static struct exynos_drm_manager_ops vidi_manager_ops = {
+       .initialize = vidi_mgr_initialize,
+       .dpms = vidi_dpms,
+       .commit = vidi_commit,
+       .enable_vblank = vidi_enable_vblank,
+       .disable_vblank = vidi_disable_vblank,
+       .win_mode_set = vidi_win_mode_set,
+       .win_commit = vidi_win_commit,
+       .win_disable = vidi_win_disable,
+};
 
-static int vidi_power_on(struct vidi_context *ctx, bool enable)
+static struct exynos_drm_manager vidi_manager = {
+       .type = EXYNOS_DISPLAY_TYPE_VIDI,
+       .ops = &vidi_manager_ops,
+};
+
+static void vidi_fake_vblank_handler(struct work_struct *work)
 {
-       struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
-       struct device *dev = subdrv->dev;
+       struct vidi_context *ctx = container_of(work, struct vidi_context,
+                                       work);
 
-       if (enable) {
-               ctx->suspended = false;
+       if (ctx->pipe < 0)
+               return;
 
-               /* if vblank was enabled status, enable it again. */
-               if (test_and_clear_bit(0, &ctx->irq_flags))
-                       vidi_enable_vblank(dev);
+       /* refresh rate is about 50Hz. */
+       usleep_range(16000, 20000);
 
-               vidi_apply(dev);
-       } else {
-               ctx->suspended = true;
+       mutex_lock(&ctx->lock);
+
+       if (ctx->direct_vblank) {
+               drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+               ctx->direct_vblank = false;
+               mutex_unlock(&ctx->lock);
+               return;
        }
 
-       return 0;
+       mutex_unlock(&ctx->lock);
+
+       exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
 }
 
 static int vidi_show_connection(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
        int rc;
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
+       struct vidi_context *ctx = mgr->ctx;
 
        mutex_lock(&ctx->lock);
 
@@ -445,7 +384,8 @@ static int vidi_store_connection(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t len)
 {
-       struct vidi_context *ctx = get_vidi_context(dev);
+       struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
+       struct vidi_context *ctx = mgr->ctx;
        int ret;
 
        ret = kstrtoint(buf, 0, &ctx->connected);
@@ -467,7 +407,7 @@ static int vidi_store_connection(struct device *dev,
 
        DRM_DEBUG_KMS("requested connection.\n");
 
-       drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+       drm_helper_hpd_irq_event(ctx->drm_dev);
 
        return len;
 }
@@ -480,8 +420,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
 {
        struct vidi_context *ctx = NULL;
        struct drm_encoder *encoder;
-       struct exynos_drm_manager *manager;
-       struct exynos_drm_display_ops *display_ops;
+       struct exynos_drm_display *display;
        struct drm_exynos_vidi_connection *vidi = data;
 
        if (!vidi) {
@@ -496,11 +435,10 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
 
        list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
                                                                head) {
-               manager = exynos_drm_get_manager(encoder);
-               display_ops = manager->display_ops;
+               display = exynos_drm_get_display(encoder);
 
-               if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
-                       ctx = get_vidi_context(manager->dev);
+               if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
+                       ctx = display->ctx;
                        break;
                }
        }
@@ -539,16 +477,119 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
        }
 
        ctx->connected = vidi->connection;
-       drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+       drm_helper_hpd_irq_event(ctx->drm_dev);
+
+       return 0;
+}
+
+static enum drm_connector_status vidi_detect(struct drm_connector *connector,
+                       bool force)
+{
+       struct vidi_context *ctx = ctx_from_connector(connector);
+
+       /*
+        * connection request would come from user side
+        * to do hotplug through specific ioctl.
+        */
+       return ctx->connected ? connector_status_connected :
+                       connector_status_disconnected;
+}
+
+static void vidi_connector_destroy(struct drm_connector *connector)
+{
+}
+
+static struct drm_connector_funcs vidi_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = vidi_detect,
+       .destroy = vidi_connector_destroy,
+};
+
+static int vidi_get_modes(struct drm_connector *connector)
+{
+       struct vidi_context *ctx = ctx_from_connector(connector);
+       struct edid *edid;
+       int edid_len;
+
+       /*
+        * the edid data comes from user side and it would be set
+        * to ctx->raw_edid through specific ioctl.
+        */
+       if (!ctx->raw_edid) {
+               DRM_DEBUG_KMS("raw_edid is null.\n");
+               return -EFAULT;
+       }
+
+       edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH;
+       edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);
+       if (!edid) {
+               DRM_DEBUG_KMS("failed to allocate edid\n");
+               return -ENOMEM;
+       }
+
+       drm_mode_connector_update_edid_property(connector, edid);
+
+       return drm_add_edid_modes(connector, edid);
+}
+
+static int vidi_mode_valid(struct drm_connector *connector,
+                       struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
+{
+       struct vidi_context *ctx = ctx_from_connector(connector);
+
+       return ctx->encoder;
+}
+
+static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
+       .get_modes = vidi_get_modes,
+       .mode_valid = vidi_mode_valid,
+       .best_encoder = vidi_best_encoder,
+};
+
+static int vidi_create_connector(struct exynos_drm_display *display,
+                               struct drm_encoder *encoder)
+{
+       struct vidi_context *ctx = display->ctx;
+       struct drm_connector *connector = &ctx->connector;
+       int ret;
+
+       ctx->encoder = encoder;
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+       ret = drm_connector_init(ctx->drm_dev, connector,
+                       &vidi_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
+       if (ret) {
+               DRM_ERROR("Failed to initialize connector with drm\n");
+               return ret;
+       }
+
+       drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
+       drm_sysfs_connector_add(connector);
+       drm_mode_connector_attach_encoder(connector, encoder);
 
        return 0;
 }
 
+
+static struct exynos_drm_display_ops vidi_display_ops = {
+       .create_connector = vidi_create_connector,
+};
+
+static struct exynos_drm_display vidi_display = {
+       .type = EXYNOS_DISPLAY_TYPE_VIDI,
+       .ops = &vidi_display_ops,
+};
+
 static int vidi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct vidi_context *ctx;
-       struct exynos_drm_subdrv *subdrv;
        int ret;
 
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
@@ -559,21 +600,19 @@ static int vidi_probe(struct platform_device *pdev)
 
        INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
 
-       subdrv = &ctx->subdrv;
-       subdrv->dev = dev;
-       subdrv->manager = &vidi_manager;
-       subdrv->probe = vidi_subdrv_probe;
-       subdrv->remove = vidi_subdrv_remove;
+       vidi_manager.ctx = ctx;
+       vidi_display.ctx = ctx;
 
        mutex_init(&ctx->lock);
 
-       platform_set_drvdata(pdev, ctx);
+       platform_set_drvdata(pdev, &vidi_manager);
 
        ret = device_create_file(dev, &dev_attr_connection);
        if (ret < 0)
                DRM_INFO("failed to create connection sysfs.\n");
 
-       exynos_drm_subdrv_register(subdrv);
+       exynos_drm_manager_register(&vidi_manager);
+       exynos_drm_display_register(&vidi_display);
 
        return 0;
 }
@@ -582,7 +621,8 @@ static int vidi_remove(struct platform_device *pdev)
 {
        struct vidi_context *ctx = platform_get_drvdata(pdev);
 
-       exynos_drm_subdrv_unregister(&ctx->subdrv);
+       exynos_drm_display_unregister(&vidi_display);
+       exynos_drm_manager_unregister(&vidi_manager);
 
        if (ctx->raw_edid != (struct edid *)fake_edid_info) {
                kfree(ctx->raw_edid);
@@ -592,32 +632,11 @@ static int vidi_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int vidi_suspend(struct device *dev)
-{
-       struct vidi_context *ctx = get_vidi_context(dev);
-
-       return vidi_power_on(ctx, false);
-}
-
-static int vidi_resume(struct device *dev)
-{
-       struct vidi_context *ctx = get_vidi_context(dev);
-
-       return vidi_power_on(ctx, true);
-}
-#endif
-
-static const struct dev_pm_ops vidi_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
-};
-
 struct platform_driver vidi_driver = {
        .probe          = vidi_probe,
        .remove         = vidi_remove,
        .driver         = {
                .name   = "exynos-drm-vidi",
                .owner  = THIS_MODULE,
-               .pm     = &vidi_pm_ops,
        },
 };