diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_connector.c | 83 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_core.c | 179 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 115 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.h | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 29 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 106 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_encoder.c | 258 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_encoder.h | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 510 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 211 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_hdmi.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_vidi.c | 119 |
14 files changed, 771 insertions, 898 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index cb8ef46e015..34a1a16520b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -23,55 +23,20 @@ drm_connector) struct exynos_drm_connector { - struct drm_connector drm_connector; - uint32_t encoder_id; - struct exynos_drm_manager *manager; + struct drm_connector drm_connector; + uint32_t encoder_id; + struct exynos_drm_display *display; }; -/* convert exynos_video_timings to drm_display_mode */ -static inline void -convert_to_display_mode(struct drm_display_mode *mode, - struct exynos_drm_panel_info *panel) -{ - struct fb_videomode *timing = &panel->timing; - - mode->clock = timing->pixclock / 1000; - mode->vrefresh = timing->refresh; - - mode->hdisplay = timing->xres; - mode->hsync_start = mode->hdisplay + timing->right_margin; - mode->hsync_end = mode->hsync_start + timing->hsync_len; - mode->htotal = mode->hsync_end + timing->left_margin; - - mode->vdisplay = timing->yres; - mode->vsync_start = mode->vdisplay + timing->lower_margin; - mode->vsync_end = mode->vsync_start + timing->vsync_len; - mode->vtotal = mode->vsync_end + timing->upper_margin; - mode->width_mm = panel->width_mm; - mode->height_mm = panel->height_mm; - - if (timing->vmode & FB_VMODE_INTERLACED) - mode->flags |= DRM_MODE_FLAG_INTERLACE; - - if (timing->vmode & FB_VMODE_DOUBLE) - mode->flags |= DRM_MODE_FLAG_DBLSCAN; -} - static int exynos_drm_connector_get_modes(struct drm_connector *connector) { struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_display_ops *display_ops = manager->display_ops; + struct exynos_drm_display *display = exynos_connector->display; struct edid *edid = NULL; unsigned int count = 0; int ret; - if (!display_ops) { - DRM_DEBUG_KMS("display_ops is null.\n"); - return 0; - } - /* * if get_edid() exists then get_edid() callback of hdmi side * is called to get edid data through i2c interface else @@ -80,8 +45,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector) * P.S. in case of lcd panel, count is always 1 if success * because lcd panel has only one mode. */ - if (display_ops->get_edid) { - edid = display_ops->get_edid(manager->dev, connector); + if (display->ops->get_edid) { + edid = display->ops->get_edid(display, connector); if (IS_ERR_OR_NULL(edid)) { ret = PTR_ERR(edid); edid = NULL; @@ -104,14 +69,16 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector) return 0; } - if (display_ops->get_panel) - panel = display_ops->get_panel(manager->dev); + if (display->ops->get_panel) + panel = display->ops->get_panel(display); else { drm_mode_destroy(connector->dev, mode); return 0; } - convert_to_display_mode(mode, panel); + drm_display_mode_from_videomode(&panel->vm, mode); + mode->width_mm = panel->width_mm; + mode->height_mm = panel->height_mm; connector->display_info.width_mm = mode->width_mm; connector->display_info.height_mm = mode->height_mm; @@ -132,14 +99,13 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector, { struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_display_ops *display_ops = manager->display_ops; + struct exynos_drm_display *display = exynos_connector->display; int ret = MODE_BAD; DRM_DEBUG_KMS("%s\n", __FILE__); - if (display_ops && display_ops->check_mode) - if (!display_ops->check_mode(manager->dev, mode)) + if (display->ops->check_mode) + if (!display->ops->check_mode(display, mode)) ret = MODE_OK; return ret; @@ -178,8 +144,7 @@ static int exynos_drm_connector_fill_modes(struct drm_connector *connector, { struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_manager_ops *ops = manager->ops; + struct exynos_drm_display *display = exynos_connector->display; unsigned int width, height; width = max_width; @@ -189,8 +154,8 @@ static int exynos_drm_connector_fill_modes(struct drm_connector *connector, * if specific driver want to find desired_mode using maxmum * resolution then get max width and height from that driver. */ - if (ops && ops->get_max_resol) - ops->get_max_resol(manager, &width, &height); + if (display->ops->get_max_resol) + display->ops->get_max_resol(display, &width, &height); return drm_helper_probe_single_connector_modes(connector, width, height); @@ -202,13 +167,11 @@ exynos_drm_connector_detect(struct drm_connector *connector, bool force) { struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - struct exynos_drm_manager *manager = exynos_connector->manager; - struct exynos_drm_display_ops *display_ops = - manager->display_ops; + struct exynos_drm_display *display = exynos_connector->display; enum drm_connector_status status = connector_status_disconnected; - if (display_ops && display_ops->is_connected) { - if (display_ops->is_connected(manager->dev)) + if (display->ops->is_connected) { + if (display->ops->is_connected(display)) status = connector_status_connected; else status = connector_status_disconnected; @@ -238,7 +201,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, struct drm_encoder *encoder) { struct exynos_drm_connector *exynos_connector; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + struct exynos_drm_display *display = exynos_drm_get_display(encoder); struct drm_connector *connector; int type; int err; @@ -249,7 +212,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, connector = &exynos_connector->drm_connector; - switch (manager->display_ops->type) { + switch (display->type) { case EXYNOS_DISPLAY_TYPE_HDMI: type = DRM_MODE_CONNECTOR_HDMIA; connector->interlace_allowed = true; @@ -275,7 +238,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, goto err_connector; exynos_connector->encoder_id = encoder->base.id; - exynos_connector->manager = manager; + exynos_connector->display = display; connector->dpms = DRM_MODE_DPMS_OFF; connector->encoder = encoder; diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 1bef6dc7747..e23611eaa90 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -14,24 +14,31 @@ #include <drm/drmP.h> #include "exynos_drm_drv.h" +#include "exynos_drm_crtc.h" #include "exynos_drm_encoder.h" #include "exynos_drm_connector.h" #include "exynos_drm_fbdev.h" static LIST_HEAD(exynos_drm_subdrv_list); +static LIST_HEAD(exynos_drm_manager_list); +static LIST_HEAD(exynos_drm_display_list); static int exynos_drm_create_enc_conn(struct drm_device *dev, - struct exynos_drm_subdrv *subdrv) + struct exynos_drm_display *display) { struct drm_encoder *encoder; struct drm_connector *connector; + struct exynos_drm_manager *manager; int ret; + unsigned long possible_crtcs = 0; - subdrv->manager->dev = subdrv->dev; + /* Find possible crtcs for this display */ + list_for_each_entry(manager, &exynos_drm_manager_list, list) + if (manager->type == display->type) + possible_crtcs |= 1 << manager->pipe; /* create and initialize a encoder for this sub driver. */ - encoder = exynos_drm_encoder_create(dev, subdrv->manager, - (1 << MAX_CRTC) - 1); + encoder = exynos_drm_encoder_create(dev, display, possible_crtcs); if (!encoder) { DRM_ERROR("failed to create encoder\n"); return -EFAULT; @@ -48,8 +55,8 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev, goto err_destroy_encoder; } - subdrv->encoder = encoder; - subdrv->connector = connector; + display->encoder = encoder; + display->connector = connector; return 0; @@ -58,21 +65,6 @@ err_destroy_encoder: return ret; } -static void exynos_drm_destroy_enc_conn(struct exynos_drm_subdrv *subdrv) -{ - if (subdrv->encoder) { - struct drm_encoder *encoder = subdrv->encoder; - encoder->funcs->destroy(encoder); - subdrv->encoder = NULL; - } - - if (subdrv->connector) { - struct drm_connector *connector = subdrv->connector; - connector->funcs->destroy(connector); - subdrv->connector = NULL; - } -} - static int exynos_drm_subdrv_probe(struct drm_device *dev, struct exynos_drm_subdrv *subdrv) { @@ -104,10 +96,98 @@ static void exynos_drm_subdrv_remove(struct drm_device *dev, subdrv->remove(dev, subdrv->dev); } +int exynos_drm_initialize_managers(struct drm_device *dev) +{ + struct exynos_drm_manager *manager, *n; + int ret, pipe = 0; + + list_for_each_entry(manager, &exynos_drm_manager_list, list) { + if (manager->ops->initialize) { + ret = manager->ops->initialize(manager, dev, pipe); + if (ret) { + DRM_ERROR("Mgr init [%d] failed with %d\n", + manager->type, ret); + goto err; + } + } + + manager->drm_dev = dev; + manager->pipe = pipe++; + + ret = exynos_drm_crtc_create(manager); + if (ret) { + DRM_ERROR("CRTC create [%d] failed with %d\n", + manager->type, ret); + goto err; + } + } + return 0; + +err: + list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) { + if (pipe-- > 0) + exynos_drm_manager_unregister(manager); + else + list_del(&manager->list); + } + return ret; +} + +void exynos_drm_remove_managers(struct drm_device *dev) +{ + struct exynos_drm_manager *manager, *n; + + list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) + exynos_drm_manager_unregister(manager); +} + +int exynos_drm_initialize_displays(struct drm_device *dev) +{ + struct exynos_drm_display *display, *n; + int ret, initialized = 0; + + list_for_each_entry(display, &exynos_drm_display_list, list) { + if (display->ops->initialize) { + ret = display->ops->initialize(display, dev); + if (ret) { + DRM_ERROR("Display init [%d] failed with %d\n", + display->type, ret); + goto err; + } + } + + initialized++; + + ret = exynos_drm_create_enc_conn(dev, display); + if (ret) { + DRM_ERROR("Encoder create [%d] failed with %d\n", + display->type, ret); + goto err; + } + } + return 0; + +err: + list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) { + if (initialized-- > 0) + exynos_drm_display_unregister(display); + else + list_del(&display->list); + } + return ret; +} + +void exynos_drm_remove_displays(struct drm_device *dev) +{ + struct exynos_drm_display *display, *n; + + list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) + exynos_drm_display_unregister(display); +} + int exynos_drm_device_register(struct drm_device *dev) { struct exynos_drm_subdrv *subdrv, *n; - unsigned int fine_cnt = 0; int err; if (!dev) @@ -120,30 +200,8 @@ int exynos_drm_device_register(struct drm_device *dev) list_del(&subdrv->list); continue; } - - /* - * if manager is null then it means that this sub driver - * doesn't need encoder and connector. - */ - if (!subdrv->manager) { - fine_cnt++; - continue; - } - - err = exynos_drm_create_enc_conn(dev, subdrv); - if (err) { - DRM_DEBUG("failed to create encoder and connector.\n"); - exynos_drm_subdrv_remove(dev, subdrv); - list_del(&subdrv->list); - continue; - } - - fine_cnt++; } - if (!fine_cnt) - return -EINVAL; - return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_register); @@ -159,13 +217,44 @@ int exynos_drm_device_unregister(struct drm_device *dev) list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { exynos_drm_subdrv_remove(dev, subdrv); - exynos_drm_destroy_enc_conn(subdrv); } return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); +int exynos_drm_manager_register(struct exynos_drm_manager *manager) +{ + BUG_ON(!manager->ops); + list_add_tail(&manager->list, &exynos_drm_manager_list); + return 0; +} + +int exynos_drm_manager_unregister(struct exynos_drm_manager *manager) +{ + if (manager->ops->remove) + manager->ops->remove(manager); + + list_del(&manager->list); + return 0; +} + +int exynos_drm_display_register(struct exynos_drm_display *display) +{ + BUG_ON(!display->ops); + list_add_tail(&display->list, &exynos_drm_display_list); + return 0; +} + +int exynos_drm_display_unregister(struct exynos_drm_display *display) +{ + if (display->ops->remove) + display->ops->remove(display); + + list_del(&display->list); + return 0; +} + int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) { if (!subdrv) diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 4aee34487e6..2235756a0f9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -37,6 +37,7 @@ enum exynos_crtc_mode { * * @drm_crtc: crtc object. * @drm_plane: pointer of private plane object for this crtc + * @manager: the manager associated with this crtc * @pipe: a crtc index created at load() with a new crtc object creation * and the crtc object would be set to private->crtc array * to get a crtc object corresponding to this pipe from private->crtc @@ -50,6 +51,7 @@ enum exynos_crtc_mode { struct exynos_drm_crtc { struct drm_crtc drm_crtc; struct drm_plane *plane; + struct exynos_drm_manager *manager; unsigned int pipe; unsigned int dpms; enum exynos_crtc_mode mode; @@ -72,6 +74,7 @@ static struct dmabuf_sync_priv_ops dmabuf_sync_ops = { static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct exynos_drm_manager *manager = exynos_crtc->manager; DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); @@ -87,7 +90,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) drm_vblank_off(crtc->dev, exynos_crtc->pipe); } - exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); + if (manager->ops->dpms) + manager->ops->dpms(manager, mode); + exynos_crtc->dpms = mode; } @@ -99,9 +104,15 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) static void exynos_drm_crtc_commit(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct exynos_drm_manager *manager = exynos_crtc->manager; exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + exynos_plane_commit(exynos_crtc->plane); + + if (manager->ops->commit) + manager->ops->commit(manager); + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); } @@ -124,7 +135,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct dmabuf_sync *sync; unsigned int crtc_w; unsigned int crtc_h; - int pipe = exynos_crtc->pipe; int ret; /* @@ -144,8 +154,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, plane->crtc = crtc; plane->fb = crtc->fb; - exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); - if (!dmabuf_sync_is_supported()) return 0; @@ -415,21 +423,24 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) drm_object_attach_property(&crtc->base, prop, 0); } -int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) +int exynos_drm_crtc_create(struct exynos_drm_manager *manager) { struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_private *private = dev->dev_private; + struct exynos_drm_private *private = manager->drm_dev->dev_private; struct drm_crtc *crtc; exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); if (!exynos_crtc) return -ENOMEM; - exynos_crtc->pipe = nr; - exynos_crtc->dpms = DRM_MODE_DPMS_OFF; init_waitqueue_head(&exynos_crtc->pending_flip_queue); atomic_set(&exynos_crtc->pending_flip, 0); - exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); + + exynos_crtc->dpms = DRM_MODE_DPMS_OFF; + exynos_crtc->manager = manager; + exynos_crtc->pipe = manager->pipe; + exynos_crtc->plane = exynos_plane_init(manager->drm_dev, + 1 << manager->pipe, true); if (!exynos_crtc->plane) { kfree(exynos_crtc); return -ENOMEM; @@ -439,9 +450,9 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) crtc = &exynos_crtc->drm_crtc; - private->crtc[nr] = crtc; + private->crtc[manager->pipe] = crtc; - drm_crtc_init(dev, crtc, &exynos_crtc_funcs); + drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs); drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); exynos_drm_crtc_attach_mode_property(crtc); @@ -449,39 +460,41 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) return 0; } -int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) +int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe) { struct exynos_drm_private *private = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = - to_exynos_crtc(private->crtc[crtc]); + to_exynos_crtc(private->crtc[pipe]); + struct exynos_drm_manager *manager = exynos_crtc->manager; if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) return -EPERM; - exynos_drm_fn_encoder(private->crtc[crtc], &crtc, - exynos_drm_enable_vblank); + if (manager->ops->enable_vblank) + manager->ops->enable_vblank(manager); return 0; } -void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) +void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe) { struct exynos_drm_private *private = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = - to_exynos_crtc(private->crtc[crtc]); + to_exynos_crtc(private->crtc[pipe]); + struct exynos_drm_manager *manager = exynos_crtc->manager; if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) return; - exynos_drm_fn_encoder(private->crtc[crtc], &crtc, - exynos_drm_disable_vblank); + if (manager->ops->disable_vblank) + manager->ops->disable_vblank(manager); } -void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) +void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe) { struct exynos_drm_private *dev_priv = dev->dev_private; struct drm_pending_vblank_event *e, *t; - struct drm_crtc *drm_crtc = dev_priv->crtc[crtc]; + struct drm_crtc *drm_crtc = dev_priv->crtc[pipe]; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc); unsigned long flags; @@ -503,7 +516,7 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, base.link) { /* if event's pipe isn't same as crtc then ignore it. */ - if (crtc != e->pipe) + if (pipe != e->pipe) continue; if (e->event.reserved && dmabuf_sync_is_supported()) { @@ -517,10 +530,66 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) list_del(&e->base.link); drm_send_vblank_event(dev, -1, e); - drm_vblank_put(dev, crtc); + drm_vblank_put(dev, pipe); atomic_set(&exynos_crtc->pending_flip, 0); wake_up(&exynos_crtc->pending_flip_queue); } spin_unlock_irqrestore(&dev->event_lock, flags); } + +void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc, + struct exynos_drm_overlay *overlay) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + + if (manager->ops->win_mode_set) + manager->ops->win_mode_set(manager, overlay); +} + +void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + + if (manager->ops->win_commit) + manager->ops->win_commit(manager, zpos); +} + +void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + + if (manager->ops->win_enable) + manager->ops->win_enable(manager, zpos); +} + +void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + + if (manager->ops->win_disable) + manager->ops->win_disable(manager, zpos); +} + +void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) +{ + struct exynos_drm_manager *manager; + struct drm_device *dev = fb->dev; + struct drm_crtc *crtc; + + /* + * make sure that overlay data are updated to real hardware + * for all encoders. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + manager = to_exynos_crtc(crtc)->manager; + + /* + * wait for vblank interrupt + * - this makes sure that overlay data are updated to + * real hardware. + */ + if (manager->ops->wait_for_vblank) + manager->ops->wait_for_vblank(manager); + } +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 3e197e6ae7d..c27b66cc5d2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -15,9 +15,21 @@ #ifndef _EXYNOS_DRM_CRTC_H_ #define _EXYNOS_DRM_CRTC_H_ -int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr); -int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); -void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); -void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc); +struct drm_device; +struct drm_crtc; +struct exynos_drm_manager; +struct exynos_drm_overlay; + +int exynos_drm_crtc_create(struct exynos_drm_manager *manager); +int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); +void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); +void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe); +void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb); + +void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc, + struct exynos_drm_overlay *overlay); +void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos); +void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos); +void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index ef659d7d654..0879c5c3d4a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -73,15 +73,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) exynos_drm_mode_config_init(dev); - /* - * EXYNOS4 is enough to have two CRTCs and each crtc would be used - * without dependency of hardware. - */ - for (nr = 0; nr < MAX_CRTC; nr++) { - ret = exynos_drm_crtc_create(dev, nr); - if (ret) - goto err_release_iommu_mapping; - } + ret = exynos_drm_initialize_managers(dev); + if (ret) + goto err_mode_config_cleanup; for (nr = 0; nr < MAX_PLANE; nr++) { struct drm_plane *plane; @@ -89,12 +83,16 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) plane = exynos_plane_init(dev, possible_crtcs, false); if (!plane) - goto err_release_iommu_mapping; + goto err_manager_cleanup; } + ret = exynos_drm_initialize_displays(dev); + if (ret) + goto err_manager_cleanup; + ret = drm_vblank_init(dev, MAX_CRTC); if (ret) - goto err_release_iommu_mapping; + goto err_display_cleanup; /* * probe sub drivers such as display controller and hdmi driver, @@ -126,7 +124,12 @@ err_drm_device: exynos_drm_device_unregister(dev); err_vblank: drm_vblank_cleanup(dev); -err_release_iommu_mapping: +err_display_cleanup: + exynos_drm_remove_displays(dev); +err_manager_cleanup: + exynos_drm_remove_managers(dev); +err_mode_config_cleanup: + drm_mode_config_cleanup(dev); drm_release_iommu_mapping(dev); err_crtc: drm_mode_config_cleanup(dev); @@ -141,6 +144,8 @@ static int exynos_drm_unload(struct drm_device *dev) exynos_drm_device_unregister(dev); drm_vblank_cleanup(dev); drm_kms_helper_poll_fini(dev); + exynos_drm_remove_displays(dev); + exynos_drm_remove_managers(dev); drm_mode_config_cleanup(dev); drm_release_iommu_mapping(dev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index e31c088c3ed..dc730e5977d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -122,34 +122,68 @@ struct exynos_drm_overlay { * Exynos DRM Display Structure. * - this structure is common to analog tv, digital tv and lcd panel. * - * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. * @initialize: initializes the display with drm_dev + * @remove: cleans up the display for removal * @is_connected: check for that display is connected or not. + * @get_max_resol: get maximum resolution to specific hardware. * @get_edid: get edid modes from display driver. * @get_panel: get panel object from display driver. + * @mode_fixup: fix mode data comparing to hw specific display mode. + * @mode_set: convert drm_display_mode to hw specific display mode and + * would be called by encoder->mode_set(). * @check_mode: check if mode is valid or not. * @dpms: display device on or off. + * @commit: apply changes to hw */ +struct exynos_drm_display; struct exynos_drm_display_ops { + int (*initialize)(struct exynos_drm_display *display, + struct drm_device *drm_dev); + void (*remove)(struct exynos_drm_display *display); + bool (*is_connected)(struct exynos_drm_display *display); + void (*get_max_resol)(struct exynos_drm_display *display, + unsigned int *width, + unsigned int *height); + struct edid *(*get_edid)(struct exynos_drm_display *display, + struct drm_connector *connector); + void *(*get_panel)(struct exynos_drm_display *display); + void (*mode_fixup)(struct exynos_drm_display *display, + struct drm_connector *connector, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + void (*mode_set)(struct exynos_drm_display *display, + struct drm_display_mode *mode); + int (*check_mode)(struct exynos_drm_display *display, + struct drm_display_mode *mode); + void (*dpms)(struct exynos_drm_display *display, int mode); + void (*commit)(struct exynos_drm_display *display); +}; + +/* + * Exynos drm display structure, maps 1:1 with an encoder/connector + * + * @list: the list entry for this manager + * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. + * @encoder: encoder object this display maps to + * @connector: connector object this display maps to + * @ops: pointer to callbacks for exynos drm specific functionality + * @ctx: A pointer to the display's implementation specific context + */ +struct exynos_drm_display { + struct list_head list; enum exynos_drm_output_type type; - int (*initialize)(struct device *dev, struct drm_device *drm_dev); - bool (*is_connected)(struct device *dev); - struct edid *(*get_edid)(struct device *dev, - struct drm_connector *connector); - void *(*get_panel)(struct device *dev); - int (*check_mode)(struct device *dev, struct drm_display_mode *mode); - int (*dpms)(struct device *dev, int mode); + struct drm_encoder *encoder; + struct drm_connector *connector; + struct exynos_drm_display_ops *ops; + void *ctx; }; /* * Exynos drm manager ops * * @initialize: initializes the manager with drm_dev + * @remove: cleans up the manager for removal * @dpms: control device power. - * @mode_fixup: fix mode data comparing to hw specific display mode. - * @mode_set: convert drm_display_mode to hw specific display mode and - * would be called by encoder->mode_set(). - * @get_max_resol: get maximum resolution to specific hardware. * @commit: set current hw specific display mode to hw. * @enable_vblank: specific driver callback for enabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt. @@ -163,15 +197,9 @@ struct exynos_drm_display_ops { struct exynos_drm_manager; struct exynos_drm_manager_ops { int (*initialize)(struct exynos_drm_manager *mgr, - struct drm_device *drm_dev); + struct drm_device *drm_dev, int pipe); + void (*remove)(struct exynos_drm_manager *mgr); void (*dpms)(struct exynos_drm_manager *mgr, int mode); - void (*mode_fixup)(struct exynos_drm_manager *mgr, - struct drm_connector *connector, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); - void (*mode_set)(struct exynos_drm_manager *mgr, void *mode); - void (*get_max_resol)(struct exynos_drm_manager *mgr, - unsigned int *width, unsigned int *height); void (*commit)(struct exynos_drm_manager *mgr); int (*enable_vblank)(struct exynos_drm_manager *mgr); void (*disable_vblank)(struct exynos_drm_manager *mgr); @@ -184,25 +212,21 @@ struct exynos_drm_manager_ops { }; /* - * Exynos drm common manager structure. + * Exynos drm common manager structure, maps 1:1 with a crtc * - * @dev: pointer to device object for subdrv device driver. - * sub drivers such as display controller or hdmi driver, - * have their own device object. - * @ops: pointer to callbacks for exynos drm specific framebuffer. - * these callbacks should be set by specific drivers such fimd - * or hdmi driver and are used to control hardware global registers. - * @display: pointer to callbacks for exynos drm specific framebuffer. - * these callbacks should be set by specific drivers such fimd - * or hdmi driver and are used to control display devices such as - * analog tv, digital tv and lcd panel and also get timing data for them. + * @list: the list entry for this manager + * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. + * @drm_dev: pointer to the drm device + * @pipe: the pipe number for this crtc/manager + * @ops: pointer to callbacks for exynos drm specific functionality * @ctx: A pointer to the manager's implementation specific context */ struct exynos_drm_manager { - struct device *dev; + struct list_head list; + enum exynos_drm_output_type type; + struct drm_device *drm_dev; int pipe; struct exynos_drm_manager_ops *ops; - struct exynos_drm_display_ops *display_ops; void *ctx; }; @@ -267,14 +291,11 @@ struct exynos_drm_private { * by probe callback. * @open: this would be called with drm device file open. * @close: this would be called with drm device file close. - * @encoder: encoder object owned by this sub driver. - * @connector: connector object owned by this sub driver. */ struct exynos_drm_subdrv { struct list_head list; struct device *dev; struct drm_device *drm_dev; - struct exynos_drm_manager *manager; int (*probe)(struct drm_device *drm_dev, struct device *dev); void (*remove)(struct drm_device *drm_dev, struct device *dev); @@ -282,9 +303,6 @@ struct exynos_drm_subdrv { struct drm_file *file); void (*close)(struct drm_device *drm_dev, struct device *dev, struct drm_file *file); - - struct drm_encoder *encoder; - struct drm_connector *connector; }; /* @@ -299,6 +317,16 @@ int exynos_drm_device_register(struct drm_device *dev); */ int exynos_drm_device_unregister(struct drm_device *dev); +int exynos_drm_initialize_managers(struct drm_device *dev); +void exynos_drm_remove_managers(struct drm_device *dev); +int exynos_drm_initialize_displays(struct drm_device *dev); +void exynos_drm_remove_displays(struct drm_device *dev); + +int exynos_drm_manager_register(struct exynos_drm_manager *manager); +int exynos_drm_manager_unregister(struct exynos_drm_manager *manager); +int exynos_drm_display_register(struct exynos_drm_display *display); +int exynos_drm_display_unregister(struct exynos_drm_display *display); + /* * this function would be called by sub drivers such as display controller * or hdmi driver to register this sub driver object to exynos drm driver diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index efe4e6000ab..d4ae664a051 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -26,24 +26,23 @@ * exynos specific encoder structure. * * @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. + * @display: the display structure that maps to this encoder */ struct exynos_drm_encoder { struct drm_crtc *old_crtc; struct drm_encoder drm_encoder; - struct exynos_drm_manager *manager; + struct exynos_drm_display *display; }; static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) { - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct exynos_drm_display_ops *display_ops = manager->display_ops; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + struct exynos_drm_display *display = exynos_encoder->display; DRM_DEBUG_KMS("encoder dpms: %d\n", mode); - if (display_ops && display_ops->dpms) - display_ops->dpms(manager->ctx, mode); + if (display->ops->dpms) + display->ops->dpms(display, mode); } static bool @@ -52,15 +51,17 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + struct exynos_drm_display *display = exynos_encoder->display; struct drm_connector *connector; - struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); - struct exynos_drm_manager_ops *manager_ops = manager->ops; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) - if (manager_ops && manager_ops->mode_fixup) - manager_ops->mode_fixup(manager, connector, - mode, adjusted_mode); + if (connector->encoder != encoder) + continue; + + if (display->ops->mode_fixup) + display->ops->mode_fixup(display, connector, mode, + adjusted_mode); } return true; @@ -102,8 +103,7 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_connector *connector; - struct exynos_drm_manager *manager; - struct exynos_drm_manager_ops *manager_ops; + struct exynos_drm_display *display; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->encoder == encoder) { @@ -123,11 +123,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, encoder->crtc); } - manager = exynos_drm_get_manager(encoder); - manager_ops = manager->ops; + display = exynos_encoder->display; - if (manager_ops && manager_ops->mode_set) - manager_ops->mode_set(manager, adjusted_mode); + if (display->ops->mode_set) + display->ops->mode_set(display, + adjusted_mode); exynos_encoder->old_crtc = encoder->crtc; } @@ -142,39 +142,15 @@ static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) static void exynos_drm_encoder_commit(struct drm_encoder *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; - - if (manager_ops && manager_ops->commit) - manager_ops->commit(manager); -} + struct exynos_drm_display *display = exynos_encoder->display; -void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb) -{ - struct exynos_drm_encoder *exynos_encoder; - struct exynos_drm_manager_ops *ops; - struct drm_device *dev = fb->dev; - struct drm_encoder *encoder; + if (display->ops->dpms) + display->ops->dpms(display, DRM_MODE_DPMS_ON); - /* - * make sure that overlay data are updated to real hardware - * for all encoders. - */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - exynos_encoder = to_exynos_encoder(encoder); - ops = exynos_encoder->manager->ops; - - /* - * wait for vblank interrupt - * - this makes sure that overlay data are updated to - * real hardware. - */ - if (ops->wait_for_vblank) - ops->wait_for_vblank(exynos_encoder->manager); - } + if (display->ops->commit) + display->ops->commit(display); } - static void exynos_drm_encoder_disable(struct drm_encoder *encoder) { struct drm_plane *plane; @@ -200,10 +176,7 @@ static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) { - struct exynos_drm_encoder *exynos_encoder = - to_exynos_encoder(encoder); - - exynos_encoder->manager->pipe = -1; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); drm_encoder_cleanup(encoder); kfree(exynos_encoder); @@ -218,13 +191,12 @@ 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; + struct exynos_drm_display *display = exynos_encoder->display; unsigned int clone_mask = 0; int cnt = 0; list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { - switch (display_ops->type) { + switch (display->type) { case EXYNOS_DISPLAY_TYPE_LCD: case EXYNOS_DISPLAY_TYPE_HDMI: case EXYNOS_DISPLAY_TYPE_VIDI: @@ -248,24 +220,20 @@ void exynos_drm_encoder_setup(struct drm_device *dev) struct drm_encoder * exynos_drm_encoder_create(struct drm_device *dev, - struct exynos_drm_manager *manager, + struct exynos_drm_display *display, unsigned long possible_crtcs) { struct drm_encoder *encoder; struct exynos_drm_encoder *exynos_encoder; - int ret; - if (!manager || !possible_crtcs) - return NULL; - - if (!manager->dev) + if (!possible_crtcs) return NULL; exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); if (!exynos_encoder) return NULL; - exynos_encoder->manager = manager; + exynos_encoder->display = display; encoder = &exynos_encoder->drm_encoder; encoder->possible_crtcs = possible_crtcs; @@ -276,174 +244,12 @@ exynos_drm_encoder_create(struct drm_device *dev, drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); - if (manager->ops && manager->ops->initialize) { - ret = manager->ops->initialize(manager, dev); - if (ret) { - DRM_ERROR("Manager initialize failed %d\n", ret); - goto error; - } - } - - if (manager->display_ops && manager->display_ops->initialize) { - ret = manager->display_ops->initialize(manager->dev, dev); - if (ret) { - DRM_ERROR("Display initialize failed %d\n", ret); - goto error; - } - } - DRM_DEBUG_KMS("encoder has been created\n"); return encoder; - -error: - exynos_drm_encoder_destroy(&exynos_encoder->drm_encoder); - return NULL; } -struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder) +struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder) { - return to_exynos_encoder(encoder)->manager; -} - -void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, - void (*fn)(struct drm_encoder *, void *)) -{ - 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 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); - } -} - -void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int crtc = *(int *)data; - - if (manager->pipe != crtc) - return; - - if (manager_ops->enable_vblank) - manager_ops->enable_vblank(manager); -} - -void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int crtc = *(int *)data; - - if (manager->pipe != crtc) - return; - - if (manager_ops->disable_vblank) - manager_ops->disable_vblank(manager); -} - -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) -{ - 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; - int mode = *(int *)data; - - if (manager_ops && manager_ops->dpms) - manager_ops->dpms(manager, 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_pipe(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - int pipe = *(int *)data; - - /* - * when crtc is detached from encoder, this pipe is used - * to select manager operation - */ - manager->pipe = pipe; -} - -void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct exynos_drm_overlay *overlay = data; - - if (manager_ops && manager_ops->win_mode_set) - manager_ops->win_mode_set(manager, overlay); -} - -void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int zpos = DEFAULT_ZPOS; - - if (data) - zpos = *(int *)data; - - if (manager_ops && manager_ops->win_commit) - manager_ops->win_commit(manager, zpos); -} - -void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int zpos = DEFAULT_ZPOS; - - if (data) - zpos = *(int *)data; - - if (manager_ops && manager_ops->win_enable) - manager_ops->win_enable(manager, zpos); -} - -void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) -{ - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - int zpos = DEFAULT_ZPOS; - - if (data) - zpos = *(int *)data; - - if (manager_ops && manager_ops->win_disable) - manager_ops->win_disable(manager, zpos); + return to_exynos_encoder(encoder)->display; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h index 0f3e5e29df4..b7a1620a7e7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -18,20 +18,8 @@ struct exynos_drm_manager; void exynos_drm_encoder_setup(struct drm_device *dev); struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, - struct exynos_drm_manager *mgr, - unsigned long possible_crtcs); -struct exynos_drm_manager * -exynos_drm_get_manager(struct drm_encoder *encoder); -void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, - void (*fn)(struct drm_encoder *, void *)); -void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); -void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb); + struct exynos_drm_display *mgr, + unsigned long possible_crtcs); +struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 55300593d37..167538376da 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -22,7 +22,7 @@ #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" #include "exynos_drm_iommu.h" -#include "exynos_drm_encoder.h" +#include "exynos_drm_crtc.h" #include <linux/dmabuf-sync.h> @@ -58,7 +58,7 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) unsigned int i; /* make sure that overlay data are updated before relesing fb. */ - exynos_drm_encoder_complete_scanout(fb); + exynos_drm_crtc_complete_scanout(fb); drm_framebuffer_cleanup(fb); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 027de42c5b4..a2a8a5fe035 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -23,9 +23,9 @@ #include <linux/regmap.h> #include <video/of_display_timing.h> +#include <video/of_videomode.h> #include <video/samsung_fimd.h> #include <drm/exynos_drm.h> -#include <drm/drm_backlight.h> #include "exynos_drm_drv.h" #include "exynos_drm_fbdev.h" @@ -33,12 +33,14 @@ #include "exynos_drm_iommu.h" /* - * FIMD is stand for Fully Interactive Mobile Display and + * FIMD stands for Fully Interactive Mobile Display and * as a display controller, it transfers contents drawn on memory * to a LCD Panel through Display Interfaces such as RGB or * CPU Interface. */ +#define FIMD_DEFAULT_FRAMERATE 60 + /* position control register for hardware window 0, 2 ~ 4.*/ #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) @@ -111,7 +113,6 @@ struct fimd_win_data { }; struct fimd_context { - struct exynos_drm_subdrv subdrv; struct device *dev; struct drm_device *drm_dev; int irq; @@ -127,15 +128,15 @@ struct fimd_context { u32 vidcon0; u32 vidcon1; bool suspended; + int pipe; struct mutex lock; wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; - struct exynos_drm_panel_info *panel; + struct exynos_drm_panel_info panel; struct fimd_driver_data *driver_data; }; -#ifdef CONFIG_OF static const struct of_device_id fimd_driver_dt_match[] = { { .compatible = "samsung,s3c6400-fimd", .data = &s3c64xx_fimd_driver_data }, @@ -145,39 +146,32 @@ static const struct of_device_id fimd_driver_dt_match[] = { .data = &exynos5_fimd_driver_data }, {}, }; -#endif static inline struct fimd_driver_data *drm_fimd_get_driver_data( struct platform_device *pdev) { -#ifdef CONFIG_OF const struct of_device_id *of_id = of_match_device(fimd_driver_dt_match, &pdev->dev); - if (of_id) - return (struct fimd_driver_data *)of_id->data; -#endif - - return (struct fimd_driver_data *) - platform_get_device_id(pdev)->driver_data; + return (struct fimd_driver_data *)of_id->data; } -static bool fimd_display_is_connected(struct device *dev) +static bool fimd_display_is_connected(struct exynos_drm_display *display) { /* TODO. */ return true; } -static void *fimd_get_panel(struct device *dev) +static void *fimd_get_panel(struct exynos_drm_display *display) { - struct exynos_drm_manager *mgr = get_fimd_manager(dev); - struct fimd_context *ctx = mgr->ctx; + struct fimd_context *ctx = display->ctx; - return ctx->panel; + return &ctx->panel; } -static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode) +static int fimd_check_mode(struct exynos_drm_display *display, + struct drm_display_mode *mode) { /* TODO. */ @@ -185,34 +179,62 @@ static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode) } static struct exynos_drm_display_ops fimd_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_LCD, .is_connected = fimd_display_is_connected, .get_panel = fimd_get_panel, .check_mode = fimd_check_mode, }; -static void fimd_apply(struct exynos_drm_manager *mgr) +static struct exynos_drm_display fimd_display = { + .type = EXYNOS_DISPLAY_TYPE_LCD, + .ops = &fimd_display_ops, +}; + +static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, + struct drm_device *drm_dev, int pipe) { struct fimd_context *ctx = mgr->ctx; - struct exynos_drm_manager_ops *mgr_ops = mgr->ops; - struct fimd_win_data *win_data; - int i; - for (i = 0; i < WINDOWS_NR; i++) { - win_data = &ctx->win_data[i]; - if (win_data->enabled && (mgr_ops && mgr_ops->win_commit)) - mgr_ops->win_commit(mgr, i); - } + ctx->drm_dev = drm_dev; + ctx->pipe = pipe; + + /* + * enable drm irq mode. + * - with irq_enabled = true, 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; - if (mgr_ops && mgr_ops->commit) - mgr_ops->commit(mgr); + /* + * with vblank_disable_allowed = true, 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; + + /* attach this sub driver to iommu mapping if supported. */ + if (is_drm_iommu_supported(ctx->drm_dev)) + drm_iommu_attach_device(ctx->drm_dev, ctx->dev); + + return 0; +} + +static void fimd_mgr_remove(struct exynos_drm_manager *mgr) +{ + struct fimd_context *ctx = mgr->ctx; + + /* detach this sub driver from iommu mapping if supported. */ + if (is_drm_iommu_supported(ctx->drm_dev)) + drm_iommu_detach_device(ctx->drm_dev, ctx->dev); } static void fimd_commit(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; - struct exynos_drm_panel_info *panel = ctx->panel; - struct fb_videomode *timing = &panel->timing; + struct exynos_drm_panel_info *panel = &ctx->panel; + struct videomode *vm = &panel->vm; struct fimd_driver_data *driver_data; u32 val; int ret; @@ -234,22 +256,22 @@ static void fimd_commit(struct exynos_drm_manager *mgr) writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); /* setup vertical timing values. */ - val = VIDTCON0_VBPD(timing->upper_margin - 1) | - VIDTCON0_VFPD(timing->lower_margin - 1) | - VIDTCON0_VSPW(timing->vsync_len - 1); + val = VIDTCON0_VBPD(vm->vback_porch - 1) | + VIDTCON0_VFPD(vm->vfront_porch - 1) | + VIDTCON0_VSPW(vm->vsync_len - 1); writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); /* setup horizontal timing values. */ - val = VIDTCON1_HBPD(timing->left_margin - 1) | - VIDTCON1_HFPD(timing->right_margin - 1) | - VIDTCON1_HSPW(timing->hsync_len - 1); + val = VIDTCON1_HBPD(vm->hback_porch - 1) | + VIDTCON1_HFPD(vm->hfront_porch - 1) | + VIDTCON1_HSPW(vm->hsync_len - 1); writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); /* setup horizontal and vertical display size. */ - val = VIDTCON2_LINEVAL(timing->yres - 1) | - VIDTCON2_HOZVAL(timing->xres - 1) | - VIDTCON2_LINEVAL_E(timing->yres - 1) | - VIDTCON2_HOZVAL_E(timing->xres - 1); + val = VIDTCON2_LINEVAL(vm->vactive - 1) | + VIDTCON2_HOZVAL(vm->hactive - 1) | + VIDTCON2_LINEVAL_E(vm->vactive - 1) | + VIDTCON2_HOZVAL_E(vm->hactive - 1); writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); /* setup clock source, clock divider, enable dma. */ @@ -332,12 +354,12 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) */ if (!wait_event_timeout(ctx->wait_vsync_queue, !atomic_read(&ctx->wait_vsync_event), - DRM_HZ/20)) + HZ/20)) DRM_DEBUG_KMS("vblank wait timed out.\n"); } static void fimd_win_mode_set(struct exynos_drm_manager *mgr, - struct exynos_drm_overlay *overlay) + struct exynos_drm_overlay *overlay) { struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; @@ -353,7 +375,7 @@ static void fimd_win_mode_set(struct exynos_drm_manager *mgr, if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; offset = overlay->fb_x * (overlay->bpp >> 3); @@ -497,7 +519,7 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; win_data = &ctx->win_data[win]; @@ -612,7 +634,7 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; win_data = &ctx->win_data[win]; @@ -643,11 +665,60 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) win_data->enabled = false; } +static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) +{ + struct fimd_context *ctx = mgr->ctx; + + DRM_DEBUG_KMS("%d\n", mode); + + mutex_lock(&ctx->lock); + + switch (mode) { + case DRM_MODE_DPMS_ON: + /* + * enable fimd hardware only if suspended status. + * + * P.S. fimd_dpms function would be called at booting time so + * clk_enable could be called double time. + */ + if (ctx->suspended) + pm_runtime_get_sync(ctx->dev); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + if (!ctx->suspended) + pm_runtime_put_sync(ctx->dev); + break; + default: + DRM_DEBUG_KMS("unspecified mode %d\n", mode); + break; + } + + mutex_unlock(&ctx->lock); +} + +static struct exynos_drm_manager_ops fimd_manager_ops = { + .initialize = fimd_mgr_initialize, + .remove = fimd_mgr_remove, + .dpms = fimd_dpms, + .commit = fimd_commit, + .enable_vblank = fimd_enable_vblank, + .disable_vblank = fimd_disable_vblank, + .wait_for_vblank = fimd_wait_for_vblank, + .win_mode_set = fimd_win_mode_set, + .win_commit = fimd_win_commit, + .win_disable = fimd_win_disable, +}; + +static struct exynos_drm_manager fimd_manager = { + .type = EXYNOS_DISPLAY_TYPE_LCD, + .ops = &fimd_manager_ops, +}; + static irqreturn_t fimd_irq_handler(int irq, void *dev_id) { struct fimd_context *ctx = (struct fimd_context *)dev_id; - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct exynos_drm_manager *manager = subdrv->manager; u32 val; val = readl(ctx->regs + VIDINTCON1); @@ -657,111 +728,70 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); /* check the crtc is detached already from encoder */ - if (manager->pipe < 0 || !ctx->drm_dev) + if (ctx->pipe < 0 || !ctx->drm_dev) goto out; - drm_handle_vblank(ctx->drm_dev, manager->pipe); - exynos_drm_crtc_finish_pageflip(ctx->drm_dev, manager->pipe); + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); /* set wait vsync event to zero and wake up queue. */ if (atomic_read(&ctx->wait_vsync_event)) { atomic_set(&ctx->wait_vsync_event, 0); - DRM_WAKEUP(&ctx->wait_vsync_queue); + wake_up(&ctx->wait_vsync_queue); } out: return IRQ_HANDLED; } -static void fimd_clear_channel(struct device *dev) -{ - struct exynos_drm_manager *mgr = get_fimd_manager(dev); - struct fimd_context *ctx = mgr->ctx; - int win, ch_enabled = 0; - DRM_DEBUG_KMS("%s\n", __FILE__); +static int fimd_configure_clocks(struct fimd_context *ctx, struct device *dev) +{ + struct videomode *vm = &ctx->panel.vm; + unsigned long clk; - /* Check if any channel is enabled */ - for (win = 0; win < WINDOWS_NR; win++) { - u32 val = readl(ctx->regs + SHADOWCON); - if (val & SHADOWCON_CHx_ENABLE(win)) { - val &= ~SHADOWCON_CHx_ENABLE(win); - writel(val, ctx->regs + SHADOWCON); - ch_enabled = 1; - } + ctx->bus_clk = devm_clk_get(dev, "fimd"); + if (IS_ERR(ctx->bus_clk)) { + dev_err(dev, "failed to get bus clock\n"); + return PTR_ERR(ctx->bus_clk); } - /* Wait for vsync, as disable channel takes effect at next vsync */ - if (ch_enabled) - fimd_wait_for_vblank(mgr); -} - -static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) -{ - /* - * 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 = 1; - - /* attach this sub driver to iommu mapping if supported. */ - if (is_drm_iommu_supported(drm_dev)) { - /* - * If any channel is already active, iommu will throw - * a PAGE FAULT when enabled. So clear any channel if enabled. - */ - fimd_clear_channel(dev); - drm_iommu_attach_device(drm_dev, dev); + ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); + if (IS_ERR(ctx->lcd_clk)) { + dev_err(dev, "failed to get lcd clock\n"); + return PTR_ERR(ctx->lcd_clk); } - return 0; -} -static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev) -{ - /* detach this sub driver from iommu mapping if supported. */ - if (is_drm_iommu_supported(drm_dev)) - drm_iommu_detach_device(drm_dev, dev); -} + clk = clk_get_rate(ctx->lcd_clk); + if (clk == 0) { + dev_err(dev, "error getting sclk_fimd clock rate\n"); + return -EINVAL; + } -static int fimd_calc_clkdiv(struct fimd_context *ctx, - struct fb_videomode *timing) -{ - unsigned long clk = clk_get_rate(ctx->lcd_clk); - u32 retrace; - u32 clkdiv; - u32 best_framerate = 0; - u32 framerate; - - retrace = timing->left_margin + timing->hsync_len + - timing->right_margin + timing->xres; - retrace *= timing->upper_margin + timing->vsync_len + - timing->lower_margin + timing->yres; - - /* default framerate is 60Hz */ - if (!timing->refresh) - timing->refresh = 60; - - clk /= retrace; - - for (clkdiv = 1; clkdiv < 0x100; clkdiv++) { - int tmp; - - /* get best framerate */ - framerate = clk / clkdiv; - tmp = timing->refresh - framerate; - if (tmp < 0) { - best_framerate = framerate; - continue; - } else { - if (!best_framerate) - best_framerate = framerate; - else if (tmp < (best_framerate - framerate)) - best_framerate = framerate; - break; + if (vm->pixelclock == 0) { + unsigned long c; + c = vm->hactive + vm->hback_porch + vm->hfront_porch + + vm->hsync_len; + c *= vm->vactive + vm->vback_porch + vm->vfront_porch + + vm->vsync_len; + vm->pixelclock = c * FIMD_DEFAULT_FRAMERATE; + if (vm->pixelclock == 0) { + dev_err(dev, "incorrect display timings\n"); + return -EINVAL; } + dev_warn(dev, "pixel clock recalculated to %luHz (%dHz frame rate)\n", + vm->pixelclock, FIMD_DEFAULT_FRAMERATE); + } + ctx->clkdiv = DIV_ROUND_UP(clk, vm->pixelclock); + if (ctx->clkdiv > 256) { + dev_warn(dev, "calculated pixel clock divider too high (%u), lowered to 256\n", + ctx->clkdiv); + ctx->clkdiv = 256; } + vm->pixelclock = clk / ctx->clkdiv; + DRM_DEBUG_KMS("pixel clock = %lu, clkdiv = %d\n", vm->pixelclock, + ctx->clkdiv); - return clkdiv; + return 0; } static void fimd_clear_win(struct fimd_context *ctx, int win) @@ -799,9 +829,8 @@ static int fimd_clock(struct fimd_context *ctx, bool enable) return 0; } -static void fimd_window_suspend(struct device *dev) +static void fimd_window_suspend(struct exynos_drm_manager *mgr) { - struct exynos_drm_manager *mgr = get_fimd_manager(dev); struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int i; @@ -814,9 +843,8 @@ static void fimd_window_suspend(struct device *dev) fimd_wait_for_vblank(mgr); } -static void fimd_window_resume(struct device *dev) +static void fimd_window_resume(struct exynos_drm_manager *mgr) { - struct exynos_drm_manager *mgr = get_fimd_manager(dev); struct fimd_context *ctx = mgr->ctx; struct fimd_win_data *win_data; int i; @@ -828,10 +856,25 @@ static void fimd_window_resume(struct device *dev) } } +static void fimd_apply(struct exynos_drm_manager *mgr) +{ + struct fimd_context *ctx = mgr->ctx; + struct fimd_win_data *win_data; + int i; + + for (i = 0; i < WINDOWS_NR; i++) { + win_data = &ctx->win_data[i]; + if (win_data->enabled) + fimd_win_commit(mgr, i); + } + + fimd_commit(mgr); +} + static int fimd_activate(struct exynos_drm_manager *mgr, bool enable) { struct fimd_context *ctx = mgr->ctx; - struct device *dev = ctx->subdrv.dev; + if (enable) { int ret; @@ -845,11 +888,11 @@ static int fimd_activate(struct exynos_drm_manager *mgr, bool enable) if (test_and_clear_bit(0, &ctx->irq_flags)) fimd_enable_vblank(mgr); - fimd_window_resume(dev); + fimd_window_resume(mgr); fimd_apply(mgr); } else { - fimd_window_suspend(dev); + fimd_window_suspend(mgr); fimd_clock(ctx, false); ctx->suspended = true; @@ -858,129 +901,66 @@ static int fimd_activate(struct exynos_drm_manager *mgr, bool enable) return 0; } -static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, - struct drm_device *drm_dev) +static int fimd_get_platform_data(struct fimd_context *ctx, struct device *dev) { - struct fimd_context *ctx = mgr->ctx; - - ctx->drm_dev = drm_dev; - - return 0; -} - -static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) -{ - struct fimd_context *ctx = mgr->ctx; - - DRM_DEBUG_KMS("%d\n", mode); - - mutex_lock(&ctx->lock); - - switch (mode) { - case DRM_MODE_DPMS_ON: - /* - * enable fimd hardware only if suspended status. - * - * P.S. fimd_dpms function would be called at booting time so - * clk_enable could be called double time. - */ - if (ctx->suspended) - pm_runtime_get_sync(ctx->dev); - - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - if (!ctx->suspended) - pm_runtime_put_sync(ctx->dev); + struct videomode *vm; + int ret; - break; - default: - DRM_DEBUG_KMS("unspecified mode %d\n", mode); - break; + vm = &ctx->panel.vm; + ret = of_get_videomode(dev->of_node, vm, OF_USE_NATIVE_MODE); + if (ret) { + DRM_ERROR("failed: of_get_videomode() : %d\n", ret); + return ret; } - mutex_unlock(&ctx->lock); -} + if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) + ctx->vidcon1 |= VIDCON1_INV_VSYNC; + if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) + ctx->vidcon1 |= VIDCON1_INV_HSYNC; + if (vm->flags & DISPLAY_FLAGS_DE_LOW) + ctx->vidcon1 |= VIDCON1_INV_VDEN; + if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + ctx->vidcon1 |= VIDCON1_INV_VCLK; -static struct exynos_drm_manager_ops fimd_manager_ops = { - .initialize = fimd_mgr_initialize, - .dpms = fimd_dpms, - .commit = fimd_commit, - .enable_vblank = fimd_enable_vblank, - .disable_vblank = fimd_disable_vblank, - .wait_for_vblank = fimd_wait_for_vblank, - .win_mode_set = fimd_win_mode_set, - .win_commit = fimd_win_commit, - .win_disable = fimd_win_disable, -}; - -static struct exynos_drm_manager fimd_manager = { - .pipe = -1, - .ops = &fimd_manager_ops, - .display_ops = &fimd_display_ops, -}; + return 0; +} static int fimd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fimd_context *ctx; - struct exynos_drm_subdrv *subdrv; - struct exynos_drm_fimd_pdata *pdata; - struct exynos_drm_panel_info *panel; struct device_node *display_np; struct resource *res; int win; int ret = -EINVAL; - if(!dev->of_node) + if (!dev->of_node) return -ENODEV; + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = dev; + + ret = fimd_get_platform_data(ctx, dev); + if (ret) + return ret; + display_np = of_parse_phandle(dev->of_node, "samsung,fimd-display", 0); if (!display_np) display_np = dev->of_node; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - ret = of_get_fb_videomode(display_np, - &pdata->panel.timing, OF_USE_NATIVE_MODE); - if (ret) { - DRM_ERROR("failed: of_get_fb_videomode() : %d\n", ret); - return ret; - } - /* FIXME */ of_property_read_u32(display_np, "samsung,panel-width-mm", - &pdata->panel.width_mm); + &ctx->panel.width_mm); of_property_read_u32(display_np, "samsung,panel-height-mm", - &pdata->panel.height_mm); - - panel = &pdata->panel; - if (!panel) { - dev_err(dev, "panel is null.\n"); - return -EINVAL; - } - - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = dev; + &ctx->panel.height_mm); - ctx->bus_clk = devm_clk_get(dev, "fimd"); - if (IS_ERR(ctx->bus_clk)) { - dev_err(dev, "failed to get bus clock\n"); - return PTR_ERR(ctx->bus_clk); - } - - ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd"); - if (IS_ERR(ctx->lcd_clk)) { - dev_err(dev, "failed to get lcd clock\n"); - return PTR_ERR(ctx->lcd_clk); - } + ret = fimd_configure_clocks(ctx, dev); + if (ret) + return ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1009,42 +989,24 @@ static int fimd_probe(struct platform_device *pdev) return PTR_ERR(ctx->sysreg); ctx->driver_data = drm_fimd_get_driver_data(pdev); - ctx->vidcon0 = pdata->vidcon0; - ctx->vidcon1 = pdata->vidcon1; - ctx->default_win = pdata->default_win; - ctx->panel = panel; - DRM_INIT_WAITQUEUE(&ctx->wait_vsync_queue); + init_waitqueue_head(&ctx->wait_vsync_queue); atomic_set(&ctx->wait_vsync_event, 0); - fimd_manager.ctx = ctx; - - subdrv = &ctx->subdrv; - - subdrv->dev = dev; - subdrv->manager = &fimd_manager; - subdrv->probe = fimd_subdrv_probe; - subdrv->remove = fimd_subdrv_remove; - mutex_init(&ctx->lock); platform_set_drvdata(pdev, &fimd_manager); - pm_runtime_enable(dev); - pm_runtime_get_sync(dev); + fimd_manager.ctx = ctx; + exynos_drm_manager_register(&fimd_manager); - ctx->clkdiv = fimd_calc_clkdiv(ctx, &panel->timing); - panel->timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; + fimd_display.ctx = ctx; + exynos_drm_display_register(&fimd_display); - DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", - panel->timing.pixclock, ctx->clkdiv); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); - for (win = 0; win < WINDOWS_NR; win++) { + for (win = 0; win < WINDOWS_NR; win++) fimd_clear_win(ctx, win); - fimd_win_disable(&fimd_manager, win); - } - fimd_wait_for_vblank(&fimd_manager); - - exynos_drm_subdrv_register(subdrv); return 0; } @@ -1055,7 +1017,8 @@ static int fimd_remove(struct platform_device *pdev) struct exynos_drm_manager *mgr = platform_get_drvdata(pdev); struct fimd_context *ctx = mgr->ctx; - exynos_drm_subdrv_unregister(&ctx->subdrv); + exynos_drm_display_unregister(&fimd_display); + exynos_drm_manager_unregister(&fimd_manager); if (ctx->suspended) goto out; @@ -1117,20 +1080,6 @@ static int fimd_runtime_resume(struct device *dev) } #endif -static struct platform_device_id fimd_driver_ids[] = { - { - .name = "s3c64xx-fb", - .driver_data = (unsigned long)&s3c64xx_fimd_driver_data, - }, { - .name = "exynos4-fb", - .driver_data = (unsigned long)&exynos4_fimd_driver_data, - }, { - .name = "exynos5-fb", - .driver_data = (unsigned long)&exynos5_fimd_driver_data, - }, - {}, -}; - static const struct dev_pm_ops fimd_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) @@ -1139,11 +1088,10 @@ static const struct dev_pm_ops fimd_pm_ops = { struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = fimd_remove, - .id_table = fimd_driver_ids, .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, .pm = &fimd_pm_ops, - .of_match_table = of_match_ptr(fimd_driver_dt_match), + .of_match_table = fimd_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index d3262af671d..e6ff3c0da07 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -23,11 +23,6 @@ #include "exynos_drm_drv.h" #include "exynos_drm_hdmi.h" -#define to_context(dev) platform_get_drvdata(to_platform_device(dev)) -#define to_subdrv(dev) to_context(dev) -#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\ - struct drm_hdmi_context, subdrv); - /* platform device pointer for common drm hdmi device. */ static struct platform_device *exynos_drm_hdmi_pdev; @@ -41,7 +36,6 @@ static struct exynos_hdmi_ops *hdmi_ops; static struct exynos_mixer_ops *mixer_ops; struct drm_hdmi_context { - struct exynos_drm_subdrv subdrv; struct exynos_drm_hdmi_context *hdmi_ctx; struct exynos_drm_hdmi_context *mixer_ctx; @@ -97,10 +91,10 @@ void exynos_mixer_ops_register(struct exynos_mixer_ops *ops) mixer_ops = ops; } -static int drm_hdmi_display_initialize(struct device *dev, +static int drm_hdmi_display_initialize(struct exynos_drm_display *display, struct drm_device *drm_dev) { - struct drm_hdmi_context *ctx = to_context(dev); + struct drm_hdmi_context *ctx = display->ctx; if (hdmi_ops && hdmi_ops->initialize) return hdmi_ops->initialize(ctx->hdmi_ctx->ctx, drm_dev); @@ -109,9 +103,9 @@ static int drm_hdmi_display_initialize(struct device *dev, } -static bool drm_hdmi_is_connected(struct device *dev) +static bool drm_hdmi_is_connected(struct exynos_drm_display *display) { - struct drm_hdmi_context *ctx = to_context(dev); + struct drm_hdmi_context *ctx = display->ctx; if (hdmi_ops && hdmi_ops->is_connected) return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx); @@ -119,10 +113,10 @@ static bool drm_hdmi_is_connected(struct device *dev) return false; } -static struct edid *drm_hdmi_get_edid(struct device *dev, +static struct edid *drm_hdmi_get_edid(struct exynos_drm_display *display, struct drm_connector *connector) { - struct drm_hdmi_context *ctx = to_context(dev); + struct drm_hdmi_context *ctx = display->ctx; if (hdmi_ops && hdmi_ops->get_edid) return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector); @@ -151,68 +145,28 @@ static int drm_hdmi_check_mode_ctx(struct drm_hdmi_context *ctx, return 0; } -static int drm_hdmi_check_mode(struct device *dev, +static int drm_hdmi_check_mode(struct exynos_drm_display *display, struct drm_display_mode *mode) { - struct drm_hdmi_context *ctx = to_context(dev); + struct drm_hdmi_context *ctx = display->ctx; return drm_hdmi_check_mode_ctx(ctx, mode); } -static int drm_hdmi_display_dpms(struct device *dev, int mode) +static void drm_hdmi_display_dpms(struct exynos_drm_display *display, int mode) { - struct drm_hdmi_context *ctx = to_context(dev); + struct drm_hdmi_context *ctx = display->ctx; if (hdmi_ops && hdmi_ops->dpms) hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode); - - return 0; } -static struct exynos_drm_display_ops drm_hdmi_display_ops = { - .type = EXYNOS_DISPLAY_TYPE_HDMI, - .initialize = drm_hdmi_display_initialize, - .is_connected = drm_hdmi_is_connected, - .get_edid = drm_hdmi_get_edid, - .check_mode = drm_hdmi_check_mode, - .dpms = drm_hdmi_display_dpms, -}; - -static int drm_hdmi_enable_vblank(struct exynos_drm_manager *mgr) -{ - struct drm_hdmi_context *ctx = mgr->ctx; - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct exynos_drm_manager *manager = subdrv->manager; - - if (mixer_ops && mixer_ops->enable_vblank) - return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, - manager->pipe); - - return 0; -} - -static void drm_hdmi_disable_vblank(struct exynos_drm_manager *mgr) -{ - struct drm_hdmi_context *ctx = mgr->ctx; - - if (mixer_ops && mixer_ops->disable_vblank) - return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); -} - -static void drm_hdmi_wait_for_vblank(struct exynos_drm_manager *mgr) -{ - struct drm_hdmi_context *ctx = mgr->ctx; - - if (mixer_ops && mixer_ops->wait_for_vblank) - mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); -} - -static void drm_hdmi_mode_fixup(struct exynos_drm_manager *mgr, +static void drm_hdmi_mode_fixup(struct exynos_drm_display *display, struct drm_connector *connector, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct drm_hdmi_context *ctx = mgr->ctx; + struct drm_hdmi_context *ctx = display->ctx; struct drm_display_mode *m; int mode_ok; @@ -252,23 +206,66 @@ static void drm_hdmi_mode_fixup(struct exynos_drm_manager *mgr, } } -static void drm_hdmi_mode_set(struct exynos_drm_manager *mgr, void *mode) +static void drm_hdmi_mode_set(struct exynos_drm_display *display, + struct drm_display_mode *mode) { - struct drm_hdmi_context *ctx = mgr->ctx; + struct drm_hdmi_context *ctx = display->ctx; if (hdmi_ops && hdmi_ops->mode_set) hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode); } -static void drm_hdmi_get_max_resol(struct exynos_drm_manager *mgr, +static void drm_hdmi_get_max_resol(struct exynos_drm_display *display, unsigned int *width, unsigned int *height) { - struct drm_hdmi_context *ctx = mgr->ctx; + struct drm_hdmi_context *ctx = display->ctx; if (hdmi_ops && hdmi_ops->get_max_resol) hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height); } +static struct exynos_drm_display_ops drm_hdmi_display_ops = { + .initialize = drm_hdmi_display_initialize, + .is_connected = drm_hdmi_is_connected, + .get_edid = drm_hdmi_get_edid, + .check_mode = drm_hdmi_check_mode, + .dpms = drm_hdmi_display_dpms, + .mode_fixup = drm_hdmi_mode_fixup, + .mode_set = drm_hdmi_mode_set, + .get_max_resol = drm_hdmi_get_max_resol, +}; + +static struct exynos_drm_display hdmi_display = { + .type = EXYNOS_DISPLAY_TYPE_HDMI, + .ops = &drm_hdmi_display_ops, +}; + +static int drm_hdmi_enable_vblank(struct exynos_drm_manager *mgr) +{ + struct drm_hdmi_context *ctx = mgr->ctx; + + if (mixer_ops && mixer_ops->enable_vblank) + return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, mgr->pipe); + + return 0; +} + +static void drm_hdmi_disable_vblank(struct exynos_drm_manager *mgr) +{ + struct drm_hdmi_context *ctx = mgr->ctx; + + if (mixer_ops && mixer_ops->disable_vblank) + return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); +} + +static void drm_hdmi_wait_for_vblank(struct exynos_drm_manager *mgr) +{ + struct drm_hdmi_context *ctx = mgr->ctx; + + if (mixer_ops && mixer_ops->wait_for_vblank) + mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); +} + static void drm_hdmi_commit(struct exynos_drm_manager *mgr) { struct drm_hdmi_context *ctx = mgr->ctx; @@ -277,11 +274,25 @@ static void drm_hdmi_commit(struct exynos_drm_manager *mgr) hdmi_ops->commit(ctx->hdmi_ctx->ctx); } -static int drm_hdmi_mgr_initialize(struct exynos_drm_manager *mgr, struct drm_device *drm_dev) +static int drm_hdmi_mgr_initialize(struct exynos_drm_manager *mgr, + struct drm_device *drm_dev, int pipe) { struct drm_hdmi_context *ctx = mgr->ctx; int ret = 0; + if (!hdmi_ctx) { + DRM_ERROR("hdmi context not initialized.\n"); + return -EFAULT; + } + + if (!mixer_ctx) { + DRM_ERROR("mixer context not initialized.\n"); + return -EFAULT; + } + + ctx->hdmi_ctx = hdmi_ctx; + ctx->mixer_ctx = mixer_ctx; + if (mixer_ops && mixer_ops->initialize) ret = mixer_ops->initialize(ctx->mixer_ctx->ctx, drm_dev); @@ -291,6 +302,14 @@ static int drm_hdmi_mgr_initialize(struct exynos_drm_manager *mgr, struct drm_de return ret; } +static void drm_hdmi_mgr_remove(struct exynos_drm_manager *mgr) +{ + struct drm_hdmi_context *ctx = mgr->ctx; + + if (mixer_ops->iommu_on) + mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false); +} + static void drm_hdmi_dpms(struct exynos_drm_manager *mgr, int mode) { struct drm_hdmi_context *ctx = mgr->ctx; @@ -345,13 +364,11 @@ static void drm_mixer_win_disable(struct exynos_drm_manager *mgr, int zpos) static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { .initialize = drm_hdmi_mgr_initialize, + .remove = drm_hdmi_mgr_remove, .dpms = drm_hdmi_dpms, .enable_vblank = drm_hdmi_enable_vblank, .disable_vblank = drm_hdmi_disable_vblank, .wait_for_vblank = drm_hdmi_wait_for_vblank, - .mode_fixup = drm_hdmi_mode_fixup, - .mode_set = drm_hdmi_mode_set, - .get_max_resol = drm_hdmi_get_max_resol, .commit = drm_hdmi_commit, .win_mode_set = drm_mixer_win_mode_set, .win_commit = drm_mixer_win_commit, @@ -359,55 +376,13 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { }; static struct exynos_drm_manager hdmi_manager = { - .pipe = -1, + .type = EXYNOS_DISPLAY_TYPE_HDMI, .ops = &drm_hdmi_manager_ops, - .display_ops = &drm_hdmi_display_ops, }; -static int hdmi_subdrv_probe(struct drm_device *drm_dev, - struct device *dev) -{ - struct exynos_drm_subdrv *subdrv = to_subdrv(dev); - struct drm_hdmi_context *ctx; - - if (!hdmi_ctx) { - DRM_ERROR("hdmi context not initialized.\n"); - return -EFAULT; - } - - if (!mixer_ctx) { - DRM_ERROR("mixer context not initialized.\n"); - return -EFAULT; - } - - ctx = get_ctx_from_subdrv(subdrv); - - if (!ctx) { - DRM_ERROR("no drm hdmi context.\n"); - return -EFAULT; - } - - ctx->hdmi_ctx = hdmi_ctx; - ctx->mixer_ctx = mixer_ctx; - - return 0; -} - -static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev) -{ - struct drm_hdmi_context *ctx; - struct exynos_drm_subdrv *subdrv = to_subdrv(dev); - - ctx = get_ctx_from_subdrv(subdrv); - - if (mixer_ops->iommu_on) - mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false); -} - static int exynos_drm_hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct exynos_drm_subdrv *subdrv; struct drm_hdmi_context *ctx; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); @@ -415,26 +390,18 @@ static int exynos_drm_hdmi_probe(struct platform_device *pdev) return -ENOMEM; hdmi_manager.ctx = ctx; + hdmi_display.ctx = ctx; - subdrv = &ctx->subdrv; - - subdrv->dev = dev; - subdrv->manager = &hdmi_manager; - subdrv->probe = hdmi_subdrv_probe; - subdrv->remove = hdmi_subdrv_remove; - - platform_set_drvdata(pdev, subdrv); - - exynos_drm_subdrv_register(subdrv); + exynos_drm_manager_register(&hdmi_manager); + exynos_drm_display_register(&hdmi_display); return 0; } static int exynos_drm_hdmi_remove(struct platform_device *pdev) { - struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); - - exynos_drm_subdrv_unregister(&ctx->subdrv); + exynos_drm_display_unregister(&hdmi_display); + exynos_drm_manager_unregister(&hdmi_manager); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index 923239bdd7e..37059ea4f33 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -19,10 +19,12 @@ * exynos hdmi common context structure. * * @drm_dev: pointer to drm_device. + * @pipe: pipe for mixer * @ctx: pointer to the context of specific device driver. * this context should be hdmi_context or mixer_context. */ struct exynos_drm_hdmi_context { + int pipe; void *ctx; }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 866a4f9087b..b590cf66785 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -13,7 +13,7 @@ #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h" +#include "exynos_drm_crtc.h" #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" #include "exynos_drm_plane.h" @@ -141,7 +141,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, overlay->crtc_x, overlay->crtc_y, overlay->crtc_width, overlay->crtc_height); - exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set); + exynos_drm_crtc_plane_mode_set(crtc, overlay); return 0; } @@ -151,8 +151,7 @@ void exynos_plane_commit(struct drm_plane *plane) struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_plane_commit); + exynos_drm_crtc_plane_commit(plane->crtc, overlay->zpos); } void exynos_plane_dpms(struct drm_plane *plane, int mode) @@ -164,17 +163,13 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode) if (exynos_plane->enabled) return; - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_plane_enable); - + exynos_drm_crtc_plane_enable(plane->crtc, overlay->zpos); exynos_plane->enabled = true; } else { if (!exynos_plane->enabled) return; - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_plane_disable); - + exynos_drm_crtc_plane_disable(plane->crtc, overlay->zpos); exynos_plane->enabled = false; } } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index cbaf1ee8f6a..02f1737a0fc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -45,7 +45,7 @@ struct vidi_win_data { }; struct vidi_context { - struct exynos_drm_subdrv subdrv; + struct drm_device *drm_dev; struct drm_crtc *crtc; struct vidi_win_data win_data[WINDOWS_NR]; struct edid *raw_edid; @@ -58,6 +58,7 @@ struct vidi_context { bool direct_vblank; struct work_struct work; struct mutex lock; + int pipe; }; static const char fake_edid_info[] = { @@ -92,10 +93,9 @@ static const struct of_device_id vidi_driver_dt_match[] = { }; #endif -static bool vidi_display_is_connected(struct device *dev) +static bool vidi_display_is_connected(struct exynos_drm_display *display) { - struct exynos_drm_manager *mgr = get_vidi_mgr(dev); - struct vidi_context *ctx = mgr->ctx; + struct vidi_context *ctx = display->ctx; /* * connection request would come from user side @@ -104,11 +104,10 @@ static bool vidi_display_is_connected(struct device *dev) return ctx->connected ? true : false; } -static struct edid *vidi_get_edid(struct device *dev, +static struct edid *vidi_get_edid(struct exynos_drm_display *display, struct drm_connector *connector) { - struct exynos_drm_manager *mgr = get_vidi_mgr(dev); - struct vidi_context *ctx = mgr->ctx; + struct vidi_context *ctx = display->ctx; struct edid *edid; int edid_len; @@ -131,14 +130,8 @@ static struct edid *vidi_get_edid(struct device *dev, 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) +static int vidi_check_mode(struct exynos_drm_display *display, + struct drm_display_mode *mode) { /* TODO. */ @@ -146,13 +139,16 @@ static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode) } 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, }; +static struct exynos_drm_display vidi_display = { + .type = EXYNOS_DISPLAY_TYPE_VIDI, + .ops = &vidi_display_ops, +}; + static void vidi_dpms(struct exynos_drm_manager *mgr, int mode) { struct vidi_context *ctx = mgr->ctx; @@ -332,7 +328,38 @@ static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos) /* TODO. */ } +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 = 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 = 1; + + /* + * 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 = 1; + + return 0; +} + static struct exynos_drm_manager_ops vidi_manager_ops = { + .initialize = vidi_mgr_initialize, .dpms = vidi_dpms, .commit = vidi_commit, .enable_vblank = vidi_enable_vblank, @@ -343,19 +370,16 @@ static struct exynos_drm_manager_ops vidi_manager_ops = { }; static struct exynos_drm_manager vidi_manager = { - .pipe = -1, - .ops = &vidi_manager_ops, - .display_ops = &vidi_display_ops, + .type = EXYNOS_DISPLAY_TYPE_VIDI, + .ops = &vidi_manager_ops, }; 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 (manager->pipe < 0) + if (ctx->pipe < 0) return; /* refresh rate is about 50Hz. */ @@ -364,7 +388,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) mutex_lock(&ctx->lock); if (ctx->direct_vblank) { - drm_handle_vblank(subdrv->drm_dev, manager->pipe); + drm_handle_vblank(ctx->drm_dev, ctx->pipe); ctx->direct_vblank = false; mutex_unlock(&ctx->lock); return; @@ -372,24 +396,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) 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) -{ - /* - * 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 = 1; - - return 0; -} - -static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev) -{ - /* TODO. */ + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); } static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable) @@ -459,7 +466,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; } @@ -472,8 +479,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; int edid_len; @@ -489,11 +495,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 = manager->ctx; + if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) { + ctx = display->ctx; break; } } @@ -533,7 +538,7 @@ 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; } @@ -542,7 +547,6 @@ 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); @@ -554,12 +558,7 @@ static int vidi_probe(struct platform_device *pdev) INIT_WORK(&ctx->work, vidi_fake_vblank_handler); vidi_manager.ctx = ctx; - - subdrv = &ctx->subdrv; - subdrv->dev = dev; - subdrv->manager = &vidi_manager; - subdrv->probe = vidi_subdrv_probe; - subdrv->remove = vidi_subdrv_remove; + vidi_display.ctx = ctx; mutex_init(&ctx->lock); @@ -569,7 +568,8 @@ static int vidi_probe(struct platform_device *pdev) 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; } @@ -578,7 +578,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); |