summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInki Dae <inki.dae@samsung.com>2014-12-02 13:05:01 (GMT)
committerInki Dae <inki.dae@samsung.com>2015-06-08 08:56:43 (GMT)
commit3bdf6a624cd1225fa8fe2e6366ca6b63c8882c72 (patch)
tree1b277cbdbc6469781b779a8f963b9f41c8d2fd7d
parent6c0f62d75991cffb9f27e0e45a22e8ea4b06e7e7 (diff)
downloadlinux-3.10-tizen_LPD.zip
linux-3.10-tizen_LPD.tar.gz
linux-3.10-tizen_LPD.tar.bz2
drm/exynos: add LPD: Low Power Display mechanism supporttizen_LPD
The purpose of this mechanism is to transfer the only framebuffer region updated by X server to Display panel to save power consumed by Display relevant DMA devices. Change-Id: I2589c617d817833011e761c9c8835e287ff2fb7c Signed-off-by: Inki Dae <inki.dae@samsung.com>
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c34
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h7
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c13
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c64
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.h10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c85
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c23
-rw-r--r--drivers/gpu/drm/panel/panel-s6e63j0x03.c38
-rw-r--r--include/drm/drm_panel.h14
12 files changed, 311 insertions, 2 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 7e52a17..d35f778 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -594,3 +594,37 @@ int exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
return ret;
}
+
+void exynos_drm_crtc_adjust_partial_region(struct drm_crtc *crtc,
+ struct exynos_drm_partial_pos *pos)
+{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_manager *mgr = exynos_crtc->manager;
+
+ if (mgr && mgr->ops->adjust_partial_region)
+ mgr->ops->adjust_partial_region(mgr, &pos->x, &pos->y,
+ &pos->w, &pos->h);
+}
+
+void exynos_drm_crtc_change_resolution(struct drm_device *drm_dev,
+ unsigned int pipe, unsigned int x,
+ unsigned int y, unsigned int w,
+ unsigned int h)
+{
+ struct exynos_drm_private *private = drm_dev->dev_private;
+ struct exynos_drm_crtc *exynos_crtc =
+ to_exynos_crtc(private->crtc[pipe]);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
+ struct drm_encoder *encoder;
+
+ /* TODO. mutex_lock */
+
+ list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == &exynos_crtc->drm_crtc) {
+ exynos_drm_encoder_change_resolution(encoder, x, y,
+ w, h);
+ break;
+ }
+ }
+}
+
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index eb78c16..ee8e5c9 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -39,4 +39,14 @@ void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
*/
int exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
+struct exynos_drm_partial_pos;
+
+void exynos_drm_crtc_adjust_partial_region(struct drm_crtc *crtc,
+ struct exynos_drm_partial_pos *pos);
+
+void exynos_drm_crtc_change_resolution(struct drm_device *drm_dev,
+ unsigned int pipe, unsigned int x,
+ unsigned int y, unsigned int w,
+ unsigned int h);
+
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index d93adc9..03d770e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -146,6 +146,7 @@ struct exynos_drm_overlay {
* @check_mode: check if mode is valid or not.
* @dpms: display device on or off.
* @commit: apply changes to hw
+ * @set_partial_region: change region region to a given position.
*/
struct exynos_drm_display;
struct exynos_drm_display_ops {
@@ -164,6 +165,9 @@ struct exynos_drm_display_ops {
struct drm_display_mode *mode);
void (*dpms)(struct exynos_drm_display *display, int mode);
void (*commit)(struct exynos_drm_display *display);
+ void (*change_resolution)(struct exynos_drm_display *display,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h);
};
/*
@@ -226,6 +230,9 @@ struct exynos_drm_manager_ops {
void (*win_enable)(struct exynos_drm_manager *mgr, int zpos);
void (*win_disable)(struct exynos_drm_manager *mgr, int zpos);
int (*te_handler)(struct exynos_drm_manager *mgr);
+ void (*adjust_partial_region)(struct exynos_drm_manager *mgr,
+ unsigned int *x, unsigned int *y,
+ unsigned int *w, unsigned int *h);
};
/*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index ea3d7b7..19982ee 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1302,6 +1302,16 @@ static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
}
}
+
+static void exynos_dsi_change_resolution(struct exynos_drm_display *display,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ struct exynos_dsi *dsi = display->ctx;
+
+ drm_panel_change_resolution(dsi->panel, x, y, w, h);
+}
+
static enum drm_connector_status
exynos_dsi_detect(struct drm_connector *connector, bool force)
{
@@ -1412,7 +1422,8 @@ static void exynos_dsi_mode_set(struct exynos_drm_display *display,
static struct exynos_drm_display_ops exynos_dsi_display_ops = {
.create_connector = exynos_dsi_create_connector,
.mode_set = exynos_dsi_mode_set,
- .dpms = exynos_dsi_dpms
+ .dpms = exynos_dsi_dpms,
+ .change_resolution = exynos_dsi_change_resolution,
};
static struct exynos_drm_display exynos_dsi_display = {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 835c0f1..fad528e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -195,3 +195,14 @@ struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder)
{
return to_exynos_encoder(encoder)->display;
}
+
+void exynos_drm_encoder_change_resolution(struct drm_encoder *encoder,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ struct exynos_drm_display *display =
+ to_exynos_encoder(encoder)->display;
+
+ if (display && display->ops->change_resolution)
+ display->ops->change_resolution(display, x, y, w, h);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index b7a1620..0755cb3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -22,4 +22,8 @@ struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
unsigned long possible_crtcs);
struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder);
+void exynos_drm_encoder_change_resolution(struct drm_encoder *encoder,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h);
+
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index bef99bb..899e9c4 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -89,12 +89,74 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
&exynos_fb->exynos_gem_obj[0]->base, handle);
}
+static void exynos_drm_fb_adjust_clips(struct exynos_drm_partial_pos *pos,
+ struct drm_clip_rect *clips, unsigned int num_clips)
+{
+ unsigned int index;
+ unsigned int min_sx = 0;
+ unsigned int min_sy = 0;
+ unsigned int max_bx = 0;
+ unsigned int max_by = 0;
+
+ for (index = 0; index < num_clips; index++) {
+ min_sx = min_t(unsigned int, clips[index].x1, min_sx);
+ if (min_sx == 0 && index == 0)
+ min_sx = clips[index].x1;
+
+ min_sy = min_t(unsigned int, clips[index].y1, min_sy);
+ if (min_sy == 0 && index == 0)
+ min_sy = clips[index].y1;
+
+ max_bx = max_t(unsigned int, clips[index].x2, max_bx);
+ max_by = max_t(unsigned int, clips[index].y2, max_by);
+
+ DRM_DEBUG_KMS("[%d] x1(%d) y1(%d) x2(%d) y2(%d)\n",
+ index, clips[index].x1, clips[index].y1,
+ clips[index].x2, clips[index].y2);
+ }
+
+ pos->x = min_sx;
+ pos->y = min_sy;
+ pos->w = max_bx - min_sx;
+ pos->h = max_by - min_sy;
+
+ DRM_DEBUG_KMS("partial region: x1(%d) y1(%d) x2(%d) y2(%d)\n",
+ pos->x, pos->y, pos->w, pos->h);
+}
+
static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clips)
{
- /* TODO */
+ struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
+
+ if (!num_clips || !clips)
+ return -EINVAL;
+
+ /*
+ * Get maximum region to all clip regions
+ * if one more clip regions exsit.
+ */
+ if (num_clips > 1) {
+ DRM_DEBUG_KMS("use only one clip region.\n");
+
+ exynos_drm_fb_adjust_clips(&exynos_fb->part_pos, clips,
+ num_clips);
+ goto out;
+ }
+
+ atomic_set(&exynos_fb->partial_mode, 1);
+
+ exynos_fb->part_pos.x = clips[0].x1;
+ exynos_fb->part_pos.y = clips[0].y1;
+ exynos_fb->part_pos.w = clips[0].x2 - clips[0].x1;
+ exynos_fb->part_pos.h = clips[0].y2 - clips[0].y1;
+
+out:
+ DRM_INFO("%s: x1 = %d, y1 = %d, w = %d, h = %d\n",
+ __func__, exynos_fb->part_pos.x, exynos_fb->part_pos.y,
+ exynos_fb->part_pos.w, exynos_fb->part_pos.h);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h
index 3163e41..085f032 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h
@@ -16,17 +16,27 @@
#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
+struct exynos_drm_partial_pos {
+ unsigned int x;
+ unsigned int y;
+ unsigned int w;
+ unsigned int h;
+};
+
/*
* exynos specific framebuffer structure.
*
* @fb: drm framebuffer obejct.
* @buf_cnt: a buffer count to drm framebuffer.
* @exynos_gem_obj: array of exynos specific gem object containing a gem object.
+ * @part_pos: a position to partial update region.
*/
struct exynos_drm_fb {
struct drm_framebuffer fb;
unsigned int buf_cnt;
struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER];
+ struct exynos_drm_partial_pos part_pos;
+ atomic_t partial_mode;
};
struct drm_framebuffer *
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 8b3c7a4..26f66dd 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -41,6 +41,8 @@
#define FIMD_DEFAULT_FRAMERATE 60
+#define WIDTH_LIMIT 16
+
/* position control register for hardware window 0, 2 ~ 4.*/
#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
@@ -173,6 +175,10 @@ struct fimd_context {
struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data;
struct notifier_block nb_ctrl;
+ unsigned int current_x;
+ unsigned int current_y;
+ unsigned int current_w;
+ unsigned int current_h;
};
static const struct of_device_id fimd_driver_dt_match[] = {
@@ -980,6 +986,76 @@ static void fimd_trigger(struct device *dev)
writel(reg, timing_base + TRIGCON);
}
+static void fimd_adjust_partial_region(struct exynos_drm_manager *mgr,
+ unsigned int *x, unsigned int *y, unsigned int *w,
+ unsigned int *h)
+{
+ struct fimd_context *ctx = mgr->ctx;
+ unsigned int old_x;
+ unsigned int old_w;
+
+ old_x = *x;
+ old_w = *w;
+
+ /*
+ * if pos->x is bigger than 0, adjusts pos->x and pos->w roughly.
+ *
+ * ######################################### <- image
+ * | | |
+ * | before(x) before(w)
+ * after(x) after(w)
+ */
+ if (*x > 0) {
+ *x = ALIGN(*x, WIDTH_LIMIT) - WIDTH_LIMIT;
+ *w += WIDTH_LIMIT;
+ }
+
+ /*
+ * pos->w should be WIDTH_LIMIT if pos->w is 0, and also pos->x should
+ * be adjusted properly if pos->x is bigger than WIDTH_LIMIT.
+ */
+ if (*w == 0) {
+ if (*x > WIDTH_LIMIT)
+ *x -= WIDTH_LIMIT;
+ *w = WIDTH_LIMIT;
+ } else
+ *w = ALIGN(*w, WIDTH_LIMIT);
+
+ /*
+ * pos->x + pos->w should be smaller than horizontal size of display.
+ * If not so, page fault exception will be occurred.
+ */
+ if (*x + *w > ctx->mode.hdisplay)
+ *w = ctx->mode.hdisplay - *x;
+
+ ctx->current_x = *x;
+ ctx->current_y = *y;
+ ctx->current_w = *w;
+ ctx->current_h = *h;
+
+ DRM_DEBUG_KMS("Adjusted x = %d -> %d, and w = %d -> %d\n",
+ old_x, *x, old_w, *w);
+}
+
+static void fimd_change_resolution(struct exynos_drm_manager *mgr)
+{
+ struct fimd_context *ctx = mgr->ctx;
+ struct fimd_driver_data *driver_data = ctx->driver_data;
+ unsigned long val;
+
+ /* setup horizontal and vertical display size. */
+ val = VIDTCON2_LINEVAL(ctx->current_h - 1) |
+ VIDTCON2_HOZVAL(ctx->current_w - 1) |
+ VIDTCON2_LINEVAL_E(ctx->current_h - 1) |
+ VIDTCON2_HOZVAL_E(ctx->current_w - 1);
+ writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
+
+ ctx->current_x = 0;
+ ctx->current_y = 0;
+ ctx->current_h = 0;
+ ctx->current_w = 0;
+}
+
static int fimd_te_handler(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
@@ -999,6 +1075,14 @@ static int fimd_te_handler(struct exynos_drm_manager *mgr)
atomic_set(&ctx->win_updated, 0);
spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
+ if (ctx->current_w && ctx->current_h) {
+ exynos_drm_crtc_change_resolution(ctx->drm_dev,
+ mgr->pipe, ctx->current_x,
+ ctx->current_y, ctx->current_w,
+ ctx->current_h);
+ fimd_change_resolution(mgr);
+ }
+
fimd_trigger(ctx->dev);
spin_lock_irqsave(&ctx->win_updated_lock, flags);
@@ -1026,6 +1110,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
.win_commit = fimd_win_commit,
.win_disable = fimd_win_disable,
.te_handler = fimd_te_handler,
+ .adjust_partial_region = fimd_adjust_partial_region,
};
static struct exynos_drm_manager fimd_manager = {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 8371cbd..38610a5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -77,6 +77,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
{
struct exynos_plane *exynos_plane = to_exynos_plane(plane);
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+ struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
unsigned int actual_w;
unsigned int actual_h;
int nr;
@@ -112,6 +113,28 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
crtc_y = 0;
}
+ /* Change overlay values to partial region. */
+ if (atomic_read(&exynos_fb->partial_mode)) {
+ struct exynos_drm_partial_pos *pos;
+
+ exynos_drm_crtc_adjust_partial_region(crtc,
+ &exynos_fb->part_pos);
+
+ pos = &exynos_fb->part_pos;
+ crtc_x = 0;
+ crtc_y = 0;
+ crtc_w = pos->w;
+ crtc_h = pos->h;
+ src_x = pos->x;
+ src_y = pos->y;
+ src_w = pos->w;
+ src_h = pos->h;
+ actual_w = pos->w;
+ actual_h = pos->h;
+
+ atomic_set(&exynos_fb->partial_mode, 0);
+ }
+
/* set drm framebuffer data. */
overlay->fb_x = src_x;
overlay->fb_y = src_y;
diff --git a/drivers/gpu/drm/panel/panel-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-s6e63j0x03.c
index de7bdfa..3eb33cf 100644
--- a/drivers/gpu/drm/panel/panel-s6e63j0x03.c
+++ b/drivers/gpu/drm/panel/panel-s6e63j0x03.c
@@ -31,6 +31,9 @@
#define MCS_MTP_SET3 0xd4
#define MCS_MTP_KEY 0xf1
+#define LDI_CASET_REG 0x2A
+#define LDI_PASET_REG 0x2B
+
#define MIN_BRIGHTNESS 0
#define MAX_BRIGHTNESS 100
#define DEFAULT_BRIGHTNESS 80
@@ -548,10 +551,45 @@ static int s6e63j0x03_get_modes(struct drm_panel *panel)
return 1;
}
+static int s6e63j0x03_change_resolution(struct drm_panel *panel,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
+ char buf[5];
+
+ if (ctx->power > FB_BLANK_UNBLANK)
+ return -EPERM;
+
+ if (y >= ctx->vm.vactive || h > ctx->vm.vactive)
+ return -EINVAL;
+
+ x += 20;
+
+ buf[0] = LDI_CASET_REG;
+ buf[1] = (x & 0xff00) >> 8;
+ buf[2] = x & 0x00ff;
+ buf[3] = ((x + w - 1) & 0xff00) >> 8;
+ buf[4] = (x + w - 1) & 0x00ff;
+
+ s6e63j0x03_dcs_write(ctx, buf, ARRAY_SIZE(buf));
+
+ buf[0] = LDI_PASET_REG;
+ buf[1] = (y & 0xff00) >> 8;
+ buf[2] = y & 0x00ff;
+ buf[3] = ((y + h - 1) & 0xff00) >> 8;
+ buf[4] = (y + h - 1) & 0x00ff;
+
+ s6e63j0x03_dcs_write(ctx, buf, ARRAY_SIZE(buf));
+
+ return 0;
+}
+
static const struct drm_panel_funcs s6e63j0x03_drm_funcs = {
.disable = s6e63j0x03_disable,
.enable = s6e63j0x03_enable,
.get_modes = s6e63j0x03_get_modes,
+ .change_resolution = s6e63j0x03_change_resolution,
};
static int s6e63j0x03_parse_dt(struct s6e63j0x03 *ctx)
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 29e3daf..cd42e809 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -38,6 +38,7 @@ struct drm_panel;
* @enable: enable panel (turn on back light, etc.)
* @get_modes: add modes to the connector that the panel is attached to and
* return the number of modes added
+ * @change_resolution: change panel resolution to a given position.
*
* The .prepare() function is typically called before the display controller
* starts to transmit video data. Panel drivers can use this to turn the panel
@@ -68,6 +69,9 @@ struct drm_panel_funcs {
int (*prepare)(struct drm_panel *panel);
int (*enable)(struct drm_panel *panel);
int (*get_modes)(struct drm_panel *panel);
+ int (*change_resolution)(struct drm_panel *panel, unsigned int x,
+ unsigned int y, unsigned int w,
+ unsigned int h);
};
struct drm_panel {
@@ -112,6 +116,16 @@ static inline int drm_panel_enable(struct drm_panel *panel)
return panel ? -ENOSYS : -EINVAL;
}
+
+static inline void drm_panel_change_resolution(struct drm_panel *panel,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+
+ if (panel && panel->funcs && panel->funcs->change_resolution)
+ panel->funcs->change_resolution(panel, x, y, w, h);
+}
+
void drm_panel_init(struct drm_panel *panel);
int drm_panel_add(struct drm_panel *panel);