Commit 7ae7a621 authored by Hugh Cole-Baker's avatar Hugh Cole-Baker Committed by Heiko Stuebner

drm/rockchip: support gamma control on RK3399

The RK3399 has a 1024-entry gamma LUT with 10 bits per component on its
"big" VOP and a 256-entry, 8 bit per component LUT on the "little" VOP.
Compared to the RK3288, it no longer requires disabling gamma while
updating the LUT. On the RK3399, the LUT can be updated at any time as
the hardware has two LUT buffers, one can be written while the other is
in use. A swap of the buffers is triggered by writing 1 to the
update_gamma_lut register.
Signed-off-by: default avatarHugh Cole-Baker <sigmaris@gmail.com>
Tested-by: default avatar"Milan P. Stanić" <mps@arvanta.net>
Tested-by: default avatarLinus Heckemann <git@sphalerite.org>
Signed-off-by: default avatarHeiko Stuebner <heiko@sntech.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20211019215843.42718-3-sigmaris@gmail.com
parent 3ba000d6
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -67,6 +68,9 @@ ...@@ -67,6 +68,9 @@
#define VOP_REG_SET(vop, group, name, v) \ #define VOP_REG_SET(vop, group, name, v) \
vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name) vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name)
#define VOP_HAS_REG(vop, group, name) \
(!!(vop->data->group->name.mask))
#define VOP_INTR_SET_TYPE(vop, name, type, v) \ #define VOP_INTR_SET_TYPE(vop, name, type, v) \
do { \ do { \
int i, reg = 0, mask = 0; \ int i, reg = 0, mask = 0; \
...@@ -1223,17 +1227,22 @@ static bool vop_dsp_lut_is_enabled(struct vop *vop) ...@@ -1223,17 +1227,22 @@ static bool vop_dsp_lut_is_enabled(struct vop *vop)
return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en); return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en);
} }
static u32 vop_lut_buffer_index(struct vop *vop)
{
return vop_read_reg(vop, 0, &vop->data->common->lut_buffer_index);
}
static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc) static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc)
{ {
struct drm_color_lut *lut = crtc->state->gamma_lut->data; struct drm_color_lut *lut = crtc->state->gamma_lut->data;
unsigned int i; unsigned int i, bpc = ilog2(vop->data->lut_size);
for (i = 0; i < crtc->gamma_size; i++) { for (i = 0; i < crtc->gamma_size; i++) {
u32 word; u32 word;
word = (drm_color_lut_extract(lut[i].red, 10) << 20) | word = (drm_color_lut_extract(lut[i].red, bpc) << (2 * bpc)) |
(drm_color_lut_extract(lut[i].green, 10) << 10) | (drm_color_lut_extract(lut[i].green, bpc) << bpc) |
drm_color_lut_extract(lut[i].blue, 10); drm_color_lut_extract(lut[i].blue, bpc);
writel(word, vop->lut_regs + i * 4); writel(word, vop->lut_regs + i * 4);
} }
} }
...@@ -1243,38 +1252,66 @@ static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc, ...@@ -1243,38 +1252,66 @@ static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc,
{ {
struct drm_crtc_state *state = crtc->state; struct drm_crtc_state *state = crtc->state;
unsigned int idle; unsigned int idle;
u32 lut_idx, old_idx;
int ret; int ret;
if (!vop->lut_regs) if (!vop->lut_regs)
return; return;
/*
* To disable gamma (gamma_lut is null) or to write
* an update to the LUT, clear dsp_lut_en.
*/
spin_lock(&vop->reg_lock);
VOP_REG_SET(vop, common, dsp_lut_en, 0);
vop_cfg_done(vop);
spin_unlock(&vop->reg_lock);
/* if (!state->gamma_lut || !VOP_HAS_REG(vop, common, update_gamma_lut)) {
* In order to write the LUT to the internal memory, /*
* we need to first make sure the dsp_lut_en bit is cleared. * To disable gamma (gamma_lut is null) or to write
*/ * an update to the LUT, clear dsp_lut_en.
ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop, */
idle, !idle, 5, 30 * 1000); spin_lock(&vop->reg_lock);
if (ret) { VOP_REG_SET(vop, common, dsp_lut_en, 0);
DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n"); vop_cfg_done(vop);
return; spin_unlock(&vop->reg_lock);
}
if (!state->gamma_lut) /*
return; * In order to write the LUT to the internal memory,
* we need to first make sure the dsp_lut_en bit is cleared.
*/
ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop,
idle, !idle, 5, 30 * 1000);
if (ret) {
DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n");
return;
}
if (!state->gamma_lut)
return;
} else {
/*
* On RK3399 the gamma LUT can updated without clearing dsp_lut_en,
* by setting update_gamma_lut then waiting for lut_buffer_index change
*/
old_idx = vop_lut_buffer_index(vop);
}
spin_lock(&vop->reg_lock); spin_lock(&vop->reg_lock);
vop_crtc_write_gamma_lut(vop, crtc); vop_crtc_write_gamma_lut(vop, crtc);
VOP_REG_SET(vop, common, dsp_lut_en, 1); VOP_REG_SET(vop, common, dsp_lut_en, 1);
VOP_REG_SET(vop, common, update_gamma_lut, 1);
vop_cfg_done(vop); vop_cfg_done(vop);
spin_unlock(&vop->reg_lock); spin_unlock(&vop->reg_lock);
if (VOP_HAS_REG(vop, common, update_gamma_lut)) {
ret = readx_poll_timeout(vop_lut_buffer_index, vop,
lut_idx, lut_idx != old_idx, 5, 30 * 1000);
if (ret) {
DRM_DEV_ERROR(vop->dev, "gamma LUT update timeout!\n");
return;
}
/*
* update_gamma_lut is auto cleared by HW, but write 0 to clear the bit
* in our backup of the regs.
*/
spin_lock(&vop->reg_lock);
VOP_REG_SET(vop, common, update_gamma_lut, 0);
spin_unlock(&vop->reg_lock);
}
} }
static void vop_crtc_atomic_begin(struct drm_crtc *crtc, static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
...@@ -1324,14 +1361,6 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, ...@@ -1324,14 +1361,6 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
return; return;
} }
/*
* If we have a GAMMA LUT in the state, then let's make sure
* it's updated. We might be coming out of suspend,
* which means the LUT internal memory needs to be re-written.
*/
if (crtc->state->gamma_lut)
vop_crtc_gamma_set(vop, crtc, old_state);
mutex_lock(&vop->vop_lock); mutex_lock(&vop->vop_lock);
WARN_ON(vop->event); WARN_ON(vop->event);
...@@ -1422,6 +1451,14 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, ...@@ -1422,6 +1451,14 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
VOP_REG_SET(vop, common, standby, 0); VOP_REG_SET(vop, common, standby, 0);
mutex_unlock(&vop->vop_lock); mutex_unlock(&vop->vop_lock);
/*
* If we have a GAMMA LUT in the state, then let's make sure
* it's updated. We might be coming out of suspend,
* which means the LUT internal memory needs to be re-written.
*/
if (crtc->state->gamma_lut)
vop_crtc_gamma_set(vop, crtc, old_state);
} }
static bool vop_fs_irq_is_pending(struct vop *vop) static bool vop_fs_irq_is_pending(struct vop *vop)
...@@ -2147,8 +2184,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data) ...@@ -2147,8 +2184,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) { if (res) {
if (!vop_data->lut_size) { if (vop_data->lut_size != 1024 && vop_data->lut_size != 256) {
DRM_DEV_ERROR(dev, "no gamma LUT size defined\n"); DRM_DEV_ERROR(dev, "unsupported gamma LUT size %d\n", vop_data->lut_size);
return -EINVAL; return -EINVAL;
} }
vop->lut_regs = devm_ioremap_resource(dev, res); vop->lut_regs = devm_ioremap_resource(dev, res);
......
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