Commit 3e54f5b7 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

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
parents d0d110e0 864ee9e6
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ 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_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) += exynosdrm.o
obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
...@@ -52,11 +52,13 @@ ...@@ -52,11 +52,13 @@
* drm framework doesn't support multiple irq yet. * drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occured through * we can refer to the crtc to current hardware interrupt occured through
* this pipe value. * this pipe value.
* @dpms: store the crtc dpms value
*/ */
struct exynos_drm_crtc { struct exynos_drm_crtc {
struct drm_crtc drm_crtc; struct drm_crtc drm_crtc;
struct exynos_drm_overlay overlay; struct exynos_drm_overlay overlay;
unsigned int pipe; unsigned int pipe;
unsigned int dpms;
}; };
static void exynos_drm_crtc_apply(struct drm_crtc *crtc) static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
...@@ -153,26 +155,37 @@ static int exynos_drm_crtc_update(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) 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); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); 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) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, exynos_drm_fn_encoder(crtc, &mode,
exynos_drm_encoder_crtc_commit); exynos_drm_encoder_crtc_dpms);
exynos_crtc->dpms = mode;
break; break;
case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF: case DRM_MODE_DPMS_OFF:
/* TODO */ exynos_drm_fn_encoder(crtc, &mode,
exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_dpms);
exynos_drm_encoder_crtc_disable); exynos_crtc->dpms = mode;
break; break;
default: default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode); DRM_ERROR("unspecified mode %d\n", mode);
break; break;
} }
mutex_unlock(&dev->struct_mutex);
} }
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
...@@ -188,6 +201,28 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) ...@@ -188,6 +201,28 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
DRM_DEBUG_KMS("%s\n", __FILE__); 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_fn_encoder(crtc, &exynos_crtc->pipe,
exynos_drm_encoder_crtc_commit); exynos_drm_encoder_crtc_commit);
} }
...@@ -344,6 +379,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) ...@@ -344,6 +379,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
} }
exynos_crtc->pipe = nr; exynos_crtc->pipe = nr;
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->overlay.zpos = DEFAULT_ZPOS;
crtc = &exynos_crtc->drm_crtc; crtc = &exynos_crtc->drm_crtc;
private->crtc[nr] = crtc; private->crtc[nr] = crtc;
...@@ -357,9 +394,14 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) ...@@ -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) int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
{ {
struct exynos_drm_private *private = dev->dev_private; 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__); 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_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_enable_vblank); exynos_drm_enable_vblank);
...@@ -369,9 +411,14 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) ...@@ -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) void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
{ {
struct exynos_drm_private *private = dev->dev_private; 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__); 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_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank); exynos_drm_disable_vblank);
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "exynos_drm_fbdev.h" #include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h" #include "exynos_drm_fb.h"
#include "exynos_drm_gem.h" #include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
#define DRIVER_NAME "exynos-drm" #define DRIVER_NAME "exynos-drm"
#define DRIVER_DESC "Samsung SoC DRM" #define DRIVER_DESC "Samsung SoC DRM"
...@@ -77,6 +78,12 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -77,6 +78,12 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
goto err_crtc; 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); ret = drm_vblank_init(dev, MAX_CRTC);
if (ret) if (ret)
goto err_crtc; goto err_crtc;
...@@ -163,6 +170,18 @@ static struct drm_ioctl_desc exynos_ioctls[] = { ...@@ -163,6 +170,18 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
DRM_AUTH), DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP,
exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), 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 = { static struct drm_driver exynos_drm_driver = {
...@@ -182,15 +201,7 @@ 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_map_offset = exynos_drm_gem_dumb_map_offset,
.dumb_destroy = exynos_drm_gem_dumb_destroy, .dumb_destroy = exynos_drm_gem_dumb_destroy,
.ioctls = exynos_ioctls, .ioctls = exynos_ioctls,
.fops = { .fops = &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,
},
.name = DRIVER_NAME, .name = DRIVER_NAME,
.desc = DRIVER_DESC, .desc = DRIVER_DESC,
.date = DRIVER_DATE, .date = DRIVER_DATE,
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include "drm.h" #include "drm.h"
#define MAX_CRTC 2 #define MAX_CRTC 2
#define MAX_PLANE 5
#define DEFAULT_ZPOS -1
struct drm_device; struct drm_device;
struct exynos_drm_overlay; struct exynos_drm_overlay;
...@@ -57,8 +59,8 @@ enum exynos_drm_output_type { ...@@ -57,8 +59,8 @@ enum exynos_drm_output_type {
struct exynos_drm_overlay_ops { struct exynos_drm_overlay_ops {
void (*mode_set)(struct device *subdrv_dev, void (*mode_set)(struct device *subdrv_dev,
struct exynos_drm_overlay *overlay); struct exynos_drm_overlay *overlay);
void (*commit)(struct device *subdrv_dev); void (*commit)(struct device *subdrv_dev, int zpos);
void (*disable)(struct device *subdrv_dev); void (*disable)(struct device *subdrv_dev, int zpos);
}; };
/* /*
...@@ -83,6 +85,7 @@ struct exynos_drm_overlay_ops { ...@@ -83,6 +85,7 @@ struct exynos_drm_overlay_ops {
* @dma_addr: bus(accessed by dma) address to the memory region allocated * @dma_addr: bus(accessed by dma) address to the memory region allocated
* for a overlay. * for a overlay.
* @vaddr: virtual memory addresss to this overlay. * @vaddr: virtual memory addresss to this overlay.
* @zpos: order of overlay layer(z position).
* @default_win: a window to be enabled. * @default_win: a window to be enabled.
* @color_key: color key on or off. * @color_key: color key on or off.
* @index_color: if using color key feature then this value would be used * @index_color: if using color key feature then this value would be used
...@@ -111,6 +114,7 @@ struct exynos_drm_overlay { ...@@ -111,6 +114,7 @@ struct exynos_drm_overlay {
unsigned int pitch; unsigned int pitch;
dma_addr_t dma_addr; dma_addr_t dma_addr;
void __iomem *vaddr; void __iomem *vaddr;
int zpos;
bool default_win; bool default_win;
bool color_key; bool color_key;
...@@ -144,17 +148,19 @@ struct exynos_drm_display_ops { ...@@ -144,17 +148,19 @@ struct exynos_drm_display_ops {
/* /*
* Exynos drm manager 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 * @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set(). * would be called by encoder->mode_set().
* @commit: set current hw specific display mode to hw. * @commit: set current hw specific display mode to hw.
* @disable: disable hardware specific display mode.
* @enable_vblank: specific driver callback for enabling vblank interrupt. * @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt.
*/ */
struct exynos_drm_manager_ops { 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 (*mode_set)(struct device *subdrv_dev, void *mode);
void (*commit)(struct device *subdrv_dev); void (*commit)(struct device *subdrv_dev);
void (*disable)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev); int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev); void (*disable_vblank)(struct device *subdrv_dev);
}; };
......
...@@ -42,49 +42,68 @@ ...@@ -42,49 +42,68 @@
* @drm_encoder: encoder object. * @drm_encoder: encoder object.
* @manager: specific encoder has its own manager to control a hardware * @manager: specific encoder has its own manager to control a hardware
* appropriately and we can access a hardware drawing on this manager. * appropriately and we can access a hardware drawing on this manager.
* @dpms: store the encoder dpms value.
*/ */
struct exynos_drm_encoder { struct exynos_drm_encoder {
struct drm_encoder drm_encoder; struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager; 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_device *dev = encoder->dev;
struct drm_connector *connector; struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); 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_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); 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) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->commit) if (manager_ops && manager_ops->apply)
manager_ops->commit(manager->dev); manager_ops->apply(manager->dev);
exynos_drm_display_power(encoder, mode);
exynos_encoder->dpms = mode;
break; break;
case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF: case DRM_MODE_DPMS_OFF:
/* TODO */ exynos_drm_display_power(encoder, mode);
if (manager_ops && manager_ops->disable) exynos_encoder->dpms = mode;
manager_ops->disable(manager->dev);
break; break;
default: default:
DRM_ERROR("unspecified mode %d\n", mode); DRM_ERROR("unspecified mode %d\n", mode);
break; break;
} }
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { mutex_unlock(&dev->struct_mutex);
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 bool static bool
...@@ -169,7 +188,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) ...@@ -169,7 +188,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
exynos_encoder->manager->pipe = -1; exynos_encoder->manager->pipe = -1;
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
encoder->dev->mode_config.num_encoder--;
kfree(exynos_encoder); kfree(exynos_encoder);
} }
...@@ -199,6 +217,7 @@ exynos_drm_encoder_create(struct drm_device *dev, ...@@ -199,6 +217,7 @@ exynos_drm_encoder_create(struct drm_device *dev,
return NULL; return NULL;
} }
exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
exynos_encoder->manager = manager; exynos_encoder->manager = manager;
encoder = &exynos_encoder->drm_encoder; encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs; encoder->possible_crtcs = possible_crtcs;
...@@ -275,12 +294,27 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) ...@@ -275,12 +294,27 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
manager_ops->disable_vblank(manager->dev); 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 = struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager; to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; 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 crtc = *(int *)data;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
...@@ -290,8 +324,53 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) ...@@ -290,8 +324,53 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
*/ */
manager->pipe = crtc; manager->pipe = crtc;
if (overlay_ops && overlay_ops->commit) exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
overlay_ops->commit(manager->dev); }
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) 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) ...@@ -310,19 +389,15 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
struct exynos_drm_manager *manager = struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager; to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("\n"); DRM_DEBUG_KMS("\n");
if (overlay_ops && overlay_ops->disable) if (data)
overlay_ops->disable(manager->dev); zpos = *(int *)data;
/* if (overlay_ops && overlay_ops->disable)
* crtc is already detached from encoder and last overlay_ops->disable(manager->dev, zpos);
* function for detaching is properly done, so
* clear pipe from manager to prevent repeated call
*/
if (!encoder->crtc)
manager->pipe = -1;
} }
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
......
...@@ -39,7 +39,12 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, ...@@ -39,7 +39,12 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *)); void (*fn)(struct drm_encoder *, void *));
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); 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_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_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_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_disable(struct drm_encoder *encoder, void *data);
......
...@@ -104,7 +104,7 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { ...@@ -104,7 +104,7 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
static struct drm_framebuffer * static struct drm_framebuffer *
exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, 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 exynos_drm_fb *exynos_fb;
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
...@@ -115,9 +115,6 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, ...@@ -115,9 +115,6 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
DRM_DEBUG_KMS("%s\n", __FILE__); 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", DRM_LOG_KMS("drm fb create(%dx%d)\n",
mode_cmd->width, mode_cmd->height); mode_cmd->width, mode_cmd->height);
...@@ -136,14 +133,14 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, ...@@ -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); 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 * with user request. if NULL, a new buffer or a gem object
* would be allocated. * would be allocated.
*/ */
if (!mode_cmd->handle) { if (!mode_cmd->handles[0]) {
if (!file_priv) { if (!file_priv) {
struct exynos_drm_gem_buf *buffer; struct exynos_drm_gem_buf *buffer;
...@@ -166,7 +163,7 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, ...@@ -166,7 +163,7 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
goto out; goto out;
} else { } else {
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
&mode_cmd->handle, &mode_cmd->handles[0],
size); size);
if (IS_ERR(exynos_gem_obj)) { if (IS_ERR(exynos_gem_obj)) {
ret = PTR_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, ...@@ -174,7 +171,8 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
} }
} }
} else { } 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) { if (!obj) {
DRM_ERROR("failed to lookup gem object.\n"); DRM_ERROR("failed to lookup gem object.\n");
goto err_buffer; goto err_buffer;
...@@ -214,8 +212,8 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, ...@@ -214,8 +212,8 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
} }
struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
struct drm_file *file_priv, struct drm_file *file_priv,
struct drm_mode_fb_cmd *mode_cmd) struct drm_mode_fb_cmd2 *mode_cmd)
{ {
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
#define _EXYNOS_DRM_FB_H #define _EXYNOS_DRM_FB_H
struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
struct drm_file *filp, struct drm_file *filp,
struct drm_mode_fb_cmd *mode_cmd); struct drm_mode_fb_cmd2 *mode_cmd);
void exynos_drm_mode_config_init(struct drm_device *dev); void exynos_drm_mode_config_init(struct drm_device *dev);
......
...@@ -126,7 +126,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, ...@@ -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 exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
struct drm_device *dev = helper->dev; struct drm_device *dev = helper->dev;
struct fb_info *fbi; 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; struct platform_device *pdev = dev->platformdev;
int ret; int ret;
...@@ -138,8 +138,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, ...@@ -138,8 +138,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
mode_cmd.width = sizes->surface_width; mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height; mode_cmd.height = sizes->surface_height;
mode_cmd.bpp = sizes->surface_bpp; mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
mode_cmd.depth = sizes->surface_depth; mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -206,7 +207,7 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, ...@@ -206,7 +207,7 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
struct drm_device *dev = helper->dev; struct drm_device *dev = helper->dev;
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
struct drm_framebuffer *fb = exynos_fbdev->fb; 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__); DRM_DEBUG_KMS("%s\n", __FILE__);
...@@ -220,8 +221,9 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, ...@@ -220,8 +221,9 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
mode_cmd.width = sizes->surface_width; mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height; mode_cmd.height = sizes->surface_height;
mode_cmd.bpp = sizes->surface_bpp; mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
mode_cmd.depth = sizes->surface_depth; mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
if (fb->funcs->destroy) if (fb->funcs->destroy)
fb->funcs->destroy(fb); fb->funcs->destroy(fb);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#include <plat/regs-fb-v4.h> #include <plat/regs-fb-v4.h>
...@@ -68,6 +69,7 @@ struct fimd_win_data { ...@@ -68,6 +69,7 @@ struct fimd_win_data {
void __iomem *vaddr; void __iomem *vaddr;
unsigned int buf_offsize; unsigned int buf_offsize;
unsigned int line_size; /* bytes */ unsigned int line_size; /* bytes */
bool enabled;
}; };
struct fimd_context { struct fimd_context {
...@@ -84,6 +86,7 @@ struct fimd_context { ...@@ -84,6 +86,7 @@ struct fimd_context {
unsigned long irq_flags; unsigned long irq_flags;
u32 vidcon0; u32 vidcon0;
u32 vidcon1; u32 vidcon1;
bool suspended;
struct fb_videomode *timing; struct fb_videomode *timing;
}; };
...@@ -119,7 +122,7 @@ static int fimd_display_power_on(struct device *dev, int mode) ...@@ -119,7 +122,7 @@ static int fimd_display_power_on(struct device *dev, int mode)
{ {
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */ /* TODO */
return 0; return 0;
} }
...@@ -132,6 +135,46 @@ static struct exynos_drm_display_ops fimd_display_ops = { ...@@ -132,6 +135,46 @@ static struct exynos_drm_display_ops fimd_display_ops = {
.power_on = fimd_display_power_on, .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) static void fimd_commit(struct device *dev)
{ {
struct fimd_context *ctx = get_fimd_context(dev); struct fimd_context *ctx = get_fimd_context(dev);
...@@ -177,40 +220,6 @@ static void fimd_commit(struct device *dev) ...@@ -177,40 +220,6 @@ static void fimd_commit(struct device *dev)
writel(val, ctx->regs + VIDCON0); 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) static int fimd_enable_vblank(struct device *dev)
{ {
struct fimd_context *ctx = get_fimd_context(dev); struct fimd_context *ctx = get_fimd_context(dev);
...@@ -218,6 +227,9 @@ static int fimd_enable_vblank(struct device *dev) ...@@ -218,6 +227,9 @@ static int fimd_enable_vblank(struct device *dev)
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (ctx->suspended)
return -EPERM;
if (!test_and_set_bit(0, &ctx->irq_flags)) { if (!test_and_set_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0); val = readl(ctx->regs + VIDINTCON0);
...@@ -242,6 +254,9 @@ static void fimd_disable_vblank(struct device *dev) ...@@ -242,6 +254,9 @@ static void fimd_disable_vblank(struct device *dev)
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (ctx->suspended)
return;
if (test_and_clear_bit(0, &ctx->irq_flags)) { if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0); val = readl(ctx->regs + VIDINTCON0);
...@@ -253,8 +268,9 @@ static void fimd_disable_vblank(struct device *dev) ...@@ -253,8 +268,9 @@ static void fimd_disable_vblank(struct device *dev)
} }
static struct exynos_drm_manager_ops fimd_manager_ops = { static struct exynos_drm_manager_ops fimd_manager_ops = {
.dpms = fimd_dpms,
.apply = fimd_apply,
.commit = fimd_commit, .commit = fimd_commit,
.disable = fimd_disable,
.enable_vblank = fimd_enable_vblank, .enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank, .disable_vblank = fimd_disable_vblank,
}; };
...@@ -264,6 +280,7 @@ static void fimd_win_mode_set(struct device *dev, ...@@ -264,6 +280,7 @@ static void fimd_win_mode_set(struct device *dev,
{ {
struct fimd_context *ctx = get_fimd_context(dev); struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int win;
unsigned long offset; unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
...@@ -273,12 +290,19 @@ static void fimd_win_mode_set(struct device *dev, ...@@ -273,12 +290,19 @@ static void fimd_win_mode_set(struct device *dev,
return; 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_x * (overlay->bpp >> 3);
offset += overlay->fb_y * overlay->pitch; offset += overlay->fb_y * overlay->pitch;
DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, 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_x = overlay->crtc_x;
win_data->offset_y = overlay->crtc_y; win_data->offset_y = overlay->crtc_y;
...@@ -381,15 +405,18 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win) ...@@ -381,15 +405,18 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win)
writel(keycon1, ctx->regs + WKEYCON1_BASE(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_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int win = ctx->default_win; int win = zpos;
unsigned long val, alpha, size; unsigned long val, alpha, size;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (win == DEFAULT_ZPOS)
win = ctx->default_win;
if (win < 0 || win > WINDOWS_NR) if (win < 0 || win > WINDOWS_NR)
return; return;
...@@ -472,24 +499,37 @@ static void fimd_win_commit(struct device *dev) ...@@ -472,24 +499,37 @@ static void fimd_win_commit(struct device *dev)
if (win != 0) if (win != 0)
fimd_win_set_colkey(dev, win); 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 */ /* Enable DMA channel and unprotect windows */
val = readl(ctx->regs + SHADOWCON); val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_CHx_ENABLE(win); val |= SHADOWCON_CHx_ENABLE(win);
val &= ~SHADOWCON_WINx_PROTECT(win); val &= ~SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON); 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); struct fimd_context *ctx = get_fimd_context(dev);
int win = ctx->default_win; struct fimd_win_data *win_data;
int win = zpos;
u32 val; u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (win == DEFAULT_ZPOS)
win = ctx->default_win;
if (win < 0 || win > WINDOWS_NR) if (win < 0 || win > WINDOWS_NR)
return; return;
win_data = &ctx->win_data[win];
/* protect windows */ /* protect windows */
val = readl(ctx->regs + SHADOWCON); val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_WINx_PROTECT(win); val |= SHADOWCON_WINx_PROTECT(win);
...@@ -505,6 +545,8 @@ static void fimd_win_disable(struct device *dev) ...@@ -505,6 +545,8 @@ static void fimd_win_disable(struct device *dev)
val &= ~SHADOWCON_CHx_ENABLE(win); val &= ~SHADOWCON_CHx_ENABLE(win);
val &= ~SHADOWCON_WINx_PROTECT(win); val &= ~SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON); writel(val, ctx->regs + SHADOWCON);
win_data->enabled = false;
} }
static struct exynos_drm_overlay_ops fimd_overlay_ops = { 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) ...@@ -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); wake_up_interruptible(&e->base.file_priv->event_wait);
} }
if (is_checked) if (is_checked) {
drm_vblank_put(drm_dev, crtc); 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); spin_unlock_irqrestore(&drm_dev->event_lock, flags);
} }
...@@ -560,19 +610,14 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) ...@@ -560,19 +610,14 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
/* VSYNC interrupt */ /* VSYNC interrupt */
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
/* /* check the crtc is detached already from encoder */
* in case that vblank_disable_allowed is 1, it could induce if (manager->pipe < 0)
* the problem that manager->pipe could be -1 because with goto out;
* 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;
drm_handle_vblank(drm_dev, manager->pipe); drm_handle_vblank(drm_dev, manager->pipe);
fimd_finish_pageflip(drm_dev, manager->pipe); fimd_finish_pageflip(drm_dev, manager->pipe);
out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -590,6 +635,13 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) ...@@ -590,6 +635,13 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
*/ */
drm_dev->irq_enabled = 1; 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; return 0;
} }
...@@ -739,15 +791,19 @@ static int __devinit fimd_probe(struct platform_device *pdev) ...@@ -739,15 +791,19 @@ static int __devinit fimd_probe(struct platform_device *pdev)
ctx->irq = res->start; 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); ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "irq request failed.\n"); dev_err(dev, "irq request failed.\n");
goto err_req_irq; 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->clkdiv = fimd_calc_clkdiv(ctx, timing);
ctx->vidcon0 = pdata->vidcon0; ctx->vidcon0 = pdata->vidcon0;
ctx->vidcon1 = pdata->vidcon1; ctx->vidcon1 = pdata->vidcon1;
...@@ -797,14 +853,25 @@ static int __devinit fimd_probe(struct platform_device *pdev) ...@@ -797,14 +853,25 @@ static int __devinit fimd_probe(struct platform_device *pdev)
static int __devexit fimd_remove(struct platform_device *pdev) static int __devexit fimd_remove(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct fimd_context *ctx = platform_get_drvdata(pdev); struct fimd_context *ctx = platform_get_drvdata(pdev);
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_subdrv_unregister(&ctx->subdrv); exynos_drm_subdrv_unregister(&ctx->subdrv);
if (ctx->suspended)
goto out;
clk_disable(ctx->lcd_clk); clk_disable(ctx->lcd_clk);
clk_disable(ctx->bus_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->lcd_clk);
clk_put(ctx->bus_clk); clk_put(ctx->bus_clk);
...@@ -818,12 +885,53 @@ static int __devexit fimd_remove(struct platform_device *pdev) ...@@ -818,12 +885,53 @@ static int __devexit fimd_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver fimd_driver = {
.probe = fimd_probe, .probe = fimd_probe,
.remove = __devexit_p(fimd_remove), .remove = __devexit_p(fimd_remove),
.driver = { .driver = {
.name = "exynos4-fb", .name = "exynos4-fb",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &fimd_pm_ops,
}, },
}; };
......
/*
* 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;
}
/*
* 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);
...@@ -74,9 +74,16 @@ struct drm_exynos_gem_mmap { ...@@ -74,9 +74,16 @@ struct drm_exynos_gem_mmap {
uint64_t mapped; uint64_t mapped;
}; };
struct drm_exynos_plane_set_zpos {
__u32 plane_id;
__s32 zpos;
};
#define DRM_EXYNOS_GEM_CREATE 0x00 #define DRM_EXYNOS_GEM_CREATE 0x00
#define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01
#define DRM_EXYNOS_GEM_MMAP 0x02 #define DRM_EXYNOS_GEM_MMAP 0x02
/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
#define DRM_EXYNOS_PLANE_SET_ZPOS 0x06
#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
...@@ -87,6 +94,9 @@ struct drm_exynos_gem_mmap { ...@@ -87,6 +94,9 @@ struct drm_exynos_gem_mmap {
#define DRM_IOCTL_EXYNOS_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + \ #define DRM_IOCTL_EXYNOS_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_GEM_MMAP, struct drm_exynos_gem_mmap) DRM_EXYNOS_GEM_MMAP, struct drm_exynos_gem_mmap)
#define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos)
/** /**
* Platform Specific Structure for DRM based FIMD. * Platform Specific Structure for DRM based FIMD.
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment