techpack: display: Introduce new exposure dim layer driver

This driver provides exposure adjustment function by Qcom SDE dim
layer without change panel hardware brightness to avoid PWM flicker
on OLED devices. Thanks to OnePlus' opensource code for inspiring
me to use dim layer.

Use expo_calc_backlight to remap brightness with hardware and SDE,
checking Disable Hardware Overlays in developer options if you face
blocks with differents brightness issue.

[Ayrton: Backported from lahaina to SMxx50.0 SDE Driver for compatibility]

Signed-off-by: DevriesL <therkduan@gmail.com>
Signed-off-by: Carlos Ayrton Lopez Arroyo <15030201@itcelaya.edu.mx>
This commit is contained in:
DevriesL 2021-06-17 00:01:33 +08:00 committed by spakkkk
parent b26fd3f0db
commit a73e986502
7 changed files with 266 additions and 0 deletions

View File

@ -80,6 +80,8 @@ msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \
sde_rsc_hw.o \
sde_rsc_hw_v3.o \
msm_drm-$(CONFIG_DRM_SDE_EXPO) += sde/sde_expo_dim_layer.o
msm_drm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi_phy.o \
dsi/dsi_pwr.o \
dsi/dsi_phy.o \

View File

@ -22,6 +22,10 @@
#include "sde_dbg.h"
#include "dsi_parser.h"
#ifdef CONFIG_DRM_SDE_EXPO
#include "sde_expo_dim_layer.h"
#endif
#define to_dsi_display(x) container_of(x, struct dsi_display, host)
#define INT_BASE_10 10
@ -234,6 +238,12 @@ int dsi_display_set_backlight(struct drm_connector *connector,
goto error;
}
#ifdef CONFIG_DRM_SDE_EXPO
if (bl_lvl && !panel->mi_cfg.in_aod) {
bl_temp = expo_map_dim_level((u32)bl_temp, dsi_display);
}
#endif
rc = dsi_panel_set_backlight(panel, (u32)bl_temp);
if (rc)
DSI_ERR("unable to set backlight\n");

View File

@ -171,6 +171,9 @@ enum msm_mdp_crtc_property {
CRTC_PROP_IDLE_PC_STATE,
CRCT_PROP_MI_FOD_SYNC_INFO,
#ifdef CONFIG_DRM_SDE_EXPO
CRTC_PROP_DIM_LAYER_EXPO,
#endif
/* total # of properties */
CRTC_PROP_COUNT
};

View File

@ -45,6 +45,11 @@
#include "xiaomi_frame_stat.h"
#include "dsi_display.h"
#ifdef CONFIG_DRM_SDE_EXPO
#include "dsi_display.h"
#include "dsi_panel.h"
#endif
#define SDE_PSTATES_MAX (SDE_STAGE_MAX * 4)
#define SDE_MULTIRECT_PLANE_MAX (SDE_STAGE_MAX * 2)
#define IDLE_TIMEOUT_DEFAULT (1100)
@ -1483,6 +1488,12 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
if (cstate->fod_dim_layer)
_sde_crtc_setup_dim_layer_cfg(crtc, sde_crtc,
mixer, cstate->fod_dim_layer);
#ifdef CONFIG_DRM_SDE_EXPO
if (cstate->exposure_dim_layer) {
_sde_crtc_setup_dim_layer_cfg(crtc, sde_crtc,
mixer, cstate->exposure_dim_layer);
}
#endif
}
_sde_crtc_program_lm_output_roi(crtc);
@ -2599,6 +2610,49 @@ static void _sde_crtc_set_dim_layer_v1(struct drm_crtc *crtc,
}
}
#ifdef CONFIG_DRM_SDE_EXPO
static int sde_crtc_config_exposure_dim_layer(struct drm_crtc_state *crtc_state, int stage)
{
struct sde_kms *kms;
struct sde_hw_dim_layer *dim_layer;
struct sde_crtc_state *cstate = to_sde_crtc_state(crtc_state);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
int alpha = sde_crtc_get_property(cstate, CRTC_PROP_DIM_LAYER_EXPO);
struct dsi_display *dsi_display = get_main_display();
struct dsi_panel *panel = dsi_display->panel;
kms = _sde_crtc_get_kms(crtc_state->crtc);
if (!kms || !kms->catalog) {
return -EINVAL;
}
if (cstate->num_dim_layers == SDE_MAX_DIM_LAYERS - 1) {
pr_err("failed to get available dim layer for exposure\n");
return -EINVAL;
}
if ((stage + SDE_STAGE_0) >= kms->catalog->mixer[0].sblk->maxblendstages) {
return -EINVAL;
}
dim_layer = &cstate->dim_layer[cstate->num_dim_layers];
dim_layer->flags = SDE_DRM_DIM_LAYER_INCLUSIVE;
dim_layer->stage = stage + SDE_STAGE_0;
dim_layer->rect.x = 0;
dim_layer->rect.y = 0;
dim_layer->rect.w = mode->hdisplay;
dim_layer->rect.h = mode->vdisplay;
dim_layer->color_fill = (struct sde_mdss_color) {0, 0, 0, alpha};
cstate->exposure_dim_layer = dim_layer;
dim_layer->flags = SDE_CRTC_DIRTY_DIM_LAYER_EXPO;
return 0;
}
#endif
/**
* _sde_crtc_set_dest_scaler - copy dest scaler settings from userspace
* @sde_crtc : Pointer to sde crtc
@ -4839,6 +4893,27 @@ static int _sde_crtc_check_get_pstates(struct drm_crtc *crtc,
return rc;
}
#ifdef CONFIG_DRM_SDE_EXPO
static int sde_crtc_exposure_atomic_check(struct sde_crtc_state *cstate,
struct plane_state *pstates, int cnt)
{
int i, zpos = 0;
for (i = 0; i < cnt; i++) {
if (pstates[i].stage > zpos)
zpos = pstates[i].stage;
}
zpos++;
if (sde_crtc_config_exposure_dim_layer(&cstate->base, zpos)) {
SDE_ERROR("Failed to config dim layer\n");
return -EINVAL;
}
return 0;
}
#endif
static int _sde_crtc_check_zpos(struct drm_crtc_state *state,
struct sde_crtc *sde_crtc,
struct plane_state *pstates,
@ -4947,6 +5022,12 @@ static int _sde_crtc_atomic_check_pstates(struct drm_crtc *crtc,
sde_crtc_fod_atomic_check(cstate, pstates, cnt);
#ifdef CONFIG_DRM_SDE_EXPO
rc = sde_crtc_exposure_atomic_check(cstate, pstates, cnt);
if (rc)
return rc;
#endif
/* assign mixer stages based on sorted zpos property */
rc = _sde_crtc_check_zpos(state, sde_crtc, pstates, cstate, mode, cnt);
if (rc)
@ -5383,6 +5464,11 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
msm_property_install_volatile_range(&sde_crtc->property_info,
"output_fence", 0x0, 0, ~0, 0, CRTC_PROP_OUTPUT_FENCE);
#ifdef CONFIG_DRM_SDE_EXPO
msm_property_install_volatile_range(&sde_crtc->property_info,
"dim_layer_exposure", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_EXPO);
#endif
msm_property_install_range(&sde_crtc->property_info,
"output_fence_offset", 0x0, 0, 1, 0,
CRTC_PROP_OUTPUT_FENCE_OFFSET);

View File

