diff options
author | Dave Airlie <airlied@redhat.com> | 2011-12-21 07:58:08 +0000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-12-21 07:58:08 +0000 |
commit | 3e54f5b72b44a5b905dc56442b147b0ee0475a1d (patch) | |
tree | dcb8ad130f42bac96fe3f03a234ffbcbe0c58bd2 /drivers | |
parent | d0d110e096298d2715aa26b3698e604e0d4a2fb9 (diff) | |
parent | 864ee9e6f643b479e0469c9865cae238590d5f6e (diff) | |
download | op-kernel-dev-3e54f5b72b44a5b905dc56442b147b0ee0475a1d.zip op-kernel-dev-3e54f5b72b44a5b905dc56442b147b0ee0475a1d.tar.gz |
Merge branch 'exynos-drm-next' of git://git.infradead.org/users/kmpark/linux-samsung into drm-core-next
* 'exynos-drm-next' of git://git.infradead.org/users/kmpark/linux-samsung:
drm/exynos: Add plane support with fimd
drm/exynos: add runtime pm feature for fimd
drm/exynos: updated crtc and encoder dpms framework.
drm/exynos: Use struct drm_mode_fb_cmd2
drm/exynos: Fix compile errors
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/exynos/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 29 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_encoder.c | 135 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_encoder.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 216 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.c | 163 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.h | 14 |
12 files changed, 553 insertions, 123 deletions
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 0496d3f..c991272 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -5,7 +5,8 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \ - exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o + exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \ + exynos_drm_plane.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 7777d41..e1ce9fd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -52,11 +52,13 @@ * drm framework doesn't support multiple irq yet. * we can refer to the crtc to current hardware interrupt occured through * this pipe value. + * @dpms: store the crtc dpms value */ struct exynos_drm_crtc { struct drm_crtc drm_crtc; struct exynos_drm_overlay overlay; unsigned int pipe; + unsigned int dpms; }; static void exynos_drm_crtc_apply(struct drm_crtc *crtc) @@ -153,26 +155,37 @@ static int exynos_drm_crtc_update(struct drm_crtc *crtc) static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) { + struct drm_device *dev = crtc->dev; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); + if (exynos_crtc->dpms == mode) { + DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); + return; + } + + mutex_lock(&dev->struct_mutex); + switch (mode) { case DRM_MODE_DPMS_ON: - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); + 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: - /* TODO */ - exynos_drm_fn_encoder(crtc, NULL, - exynos_drm_encoder_crtc_disable); + exynos_drm_fn_encoder(crtc, &mode, + exynos_drm_encoder_crtc_dpms); + exynos_crtc->dpms = mode; break; default: - DRM_DEBUG_KMS("unspecified mode %d\n", mode); + DRM_ERROR("unspecified mode %d\n", mode); break; } + + mutex_unlock(&dev->struct_mutex); } static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) @@ -188,6 +201,28 @@ 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); } @@ -344,6 +379,8 @@ 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; crtc = &exynos_crtc->drm_crtc; private->crtc[nr] = crtc; @@ -357,9 +394,14 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) { struct exynos_drm_private *private = dev->dev_private; + struct exynos_drm_crtc *exynos_crtc = + to_exynos_crtc(private->crtc[crtc]); DRM_DEBUG_KMS("%s\n", __FILE__); + if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) + return -EPERM; + exynos_drm_fn_encoder(private->crtc[crtc], &crtc, exynos_drm_enable_vblank); @@ -369,9 +411,14 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) { struct exynos_drm_private *private = dev->dev_private; + struct exynos_drm_crtc *exynos_crtc = + to_exynos_crtc(private->crtc[crtc]); DRM_DEBUG_KMS("%s\n", __FILE__); + if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) + return; + exynos_drm_fn_encoder(private->crtc[crtc], &crtc, exynos_drm_disable_vblank); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 53e2216..050684c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -36,6 +36,7 @@ #include "exynos_drm_fbdev.h" #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" +#include "exynos_drm_plane.h" #define DRIVER_NAME "exynos-drm" #define DRIVER_DESC "Samsung SoC DRM" @@ -77,6 +78,12 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) goto err_crtc; } + for (nr = 0; nr < MAX_PLANE; nr++) { + ret = exynos_plane_init(dev, nr); + if (ret) + goto err_crtc; + } + ret = drm_vblank_init(dev, MAX_CRTC); if (ret) goto err_crtc; @@ -163,6 +170,18 @@ static struct drm_ioctl_desc exynos_ioctls[] = { DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, + DRM_UNLOCKED | DRM_AUTH), +}; + +static const struct file_operations exynos_drm_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = exynos_drm_gem_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, }; static struct drm_driver exynos_drm_driver = { @@ -182,15 +201,7 @@ static struct drm_driver exynos_drm_driver = { .dumb_map_offset = exynos_drm_gem_dumb_map_offset, .dumb_destroy = exynos_drm_gem_dumb_destroy, .ioctls = exynos_ioctls, - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = exynos_drm_gem_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .release = drm_release, - }, + .fops = &exynos_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 5e02e6e..8e8d8f0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -33,6 +33,8 @@ #include "drm.h" #define MAX_CRTC 2 +#define MAX_PLANE 5 +#define DEFAULT_ZPOS -1 struct drm_device; struct exynos_drm_overlay; @@ -57,8 +59,8 @@ enum exynos_drm_output_type { struct exynos_drm_overlay_ops { void (*mode_set)(struct device *subdrv_dev, struct exynos_drm_overlay *overlay); - void (*commit)(struct device *subdrv_dev); - void (*disable)(struct device *subdrv_dev); + void (*commit)(struct device *subdrv_dev, int zpos); + void (*disable)(struct device *subdrv_dev, int zpos); }; /* @@ -83,6 +85,7 @@ struct exynos_drm_overlay_ops { * @dma_addr: bus(accessed by dma) address to the memory region allocated * for a overlay. * @vaddr: virtual memory addresss to this overlay. + * @zpos: order of overlay layer(z position). * @default_win: a window to be enabled. * @color_key: color key on or off. * @index_color: if using color key feature then this value would be used @@ -111,6 +114,7 @@ struct exynos_drm_overlay { unsigned int pitch; dma_addr_t dma_addr; void __iomem *vaddr; + int zpos; bool default_win; bool color_key; @@ -144,17 +148,19 @@ struct exynos_drm_display_ops { /* * Exynos drm manager ops * + * @dpms: control device power. + * @apply: set timing, vblank and overlay data to registers. * @mode_set: convert drm_display_mode to hw specific display mode and * would be called by encoder->mode_set(). * @commit: set current hw specific display mode to hw. - * @disable: disable hardware specific display mode. * @enable_vblank: specific driver callback for enabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt. */ struct exynos_drm_manager_ops { + void (*dpms)(struct device *subdrv_dev, int mode); + void (*apply)(struct device *subdrv_dev); void (*mode_set)(struct device *subdrv_dev, void *mode); void (*commit)(struct device *subdrv_dev); - void (*disable)(struct device *subdrv_dev); int (*enable_vblank)(struct device *subdrv_dev); void (*disable_vblank)(struct device *subdrv_dev); }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 1530614..86b93dd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -42,49 +42,68 @@ * @drm_encoder: encoder object. * @manager: specific encoder has its own manager to control a hardware * appropriately and we can access a hardware drawing on this manager. + * @dpms: store the encoder dpms value. */ struct exynos_drm_encoder { struct drm_encoder drm_encoder; struct exynos_drm_manager *manager; + int dpms; }; -static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) +static void exynos_drm_display_power(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; struct drm_connector *connector; struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + struct exynos_drm_display_ops *display_ops = + manager->display_ops; + + DRM_DEBUG_KMS("connector[%d] dpms[%d]\n", + connector->base.id, mode); + if (display_ops && display_ops->power_on) + display_ops->power_on(manager->dev, mode); + } + } +} + +static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager_ops *manager_ops = manager->ops; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); + if (exynos_encoder->dpms == mode) { + DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); + return; + } + + mutex_lock(&dev->struct_mutex); + switch (mode) { case DRM_MODE_DPMS_ON: - if (manager_ops && manager_ops->commit) - manager_ops->commit(manager->dev); + if (manager_ops && manager_ops->apply) + manager_ops->apply(manager->dev); + exynos_drm_display_power(encoder, mode); + exynos_encoder->dpms = mode; break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - /* TODO */ - if (manager_ops && manager_ops->disable) - manager_ops->disable(manager->dev); + exynos_drm_display_power(encoder, mode); + exynos_encoder->dpms = mode; break; default: DRM_ERROR("unspecified mode %d\n", mode); break; } - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - struct exynos_drm_display_ops *display_ops = - manager->display_ops; - - DRM_DEBUG_KMS("connector[%d] dpms[%d]\n", - connector->base.id, mode); - if (display_ops && display_ops->power_on) - display_ops->power_on(manager->dev, mode); - } - } + mutex_unlock(&dev->struct_mutex); } static bool @@ -169,7 +188,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) exynos_encoder->manager->pipe = -1; drm_encoder_cleanup(encoder); - encoder->dev->mode_config.num_encoder--; kfree(exynos_encoder); } @@ -199,6 +217,7 @@ exynos_drm_encoder_create(struct drm_device *dev, return NULL; } + exynos_encoder->dpms = DRM_MODE_DPMS_OFF; exynos_encoder->manager = manager; encoder = &exynos_encoder->drm_encoder; encoder->possible_crtcs = possible_crtcs; @@ -275,12 +294,27 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) manager_ops->disable_vblank(manager->dev); } -void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) +void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder, + void *data) { struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + int zpos = DEFAULT_ZPOS; + + if (data) + zpos = *(int *)data; + + if (overlay_ops && overlay_ops->commit) + overlay_ops->commit(manager->dev, zpos); +} + +void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) +{ + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; int crtc = *(int *)data; + int zpos = DEFAULT_ZPOS; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -290,8 +324,53 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) */ manager->pipe = crtc; - if (overlay_ops && overlay_ops->commit) - overlay_ops->commit(manager->dev); + exynos_drm_encoder_crtc_plane_commit(encoder, &zpos); +} + +void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data) +{ + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + int mode = *(int *)data; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_drm_encoder_dpms(encoder, mode); + + exynos_encoder->dpms = mode; +} + +void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) +{ + struct drm_device *dev = encoder->dev; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + struct exynos_drm_manager *manager = exynos_encoder->manager; + struct exynos_drm_manager_ops *manager_ops = manager->ops; + struct drm_connector *connector; + int mode = *(int *)data; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (manager_ops && manager_ops->dpms) + manager_ops->dpms(manager->dev, mode); + + /* + * set current dpms mode to the connector connected to + * current encoder. connector->dpms would be checked + * at drm_helper_connector_dpms() + */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + if (connector->encoder == encoder) + connector->dpms = mode; + + /* + * if this condition is ok then it means that the crtc is already + * detached from encoder and last function for detaching is properly + * done, so clear pipe from manager to prevent repeated call. + */ + if (mode > DRM_MODE_DPMS_ON) { + if (!encoder->crtc) + manager->pipe = -1; + } } void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) @@ -310,19 +389,15 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) struct exynos_drm_manager *manager = to_exynos_encoder(encoder)->manager; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + int zpos = DEFAULT_ZPOS; DRM_DEBUG_KMS("\n"); - if (overlay_ops && overlay_ops->disable) - overlay_ops->disable(manager->dev); + if (data) + zpos = *(int *)data; - /* - * crtc is already detached from encoder and last - * function for detaching is properly done, so - * clear pipe from manager to prevent repeated call - */ - if (!encoder->crtc) - manager->pipe = -1; + if (overlay_ops && overlay_ops->disable) + overlay_ops->disable(manager->dev, zpos); } MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h index a22acfb..97b087a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -39,7 +39,12 @@ 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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 5bf4a1a..df5eec6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -104,7 +104,7 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { static struct drm_framebuffer * exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, - struct drm_mode_fb_cmd *mode_cmd) + struct drm_mode_fb_cmd2 *mode_cmd) { struct exynos_drm_fb *exynos_fb; struct drm_framebuffer *fb; @@ -115,9 +115,6 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, DRM_DEBUG_KMS("%s\n", __FILE__); - mode_cmd->pitch = max(mode_cmd->pitch, - mode_cmd->width * (mode_cmd->bpp >> 3)); - DRM_LOG_KMS("drm fb create(%dx%d)\n", mode_cmd->width, mode_cmd->height); @@ -136,14 +133,14 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, DRM_LOG_KMS("create: fb id: %d\n", fb->base.id); - size = mode_cmd->pitch * mode_cmd->height; + size = mode_cmd->pitches[0] * mode_cmd->height; /* - * mode_cmd->handle could be NULL at booting time or + * mode_cmd->handles[0] could be NULL at booting time or * with user request. if NULL, a new buffer or a gem object * would be allocated. */ - if (!mode_cmd->handle) { + if (!mode_cmd->handles[0]) { if (!file_priv) { struct exynos_drm_gem_buf *buffer; @@ -166,7 +163,7 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, goto out; } else { exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, - &mode_cmd->handle, + &mode_cmd->handles[0], size); if (IS_ERR(exynos_gem_obj)) { ret = PTR_ERR(exynos_gem_obj); @@ -174,7 +171,8 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, } } } else { - obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); + obj = drm_gem_object_lookup(dev, file_priv, + mode_cmd->handles[0]); if (!obj) { DRM_ERROR("failed to lookup gem object.\n"); goto err_buffer; @@ -214,8 +212,8 @@ err_init: } struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_mode_fb_cmd *mode_cmd) + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) { DRM_DEBUG_KMS("%s\n", __FILE__); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h index eb35931..52c5982 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h @@ -29,8 +29,8 @@ #define _EXYNOS_DRM_FB_H struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, - struct drm_file *filp, - struct drm_mode_fb_cmd *mode_cmd); + struct drm_file *filp, + struct drm_mode_fb_cmd2 *mode_cmd); void exynos_drm_mode_config_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index f79f768..c8b2784 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -126,7 +126,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); struct drm_device *dev = helper->dev; struct fb_info *fbi; - struct drm_mode_fb_cmd mode_cmd = { 0 }; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; struct platform_device *pdev = dev->platformdev; int ret; @@ -138,8 +138,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; - mode_cmd.bpp = sizes->surface_bpp; - mode_cmd.depth = sizes->surface_depth; + mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); mutex_lock(&dev->struct_mutex); @@ -206,7 +207,7 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, struct drm_device *dev = helper->dev; struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); struct drm_framebuffer *fb = exynos_fbdev->fb; - struct drm_mode_fb_cmd mode_cmd = { 0 }; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -220,8 +221,9 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; - mode_cmd.bpp = sizes->surface_bpp; - mode_cmd.depth = sizes->surface_depth; + mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); if (fb->funcs->destroy) fb->funcs->destroy(fb); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index db3b3d9..fe4172e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/pm_runtime.h> #include <drm/exynos_drm.h> #include <plat/regs-fb-v4.h> @@ -68,6 +69,7 @@ struct fimd_win_data { void __iomem *vaddr; unsigned int buf_offsize; unsigned int line_size; /* bytes */ + bool enabled; }; struct fimd_context { @@ -84,6 +86,7 @@ struct fimd_context { unsigned long irq_flags; u32 vidcon0; u32 vidcon1; + bool suspended; struct fb_videomode *timing; }; @@ -119,7 +122,7 @@ static int fimd_display_power_on(struct device *dev, int mode) { DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO. */ + /* TODO */ return 0; } @@ -132,6 +135,46 @@ static struct exynos_drm_display_ops fimd_display_ops = { .power_on = fimd_display_power_on, }; +static void fimd_dpms(struct device *subdrv_dev, int mode) +{ + DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); + + switch (mode) { + case DRM_MODE_DPMS_ON: + pm_runtime_get_sync(subdrv_dev); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + pm_runtime_put_sync(subdrv_dev); + break; + default: + DRM_DEBUG_KMS("unspecified mode %d\n", mode); + break; + } +} + +static void fimd_apply(struct device *subdrv_dev) +{ + struct fimd_context *ctx = get_fimd_context(subdrv_dev); + struct exynos_drm_manager *mgr = &ctx->subdrv.manager; + struct exynos_drm_manager_ops *mgr_ops = mgr->ops; + struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; + struct fimd_win_data *win_data; + int i; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + for (i = 0; i < WINDOWS_NR; i++) { + win_data = &ctx->win_data[i]; + if (win_data->enabled && (ovl_ops && ovl_ops->commit)) + ovl_ops->commit(subdrv_dev, i); + } + + if (mgr_ops && mgr_ops->commit) + mgr_ops->commit(subdrv_dev); +} + static void fimd_commit(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); @@ -177,40 +220,6 @@ static void fimd_commit(struct device *dev) writel(val, ctx->regs + VIDCON0); } -static void fimd_disable(struct device *dev) -{ - struct fimd_context *ctx = get_fimd_context(dev); - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct drm_device *drm_dev = subdrv->drm_dev; - struct exynos_drm_manager *manager = &subdrv->manager; - u32 val; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* fimd dma off */ - val = readl(ctx->regs + VIDCON0); - val &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F); - writel(val, ctx->regs + VIDCON0); - - /* - * if vblank is enabled status with dma off then - * it disables vsync interrupt. - */ - if (drm_dev->vblank_enabled[manager->pipe] && - atomic_read(&drm_dev->vblank_refcount[manager->pipe])) { - drm_vblank_put(drm_dev, manager->pipe); - - /* - * if vblank_disable_allowed is 0 then disable - * vsync interrupt right now else the vsync interrupt - * would be disabled by drm timer once a current process - * gives up ownershop of vblank event. - */ - if (!drm_dev->vblank_disable_allowed) - drm_vblank_off(drm_dev, manager->pipe); - } -} - static int fimd_enable_vblank(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); @@ -218,6 +227,9 @@ static int fimd_enable_vblank(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); + if (ctx->suspended) + return -EPERM; + if (!test_and_set_bit(0, &ctx->irq_flags)) { val = readl(ctx->regs + VIDINTCON0); @@ -242,6 +254,9 @@ static void fimd_disable_vblank(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); + if (ctx->suspended) + return; + if (test_and_clear_bit(0, &ctx->irq_flags)) { val = readl(ctx->regs + VIDINTCON0); @@ -253,8 +268,9 @@ static void fimd_disable_vblank(struct device *dev) } static struct exynos_drm_manager_ops fimd_manager_ops = { + .dpms = fimd_dpms, + .apply = fimd_apply, .commit = fimd_commit, - .disable = fimd_disable, .enable_vblank = fimd_enable_vblank, .disable_vblank = fimd_disable_vblank, }; @@ -264,6 +280,7 @@ static void fimd_win_mode_set(struct device *dev, { struct fimd_context *ctx = get_fimd_context(dev); struct fimd_win_data *win_data; + int win; unsigned long offset; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -273,12 +290,19 @@ static void fimd_win_mode_set(struct device *dev, return; } + win = overlay->zpos; + if (win == DEFAULT_ZPOS) + win = ctx->default_win; + + if (win < 0 || win > WINDOWS_NR) + return; + offset = overlay->fb_x * (overlay->bpp >> 3); offset += overlay->fb_y * overlay->pitch; DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); - win_data = &ctx->win_data[ctx->default_win]; + win_data = &ctx->win_data[win]; win_data->offset_x = overlay->crtc_x; win_data->offset_y = overlay->crtc_y; @@ -381,15 +405,18 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win) writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); } -static void fimd_win_commit(struct device *dev) +static void fimd_win_commit(struct device *dev, int zpos) { struct fimd_context *ctx = get_fimd_context(dev); struct fimd_win_data *win_data; - int win = ctx->default_win; + int win = zpos; unsigned long val, alpha, size; DRM_DEBUG_KMS("%s\n", __FILE__); + if (win == DEFAULT_ZPOS) + win = ctx->default_win; + if (win < 0 || win > WINDOWS_NR) return; @@ -472,24 +499,37 @@ static void fimd_win_commit(struct device *dev) if (win != 0) fimd_win_set_colkey(dev, win); + /* wincon */ + val = readl(ctx->regs + WINCON(win)); + val |= WINCONx_ENWIN; + writel(val, ctx->regs + WINCON(win)); + /* Enable DMA channel and unprotect windows */ val = readl(ctx->regs + SHADOWCON); val |= SHADOWCON_CHx_ENABLE(win); val &= ~SHADOWCON_WINx_PROTECT(win); writel(val, ctx->regs + SHADOWCON); + + win_data->enabled = true; } -static void fimd_win_disable(struct device *dev) +static void fimd_win_disable(struct device *dev, int zpos) { struct fimd_context *ctx = get_fimd_context(dev); - int win = ctx->default_win; + struct fimd_win_data *win_data; + int win = zpos; u32 val; DRM_DEBUG_KMS("%s\n", __FILE__); + if (win == DEFAULT_ZPOS) + win = ctx->default_win; + if (win < 0 || win > WINDOWS_NR) return; + win_data = &ctx->win_data[win]; + /* protect windows */ val = readl(ctx->regs + SHADOWCON); val |= SHADOWCON_WINx_PROTECT(win); @@ -505,6 +545,8 @@ static void fimd_win_disable(struct device *dev) val &= ~SHADOWCON_CHx_ENABLE(win); val &= ~SHADOWCON_WINx_PROTECT(win); writel(val, ctx->regs + SHADOWCON); + + win_data->enabled = false; } static struct exynos_drm_overlay_ops fimd_overlay_ops = { @@ -540,9 +582,17 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) wake_up_interruptible(&e->base.file_priv->event_wait); } - if (is_checked) + if (is_checked) { drm_vblank_put(drm_dev, crtc); + /* + * don't off vblank if vblank_disable_allowed is 1, + * because vblank would be off by timer handler. + */ + if (!drm_dev->vblank_disable_allowed) + drm_vblank_off(drm_dev, crtc); + } + spin_unlock_irqrestore(&drm_dev->event_lock, flags); } @@ -560,19 +610,14 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) /* VSYNC interrupt */ writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); - /* - * in case that vblank_disable_allowed is 1, it could induce - * the problem that manager->pipe could be -1 because with - * disable callback, vsync interrupt isn't disabled and at this moment, - * vsync interrupt could occur. the vsync interrupt would be disabled - * by timer handler later. - */ - if (manager->pipe == -1) - return IRQ_HANDLED; + /* check the crtc is detached already from encoder */ + if (manager->pipe < 0) + goto out; drm_handle_vblank(drm_dev, manager->pipe); fimd_finish_pageflip(drm_dev, manager->pipe); +out: return IRQ_HANDLED; } @@ -590,6 +635,13 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) */ 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; } @@ -739,15 +791,19 @@ static int __devinit fimd_probe(struct platform_device *pdev) ctx->irq = res->start; - for (win = 0; win < WINDOWS_NR; win++) - fimd_clear_win(ctx, win); - ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); if (ret < 0) { dev_err(dev, "irq request failed.\n"); goto err_req_irq; } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + for (win = 0; win < WINDOWS_NR; win++) + fimd_clear_win(ctx, win); + ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); ctx->vidcon0 = pdata->vidcon0; ctx->vidcon1 = pdata->vidcon1; @@ -797,14 +853,25 @@ err_clk_get: static int __devexit fimd_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct fimd_context *ctx = platform_get_drvdata(pdev); DRM_DEBUG_KMS("%s\n", __FILE__); exynos_drm_subdrv_unregister(&ctx->subdrv); + if (ctx->suspended) + goto out; + clk_disable(ctx->lcd_clk); clk_disable(ctx->bus_clk); + + pm_runtime_set_suspended(dev); + pm_runtime_put_sync(dev); + +out: + pm_runtime_disable(dev); + clk_put(ctx->lcd_clk); clk_put(ctx->bus_clk); @@ -818,12 +885,53 @@ static int __devexit fimd_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int fimd_runtime_suspend(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + clk_disable(ctx->lcd_clk); + clk_disable(ctx->bus_clk); + + ctx->suspended = true; + return 0; +} + +static int fimd_runtime_resume(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + ret = clk_enable(ctx->bus_clk); + if (ret < 0) + return ret; + + ret = clk_enable(ctx->lcd_clk); + if (ret < 0) { + clk_disable(ctx->bus_clk); + return ret; + } + + ctx->suspended = false; + return 0; +} +#endif + +static const struct dev_pm_ops fimd_pm_ops = { + SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) +}; + static struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = __devexit_p(fimd_remove), .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, + .pm = &fimd_pm_ops, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c new file mode 100644 index 0000000..c785e34 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include "drmP.h" + +#include "exynos_drm.h" +#include "exynos_drm_crtc.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_encoder.h" + +struct exynos_plane { + struct drm_plane base; + struct exynos_drm_overlay overlay; + bool enabled; +}; + +static int +exynos_update_plane(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 = + container_of(plane, struct exynos_plane, base); + struct exynos_drm_overlay *overlay = &exynos_plane->overlay; + struct exynos_drm_crtc_pos pos; + unsigned int x = src_x >> 16; + unsigned int y = src_y >> 16; + 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; + + pos.fb_x = x; + pos.fb_y = y; + + /* TODO: scale feature */ + ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos); + 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); + + exynos_plane->enabled = true; + + 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; + + return 0; +} + +static void exynos_plane_destroy(struct drm_plane *plane) +{ + struct exynos_plane *exynos_plane = + container_of(plane, struct exynos_plane, base); + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + exynos_disable_plane(plane); + drm_plane_cleanup(plane); + kfree(exynos_plane); +} + +static struct drm_plane_funcs exynos_plane_funcs = { + .update_plane = exynos_update_plane, + .disable_plane = exynos_disable_plane, + .destroy = exynos_plane_destroy, +}; + +int exynos_plane_init(struct drm_device *dev, unsigned int nr) +{ + struct exynos_plane *exynos_plane; + uint32_t possible_crtcs; + + exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); + if (!exynos_plane) + return -ENOMEM; + + /* all CRTCs are available */ + possible_crtcs = (1 << MAX_CRTC) - 1; + + exynos_plane->overlay.zpos = DEFAULT_ZPOS; + + /* TODO: format */ + return drm_plane_init(dev, &exynos_plane->base, possible_crtcs, + &exynos_plane_funcs, NULL, 0); +} + +int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_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; + + 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; + } + } + + 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; + } + + plane = obj_to_plane(obj); + exynos_plane = container_of(plane, struct exynos_plane, base); + + exynos_plane->overlay.zpos = zpos_req->zpos; + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h new file mode 100644 index 0000000..16b71f8 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +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); |