diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-30 10:06:23 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-30 10:06:23 -0700 |
commit | 219c6734380e810adcf9d4c98c7ffed81ed44b4f (patch) | |
tree | 2c7a1b008b967b91589b636f5b7a0ccfdec1539d | |
parent | f1115bb686207a32c655602b870b9e2b6b2d32c0 (diff) | |
parent | cb364e342d8ac2bac694ab7e09e7090c72346c5e (diff) |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull Exynos DRM changes from Dave Airlie:
"So I totally missed Inki's pull request for -next, its fully exynos
self contained."
(I took just the actual commits, not Dave's two extraneous merges)
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (30 commits)
drm/exynos: fixed exception to page allocation failure
drm/exynos: use __free_page() to deallocate memory
drm/exynos: fixed a comment to gem size.
drm/exynos: removed unnecessary variable
drm/exynos: do not release memory region from exporter.
drm/exynos: set buffer type from exporter.
drm/exynos: use alloc_page() to allocate pages.
drm/exynos: fixed build warning.
drm/exynos: fixed edid data setting at vidi connection request
drm/exynos: check if raw edid data is fake or not for test
drm/exynos: set edid fake data only for test.
drm/exynos: removed unnecessary declaration.
drm/exynos: fix buffer pitch calculation
drm/exynos: check for null in return value of dma_buf_map_attachment()
drm/exynos: return NULL if exynos_pages_to_sg fails
drm/exynos: Use devm_* functions in exynos_mixer.c
drm/exynos: Use devm_* functions in exynos_hdmi.c
drm/exynos: Use devm_* functions in exynos_drm_fimd.c
drm/exynos: Add missing static storage class specifier
drm/exynos: add property for crtc mode
...
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_connector.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_core.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 294 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.h | 31 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_encoder.c | 123 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_encoder.h | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_gem.c | 49 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_gem.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.c | 246 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.h | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_vidi.c | 53 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 36 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.c | 48 | ||||
-rw-r--r-- | include/drm/exynos_drm.h | 9 |
18 files changed, 493 insertions, 517 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index bf791fa0e50d..d9568198c300 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -196,7 +196,8 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector, return ret; } -struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) +static struct drm_encoder *exynos_drm_best_encoder( + struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct exynos_drm_connector *exynos_connector = diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index eaf630dc5dba..84dd099eae3b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -33,7 +33,6 @@ #include "exynos_drm_fbdev.h" static LIST_HEAD(exynos_drm_subdrv_list); -static struct drm_device *drm_dev; static int exynos_drm_subdrv_probe(struct drm_device *dev, struct exynos_drm_subdrv *subdrv) @@ -120,8 +119,6 @@ int exynos_drm_device_register(struct drm_device *dev) if (!dev) return -EINVAL; - drm_dev = dev; - list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { subdrv->drm_dev = dev; err = exynos_drm_subdrv_probe(dev, subdrv); @@ -149,8 +146,6 @@ 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); - drm_dev = NULL; - return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 32a34c85899b..abb1e2f8227f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -29,21 +29,23 @@ #include "drmP.h" #include "drm_crtc_helper.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" -#include "exynos_drm_fb.h" #include "exynos_drm_encoder.h" -#include "exynos_drm_gem.h" +#include "exynos_drm_plane.h" #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ drm_crtc) +enum exynos_crtc_mode { + CRTC_MODE_NORMAL, /* normal mode */ + CRTC_MODE_BLANK, /* The private plane of crtc is blank */ +}; + /* * Exynos specific crtc structure. * * @drm_crtc: crtc object. - * @overlay: contain information common to display controller and hdmi and - * contents of this overlay object would be copied to sub driver size. + * @drm_plane: pointer of private plane object for 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 @@ -52,115 +54,16 @@ * we can refer to the crtc to current hardware interrupt occured through * this pipe value. * @dpms: store the crtc dpms value + * @mode: store the crtc mode value */ struct exynos_drm_crtc { struct drm_crtc drm_crtc; - struct exynos_drm_overlay overlay; + struct drm_plane *plane; unsigned int pipe; unsigned int dpms; + enum exynos_crtc_mode mode; }; -static void exynos_drm_crtc_apply(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; - - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); -} - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos) -{ - struct exynos_drm_gem_buf *buffer; - unsigned int actual_w; - unsigned int actual_h; - int nr = exynos_drm_format_num_buffers(fb->pixel_format); - int i; - - for (i = 0; i < nr; i++) { - buffer = exynos_drm_fb_buffer(fb, i); - if (!buffer) { - DRM_LOG_KMS("buffer is null\n"); - return -EFAULT; - } - - overlay->dma_addr[i] = buffer->dma_addr; - overlay->vaddr[i] = buffer->kvaddr; - - DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", - i, (unsigned long)overlay->vaddr[i], - (unsigned long)overlay->dma_addr[i]); - } - - actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); - actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); - - /* set drm framebuffer data. */ - overlay->fb_x = pos->fb_x; - overlay->fb_y = pos->fb_y; - overlay->fb_width = fb->width; - overlay->fb_height = fb->height; - overlay->src_width = pos->src_w; - overlay->src_height = pos->src_h; - overlay->bpp = fb->bits_per_pixel; - overlay->pitch = fb->pitches[0]; - overlay->pixel_format = fb->pixel_format; - - /* set overlay range to be displayed. */ - overlay->crtc_x = pos->crtc_x; - overlay->crtc_y = pos->crtc_y; - overlay->crtc_width = actual_w; - overlay->crtc_height = actual_h; - - /* set drm mode data. */ - overlay->mode_width = mode->hdisplay; - overlay->mode_height = mode->vdisplay; - overlay->refresh = mode->vrefresh; - overlay->scan_flag = mode->flags; - - DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", - overlay->crtc_x, overlay->crtc_y, - overlay->crtc_width, overlay->crtc_height); - - return 0; -} - -static int exynos_drm_crtc_update(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_overlay *overlay; - struct exynos_drm_crtc_pos pos; - struct drm_display_mode *mode = &crtc->mode; - struct drm_framebuffer *fb = crtc->fb; - - if (!mode || !fb) - return -EINVAL; - - exynos_crtc = to_exynos_crtc(crtc); - overlay = &exynos_crtc->overlay; - - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - - /* it means the offset of framebuffer to be displayed. */ - pos.fb_x = crtc->x; - pos.fb_y = crtc->y; - - /* OSD position to be displayed. */ - pos.crtc_x = 0; - pos.crtc_y = 0; - pos.crtc_w = fb->width - crtc->x; - pos.crtc_h = fb->height - crtc->y; - pos.src_w = pos.crtc_w; - pos.src_h = pos.crtc_h; - - return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); -} - static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; @@ -175,23 +78,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) mutex_lock(&dev->struct_mutex); - switch (mode) { - case DRM_MODE_DPMS_ON: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_crtc_dpms); - exynos_crtc->dpms = mode; - break; - default: - DRM_ERROR("unspecified mode %d\n", mode); - break; - } + exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); + exynos_crtc->dpms = mode; mutex_unlock(&dev->struct_mutex); } @@ -209,30 +97,8 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) DRM_DEBUG_KMS("%s\n", __FILE__); - /* - * when set_crtc is requested from user or at booting time, - * crtc->commit would be called without dpms call so if dpms is - * no power on then crtc->dpms should be called - * with DRM_MODE_DPMS_ON for the hardware power to be on. - */ - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) { - int mode = DRM_MODE_DPMS_ON; - - /* - * enable hardware(power on) to all encoders hdmi connected - * to current crtc. - */ - exynos_drm_crtc_dpms(crtc, mode); - /* - * enable dma to all encoders connected to current crtc and - * lcd panel. - */ - exynos_drm_fn_encoder(crtc, &mode, - exynos_drm_encoder_dpms_from_crtc); - } - - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); + exynos_plane_commit(exynos_crtc->plane); + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); } static bool @@ -251,31 +117,61 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane = exynos_crtc->plane; + unsigned int crtc_w; + unsigned int crtc_h; + int pipe = exynos_crtc->pipe; + int ret; + DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + /* * copy the mode data adjusted by mode_fixup() into crtc->mode * so that hardware can be seet to proper mode. */ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); - return exynos_drm_crtc_update(crtc); + crtc_w = crtc->fb->width - x; + crtc_h = crtc->fb->height - y; + + ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, + x, y, crtc_w, crtc_h); + if (ret) + return ret; + + plane->crtc = crtc; + plane->fb = crtc->fb; + + exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); + + return 0; } static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane = exynos_crtc->plane; + unsigned int crtc_w; + unsigned int crtc_h; int ret; DRM_DEBUG_KMS("%s\n", __FILE__); - ret = exynos_drm_crtc_update(crtc); + crtc_w = crtc->fb->width - x; + crtc_h = crtc->fb->height - y; + + ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, + x, y, crtc_w, crtc_h); if (ret) return ret; - exynos_drm_crtc_apply(crtc); + exynos_drm_crtc_commit(crtc); - return ret; + return 0; } static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) @@ -284,6 +180,16 @@ static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) /* drm framework doesn't check NULL */ } +static void exynos_drm_crtc_disable(struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF); + exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .dpms = exynos_drm_crtc_dpms, .prepare = exynos_drm_crtc_prepare, @@ -292,6 +198,7 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { .mode_set = exynos_drm_crtc_mode_set, .mode_set_base = exynos_drm_crtc_mode_set_base, .load_lut = exynos_drm_crtc_load_lut, + .disable = exynos_drm_crtc_disable, }; static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, @@ -327,7 +234,8 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, &dev_priv->pageflip_event_list); crtc->fb = fb; - ret = exynos_drm_crtc_update(crtc); + ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, + NULL); if (ret) { crtc->fb = old_fb; drm_vblank_put(dev, exynos_crtc->pipe); @@ -335,14 +243,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, goto out; } - - /* - * the values related to a buffer of the drm framebuffer - * to be applied should be set at here. because these values - * first, are set to shadow registers and then to - * real registers at vsync front porch period. - */ - exynos_drm_crtc_apply(crtc); } out: mutex_unlock(&dev->struct_mutex); @@ -362,18 +262,73 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) kfree(exynos_crtc); } +static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + DRM_DEBUG_KMS("%s\n", __func__); + + if (property == dev_priv->crtc_mode_property) { + enum exynos_crtc_mode mode = val; + + if (mode == exynos_crtc->mode) + return 0; + + exynos_crtc->mode = mode; + + switch (mode) { + case CRTC_MODE_NORMAL: + exynos_drm_crtc_commit(crtc); + break; + case CRTC_MODE_BLANK: + exynos_plane_dpms(exynos_crtc->plane, + DRM_MODE_DPMS_OFF); + break; + default: + break; + } + + return 0; + } + + return -EINVAL; +} + static struct drm_crtc_funcs exynos_crtc_funcs = { .set_config = drm_crtc_helper_set_config, .page_flip = exynos_drm_crtc_page_flip, .destroy = exynos_drm_crtc_destroy, + .set_property = exynos_drm_crtc_set_property, +}; + +static const struct drm_prop_enum_list mode_names[] = { + { CRTC_MODE_NORMAL, "normal" }, + { CRTC_MODE_BLANK, "blank" }, }; -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc) +static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) { - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; - return &exynos_crtc->overlay; + DRM_DEBUG_KMS("%s\n", __func__); + + prop = dev_priv->crtc_mode_property; + if (!prop) { + prop = drm_property_create_enum(dev, 0, "mode", mode_names, + ARRAY_SIZE(mode_names)); + if (!prop) + return; + + dev_priv->crtc_mode_property = prop; + } + + drm_object_attach_property(&crtc->base, prop, 0); } int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) @@ -392,7 +347,12 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) exynos_crtc->pipe = nr; exynos_crtc->dpms = DRM_MODE_DPMS_OFF; - exynos_crtc->overlay.zpos = DEFAULT_ZPOS; + exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); + if (!exynos_crtc->plane) { + kfree(exynos_crtc); + return -ENOMEM; + } + crtc = &exynos_crtc->drm_crtc; private->crtc[nr] = crtc; @@ -400,6 +360,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) drm_crtc_init(dev, crtc, &exynos_crtc_funcs); drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); + exynos_drm_crtc_attach_mode_property(crtc); + return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 16b8e2195a0d..6bae8d8c250e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -29,39 +29,8 @@ #ifndef _EXYNOS_DRM_CRTC_H_ #define _EXYNOS_DRM_CRTC_H_ -struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, - struct drm_crtc *crtc); 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); -/* - * Exynos specific crtc postion structure. - * - * @fb_x: offset x on a framebuffer to be displyed - * - the unit is screen coordinates. - * @fb_y: offset y on a framebuffer to be displayed - * - the unit is screen coordinates. - * @src_w: width of source area to be displayed from a framebuffer. - * @src_h: height of source area to be displayed from a framebuffer. - * @crtc_x: offset x on hardware screen. - * @crtc_y: offset y on hardware screen. - * @crtc_w: width of hardware screen. - * @crtc_h: height of hardware screen. - */ -struct exynos_drm_crtc_pos { - unsigned int fb_x; - unsigned int fb_y; - unsigned int src_w; - unsigned int src_h; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_w; - unsigned int crtc_h; -}; - -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index 274909271c36..613bf8a5d9b2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -25,6 +25,7 @@ #include "drmP.h" #include "drm.h" +#include "exynos_drm.h" #include "exynos_drm_drv.h" #include "exynos_drm_gem.h" @@ -86,6 +87,10 @@ static struct sg_table * npages = buf->size / buf->page_size; sgt = exynos_pages_to_sg(buf->pages, npages, buf->page_size); + if (!sgt) { + DRM_DEBUG_PRIME("exynos_pages_to_sg returned NULL!\n"); + goto err_unlock; + } nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); DRM_DEBUG_PRIME("npages = %d buffer size = 0x%lx page_size = 0x%lx\n", @@ -186,7 +191,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_buf *buffer; struct page *page; - int ret, i = 0; + int ret; DRM_DEBUG_PRIME("%s\n", __FILE__); @@ -210,7 +215,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sgt)) { + if (IS_ERR_OR_NULL(sgt)) { ret = PTR_ERR(sgt); goto err_buf_detach; } @@ -236,13 +241,25 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, } sgl = sgt->sgl; - buffer->dma_addr = sg_dma_address(sgl); - while (i < sgt->nents) { - buffer->pages[i] = sg_page(sgl); - buffer->size += sg_dma_len(sgl); - sgl = sg_next(sgl); - i++; + if (sgt->nents == 1) { + buffer->dma_addr = sg_dma_address(sgt->sgl); + buffer->size = sg_dma_len(sgt->sgl); + + /* always physically continuous memory if sgt->nents is 1. */ + exynos_gem_obj->flags |= EXYNOS_BO_CONTIG; + } else { + unsigned int i = 0; + + buffer->dma_addr = sg_dma_address(sgl); + while (i < sgt->nents) { + buffer->pages[i] = sg_page(sgl); + buffer->size += sg_dma_len(sgl); + sgl = sg_next(sgl); + i++; + } + + exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG; } exynos_gem_obj->buffer = buffer; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index d6de2e07fa03..ebacec6f1e48 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -85,8 +85,11 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) } for (nr = 0; nr < MAX_PLANE; nr++) { - ret = exynos_plane_init(dev, nr); - if (ret) + struct drm_plane *plane; + unsigned int possible_crtcs = (1 << MAX_CRTC) - 1; + + plane = exynos_plane_init(dev, possible_crtcs, false); + if (!plane) goto err_crtc; } @@ -221,8 +224,6 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, - DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 277653d5fda0..e22704b249d7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -59,12 +59,14 @@ enum exynos_drm_output_type { * * @mode_set: copy drm overlay info to hw specific overlay info. * @commit: apply hardware specific overlay data to registers. + * @enable: enable hardware specific overlay. * @disable: disable hardware specific overlay. */ struct exynos_drm_overlay_ops { void (*mode_set)(struct device *subdrv_dev, struct exynos_drm_overlay *overlay); void (*commit)(struct device *subdrv_dev, int zpos); + void (*enable)(struct device *subdrv_dev, int zpos); void (*disable)(struct device *subdrv_dev, int zpos); }; @@ -235,6 +237,8 @@ struct exynos_drm_private { * this array is used to be aware of which crtc did it request vblank. */ struct drm_crtc *crtc[MAX_CRTC]; + struct drm_property *plane_zpos_property; + struct drm_property *crtc_mode_property; }; /* diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 4a13a747f5d4..2c037cd7d2d4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -30,7 +30,6 @@ #include "drm_crtc_helper.h" #include "exynos_drm_drv.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_encoder.h" #define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ @@ -136,21 +135,16 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, struct drm_connector *connector; struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev, - encoder->crtc); DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { + if (connector->encoder == encoder) if (manager_ops && manager_ops->mode_set) manager_ops->mode_set(manager->dev, adjusted_mode); - - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); - } } } @@ -310,8 +304,8 @@ void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int crtc = *(int *)data; - if (manager->pipe == -1) - manager->pipe = crtc; + if (manager->pipe != crtc) + return; if (manager_ops->enable_vblank) manager_ops->enable_vblank(manager->dev); @@ -324,34 +318,41 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int crtc = *(int *)data; - if (manager->pipe == -1) - manager->pipe = crtc; + if (manager->pipe != crtc) + return; if (manager_ops->disable_vblank) manager_ops->disable_vblank(manager->dev); } -void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, - void *data) +void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) { - struct exynos_drm_manager *manager = - to_exynos_encoder(encoder)->manager; - struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; - int zpos = DEFAULT_ZPOS; + 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 (data) - zpos = *(int *)data; + DRM_DEBUG_KMS("%s\n", __FILE__); - if (overlay_ops && overlay_ops->commit) - overlay_ops->commit(manager->dev, zpos); + if (manager_ops && manager_ops->dpms) + manager_ops->dpms(manager->dev, 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_commit(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; - int crtc = *(int *)data; - int zpos = DEFAULT_ZPOS; + int pipe = *(int *)data; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -359,76 +360,62 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) * when crtc is detached from encoder, this pipe is used * to select manager operation */ - manager->pipe = crtc; - - exynos_drm_encoder_crtc_plane_commit(encoder, &zpos); + manager->pipe = pipe; } -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) { - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - int mode = *(int *)data; + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + struct exynos_drm_overlay *overlay = data; DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_encoder_dpms(encoder, mode); - - exynos_encoder->dpms = mode; + if (overlay_ops && overlay_ops->mode_set) + overlay_ops->mode_set(manager->dev, overlay); } -void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) { - struct drm_device *dev = encoder->dev; - struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - struct exynos_drm_manager *manager = exynos_encoder->manager; - struct exynos_drm_manager_ops *manager_ops = manager->ops; - struct drm_connector *connector; - int mode = *(int *)data; + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + int zpos = DEFAULT_ZPOS; DRM_DEBUG_KMS("%s\n", __FILE__); - if (manager_ops && manager_ops->dpms) - manager_ops->dpms(manager->dev, mode); - - /* - * set current dpms mode to the connector connected to - * current encoder. connector->dpms would be checked - * at drm_helper_connector_dpms() - */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - if (connector->encoder == encoder) - connector->dpms = mode; + if (data) + zpos = *(int *)data; - /* - * 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; - } + if (overlay_ops && overlay_ops->commit) + overlay_ops->commit(manager->dev, zpos); } -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) +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_overlay_ops *overlay_ops = manager->overlay_ops; - struct exynos_drm_overlay *overlay = data; + int zpos = DEFAULT_ZPOS; - if (overlay_ops && overlay_ops->mode_set) - overlay_ops->mode_set(manager->dev, overlay); + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (data) + zpos = *(int *)data; + + if (overlay_ops && overlay_ops->enable) + overlay_ops->enable(manager->dev, zpos); } -void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) +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_overlay_ops *overlay_ops = manager->overlay_ops; int zpos = DEFAULT_ZPOS; - DRM_DEBUG_KMS("\n"); + DRM_DEBUG_KMS("%s\n", __FILE__); if (data) zpos = *(int *)data; diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h index eb7d2316847e..6470d9ddf5a1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -40,13 +40,11 @@ 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_plane_commit(struct drm_encoder *encoder, - void *data); -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, - void *data); void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); -void exynos_drm_encoder_crtc_disable(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); #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 29fdbfeb43cb..a68d2b313f03 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -78,7 +78,6 @@ struct fimd_context { struct drm_crtc *crtc; struct clk *bus_clk; struct clk *lcd_clk; - struct resource *regs_res; void __iomem *regs; struct fimd_win_data win_data[WINDOWS_NR]; unsigned int clkdiv; @@ -813,7 +812,7 @@ static int __devinit fimd_probe(struct platform_device *pdev) return -EINVAL; } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -838,33 +837,26 @@ static int __devinit fimd_probe(struct platform_device *pdev) goto err_clk; } - ctx->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!ctx->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_clk; - } - - ctx->regs = ioremap(res->start, resource_size(res)); + ctx->regs = devm_request_and_ioremap(&pdev->dev, res); if (!ctx->regs) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; - goto err_req_region_io; + goto err_clk; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "irq request failed.\n"); - goto err_req_region_irq; + goto err_clk; } ctx->irq = res->start; - ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); - if (ret < 0) { + ret = devm_request_irq(&pdev->dev, ctx->irq, fimd_irq_handler, + 0, "drm_fimd", ctx); + if (ret) { dev_err(dev, "irq request failed.\n"); - goto err_req_irq; + goto err_clk; } ctx->vidcon0 = pdata->vidcon0; @@ -899,14 +891,6 @@ static int __devinit fimd_probe(struct platform_device *pdev) return 0; -err_req_irq: -err_req_region_irq: - iounmap(ctx->regs); - -err_req_region_io: - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - err_clk: clk_disable(ctx->lcd_clk); clk_put(ctx->lcd_clk); @@ -916,7 +900,6 @@ err_bus_clk: clk_put(ctx->bus_clk); err_clk_get: - kfree(ctx); return ret; } @@ -944,13 +927,6 @@ out: clk_put(ctx->lcd_clk); clk_put(ctx->bus_clk); - iounmap(ctx->regs); - release_resource(ctx->regs_res); - kfree(ctx->regs_res); - free_irq(ctx->irq, ctx); - - kfree(ctx); - return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 5c8b683029ea..f9efde40c097 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -99,25 +99,17 @@ out: struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) { - struct inode *inode; - struct address_space *mapping; struct page *p, **pages; int i, npages; - /* This is the shared memory object that backs the GEM resource */ - inode = obj->filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - npages = obj->size >> PAGE_SHIFT; pages = drm_malloc_ab(npages, sizeof(struct page *)); if (pages == NULL) return ERR_PTR(-ENOMEM); - gfpmask |= mapping_gfp_mask(mapping); - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + p = alloc_page(gfpmask); if (IS_ERR(p)) goto fail; pages[i] = p; @@ -126,31 +118,22 @@ struct page **exynos_gem_get_pages(struct drm_gem_object *obj, return pages; fail: - while (i--) - page_cache_release(pages[i]); + while (--i) + __free_page(pages[i]); drm_free_large(pages); return ERR_PTR(PTR_ERR(p)); } static void exynos_gem_put_pages(struct drm_gem_object *obj, - struct page **pages, - bool dirty, bool accessed) + struct page **pages) { - int i, npages; + int npages; npages = obj->size >> PAGE_SHIFT; - for (i = 0; i < npages; i++) { - if (dirty) - set_page_dirty(pages[i]); - - if (accessed) - mark_page_accessed(pages[i]); - - /* Undo the reference we took when populating the table */ - page_cache_release(pages[i]); - } + while (--npages >= 0) + __free_page(pages[npages]); drm_free_large(pages); } @@ -189,7 +172,7 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) return -EINVAL; } - pages = exynos_gem_get_pages(obj, GFP_KERNEL); + pages = exynos_gem_get_pages(obj, GFP_HIGHUSER_MOVABLE); if (IS_ERR(pages)) { DRM_ERROR("failed to get pages.\n"); return PTR_ERR(pages); @@ -230,7 +213,7 @@ err1: kfree(buf->sgt); buf->sgt = NULL; err: - exynos_gem_put_pages(obj, pages, true, false); + exynos_gem_put_pages(obj, pages); return ret; } @@ -248,7 +231,7 @@ static void exynos_drm_gem_put_pages(struct drm_gem_object *obj) kfree(buf->sgt); buf->sgt = NULL; - exynos_gem_put_pages(obj, buf->pages, true, false); + exynos_gem_put_pages(obj, buf->pages); buf->pages = NULL; /* add some codes for UNCACHED type here. TODO */ @@ -291,11 +274,21 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) if (!buf->pages) return; + /* + * do not release memory region from exporter. + * + * the region will be released by exporter + * once dmabuf's refcount becomes 0. + */ + if (obj->import_attach) + goto out; + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) exynos_drm_gem_put_pages(obj); else exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf); +out: exynos_drm_fini_buf(obj->dev, buf); exynos_gem_obj->buffer = NULL; @@ -668,7 +661,7 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, * with DRM_IOCTL_MODE_CREATE_DUMB command. */ - args->pitch = args->width * args->bpp >> 3; + args->pitch = args->width * ((args->bpp + 7) / 8); args->size = PAGE_ALIGN(args->pitch * args->height); exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 14d038b6cb02..085b2a5d5f70 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -63,7 +63,8 @@ struct exynos_drm_gem_buf { * by user request or at framebuffer creation. * continuous memory region allocated by user request * or at framebuffer creation. - * @size: total memory size to physically non-continuous memory region. + * @size: size requested from user, in bytes and this size is aligned + * in page unit. * @flags: indicate memory type to allocated buffer and cache attruibute. * * P.S. this object would be transfered to user as kms_bo.handle so diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index c4c6525d4653..b89829e5043a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -12,9 +12,12 @@ #include "drmP.h" #include "exynos_drm.h" -#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_gem.h" + +#define to_exynos_plane(x) container_of(x, struct exynos_plane, base) struct exynos_plane { struct drm_plane base; @@ -30,6 +33,108 @@ static const uint32_t formats[] = { DRM_FORMAT_NV12MT, }; +int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + unsigned int actual_w; + unsigned int actual_h; + int nr; + int i; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + nr = exynos_drm_format_num_buffers(fb->pixel_format); + for (i = 0; i < nr; i++) { + struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i); + + if (!buffer) { + DRM_LOG_KMS("buffer is null\n"); + return -EFAULT; + } + + overlay->dma_addr[i] = buffer->dma_addr; + overlay->vaddr[i] = buffer->kvaddr; + + DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", + i, (unsigned long)overlay->vaddr[i], + (unsigned long)overlay->dma_addr[i]); + } + + actual_w = min((unsigned)(crtc->mode.hdisplay - crtc_x), crtc_w); + actual_h = min((unsigned)(crtc->mode.vdisplay - crtc_y), crtc_h); + + /* set drm framebuffer data. */ + overlay->fb_x = src_x; + overlay->fb_y = src_y; + overlay->fb_width = fb->width; + overlay->fb_height = fb->height; + overlay->src_width = src_w; + overlay->src_height = src_h; + overlay->bpp = fb->bits_per_pixel; + overlay->pitch = fb->pitches[0]; + overlay->pixel_format = fb->pixel_format; + + /* set overlay range to be displayed. */ + overlay->crtc_x = crtc_x; + overlay->crtc_y = crtc_y; + overlay->crtc_width = actual_w; + overlay->crtc_height = actual_h; + + /* set drm mode data. */ + overlay->mode_width = crtc->mode.hdisplay; + overlay->mode_height = crtc->mode.vdisplay; + overlay->refresh = crtc->mode.vrefresh; + overlay->scan_flag = crtc->mode.flags; + + DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", + overlay->crtc_x, overlay->crtc_y, + overlay->crtc_width, overlay->crtc_height); + + exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set); + + return 0; +} + +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); +} + +void exynos_plane_dpms(struct drm_plane *plane, int mode) +{ + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (mode == DRM_MODE_DPMS_ON) { + if (exynos_plane->enabled) + return; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_enable); + + exynos_plane->enabled = true; + } else { + if (!exynos_plane->enabled) + return; + + exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_encoder_plane_disable); + + exynos_plane->enabled = false; + } +} + static int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -37,64 +142,37 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - struct exynos_drm_crtc_pos pos; int ret; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); - pos.crtc_x = crtc_x; - pos.crtc_y = crtc_y; - pos.crtc_w = crtc_w; - pos.crtc_h = crtc_h; - - /* considering 16.16 fixed point of source values */ - pos.fb_x = src_x >> 16; - pos.fb_y = src_y >> 16; - pos.src_w = src_w >> 16; - pos.src_h = src_h >> 16; - - ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos); + ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, + crtc_w, crtc_h, src_x >> 16, src_y >> 16, + src_w >> 16, src_h >> 16); if (ret < 0) return ret; - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_mode_set); - exynos_drm_fn_encoder(crtc, &overlay->zpos, - exynos_drm_encoder_crtc_plane_commit); + plane->crtc = crtc; + plane->fb = crtc->fb; - exynos_plane->enabled = true; + exynos_plane_commit(plane); + exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); return 0; } static int exynos_disable_plane(struct drm_plane *plane) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); - struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!exynos_plane->enabled) - return 0; - - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, - exynos_drm_encoder_crtc_disable); - - exynos_plane->enabled = false; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; + exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF); return 0; } static void exynos_plane_destroy(struct drm_plane *plane) { - struct exynos_plane *exynos_plane = - container_of(plane, struct exynos_plane, base); + struct exynos_plane *exynos_plane = to_exynos_plane(plane); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -103,69 +181,79 @@ static void exynos_plane_destroy(struct drm_plane *plane) kfree(exynos_plane); } +static int exynos_plane_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct exynos_plane *exynos_plane = to_exynos_plane(plane); + struct exynos_drm_private *dev_priv = dev->dev_private; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (property == dev_priv->plane_zpos_property) { + exynos_plane->overlay.zpos = val; + return 0; + } + + return -EINVAL; +} + static struct drm_plane_funcs exynos_plane_funcs = { .update_plane = exynos_update_plane, .disable_plane = exynos_disable_plane, .destroy = exynos_plane_destroy, + .set_property = exynos_plane_set_property, }; -int exynos_plane_init(struct drm_device *dev, unsigned int nr) +static void exynos_plane_attach_zpos_property(struct drm_plane *plane) { - struct exynos_plane *exynos_plane; - uint32_t possible_crtcs; + struct drm_device *dev = plane->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_property *prop; - exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); - if (!exynos_plane) - return -ENOMEM; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - /* all CRTCs are available */ - possible_crtcs = (1 << MAX_CRTC) - 1; + prop = dev_priv->plane_zpos_property; + if (!prop) { + prop = drm_property_create_range(dev, 0, "zpos", 0, + MAX_PLANE - 1); + if (!prop) + return; - exynos_plane->overlay.zpos = DEFAULT_ZPOS; + dev_priv->plane_zpos_property = prop; + } - return drm_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, formats, ARRAY_SIZE(formats), - false); + drm_object_attach_property(&plane->base, prop, 0); } -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +struct drm_plane *exynos_plane_init(struct drm_device *dev, + unsigned int possible_crtcs, bool priv) { - struct drm_exynos_plane_set_zpos *zpos_req = data; - struct drm_mode_object *obj; - struct drm_plane *plane; struct exynos_plane *exynos_plane; - int ret = 0; + int err; DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) { - if (zpos_req->zpos != DEFAULT_ZPOS) { - DRM_ERROR("zpos not within limits\n"); - return -EINVAL; - } + exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); + if (!exynos_plane) { + DRM_ERROR("failed to allocate plane\n"); + return NULL; } - mutex_lock(&dev->mode_config.mutex); - - obj = drm_mode_object_find(dev, zpos_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - zpos_req->plane_id); - ret = -EINVAL; - goto out; + err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs, + &exynos_plane_funcs, formats, ARRAY_SIZE(formats), + priv); + if (err) { + DRM_ERROR("failed to initialize plane\n"); + kfree(exynos_plane); + return NULL; } - plane = obj_to_plane(obj); - exynos_plane = container_of(plane, struct exynos_plane, base); - - exynos_plane->overlay.zpos = zpos_req->zpos; + if (priv) + exynos_plane->overlay.zpos = DEFAULT_ZPOS; + else + exynos_plane_attach_zpos_property(&exynos_plane->base); -out: - mutex_unlock(&dev->mode_config.mutex); - return ret; + return &exynos_plane->base; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index 16b71f8217e7..88312458580d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -9,6 +9,12 @@ * */ -int exynos_plane_init(struct drm_device *dev, unsigned int nr); -int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); +void exynos_plane_commit(struct drm_plane *plane); +void exynos_plane_dpms(struct drm_plane *plane, int mode); +struct drm_plane *exynos_plane_init(struct drm_device *dev, + unsigned int possible_crtcs, bool priv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 7b9c153dceb6..bb1550c4dd57 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -85,8 +85,6 @@ static const char fake_edid_info[] = { 0x00, 0x00, 0x00, 0x06 }; -static void vidi_fake_vblank_handler(struct work_struct *work); - static bool vidi_display_is_connected(struct device *dev) { struct vidi_context *ctx = get_vidi_context(dev); @@ -531,6 +529,16 @@ static int vidi_store_connection(struct device *dev, if (ctx->connected > 1) return -EINVAL; + /* use fake edid data for test. */ + if (!ctx->raw_edid) + ctx->raw_edid = (struct edid *)fake_edid_info; + + /* if raw_edid isn't same as fake data then it can't be tested. */ + if (ctx->raw_edid != (struct edid *)fake_edid_info) { + DRM_DEBUG_KMS("edid data is not fake data.\n"); + return -EINVAL; + } + DRM_DEBUG_KMS("requested connection.\n"); drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); @@ -549,6 +557,8 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, struct exynos_drm_manager *manager; struct exynos_drm_display_ops *display_ops; struct drm_exynos_vidi_connection *vidi = data; + struct edid *raw_edid; + int edid_len; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -557,11 +567,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - if (!vidi->edid) { - DRM_DEBUG_KMS("edid data is null.\n"); - return -EINVAL; - } - if (vidi->connection > 1) { DRM_DEBUG_KMS("connection should be 0 or 1.\n"); return -EINVAL; @@ -588,8 +593,30 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, return -EINVAL; } - if (vidi->connection) - ctx->raw_edid = (struct edid *)vidi->edid; + if (vidi->connection) { + if (!vidi->edid) { + DRM_DEBUG_KMS("edid data is null.\n"); + return -EINVAL; + } + raw_edid = (struct edid *)(uint32_t)vidi->edid; + edid_len = (1 + raw_edid->extensions) * EDID_LENGTH; + ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL); + if (!ctx->raw_edid) { + DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); + return -ENOMEM; + } + memcpy(ctx->raw_edid, raw_edid, edid_len); + } else { + /* + * with connection = 0, free raw_edid + * only if raw edid data isn't same as fake data. + */ + if (ctx->raw_edid && ctx->raw_edid != + (struct edid *)fake_edid_info) { + kfree(ctx->raw_edid); + ctx->raw_edid = NULL; + } + } ctx->connected = vidi->connection; drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); @@ -614,9 +641,6 @@ static int __devinit vidi_probe(struct platform_device *pdev) INIT_WORK(&ctx->work, vidi_fake_vblank_handler); - /* for test */ - ctx->raw_edid = (struct edid *)fake_edid_info; - subdrv = &ctx->subdrv; subdrv->dev = dev; subdrv->manager = &vidi_manager; @@ -644,6 +668,11 @@ static int __devexit vidi_remove(struct platform_device *pdev) exynos_drm_subdrv_unregister(&ctx->subdrv); + if (ctx->raw_edid != (struct edid *)fake_edid_info) { + kfree(ctx->raw_edid); + ctx->raw_edid = NULL; + } + kfree(ctx); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 066bde3f19c4..409e2ec1207c 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -63,7 +63,6 @@ struct hdmi_context { bool dvi_mode; struct mutex hdmi_mutex; - struct resource *regs_res; void __iomem *regs; unsigned int external_irq; unsigned int internal_irq; @@ -2280,16 +2279,17 @@ static int __devinit hdmi_probe(struct platform_device *pdev) return -EINVAL; } - drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); + drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx), + GFP_KERNEL); if (!drm_hdmi_ctx) { DRM_ERROR("failed to allocate common hdmi context.\n"); return -ENOMEM; } - hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL); + hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_context), + GFP_KERNEL); if (!hdata) { DRM_ERROR("out of memory\n"); - kfree(drm_hdmi_ctx); return -ENOMEM; } @@ -2318,26 +2318,18 @@ static int __devinit hdmi_probe(struct platform_device *pdev) goto err_resource; } - hdata->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!hdata->regs_res) { - DRM_ERROR("failed to claim register region\n"); - ret = -ENOENT; - goto err_resource; - } - - hdata->regs = ioremap(res->start, resource_size(res)); + hdata->regs = devm_request_and_ioremap(&pdev->dev, res); if (!hdata->regs) { DRM_ERROR("failed to map registers\n"); ret = -ENXIO; - goto err_req_region; + goto err_resource; } /* DDC i2c driver */ if (i2c_add_driver(&ddc_driver)) { DRM_ERROR("failed to register ddc i2c driver\n"); ret = -ENOENT; - goto err_iomap; + goto err_resource; } hdata->ddc_port = hdmi_ddc; @@ -2398,16 +2390,9 @@ err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: i2c_del_driver(&ddc_driver); -err_iomap: - iounmap(hdata->regs); -err_req_region: - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); err_resource: hdmi_resources_cleanup(hdata); err_data: - kfree(hdata); - kfree(drm_hdmi_ctx); return ret; } @@ -2425,18 +2410,11 @@ static int __devexit hdmi_remove(struct platform_device *pdev) hdmi_resources_cleanup(hdata); - iounmap(hdata->regs); - - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); - /* hdmiphy i2c driver */ i2c_del_driver(&hdmiphy_driver); /* DDC i2c driver */ i2c_del_driver(&ddc_driver); - kfree(hdata); - return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e2147a2ddcec..30fcc12f81dd 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -956,7 +956,8 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); - mixer_res->mixer_regs = ioremap(res->start, resource_size(res)); + mixer_res->mixer_regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (mixer_res->mixer_regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; @@ -967,38 +968,34 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx, if (res == NULL) { dev_err(dev, "get memory resource failed.\n"); ret = -ENXIO; - goto fail_mixer_regs; + goto fail; } - mixer_res->vp_regs = ioremap(res->start, resource_size(res)); + mixer_res->vp_regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (mixer_res->vp_regs == NULL) { dev_err(dev, "register mapping failed.\n"); ret = -ENXIO; - goto fail_mixer_regs; + goto fail; } res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); if (res == NULL) { dev_err(dev, "get interrupt resource failed.\n"); ret = -ENXIO; - goto fail_vp_regs; + goto fail; } - ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx); + ret = devm_request_irq(&pdev->dev, res->start, mixer_irq_handler, + 0, "drm_mixer", ctx); if (ret) { dev_err(dev, "request interrupt failed.\n"); - goto fail_vp_regs; + goto fail; } mixer_res->irq = res->start; return 0; -fail_vp_regs: - iounmap(mixer_res->vp_regs); - -fail_mixer_regs: - iounmap(mixer_res->mixer_regs); - fail: if (!IS_ERR_OR_NULL(mixer_res->sclk_dac)) clk_put(mixer_res->sclk_dac); @@ -1013,16 +1010,6 @@ fail: return ret; } -static void mixer_resources_cleanup(struct mixer_context *ctx) -{ - struct mixer_resources *res = &ctx->mixer_res; - - free_irq(res->irq, ctx); - - iounmap(res->vp_regs); - iounmap(res->mixer_regs); -} - static int __devinit mixer_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1032,16 +1019,16 @@ static int __devinit mixer_probe(struct platform_device *pdev) dev_info(dev, "probe start\n"); - drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL); + drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx), + GFP_KERNEL); if (!drm_hdmi_ctx) { DRM_ERROR("failed to allocate common hdmi context.\n"); return -ENOMEM; } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) { DRM_ERROR("failed to alloc mixer context.\n"); - kfree(drm_hdmi_ctx); return -ENOMEM; } @@ -1072,17 +1059,10 @@ fail: static int mixer_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *drm_hdmi_ctx = - platform_get_drvdata(pdev); - struct mixer_context *ctx = drm_hdmi_ctx->ctx; - - dev_info(dev, "remove successful\n"); + dev_info(&pdev->dev, "remove successful\n"); pm_runtime_disable(&pdev->dev); - mixer_resources_cleanup(ctx); - return 0; } diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 68733587e700..c20b00181530 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -107,11 +107,6 @@ struct drm_exynos_vidi_connection { uint64_t edid; }; -struct drm_exynos_plane_set_zpos { - __u32 plane_id; - __s32 zpos; -}; - /* memory type definitions. */ enum e_drm_exynos_gem_mem_type { /* Physically Continuous memory and used as default. */ @@ -164,7 +159,6 @@ struct drm_exynos_g2d_exec { #define DRM_EXYNOS_GEM_MMAP 0x02 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ #define DRM_EXYNOS_GEM_GET 0x04 -#define DRM_EXYNOS_PLANE_SET_ZPOS 0x06 #define DRM_EXYNOS_VIDI_CONNECTION 0x07 /* G2D */ @@ -184,9 +178,6 @@ struct drm_exynos_g2d_exec { #define DRM_IOCTL_EXYNOS_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_GEM_GET, struct drm_exynos_gem_info) -#define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS DRM_IOWR(DRM_COMMAND_BASE + \ - DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos) - #define DRM_IOCTL_EXYNOS_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection) |