diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_vidi.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_vidi.c | 441 |
1 files changed, 230 insertions, 211 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index ddaaedde173d..7afead9c3f30 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -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, }, }; |