@ -409,6 +409,12 @@ typedef struct sde_crtc_mi_dc_backlight
int32_t mi_dc_bl_layer_alpha;
} sde_crtc_mi_dc_backlight;
#ifdef CONFIG_DRM_SDE_EXPO
enum sde_crtc_dirty_flags {
SDE_CRTC_DIRTY_DIM_LAYER_EXPO,
};
#endif
typedef struct sde_crtc_mi_layer
{
int32_t layer_index;
@ -489,6 +495,9 @@ struct sde_crtc_state {
/* Mi crtc state */
struct sde_crtc_mi_state mi_state;
uint32_t num_dim_layers_bank;
#ifdef CONFIG_DRM_SDE_EXPO
struct sde_hw_dim_layer *exposure_dim_layer;
#endif
};
enum sde_crtc_irq_state {

View File

@ -0,0 +1,102 @@
/*
* A new exposure driver based on SDE dim layer for OLED devices
*
* Copyright (C) 2012-2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2019, Devries <therkduan@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "dsi_display.h"
#include "sde_crtc.h"
#include "sde_expo_dim_layer.h"
static int interpolate(int x, int xa, int xb, int ya, int yb)
{
int bf, factor, plus;
int sub = 0;
bf = 2 * (yb - ya) * (x - xa) / (xb - xa);
factor = bf / 2;
plus = bf % 2;
if ((xa - xb) && (yb - ya))
sub = 2 * (x - xa) * (x - xb) / (yb - ya) / (xa - xb);
return ya + factor + plus + sub;
}
static int brightness_to_alpha(uint8_t brightness)
{
int level = ARRAY_SIZE(brightness_alpha_lut);
int index, alpha;
for (index = 0; index < ARRAY_SIZE(brightness_alpha_lut); index++) {
if (brightness_alpha_lut[index][BRIGHTNESS] >= brightness)
break;
}
if (index == 0)
alpha = brightness_alpha_lut[0][ALPHA];
else if (index == level)
alpha = brightness_alpha_lut[level - 1][ALPHA];
else
alpha = interpolate(brightness,
brightness_alpha_lut[index - 1][BRIGHTNESS],
brightness_alpha_lut[index][BRIGHTNESS],
brightness_alpha_lut[index - 1][ALPHA],
brightness_alpha_lut[index][ALPHA]);
return alpha;
}
static void set_dim_layer_exposure(uint8_t brightness, struct dsi_display *display)
{
struct drm_crtc *crtc;
struct drm_crtc_state *state;
struct msm_drm_private *priv;
struct drm_property *prop;
if (!display->drm_conn) {
pr_err("The display is not connected!!\n");
return;
};
if (!display->drm_conn->state->crtc) {
pr_err("No CRTC on display connector!!\n");
return;
}
crtc = display->drm_conn->state->crtc;
state = crtc->state;
priv = crtc->dev->dev_private;
prop = priv->crtc_property[CRTC_PROP_DIM_LAYER_EXPO];
crtc->funcs->atomic_set_property(crtc, state, prop, (uint64_t)brightness_to_alpha(brightness));
}
uint32_t expo_map_dim_level(uint32_t level, struct dsi_display *display)
{
uint32_t override_level, brightness;
uint8_t dim_brightness;
if (level < DIM_THRES_LEVEL) {
override_level = DIM_THRES_LEVEL;
} else {
override_level = level;
}
brightness = level / BACKLIGHT_DIM_SCALE;
dim_brightness = brightness > U8_MAX ? U8_MAX : brightness;
set_dim_layer_exposure(dim_brightness, display);
return override_level;
}

View File

@ -0,0 +1,54 @@
/*
* A new exposure driver based on SDE dim layer for OLED devices
*
* Copyright (C) 2012-2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2019, Devries <therkduan@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef SDE_EXPO_DIM_LAYER_H
#define SDE_EXPO_DIM_LAYER_H
#define DIM_THRES_LEVEL 440
#define BACKLIGHT_DIM_SCALE 6
enum {
BRIGHTNESS = 0,
ALPHA = 1,
LUT_MAX,
};
static const uint8_t brightness_alpha_lut[][LUT_MAX] = {
/* {brightness, alpha} */
{0, 0xFF},
{2, 0xE0},
{3, 0xD5},
{4, 0xD3},
{5, 0xD0},
{6, 0xCE},
{7, 0xCB},
{8, 0xC8},
{9, 0xC4},
{10, 0xBA},
{12, 0xB0},
{15, 0xA0},
{20, 0x8B},
{30, 0x72},
{32, 0x5A},
{45, 0x38},
{60, 0x0E},
{78, 0x00}
};
uint32_t expo_map_dim_level(uint32_t level, struct dsi_display *display);
#endif /* SDE_EXPO_DIM_LAYER_H */