mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 18:39:59 +00:00
drm: mali-dp: enable gamma support
Add gamma via the DRM GAMMA_LUT/GAMMA_LUT_SIZE CRTC properties. The expected LUT size is 4096 in order to produce as accurate a set of segments as possible. This version uses only the green channel's gamma curve to set the hardware curve on DP550/650. For the sake of simplicity, it uses the same table of coefficients for all 3 curves on DP500. Signed-off-by: Mihail Atanassov <mihail.atanassov@arm.com> Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
This commit is contained in:
parent
99665d0721
commit
02725d3137
6 changed files with 196 additions and 9 deletions
|
@ -93,6 +93,108 @@ static void malidp_crtc_disable(struct drm_crtc *crtc)
|
|||
}
|
||||
}
|
||||
|
||||
static const struct gamma_curve_segment {
|
||||
u16 start;
|
||||
u16 end;
|
||||
} segments[MALIDP_COEFFTAB_NUM_COEFFS] = {
|
||||
/* sector 0 */
|
||||
{ 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 },
|
||||
{ 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 },
|
||||
{ 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 },
|
||||
{ 12, 12 }, { 13, 13 }, { 14, 14 }, { 15, 15 },
|
||||
/* sector 1 */
|
||||
{ 16, 19 }, { 20, 23 }, { 24, 27 }, { 28, 31 },
|
||||
/* sector 2 */
|
||||
{ 32, 39 }, { 40, 47 }, { 48, 55 }, { 56, 63 },
|
||||
/* sector 3 */
|
||||
{ 64, 79 }, { 80, 95 }, { 96, 111 }, { 112, 127 },
|
||||
/* sector 4 */
|
||||
{ 128, 159 }, { 160, 191 }, { 192, 223 }, { 224, 255 },
|
||||
/* sector 5 */
|
||||
{ 256, 319 }, { 320, 383 }, { 384, 447 }, { 448, 511 },
|
||||
/* sector 6 */
|
||||
{ 512, 639 }, { 640, 767 }, { 768, 895 }, { 896, 1023 },
|
||||
{ 1024, 1151 }, { 1152, 1279 }, { 1280, 1407 }, { 1408, 1535 },
|
||||
{ 1536, 1663 }, { 1664, 1791 }, { 1792, 1919 }, { 1920, 2047 },
|
||||
{ 2048, 2175 }, { 2176, 2303 }, { 2304, 2431 }, { 2432, 2559 },
|
||||
{ 2560, 2687 }, { 2688, 2815 }, { 2816, 2943 }, { 2944, 3071 },
|
||||
{ 3072, 3199 }, { 3200, 3327 }, { 3328, 3455 }, { 3456, 3583 },
|
||||
{ 3584, 3711 }, { 3712, 3839 }, { 3840, 3967 }, { 3968, 4095 },
|
||||
};
|
||||
|
||||
#define DE_COEFTAB_DATA(a, b) ((((a) & 0xfff) << 16) | (((b) & 0xfff)))
|
||||
|
||||
static void malidp_generate_gamma_table(struct drm_property_blob *lut_blob,
|
||||
u32 coeffs[MALIDP_COEFFTAB_NUM_COEFFS])
|
||||
{
|
||||
struct drm_color_lut *lut = (struct drm_color_lut *)lut_blob->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) {
|
||||
u32 a, b, delta_in, out_start, out_end;
|
||||
|
||||
delta_in = segments[i].end - segments[i].start;
|
||||
/* DP has 12-bit internal precision for its LUTs. */
|
||||
out_start = drm_color_lut_extract(lut[segments[i].start].green,
|
||||
12);
|
||||
out_end = drm_color_lut_extract(lut[segments[i].end].green, 12);
|
||||
a = (delta_in == 0) ? 0 : ((out_end - out_start) * 256) / delta_in;
|
||||
b = out_start;
|
||||
coeffs[i] = DE_COEFTAB_DATA(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there is a new gamma LUT and if it is of an acceptable size. Also,
|
||||
* reject any LUTs that use distinct red, green, and blue curves.
|
||||
*/
|
||||
static int malidp_crtc_atomic_check_gamma(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
|
||||
struct drm_color_lut *lut;
|
||||
size_t lut_size;
|
||||
int i;
|
||||
|
||||
if (!state->color_mgmt_changed || !state->gamma_lut)
|
||||
return 0;
|
||||
|
||||
if (crtc->state->gamma_lut &&
|
||||
(crtc->state->gamma_lut->base.id == state->gamma_lut->base.id))
|
||||
return 0;
|
||||
|
||||
if (state->gamma_lut->length % sizeof(struct drm_color_lut))
|
||||
return -EINVAL;
|
||||
|
||||
lut_size = state->gamma_lut->length / sizeof(struct drm_color_lut);
|
||||
if (lut_size != MALIDP_GAMMA_LUT_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
lut = (struct drm_color_lut *)state->gamma_lut->data;
|
||||
for (i = 0; i < lut_size; ++i)
|
||||
if (!((lut[i].red == lut[i].green) &&
|
||||
(lut[i].red == lut[i].blue)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->mode_changed) {
|
||||
int ret;
|
||||
|
||||
state->mode_changed = true;
|
||||
/*
|
||||
* Kerneldoc for drm_atomic_helper_check_modeset mandates that
|
||||
* it be invoked when the driver sets ->mode_changed. Since
|
||||
* changing the gamma LUT doesn't depend on any external
|
||||
* resources, it is safe to call it only once.
|
||||
*/
|
||||
ret = drm_atomic_helper_check_modeset(crtc->dev, state->state);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
malidp_generate_gamma_table(state->gamma_lut, mc->gamma_coeffs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
|
@ -168,7 +270,7 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
|
|||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return malidp_crtc_atomic_check_gamma(crtc, state);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
|
||||
|
@ -180,16 +282,19 @@ static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
|
|||
|
||||
static struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc)
|
||||
{
|
||||
struct malidp_crtc_state *state;
|
||||
struct malidp_crtc_state *state, *old_state;
|
||||
|
||||
if (WARN_ON(!crtc->state))
|
||||
return NULL;
|
||||
|
||||
old_state = to_malidp_crtc_state(crtc->state);
|
||||
state = kmalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
|
||||
memcpy(state->gamma_coeffs, old_state->gamma_coeffs,
|
||||
sizeof(state->gamma_coeffs));
|
||||
|
||||
return &state->base;
|
||||
}
|
||||
|
@ -244,6 +349,7 @@ static void malidp_crtc_disable_vblank(struct drm_crtc *crtc)
|
|||
}
|
||||
|
||||
static const struct drm_crtc_funcs malidp_crtc_funcs = {
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
|
@ -281,11 +387,15 @@ int malidp_crtc_init(struct drm_device *drm)
|
|||
|
||||
ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
|
||||
&malidp_crtc_funcs, NULL);
|
||||
if (ret)
|
||||
goto crtc_cleanup_planes;
|
||||
|
||||
if (!ret) {
|
||||
drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
|
||||
return 0;
|
||||
}
|
||||
drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
|
||||
drm_mode_crtc_set_gamma_size(&malidp->crtc, MALIDP_GAMMA_LUT_SIZE);
|
||||
/* No inverse-gamma and color adjustments yet. */
|
||||
drm_crtc_enable_color_mgmt(&malidp->crtc, 0, false, MALIDP_GAMMA_LUT_SIZE);
|
||||
|
||||
return 0;
|
||||
|
||||
crtc_cleanup_planes:
|
||||
malidp_de_planes_destroy(drm);
|
||||
|
|
|
@ -34,6 +34,51 @@
|
|||
|
||||
#define MALIDP_CONF_VALID_TIMEOUT 250
|
||||
|
||||
static void malidp_write_gamma_table(struct malidp_hw_device *hwdev,
|
||||
u32 data[MALIDP_COEFFTAB_NUM_COEFFS])
|
||||
{
|
||||
int i;
|
||||
/* Update all channels with a single gamma curve. */
|
||||
const u32 gamma_write_mask = GENMASK(18, 16);
|
||||
/*
|
||||
* Always write an entire table, so the address field in
|
||||
* DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask
|
||||
* directly.
|
||||
*/
|
||||
malidp_hw_write(hwdev, gamma_write_mask,
|
||||
hwdev->map.coeffs_base + MALIDP_COEF_TABLE_ADDR);
|
||||
for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i)
|
||||
malidp_hw_write(hwdev, data[i],
|
||||
hwdev->map.coeffs_base +
|
||||
MALIDP_COEF_TABLE_DATA);
|
||||
}
|
||||
|
||||
static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
||||
struct malidp_hw_device *hwdev = malidp->dev;
|
||||
|
||||
if (!crtc->state->color_mgmt_changed)
|
||||
return;
|
||||
|
||||
if (!crtc->state->gamma_lut) {
|
||||
malidp_hw_clearbits(hwdev,
|
||||
MALIDP_DISP_FUNC_GAMMA,
|
||||
MALIDP_DE_DISPLAY_FUNC);
|
||||
} else {
|
||||
struct malidp_crtc_state *mc =
|
||||
to_malidp_crtc_state(crtc->state);
|
||||
|
||||
if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id !=
|
||||
old_state->gamma_lut->base.id))
|
||||
malidp_write_gamma_table(hwdev, mc->gamma_coeffs);
|
||||
|
||||
malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA,
|
||||
MALIDP_DE_DISPLAY_FUNC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set the "config valid" bit and wait until the hardware acts on it
|
||||
*/
|
||||
|
@ -92,11 +137,17 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
|
|||
static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = state->dev;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(drm->dev);
|
||||
|
||||
drm_atomic_helper_commit_modeset_disables(drm, state);
|
||||
|
||||
for_each_crtc_in_state(state, crtc, old_crtc_state, i)
|
||||
malidp_atomic_commit_update_gamma(crtc, old_crtc_state);
|
||||
|
||||
drm_atomic_helper_commit_planes(drm, state, 0);
|
||||
|
||||
drm_atomic_helper_commit_modeset_enables(drm, state);
|
||||
|
|
|
@ -50,6 +50,7 @@ struct malidp_plane_state {
|
|||
|
||||
struct malidp_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
u32 gamma_coeffs[MALIDP_COEFFTAB_NUM_COEFFS];
|
||||
};
|
||||
|
||||
#define to_malidp_crtc_state(x) container_of(x, struct malidp_crtc_state, base)
|
||||
|
|
|
@ -415,6 +415,7 @@ static int malidp650_query_hw(struct malidp_hw_device *hwdev)
|
|||
const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
||||
[MALIDP_500] = {
|
||||
.map = {
|
||||
.coeffs_base = MALIDP500_COEFFS_BASE,
|
||||
.se_base = MALIDP500_SE_BASE,
|
||||
.dc_base = MALIDP500_DC_BASE,
|
||||
.out_depth_base = MALIDP500_OUTPUT_DEPTH,
|
||||
|
@ -451,6 +452,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
|||
},
|
||||
[MALIDP_550] = {
|
||||
.map = {
|
||||
.coeffs_base = MALIDP550_COEFFS_BASE,
|
||||
.se_base = MALIDP550_SE_BASE,
|
||||
.dc_base = MALIDP550_DC_BASE,
|
||||
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
||||
|
@ -485,6 +487,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
|||
},
|
||||
[MALIDP_650] = {
|
||||
.map = {
|
||||
.coeffs_base = MALIDP550_COEFFS_BASE,
|
||||
.se_base = MALIDP550_SE_BASE,
|
||||
.dc_base = MALIDP550_DC_BASE,
|
||||
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
||||
|
|
|
@ -67,6 +67,8 @@ struct malidp_layer {
|
|||
struct malidp_hw_regmap {
|
||||
/* address offset of the DE register bank */
|
||||
/* is always 0x0000 */
|
||||
/* address offset of the DE coefficients registers */
|
||||
const u16 coeffs_base;
|
||||
/* address offset of the SE registers bank */
|
||||
const u16 se_base;
|
||||
/* address offset of the DC registers bank */
|
||||
|
@ -257,4 +259,8 @@ static inline bool malidp_hw_pitch_valid(struct malidp_hw_device *hwdev,
|
|||
#define MALIDP_BGND_COLOR_G 0x000
|
||||
#define MALIDP_BGND_COLOR_B 0x000
|
||||
|
||||
#define MALIDP_COEFFTAB_NUM_COEFFS 64
|
||||
|
||||
#define MALIDP_GAMMA_LUT_SIZE 4096
|
||||
|
||||
#endif /* __MALIDP_HW_H__ */
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
|
||||
/* bit masks that are common between products */
|
||||
#define MALIDP_CFG_VALID (1 << 0)
|
||||
#define MALIDP_DISP_FUNC_GAMMA (1 << 0)
|
||||
#define MALIDP_DISP_FUNC_ILACED (1 << 8)
|
||||
|
||||
/* register offsets for IRQ management */
|
||||
|
@ -99,6 +100,11 @@
|
|||
|
||||
#define MALIDP_PRODUCT_ID(__core_id) ((u32)(__core_id) >> 16)
|
||||
|
||||
/* register offsets relative to MALIDP5x0_COEFFS_BASE */
|
||||
#define MALIDP_COLOR_ADJ_COEF 0x00000
|
||||
#define MALIDP_COEF_TABLE_ADDR 0x00030
|
||||
#define MALIDP_COEF_TABLE_DATA 0x00034
|
||||
|
||||
/* register offsets and bits specific to DP500 */
|
||||
#define MALIDP500_ADDR_SPACE_SIZE 0x01000
|
||||
#define MALIDP500_DC_BASE 0x00000
|
||||
|
@ -120,6 +126,18 @@
|
|||
#define MALIDP500_COLOR_ADJ_COEF 0x00078
|
||||
#define MALIDP500_COEF_TABLE_ADDR 0x000a8
|
||||
#define MALIDP500_COEF_TABLE_DATA 0x000ac
|
||||
|
||||
/*
|
||||
* The YUV2RGB coefficients on the DP500 are not in the video layer's register
|
||||
* block. They belong in a separate block above the layer's registers, hence
|
||||
* the negative offset.
|
||||
*/
|
||||
#define MALIDP500_LV_YUV2RGB ((s16)(-0xB8))
|
||||
/*
|
||||
* To match DP550/650, the start of the coeffs registers is
|
||||
* at COLORADJ_COEFF0 instead of at YUV_RGB_COEF1.
|
||||
*/
|
||||
#define MALIDP500_COEFFS_BASE 0x00078
|
||||
#define MALIDP500_DE_LV_BASE 0x00100
|
||||
#define MALIDP500_DE_LV_PTR_BASE 0x00124
|
||||
#define MALIDP500_DE_LG1_BASE 0x00200
|
||||
|
@ -145,9 +163,7 @@
|
|||
#define MALIDP550_DE_DISP_SIDEBAND 0x00040
|
||||
#define MALIDP550_DE_BGND_COLOR 0x00044
|
||||
#define MALIDP550_DE_OUTPUT_DEPTH 0x0004c
|
||||
#define MALIDP550_DE_COLOR_COEF 0x00050
|
||||
#define MALIDP550_DE_COEF_TABLE_ADDR 0x00080
|
||||
#define MALIDP550_DE_COEF_TABLE_DATA 0x00084
|
||||
#define MALIDP550_COEFFS_BASE 0x00050
|
||||
#define MALIDP550_DE_LV1_BASE 0x00100
|
||||
#define MALIDP550_DE_LV1_PTR_BASE 0x00124
|
||||
#define MALIDP550_DE_LV2_BASE 0x00200
|
||||
|
|
Loading…
Reference in a new issue