Merge branch 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next
Latest updates on Mali DP, adding support for colour management, plane scaling and power management. (these have been in -next for a while). * 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld: drm: mali-dp: use div_u64 for expensive 64-bit divisions drm: mali-dp: Check the mclk rate and allow up/down scaling drm: mali-dp: Enable image enhancement when scaling drm: mali-dp: Add plane upscaling support drm/mali-dp: Add core_id file to the sysfs interface drm: mali-dp: Add CTM support drm: mali-dp: enable gamma support drm: mali-dp: add malidp_crtc_state struct drm: mali-dp: add custom reset hook for planes drm: mali-dp: remove unused variable drm: mali-dp: add atomic_print_state for planes drm: mali-dp: Enable power management for the device. drm: mali-dp: Update the state of all planes before re-enabling active CRTCs.
This commit is contained in:
commit
97643d75b0
|
@ -16,6 +16,7 @@
|
||||||
#include <drm/drm_crtc.h>
|
#include <drm/drm_crtc.h>
|
||||||
#include <drm/drm_crtc_helper.h>
|
#include <drm/drm_crtc_helper.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <video/videomode.h>
|
#include <video/videomode.h>
|
||||||
|
|
||||||
#include "malidp_drv.h"
|
#include "malidp_drv.h"
|
||||||
|
@ -35,13 +36,6 @@ static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||||
long rate, req_rate = mode->crtc_clock * 1000;
|
long rate, req_rate = mode->crtc_clock * 1000;
|
||||||
|
|
||||||
if (req_rate) {
|
if (req_rate) {
|
||||||
rate = clk_round_rate(hwdev->mclk, req_rate);
|
|
||||||
if (rate < req_rate) {
|
|
||||||
DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
|
|
||||||
mode->crtc_clock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rate = clk_round_rate(hwdev->pxlclk, req_rate);
|
rate = clk_round_rate(hwdev->pxlclk, req_rate);
|
||||||
if (rate != req_rate) {
|
if (rate != req_rate) {
|
||||||
DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
|
DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
|
||||||
|
@ -58,9 +52,14 @@ static void malidp_crtc_enable(struct drm_crtc *crtc)
|
||||||
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
||||||
struct malidp_hw_device *hwdev = malidp->dev;
|
struct malidp_hw_device *hwdev = malidp->dev;
|
||||||
struct videomode vm;
|
struct videomode vm;
|
||||||
|
int err = pm_runtime_get_sync(crtc->dev->dev);
|
||||||
|
|
||||||
|
if (err < 0) {
|
||||||
|
DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
|
drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
|
||||||
|
|
||||||
clk_prepare_enable(hwdev->pxlclk);
|
clk_prepare_enable(hwdev->pxlclk);
|
||||||
|
|
||||||
/* We rely on firmware to set mclk to a sensible level. */
|
/* We rely on firmware to set mclk to a sensible level. */
|
||||||
|
@ -75,10 +74,254 @@ static void malidp_crtc_disable(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
||||||
struct malidp_hw_device *hwdev = malidp->dev;
|
struct malidp_hw_device *hwdev = malidp->dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
drm_crtc_vblank_off(crtc);
|
drm_crtc_vblank_off(crtc);
|
||||||
hwdev->enter_config_mode(hwdev);
|
hwdev->enter_config_mode(hwdev);
|
||||||
clk_disable_unprepare(hwdev->pxlclk);
|
clk_disable_unprepare(hwdev->pxlclk);
|
||||||
|
|
||||||
|
err = pm_runtime_put(crtc->dev->dev);
|
||||||
|
if (err < 0) {
|
||||||
|
DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if there is a new CTM and if it contains valid input. Valid here means
|
||||||
|
* that the number is inside the representable range for a Q3.12 number,
|
||||||
|
* excluding truncating the fractional part of the input data.
|
||||||
|
*
|
||||||
|
* The COLORADJ registers can be changed atomically.
|
||||||
|
*/
|
||||||
|
static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
|
||||||
|
struct drm_crtc_state *state)
|
||||||
|
{
|
||||||
|
struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
|
||||||
|
struct drm_color_ctm *ctm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!state->color_mgmt_changed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!state->ctm)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (crtc->state->ctm && (crtc->state->ctm->base.id ==
|
||||||
|
state->ctm->base.id))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The size of the ctm is checked in
|
||||||
|
* drm_atomic_replace_property_blob_from_id.
|
||||||
|
*/
|
||||||
|
ctm = (struct drm_color_ctm *)state->ctm->data;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) {
|
||||||
|
/* Convert from S31.32 to Q3.12. */
|
||||||
|
s64 val = ctm->matrix[i];
|
||||||
|
u32 mag = ((((u64)val) & ~BIT_ULL(63)) >> 20) &
|
||||||
|
GENMASK_ULL(14, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert to 2s complement and check the destination's top bit
|
||||||
|
* for overflow. NB: Can't check before converting or it'd
|
||||||
|
* incorrectly reject the case:
|
||||||
|
* sign == 1
|
||||||
|
* mag == 0x2000
|
||||||
|
*/
|
||||||
|
if (val & BIT_ULL(63))
|
||||||
|
mag = ~mag + 1;
|
||||||
|
if (!!(val & BIT_ULL(63)) != !!(mag & BIT(14)))
|
||||||
|
return -EINVAL;
|
||||||
|
mc->coloradj_coeffs[i] = mag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
|
||||||
|
struct drm_crtc_state *state)
|
||||||
|
{
|
||||||
|
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
||||||
|
struct malidp_hw_device *hwdev = malidp->dev;
|
||||||
|
struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
|
||||||
|
struct malidp_se_config *s = &cs->scaler_config;
|
||||||
|
struct drm_plane *plane;
|
||||||
|
struct videomode vm;
|
||||||
|
const struct drm_plane_state *pstate;
|
||||||
|
u32 h_upscale_factor = 0; /* U16.16 */
|
||||||
|
u32 v_upscale_factor = 0; /* U16.16 */
|
||||||
|
u8 scaling = cs->scaled_planes_mask;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!scaling) {
|
||||||
|
s->scale_enable = false;
|
||||||
|
goto mclk_calc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The scaling engine can only handle one plane at a time. */
|
||||||
|
if (scaling & (scaling - 1))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
|
||||||
|
struct malidp_plane *mp = to_malidp_plane(plane);
|
||||||
|
u32 phase;
|
||||||
|
|
||||||
|
if (!(mp->layer->id & scaling))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert crtc_[w|h] to U32.32, then divide by U16.16 src_[w|h]
|
||||||
|
* to get the U16.16 result.
|
||||||
|
*/
|
||||||
|
h_upscale_factor = div_u64((u64)pstate->crtc_w << 32,
|
||||||
|
pstate->src_w);
|
||||||
|
v_upscale_factor = div_u64((u64)pstate->crtc_h << 32,
|
||||||
|
pstate->src_h);
|
||||||
|
|
||||||
|
s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 ||
|
||||||
|
(v_upscale_factor >> 16) >= 2);
|
||||||
|
|
||||||
|
s->input_w = pstate->src_w >> 16;
|
||||||
|
s->input_h = pstate->src_h >> 16;
|
||||||
|
s->output_w = pstate->crtc_w;
|
||||||
|
s->output_h = pstate->crtc_h;
|
||||||
|
|
||||||
|
#define SE_N_PHASE 4
|
||||||
|
#define SE_SHIFT_N_PHASE 12
|
||||||
|
/* Calculate initial_phase and delta_phase for horizontal. */
|
||||||
|
phase = s->input_w;
|
||||||
|
s->h_init_phase =
|
||||||
|
((phase << SE_N_PHASE) / s->output_w + 1) / 2;
|
||||||
|
|
||||||
|
phase = s->input_w;
|
||||||
|
phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
|
||||||
|
s->h_delta_phase = phase / s->output_w;
|
||||||
|
|
||||||
|
/* Same for vertical. */
|
||||||
|
phase = s->input_h;
|
||||||
|
s->v_init_phase =
|
||||||
|
((phase << SE_N_PHASE) / s->output_h + 1) / 2;
|
||||||
|
|
||||||
|
phase = s->input_h;
|
||||||
|
phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
|
||||||
|
s->v_delta_phase = phase / s->output_h;
|
||||||
|
#undef SE_N_PHASE
|
||||||
|
#undef SE_SHIFT_N_PHASE
|
||||||
|
s->plane_src_id = mp->layer->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scale_enable = true;
|
||||||
|
s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
|
||||||
|
s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);
|
||||||
|
|
||||||
|
mclk_calc:
|
||||||
|
drm_display_mode_to_videomode(&state->adjusted_mode, &vm);
|
||||||
|
ret = hwdev->se_calc_mclk(hwdev, s, &vm);
|
||||||
|
if (ret < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
|
static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
|
||||||
|
@ -90,6 +333,7 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
|
||||||
const struct drm_plane_state *pstate;
|
const struct drm_plane_state *pstate;
|
||||||
u32 rot_mem_free, rot_mem_usable;
|
u32 rot_mem_free, rot_mem_usable;
|
||||||
int rotated_planes = 0;
|
int rotated_planes = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check if there is enough rotation memory available for planes
|
* check if there is enough rotation memory available for planes
|
||||||
|
@ -156,7 +400,11 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
ret = malidp_crtc_atomic_check_gamma(crtc, state);
|
||||||
|
ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
|
||||||
|
ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
|
static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
|
||||||
|
@ -166,6 +414,60 @@ static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
|
||||||
.atomic_check = malidp_crtc_atomic_check,
|
.atomic_check = malidp_crtc_atomic_check,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
memcpy(state->coloradj_coeffs, old_state->coloradj_coeffs,
|
||||||
|
sizeof(state->coloradj_coeffs));
|
||||||
|
memcpy(&state->scaler_config, &old_state->scaler_config,
|
||||||
|
sizeof(state->scaler_config));
|
||||||
|
state->scaled_planes_mask = 0;
|
||||||
|
|
||||||
|
return &state->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void malidp_crtc_reset(struct drm_crtc *crtc)
|
||||||
|
{
|
||||||
|
struct malidp_crtc_state *state = NULL;
|
||||||
|
|
||||||
|
if (crtc->state) {
|
||||||
|
state = to_malidp_crtc_state(crtc->state);
|
||||||
|
__drm_atomic_helper_crtc_destroy_state(crtc->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(state);
|
||||||
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||||
|
if (state) {
|
||||||
|
crtc->state = &state->base;
|
||||||
|
crtc->state->crtc = crtc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void malidp_crtc_destroy_state(struct drm_crtc *crtc,
|
||||||
|
struct drm_crtc_state *state)
|
||||||
|
{
|
||||||
|
struct malidp_crtc_state *mali_state = NULL;
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
mali_state = to_malidp_crtc_state(state);
|
||||||
|
__drm_atomic_helper_crtc_destroy_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(mali_state);
|
||||||
|
}
|
||||||
|
|
||||||
static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
|
static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
||||||
|
@ -186,12 +488,13 @@ static void malidp_crtc_disable_vblank(struct drm_crtc *crtc)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct drm_crtc_funcs malidp_crtc_funcs = {
|
static const struct drm_crtc_funcs malidp_crtc_funcs = {
|
||||||
|
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||||
.destroy = drm_crtc_cleanup,
|
.destroy = drm_crtc_cleanup,
|
||||||
.set_config = drm_atomic_helper_set_config,
|
.set_config = drm_atomic_helper_set_config,
|
||||||
.page_flip = drm_atomic_helper_page_flip,
|
.page_flip = drm_atomic_helper_page_flip,
|
||||||
.reset = drm_atomic_helper_crtc_reset,
|
.reset = malidp_crtc_reset,
|
||||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
.atomic_duplicate_state = malidp_crtc_duplicate_state,
|
||||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
.atomic_destroy_state = malidp_crtc_destroy_state,
|
||||||
.enable_vblank = malidp_crtc_enable_vblank,
|
.enable_vblank = malidp_crtc_enable_vblank,
|
||||||
.disable_vblank = malidp_crtc_disable_vblank,
|
.disable_vblank = malidp_crtc_disable_vblank,
|
||||||
};
|
};
|
||||||
|
@ -223,11 +526,17 @@ int malidp_crtc_init(struct drm_device *drm)
|
||||||
|
|
||||||
ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
|
ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
|
||||||
&malidp_crtc_funcs, NULL);
|
&malidp_crtc_funcs, NULL);
|
||||||
|
if (ret)
|
||||||
|
goto crtc_cleanup_planes;
|
||||||
|
|
||||||
if (!ret) {
|
drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
|
||||||
drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
|
drm_mode_crtc_set_gamma_size(&malidp->crtc, MALIDP_GAMMA_LUT_SIZE);
|
||||||
return 0;
|
/* No inverse-gamma: it is per-plane. */
|
||||||
}
|
drm_crtc_enable_color_mgmt(&malidp->crtc, 0, true, MALIDP_GAMMA_LUT_SIZE);
|
||||||
|
|
||||||
|
malidp_se_set_enh_coeffs(malidp->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
crtc_cleanup_planes:
|
crtc_cleanup_planes:
|
||||||
malidp_de_planes_destroy(drm);
|
malidp_de_planes_destroy(drm);
|
||||||
|
|
|
@ -13,9 +13,11 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/component.h>
|
#include <linux/component.h>
|
||||||
|
#include <linux/console.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_graph.h>
|
#include <linux/of_graph.h>
|
||||||
#include <linux/of_reserved_mem.h>
|
#include <linux/of_reserved_mem.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <drm/drmP.h>
|
#include <drm/drmP.h>
|
||||||
#include <drm/drm_atomic.h>
|
#include <drm/drm_atomic.h>
|
||||||
|
@ -32,6 +34,131 @@
|
||||||
|
|
||||||
#define MALIDP_CONF_VALID_TIMEOUT 250
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void malidp_atomic_commit_update_coloradj(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;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!crtc->state->color_mgmt_changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!crtc->state->ctm) {
|
||||||
|
malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ,
|
||||||
|
MALIDP_DE_DISPLAY_FUNC);
|
||||||
|
} else {
|
||||||
|
struct malidp_crtc_state *mc =
|
||||||
|
to_malidp_crtc_state(crtc->state);
|
||||||
|
|
||||||
|
if (!old_state->ctm || (crtc->state->ctm->base.id !=
|
||||||
|
old_state->ctm->base.id))
|
||||||
|
for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i)
|
||||||
|
malidp_hw_write(hwdev,
|
||||||
|
mc->coloradj_coeffs[i],
|
||||||
|
hwdev->map.coeffs_base +
|
||||||
|
MALIDP_COLOR_ADJ_COEF + 4 * i);
|
||||||
|
|
||||||
|
malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ,
|
||||||
|
MALIDP_DE_DISPLAY_FUNC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void malidp_atomic_commit_se_config(struct drm_crtc *crtc,
|
||||||
|
struct drm_crtc_state *old_state)
|
||||||
|
{
|
||||||
|
struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state);
|
||||||
|
struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state);
|
||||||
|
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
|
||||||
|
struct malidp_hw_device *hwdev = malidp->dev;
|
||||||
|
struct malidp_se_config *s = &cs->scaler_config;
|
||||||
|
struct malidp_se_config *old_s = &old_cs->scaler_config;
|
||||||
|
u32 se_control = hwdev->map.se_base +
|
||||||
|
((hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ?
|
||||||
|
0x10 : 0xC);
|
||||||
|
u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL;
|
||||||
|
u32 scr = se_control + MALIDP_SE_SCALING_CONTROL;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
/* Set SE_CONTROL */
|
||||||
|
if (!s->scale_enable) {
|
||||||
|
val = malidp_hw_read(hwdev, se_control);
|
||||||
|
val &= ~MALIDP_SE_SCALING_EN;
|
||||||
|
malidp_hw_write(hwdev, val, se_control);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwdev->se_set_scaling_coeffs(hwdev, s, old_s);
|
||||||
|
val = malidp_hw_read(hwdev, se_control);
|
||||||
|
val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN;
|
||||||
|
|
||||||
|
val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK);
|
||||||
|
val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0;
|
||||||
|
|
||||||
|
val |= MALIDP_SE_RGBO_IF_EN;
|
||||||
|
malidp_hw_write(hwdev, val, se_control);
|
||||||
|
|
||||||
|
/* Set IN_SIZE & OUT_SIZE. */
|
||||||
|
val = MALIDP_SE_SET_V_SIZE(s->input_h) |
|
||||||
|
MALIDP_SE_SET_H_SIZE(s->input_w);
|
||||||
|
malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE);
|
||||||
|
val = MALIDP_SE_SET_V_SIZE(s->output_h) |
|
||||||
|
MALIDP_SE_SET_H_SIZE(s->output_w);
|
||||||
|
malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE);
|
||||||
|
|
||||||
|
/* Set phase regs. */
|
||||||
|
malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH);
|
||||||
|
malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH);
|
||||||
|
malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH);
|
||||||
|
malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set the "config valid" bit and wait until the hardware acts on it
|
* set the "config valid" bit and wait until the hardware acts on it
|
||||||
*/
|
*/
|
||||||
|
@ -66,10 +193,12 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
|
||||||
struct drm_pending_vblank_event *event;
|
struct drm_pending_vblank_event *event;
|
||||||
struct drm_device *drm = state->dev;
|
struct drm_device *drm = state->dev;
|
||||||
struct malidp_drm *malidp = drm->dev_private;
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
int ret = malidp_set_and_wait_config_valid(drm);
|
|
||||||
|
|
||||||
if (ret)
|
if (malidp->crtc.enabled) {
|
||||||
DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");
|
/* only set config_valid if the CRTC is enabled */
|
||||||
|
if (malidp_set_and_wait_config_valid(drm))
|
||||||
|
DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");
|
||||||
|
}
|
||||||
|
|
||||||
event = malidp->crtc.state->event;
|
event = malidp->crtc.state->event;
|
||||||
if (event) {
|
if (event) {
|
||||||
|
@ -88,15 +217,30 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
|
||||||
static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
|
static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
|
||||||
{
|
{
|
||||||
struct drm_device *drm = state->dev;
|
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);
|
drm_atomic_helper_commit_modeset_disables(drm, state);
|
||||||
drm_atomic_helper_commit_modeset_enables(drm, state);
|
|
||||||
|
for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
|
||||||
|
malidp_atomic_commit_update_gamma(crtc, old_crtc_state);
|
||||||
|
malidp_atomic_commit_update_coloradj(crtc, old_crtc_state);
|
||||||
|
malidp_atomic_commit_se_config(crtc, old_crtc_state);
|
||||||
|
}
|
||||||
|
|
||||||
drm_atomic_helper_commit_planes(drm, state, 0);
|
drm_atomic_helper_commit_planes(drm, state, 0);
|
||||||
|
|
||||||
|
drm_atomic_helper_commit_modeset_enables(drm, state);
|
||||||
|
|
||||||
malidp_atomic_commit_hw_done(state);
|
malidp_atomic_commit_hw_done(state);
|
||||||
|
|
||||||
drm_atomic_helper_wait_for_vblanks(drm, state);
|
drm_atomic_helper_wait_for_vblanks(drm, state);
|
||||||
|
|
||||||
|
pm_runtime_put(drm->dev);
|
||||||
|
|
||||||
drm_atomic_helper_cleanup_planes(drm, state);
|
drm_atomic_helper_cleanup_planes(drm, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,8 +421,65 @@ static bool malidp_has_sufficient_address_space(const struct resource *res,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t core_id_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE_ATTR_RO(core_id);
|
||||||
|
|
||||||
|
static int malidp_init_sysfs(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret = device_create_file(dev, &dev_attr_core_id);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
DRM_ERROR("failed to create device file for core_id\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void malidp_fini_sysfs(struct device *dev)
|
||||||
|
{
|
||||||
|
device_remove_file(dev, &dev_attr_core_id);
|
||||||
|
}
|
||||||
|
|
||||||
#define MAX_OUTPUT_CHANNELS 3
|
#define MAX_OUTPUT_CHANNELS 3
|
||||||
|
|
||||||
|
static int malidp_runtime_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
|
struct malidp_hw_device *hwdev = malidp->dev;
|
||||||
|
|
||||||
|
/* we can only suspend if the hardware is in config mode */
|
||||||
|
WARN_ON(!hwdev->in_config_mode(hwdev));
|
||||||
|
|
||||||
|
hwdev->pm_suspended = true;
|
||||||
|
clk_disable_unprepare(hwdev->mclk);
|
||||||
|
clk_disable_unprepare(hwdev->aclk);
|
||||||
|
clk_disable_unprepare(hwdev->pclk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int malidp_runtime_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
|
struct malidp_hw_device *hwdev = malidp->dev;
|
||||||
|
|
||||||
|
clk_prepare_enable(hwdev->pclk);
|
||||||
|
clk_prepare_enable(hwdev->aclk);
|
||||||
|
clk_prepare_enable(hwdev->mclk);
|
||||||
|
hwdev->pm_suspended = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int malidp_bind(struct device *dev)
|
static int malidp_bind(struct device *dev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -307,7 +508,6 @@ static int malidp_bind(struct device *dev)
|
||||||
memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
|
memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
|
||||||
malidp->dev = hwdev;
|
malidp->dev = hwdev;
|
||||||
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
hwdev->regs = devm_ioremap_resource(dev, res);
|
hwdev->regs = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(hwdev->regs))
|
if (IS_ERR(hwdev->regs))
|
||||||
|
@ -340,14 +540,17 @@ static int malidp_bind(struct device *dev)
|
||||||
goto alloc_fail;
|
goto alloc_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable APB clock in order to get access to the registers */
|
drm->dev_private = malidp;
|
||||||
clk_prepare_enable(hwdev->pclk);
|
dev_set_drvdata(dev, drm);
|
||||||
/*
|
|
||||||
* Enable AXI clock and main clock so that prefetch can start once
|
/* Enable power management */
|
||||||
* the registers are set
|
pm_runtime_enable(dev);
|
||||||
*/
|
|
||||||
clk_prepare_enable(hwdev->aclk);
|
/* Resume device to enable the clocks */
|
||||||
clk_prepare_enable(hwdev->mclk);
|
if (pm_runtime_enabled(dev))
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
|
else
|
||||||
|
malidp_runtime_pm_resume(dev);
|
||||||
|
|
||||||
dev_id = of_match_device(malidp_drm_of_match, dev);
|
dev_id = of_match_device(malidp_drm_of_match, dev);
|
||||||
if (!dev_id) {
|
if (!dev_id) {
|
||||||
|
@ -376,6 +579,8 @@ static int malidp_bind(struct device *dev)
|
||||||
DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
|
DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
|
||||||
(version >> 12) & 0xf, (version >> 8) & 0xf);
|
(version >> 12) & 0xf, (version >> 8) & 0xf);
|
||||||
|
|
||||||
|
malidp->core_id = version;
|
||||||
|
|
||||||
/* set the number of lines used for output of RGB data */
|
/* set the number of lines used for output of RGB data */
|
||||||
ret = of_property_read_u8_array(dev->of_node,
|
ret = of_property_read_u8_array(dev->of_node,
|
||||||
"arm,malidp-output-port-lines",
|
"arm,malidp-output-port-lines",
|
||||||
|
@ -387,13 +592,15 @@ static int malidp_bind(struct device *dev)
|
||||||
out_depth = (out_depth << 8) | (output_width[i] & 0xf);
|
out_depth = (out_depth << 8) | (output_width[i] & 0xf);
|
||||||
malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base);
|
malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base);
|
||||||
|
|
||||||
drm->dev_private = malidp;
|
|
||||||
dev_set_drvdata(dev, drm);
|
|
||||||
atomic_set(&malidp->config_valid, 0);
|
atomic_set(&malidp->config_valid, 0);
|
||||||
init_waitqueue_head(&malidp->wq);
|
init_waitqueue_head(&malidp->wq);
|
||||||
|
|
||||||
ret = malidp_init(drm);
|
ret = malidp_init(drm);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
goto query_hw_fail;
|
||||||
|
|
||||||
|
ret = malidp_init_sysfs(dev);
|
||||||
|
if (ret)
|
||||||
goto init_fail;
|
goto init_fail;
|
||||||
|
|
||||||
/* Set the CRTC's port so that the encoder component can find it */
|
/* Set the CRTC's port so that the encoder component can find it */
|
||||||
|
@ -416,6 +623,7 @@ static int malidp_bind(struct device *dev)
|
||||||
DRM_ERROR("failed to initialise vblank\n");
|
DRM_ERROR("failed to initialise vblank\n");
|
||||||
goto vblank_fail;
|
goto vblank_fail;
|
||||||
}
|
}
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
|
||||||
drm_mode_config_reset(drm);
|
drm_mode_config_reset(drm);
|
||||||
|
|
||||||
|
@ -441,7 +649,9 @@ static int malidp_bind(struct device *dev)
|
||||||
drm_fbdev_cma_fini(malidp->fbdev);
|
drm_fbdev_cma_fini(malidp->fbdev);
|
||||||
malidp->fbdev = NULL;
|
malidp->fbdev = NULL;
|
||||||
}
|
}
|
||||||
|
drm_kms_helper_poll_fini(drm);
|
||||||
fbdev_fail:
|
fbdev_fail:
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
drm_vblank_cleanup(drm);
|
drm_vblank_cleanup(drm);
|
||||||
vblank_fail:
|
vblank_fail:
|
||||||
malidp_se_irq_fini(drm);
|
malidp_se_irq_fini(drm);
|
||||||
|
@ -452,14 +662,17 @@ static int malidp_bind(struct device *dev)
|
||||||
bind_fail:
|
bind_fail:
|
||||||
of_node_put(malidp->crtc.port);
|
of_node_put(malidp->crtc.port);
|
||||||
malidp->crtc.port = NULL;
|
malidp->crtc.port = NULL;
|
||||||
malidp_fini(drm);
|
|
||||||
init_fail:
|
init_fail:
|
||||||
|
malidp_fini_sysfs(dev);
|
||||||
|
malidp_fini(drm);
|
||||||
|
query_hw_fail:
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
if (pm_runtime_enabled(dev))
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
else
|
||||||
|
malidp_runtime_pm_suspend(dev);
|
||||||
drm->dev_private = NULL;
|
drm->dev_private = NULL;
|
||||||
dev_set_drvdata(dev, NULL);
|
dev_set_drvdata(dev, NULL);
|
||||||
query_hw_fail:
|
|
||||||
clk_disable_unprepare(hwdev->mclk);
|
|
||||||
clk_disable_unprepare(hwdev->aclk);
|
|
||||||
clk_disable_unprepare(hwdev->pclk);
|
|
||||||
drm_dev_unref(drm);
|
drm_dev_unref(drm);
|
||||||
alloc_fail:
|
alloc_fail:
|
||||||
of_reserved_mem_device_release(dev);
|
of_reserved_mem_device_release(dev);
|
||||||
|
@ -471,7 +684,6 @@ static void malidp_unbind(struct device *dev)
|
||||||
{
|
{
|
||||||
struct drm_device *drm = dev_get_drvdata(dev);
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
struct malidp_drm *malidp = drm->dev_private;
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
struct malidp_hw_device *hwdev = malidp->dev;
|
|
||||||
|
|
||||||
drm_dev_unregister(drm);
|
drm_dev_unregister(drm);
|
||||||
if (malidp->fbdev) {
|
if (malidp->fbdev) {
|
||||||
|
@ -479,18 +691,22 @@ static void malidp_unbind(struct device *dev)
|
||||||
malidp->fbdev = NULL;
|
malidp->fbdev = NULL;
|
||||||
}
|
}
|
||||||
drm_kms_helper_poll_fini(drm);
|
drm_kms_helper_poll_fini(drm);
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
|
drm_vblank_cleanup(drm);
|
||||||
malidp_se_irq_fini(drm);
|
malidp_se_irq_fini(drm);
|
||||||
malidp_de_irq_fini(drm);
|
malidp_de_irq_fini(drm);
|
||||||
drm_vblank_cleanup(drm);
|
|
||||||
component_unbind_all(dev, drm);
|
component_unbind_all(dev, drm);
|
||||||
of_node_put(malidp->crtc.port);
|
of_node_put(malidp->crtc.port);
|
||||||
malidp->crtc.port = NULL;
|
malidp->crtc.port = NULL;
|
||||||
|
malidp_fini_sysfs(dev);
|
||||||
malidp_fini(drm);
|
malidp_fini(drm);
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
if (pm_runtime_enabled(dev))
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
else
|
||||||
|
malidp_runtime_pm_suspend(dev);
|
||||||
drm->dev_private = NULL;
|
drm->dev_private = NULL;
|
||||||
dev_set_drvdata(dev, NULL);
|
dev_set_drvdata(dev, NULL);
|
||||||
clk_disable_unprepare(hwdev->mclk);
|
|
||||||
clk_disable_unprepare(hwdev->aclk);
|
|
||||||
clk_disable_unprepare(hwdev->pclk);
|
|
||||||
drm_dev_unref(drm);
|
drm_dev_unref(drm);
|
||||||
of_reserved_mem_device_release(dev);
|
of_reserved_mem_device_release(dev);
|
||||||
}
|
}
|
||||||
|
@ -533,11 +749,52 @@ static int malidp_platform_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused malidp_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
|
|
||||||
|
drm_kms_helper_poll_disable(drm);
|
||||||
|
console_lock();
|
||||||
|
drm_fbdev_cma_set_suspend(malidp->fbdev, 1);
|
||||||
|
console_unlock();
|
||||||
|
malidp->pm_state = drm_atomic_helper_suspend(drm);
|
||||||
|
if (IS_ERR(malidp->pm_state)) {
|
||||||
|
console_lock();
|
||||||
|
drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
|
||||||
|
console_unlock();
|
||||||
|
drm_kms_helper_poll_enable(drm);
|
||||||
|
return PTR_ERR(malidp->pm_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused malidp_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct malidp_drm *malidp = drm->dev_private;
|
||||||
|
|
||||||
|
drm_atomic_helper_resume(drm, malidp->pm_state);
|
||||||
|
console_lock();
|
||||||
|
drm_fbdev_cma_set_suspend(malidp->fbdev, 0);
|
||||||
|
console_unlock();
|
||||||
|
drm_kms_helper_poll_enable(drm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops malidp_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \
|
||||||
|
SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver malidp_platform_driver = {
|
static struct platform_driver malidp_platform_driver = {
|
||||||
.probe = malidp_platform_probe,
|
.probe = malidp_platform_probe,
|
||||||
.remove = malidp_platform_remove,
|
.remove = malidp_platform_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mali-dp",
|
.name = "mali-dp",
|
||||||
|
.pm = &malidp_pm_ops,
|
||||||
.of_match_table = malidp_drm_of_match,
|
.of_match_table = malidp_drm_of_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,8 @@ struct malidp_drm {
|
||||||
struct drm_crtc crtc;
|
struct drm_crtc crtc;
|
||||||
wait_queue_head_t wq;
|
wait_queue_head_t wq;
|
||||||
atomic_t config_valid;
|
atomic_t config_valid;
|
||||||
|
struct drm_atomic_state *pm_state;
|
||||||
|
u32 core_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
|
#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
|
||||||
|
@ -47,6 +49,17 @@ struct malidp_plane_state {
|
||||||
#define to_malidp_plane(x) container_of(x, struct malidp_plane, base)
|
#define to_malidp_plane(x) container_of(x, struct malidp_plane, base)
|
||||||
#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base)
|
#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base)
|
||||||
|
|
||||||
|
struct malidp_crtc_state {
|
||||||
|
struct drm_crtc_state base;
|
||||||
|
u32 gamma_coeffs[MALIDP_COEFFTAB_NUM_COEFFS];
|
||||||
|
u32 coloradj_coeffs[MALIDP_COLORADJ_NUM_COEFFS];
|
||||||
|
struct malidp_se_config scaler_config;
|
||||||
|
/* Bitfield of all the planes that have requested a scaled output. */
|
||||||
|
u8 scaled_planes_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_malidp_crtc_state(x) container_of(x, struct malidp_crtc_state, base)
|
||||||
|
|
||||||
int malidp_de_planes_init(struct drm_device *drm);
|
int malidp_de_planes_init(struct drm_device *drm);
|
||||||
void malidp_de_planes_destroy(struct drm_device *drm);
|
void malidp_de_planes_destroy(struct drm_device *drm);
|
||||||
int malidp_crtc_init(struct drm_device *drm);
|
int malidp_crtc_init(struct drm_device *drm);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* in an attempt to provide to the rest of the driver code a unified view
|
* in an attempt to provide to the rest of the driver code a unified view
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <drm/drmP.h>
|
#include <drm/drmP.h>
|
||||||
|
@ -86,6 +87,80 @@ static const struct malidp_layer malidp550_layers[] = {
|
||||||
{ DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE, MALIDP550_DE_LS_R1_STRIDE },
|
{ DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE, MALIDP550_DE_LS_R1_STRIDE },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SE_N_SCALING_COEFFS 96
|
||||||
|
static const u16 dp500_se_scaling_coeffs[][SE_N_SCALING_COEFFS] = {
|
||||||
|
[MALIDP_UPSCALING_COEFFS - 1] = {
|
||||||
|
0x0000, 0x0001, 0x0007, 0x0011, 0x001e, 0x002e, 0x003f, 0x0052,
|
||||||
|
0x0064, 0x0073, 0x007d, 0x0080, 0x007a, 0x006c, 0x0053, 0x002f,
|
||||||
|
0x0000, 0x3fc6, 0x3f83, 0x3f39, 0x3eea, 0x3e9b, 0x3e4f, 0x3e0a,
|
||||||
|
0x3dd4, 0x3db0, 0x3da2, 0x3db1, 0x3dde, 0x3e2f, 0x3ea5, 0x3f40,
|
||||||
|
0x0000, 0x00e5, 0x01ee, 0x0315, 0x0456, 0x05aa, 0x0709, 0x086c,
|
||||||
|
0x09c9, 0x0b15, 0x0c4a, 0x0d5d, 0x0e4a, 0x0f06, 0x0f91, 0x0fe5,
|
||||||
|
0x1000, 0x0fe5, 0x0f91, 0x0f06, 0x0e4a, 0x0d5d, 0x0c4a, 0x0b15,
|
||||||
|
0x09c9, 0x086c, 0x0709, 0x05aa, 0x0456, 0x0315, 0x01ee, 0x00e5,
|
||||||
|
0x0000, 0x3f40, 0x3ea5, 0x3e2f, 0x3dde, 0x3db1, 0x3da2, 0x3db0,
|
||||||
|
0x3dd4, 0x3e0a, 0x3e4f, 0x3e9b, 0x3eea, 0x3f39, 0x3f83, 0x3fc6,
|
||||||
|
0x0000, 0x002f, 0x0053, 0x006c, 0x007a, 0x0080, 0x007d, 0x0073,
|
||||||
|
0x0064, 0x0052, 0x003f, 0x002e, 0x001e, 0x0011, 0x0007, 0x0001
|
||||||
|
},
|
||||||
|
[MALIDP_DOWNSCALING_1_5_COEFFS - 1] = {
|
||||||
|
0x0059, 0x004f, 0x0041, 0x002e, 0x0016, 0x3ffb, 0x3fd9, 0x3fb4,
|
||||||
|
0x3f8c, 0x3f62, 0x3f36, 0x3f09, 0x3edd, 0x3eb3, 0x3e8d, 0x3e6c,
|
||||||
|
0x3e52, 0x3e3f, 0x3e35, 0x3e37, 0x3e46, 0x3e61, 0x3e8c, 0x3ec5,
|
||||||
|
0x3f0f, 0x3f68, 0x3fd1, 0x004a, 0x00d3, 0x0169, 0x020b, 0x02b8,
|
||||||
|
0x036e, 0x042d, 0x04f2, 0x05b9, 0x0681, 0x0745, 0x0803, 0x08ba,
|
||||||
|
0x0965, 0x0a03, 0x0a91, 0x0b0d, 0x0b75, 0x0bc6, 0x0c00, 0x0c20,
|
||||||
|
0x0c28, 0x0c20, 0x0c00, 0x0bc6, 0x0b75, 0x0b0d, 0x0a91, 0x0a03,
|
||||||
|
0x0965, 0x08ba, 0x0803, 0x0745, 0x0681, 0x05b9, 0x04f2, 0x042d,
|
||||||
|
0x036e, 0x02b8, 0x020b, 0x0169, 0x00d3, 0x004a, 0x3fd1, 0x3f68,
|
||||||
|
0x3f0f, 0x3ec5, 0x3e8c, 0x3e61, 0x3e46, 0x3e37, 0x3e35, 0x3e3f,
|
||||||
|
0x3e52, 0x3e6c, 0x3e8d, 0x3eb3, 0x3edd, 0x3f09, 0x3f36, 0x3f62,
|
||||||
|
0x3f8c, 0x3fb4, 0x3fd9, 0x3ffb, 0x0016, 0x002e, 0x0041, 0x004f
|
||||||
|
},
|
||||||
|
[MALIDP_DOWNSCALING_2_COEFFS - 1] = {
|
||||||
|
0x3f19, 0x3f03, 0x3ef0, 0x3edf, 0x3ed0, 0x3ec5, 0x3ebd, 0x3eb9,
|
||||||
|
0x3eb9, 0x3ebf, 0x3eca, 0x3ed9, 0x3eef, 0x3f0a, 0x3f2c, 0x3f52,
|
||||||
|
0x3f7f, 0x3fb0, 0x3fe8, 0x0026, 0x006a, 0x00b4, 0x0103, 0x0158,
|
||||||
|
0x01b1, 0x020d, 0x026c, 0x02cd, 0x032f, 0x0392, 0x03f4, 0x0455,
|
||||||
|
0x04b4, 0x051e, 0x0585, 0x05eb, 0x064c, 0x06a8, 0x06fe, 0x074e,
|
||||||
|
0x0796, 0x07d5, 0x080c, 0x0839, 0x085c, 0x0875, 0x0882, 0x0887,
|
||||||
|
0x0881, 0x0887, 0x0882, 0x0875, 0x085c, 0x0839, 0x080c, 0x07d5,
|
||||||
|
0x0796, 0x074e, 0x06fe, 0x06a8, 0x064c, 0x05eb, 0x0585, 0x051e,
|
||||||
|
0x04b4, 0x0455, 0x03f4, 0x0392, 0x032f, 0x02cd, 0x026c, 0x020d,
|
||||||
|
0x01b1, 0x0158, 0x0103, 0x00b4, 0x006a, 0x0026, 0x3fe8, 0x3fb0,
|
||||||
|
0x3f7f, 0x3f52, 0x3f2c, 0x3f0a, 0x3eef, 0x3ed9, 0x3eca, 0x3ebf,
|
||||||
|
0x3eb9, 0x3eb9, 0x3ebd, 0x3ec5, 0x3ed0, 0x3edf, 0x3ef0, 0x3f03
|
||||||
|
},
|
||||||
|
[MALIDP_DOWNSCALING_2_75_COEFFS - 1] = {
|
||||||
|
0x3f51, 0x3f60, 0x3f71, 0x3f84, 0x3f98, 0x3faf, 0x3fc8, 0x3fe3,
|
||||||
|
0x0000, 0x001f, 0x0040, 0x0064, 0x008a, 0x00b1, 0x00da, 0x0106,
|
||||||
|
0x0133, 0x0160, 0x018e, 0x01bd, 0x01ec, 0x021d, 0x024e, 0x0280,
|
||||||
|
0x02b2, 0x02e4, 0x0317, 0x0349, 0x037c, 0x03ad, 0x03df, 0x0410,
|
||||||
|
0x0440, 0x0468, 0x048f, 0x04b3, 0x04d6, 0x04f8, 0x0516, 0x0533,
|
||||||
|
0x054e, 0x0566, 0x057c, 0x0590, 0x05a0, 0x05ae, 0x05ba, 0x05c3,
|
||||||
|
0x05c9, 0x05c3, 0x05ba, 0x05ae, 0x05a0, 0x0590, 0x057c, 0x0566,
|
||||||
|
0x054e, 0x0533, 0x0516, 0x04f8, 0x04d6, 0x04b3, 0x048f, 0x0468,
|
||||||
|
0x0440, 0x0410, 0x03df, 0x03ad, 0x037c, 0x0349, 0x0317, 0x02e4,
|
||||||
|
0x02b2, 0x0280, 0x024e, 0x021d, 0x01ec, 0x01bd, 0x018e, 0x0160,
|
||||||
|
0x0133, 0x0106, 0x00da, 0x00b1, 0x008a, 0x0064, 0x0040, 0x001f,
|
||||||
|
0x0000, 0x3fe3, 0x3fc8, 0x3faf, 0x3f98, 0x3f84, 0x3f71, 0x3f60
|
||||||
|
},
|
||||||
|
[MALIDP_DOWNSCALING_4_COEFFS - 1] = {
|
||||||
|
0x0094, 0x00a9, 0x00be, 0x00d4, 0x00ea, 0x0101, 0x0118, 0x012f,
|
||||||
|
0x0148, 0x0160, 0x017a, 0x0193, 0x01ae, 0x01c8, 0x01e4, 0x01ff,
|
||||||
|
0x021c, 0x0233, 0x024a, 0x0261, 0x0278, 0x028f, 0x02a6, 0x02bd,
|
||||||
|
0x02d4, 0x02eb, 0x0302, 0x0319, 0x032f, 0x0346, 0x035d, 0x0374,
|
||||||
|
0x038a, 0x0397, 0x03a3, 0x03af, 0x03bb, 0x03c6, 0x03d1, 0x03db,
|
||||||
|
0x03e4, 0x03ed, 0x03f6, 0x03fe, 0x0406, 0x040d, 0x0414, 0x041a,
|
||||||
|
0x0420, 0x041a, 0x0414, 0x040d, 0x0406, 0x03fe, 0x03f6, 0x03ed,
|
||||||
|
0x03e4, 0x03db, 0x03d1, 0x03c6, 0x03bb, 0x03af, 0x03a3, 0x0397,
|
||||||
|
0x038a, 0x0374, 0x035d, 0x0346, 0x032f, 0x0319, 0x0302, 0x02eb,
|
||||||
|
0x02d4, 0x02bd, 0x02a6, 0x028f, 0x0278, 0x0261, 0x024a, 0x0233,
|
||||||
|
0x021c, 0x01ff, 0x01e4, 0x01c8, 0x01ae, 0x0193, 0x017a, 0x0160,
|
||||||
|
0x0148, 0x012f, 0x0118, 0x0101, 0x00ea, 0x00d4, 0x00be, 0x00a9
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#define MALIDP_DE_DEFAULT_PREFETCH_START 5
|
#define MALIDP_DE_DEFAULT_PREFETCH_START 5
|
||||||
|
|
||||||
static int malidp500_query_hw(struct malidp_hw_device *hwdev)
|
static int malidp500_query_hw(struct malidp_hw_device *hwdev)
|
||||||
|
@ -211,6 +286,88 @@ static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
|
||||||
return w * drm_format_plane_cpp(fmt, 0) * 8;
|
return w * drm_format_plane_cpp(fmt, 0) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void malidp500_se_write_pp_coefftab(struct malidp_hw_device *hwdev,
|
||||||
|
u32 direction,
|
||||||
|
u16 addr,
|
||||||
|
u8 coeffs_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u16 scaling_control = MALIDP500_SE_CONTROL + MALIDP_SE_SCALING_CONTROL;
|
||||||
|
|
||||||
|
malidp_hw_write(hwdev,
|
||||||
|
direction | (addr & MALIDP_SE_COEFFTAB_ADDR_MASK),
|
||||||
|
scaling_control + MALIDP_SE_COEFFTAB_ADDR);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(dp500_se_scaling_coeffs); ++i)
|
||||||
|
malidp_hw_write(hwdev, MALIDP_SE_SET_COEFFTAB_DATA(
|
||||||
|
dp500_se_scaling_coeffs[coeffs_id][i]),
|
||||||
|
scaling_control + MALIDP_SE_COEFFTAB_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int malidp500_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
|
||||||
|
struct malidp_se_config *se_config,
|
||||||
|
struct malidp_se_config *old_config)
|
||||||
|
{
|
||||||
|
/* Get array indices into dp500_se_scaling_coeffs. */
|
||||||
|
u8 h = (u8)se_config->hcoeff - 1;
|
||||||
|
u8 v = (u8)se_config->vcoeff - 1;
|
||||||
|
|
||||||
|
if (WARN_ON(h >= ARRAY_SIZE(dp500_se_scaling_coeffs) ||
|
||||||
|
v >= ARRAY_SIZE(dp500_se_scaling_coeffs)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((h == v) && (se_config->hcoeff != old_config->hcoeff ||
|
||||||
|
se_config->vcoeff != old_config->vcoeff)) {
|
||||||
|
malidp500_se_write_pp_coefftab(hwdev,
|
||||||
|
(MALIDP_SE_V_COEFFTAB |
|
||||||
|
MALIDP_SE_H_COEFFTAB),
|
||||||
|
0, v);
|
||||||
|
} else {
|
||||||
|
if (se_config->vcoeff != old_config->vcoeff)
|
||||||
|
malidp500_se_write_pp_coefftab(hwdev,
|
||||||
|
MALIDP_SE_V_COEFFTAB,
|
||||||
|
0, v);
|
||||||
|
if (se_config->hcoeff != old_config->hcoeff)
|
||||||
|
malidp500_se_write_pp_coefftab(hwdev,
|
||||||
|
MALIDP_SE_H_COEFFTAB,
|
||||||
|
0, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
|
||||||
|
struct malidp_se_config *se_config,
|
||||||
|
struct videomode *vm)
|
||||||
|
{
|
||||||
|
unsigned long mclk;
|
||||||
|
unsigned long pxlclk = vm->pixelclock; /* Hz */
|
||||||
|
unsigned long htotal = vm->hactive + vm->hfront_porch +
|
||||||
|
vm->hback_porch + vm->hsync_len;
|
||||||
|
unsigned long input_size = se_config->input_w * se_config->input_h;
|
||||||
|
unsigned long a = 10;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mclk = max(a, 1.5) * pxlclk
|
||||||
|
*
|
||||||
|
* To avoid float calculaiton, using 15 instead of 1.5 and div by
|
||||||
|
* 10 to get mclk.
|
||||||
|
*/
|
||||||
|
if (se_config->scale_enable) {
|
||||||
|
a = 15 * input_size / (htotal * se_config->output_h);
|
||||||
|
if (a < 15)
|
||||||
|
a = 15;
|
||||||
|
}
|
||||||
|
mclk = a * pxlclk / 10;
|
||||||
|
ret = clk_get_rate(hwdev->mclk);
|
||||||
|
if (ret < mclk) {
|
||||||
|
DRM_DEBUG_DRIVER("mclk requirement of %lu kHz can't be met.\n",
|
||||||
|
mclk / 1000);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int malidp550_query_hw(struct malidp_hw_device *hwdev)
|
static int malidp550_query_hw(struct malidp_hw_device *hwdev)
|
||||||
{
|
{
|
||||||
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
|
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
|
||||||
|
@ -384,6 +541,53 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
|
||||||
return w * bytes_per_col;
|
return w * bytes_per_col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
|
||||||
|
struct malidp_se_config *se_config,
|
||||||
|
struct malidp_se_config *old_config)
|
||||||
|
{
|
||||||
|
u32 mask = MALIDP550_SE_CTL_VCSEL(MALIDP550_SE_CTL_SEL_MASK) |
|
||||||
|
MALIDP550_SE_CTL_HCSEL(MALIDP550_SE_CTL_SEL_MASK);
|
||||||
|
u32 new_value = MALIDP550_SE_CTL_VCSEL(se_config->vcoeff) |
|
||||||
|
MALIDP550_SE_CTL_HCSEL(se_config->hcoeff);
|
||||||
|
|
||||||
|
malidp_hw_clearbits(hwdev, mask, MALIDP550_SE_CONTROL);
|
||||||
|
malidp_hw_setbits(hwdev, new_value, MALIDP550_SE_CONTROL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long malidp550_se_calc_mclk(struct malidp_hw_device *hwdev,
|
||||||
|
struct malidp_se_config *se_config,
|
||||||
|
struct videomode *vm)
|
||||||
|
{
|
||||||
|
unsigned long mclk;
|
||||||
|
unsigned long pxlclk = vm->pixelclock;
|
||||||
|
unsigned long htotal = vm->hactive + vm->hfront_porch +
|
||||||
|
vm->hback_porch + vm->hsync_len;
|
||||||
|
unsigned long numerator = 1, denominator = 1;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
if (se_config->scale_enable) {
|
||||||
|
numerator = max(se_config->input_w, se_config->output_w) *
|
||||||
|
se_config->input_h;
|
||||||
|
numerator += se_config->output_w *
|
||||||
|
(se_config->output_h -
|
||||||
|
min(se_config->input_h, se_config->output_h));
|
||||||
|
denominator = (htotal - 2) * se_config->output_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mclk can't be slower than pxlclk. */
|
||||||
|
if (numerator < denominator)
|
||||||
|
numerator = denominator = 1;
|
||||||
|
mclk = (pxlclk * numerator) / denominator;
|
||||||
|
ret = clk_get_rate(hwdev->mclk);
|
||||||
|
if (ret < mclk) {
|
||||||
|
DRM_DEBUG_DRIVER("mclk requirement of %lu kHz can't be met.\n",
|
||||||
|
mclk / 1000);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int malidp650_query_hw(struct malidp_hw_device *hwdev)
|
static int malidp650_query_hw(struct malidp_hw_device *hwdev)
|
||||||
{
|
{
|
||||||
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
|
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
|
||||||
|
@ -415,6 +619,7 @@ static int malidp650_query_hw(struct malidp_hw_device *hwdev)
|
||||||
const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
||||||
[MALIDP_500] = {
|
[MALIDP_500] = {
|
||||||
.map = {
|
.map = {
|
||||||
|
.coeffs_base = MALIDP500_COEFFS_BASE,
|
||||||
.se_base = MALIDP500_SE_BASE,
|
.se_base = MALIDP500_SE_BASE,
|
||||||
.dc_base = MALIDP500_DC_BASE,
|
.dc_base = MALIDP500_DC_BASE,
|
||||||
.out_depth_base = MALIDP500_OUTPUT_DEPTH,
|
.out_depth_base = MALIDP500_OUTPUT_DEPTH,
|
||||||
|
@ -447,10 +652,13 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
||||||
.set_config_valid = malidp500_set_config_valid,
|
.set_config_valid = malidp500_set_config_valid,
|
||||||
.modeset = malidp500_modeset,
|
.modeset = malidp500_modeset,
|
||||||
.rotmem_required = malidp500_rotmem_required,
|
.rotmem_required = malidp500_rotmem_required,
|
||||||
|
.se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
|
||||||
|
.se_calc_mclk = malidp500_se_calc_mclk,
|
||||||
.features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
|
.features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
|
||||||
},
|
},
|
||||||
[MALIDP_550] = {
|
[MALIDP_550] = {
|
||||||
.map = {
|
.map = {
|
||||||
|
.coeffs_base = MALIDP550_COEFFS_BASE,
|
||||||
.se_base = MALIDP550_SE_BASE,
|
.se_base = MALIDP550_SE_BASE,
|
||||||
.dc_base = MALIDP550_DC_BASE,
|
.dc_base = MALIDP550_DC_BASE,
|
||||||
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
||||||
|
@ -481,10 +689,13 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
||||||
.set_config_valid = malidp550_set_config_valid,
|
.set_config_valid = malidp550_set_config_valid,
|
||||||
.modeset = malidp550_modeset,
|
.modeset = malidp550_modeset,
|
||||||
.rotmem_required = malidp550_rotmem_required,
|
.rotmem_required = malidp550_rotmem_required,
|
||||||
|
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
|
||||||
|
.se_calc_mclk = malidp550_se_calc_mclk,
|
||||||
.features = 0,
|
.features = 0,
|
||||||
},
|
},
|
||||||
[MALIDP_650] = {
|
[MALIDP_650] = {
|
||||||
.map = {
|
.map = {
|
||||||
|
.coeffs_base = MALIDP550_COEFFS_BASE,
|
||||||
.se_base = MALIDP550_SE_BASE,
|
.se_base = MALIDP550_SE_BASE,
|
||||||
.dc_base = MALIDP550_DC_BASE,
|
.dc_base = MALIDP550_DC_BASE,
|
||||||
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
||||||
|
@ -516,6 +727,8 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
|
||||||
.set_config_valid = malidp550_set_config_valid,
|
.set_config_valid = malidp550_set_config_valid,
|
||||||
.modeset = malidp550_modeset,
|
.modeset = malidp550_modeset,
|
||||||
.rotmem_required = malidp550_rotmem_required,
|
.rotmem_required = malidp550_rotmem_required,
|
||||||
|
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
|
||||||
|
.se_calc_mclk = malidp550_se_calc_mclk,
|
||||||
.features = 0,
|
.features = 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,12 +61,34 @@ struct malidp_layer {
|
||||||
u16 stride_offset; /* Offset to the first stride register. */
|
u16 stride_offset; /* Offset to the first stride register. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum malidp_scaling_coeff_set {
|
||||||
|
MALIDP_UPSCALING_COEFFS = 1,
|
||||||
|
MALIDP_DOWNSCALING_1_5_COEFFS = 2,
|
||||||
|
MALIDP_DOWNSCALING_2_COEFFS = 3,
|
||||||
|
MALIDP_DOWNSCALING_2_75_COEFFS = 4,
|
||||||
|
MALIDP_DOWNSCALING_4_COEFFS = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct malidp_se_config {
|
||||||
|
u8 scale_enable : 1;
|
||||||
|
u8 enhancer_enable : 1;
|
||||||
|
u8 hcoeff : 3;
|
||||||
|
u8 vcoeff : 3;
|
||||||
|
u8 plane_src_id;
|
||||||
|
u16 input_w, input_h;
|
||||||
|
u16 output_w, output_h;
|
||||||
|
u32 h_init_phase, h_delta_phase;
|
||||||
|
u32 v_init_phase, v_delta_phase;
|
||||||
|
};
|
||||||
|
|
||||||
/* regmap features */
|
/* regmap features */
|
||||||
#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0)
|
#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0)
|
||||||
|
|
||||||
struct malidp_hw_regmap {
|
struct malidp_hw_regmap {
|
||||||
/* address offset of the DE register bank */
|
/* address offset of the DE register bank */
|
||||||
/* is always 0x0000 */
|
/* is always 0x0000 */
|
||||||
|
/* address offset of the DE coefficients registers */
|
||||||
|
const u16 coeffs_base;
|
||||||
/* address offset of the SE registers bank */
|
/* address offset of the SE registers bank */
|
||||||
const u16 se_base;
|
const u16 se_base;
|
||||||
/* address offset of the DC registers bank */
|
/* address offset of the DC registers bank */
|
||||||
|
@ -151,11 +173,22 @@ struct malidp_hw_device {
|
||||||
*/
|
*/
|
||||||
int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
|
int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
|
||||||
|
|
||||||
|
int (*se_set_scaling_coeffs)(struct malidp_hw_device *hwdev,
|
||||||
|
struct malidp_se_config *se_config,
|
||||||
|
struct malidp_se_config *old_config);
|
||||||
|
|
||||||
|
long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
|
||||||
|
struct malidp_se_config *se_config,
|
||||||
|
struct videomode *vm);
|
||||||
|
|
||||||
u8 features;
|
u8 features;
|
||||||
|
|
||||||
u8 min_line_size;
|
u8 min_line_size;
|
||||||
u16 max_line_size;
|
u16 max_line_size;
|
||||||
|
|
||||||
|
/* track the device PM state */
|
||||||
|
bool pm_suspended;
|
||||||
|
|
||||||
/* size of memory used for rotating layers, up to two banks available */
|
/* size of memory used for rotating layers, up to two banks available */
|
||||||
u32 rotation_memory[2];
|
u32 rotation_memory[2];
|
||||||
};
|
};
|
||||||
|
@ -173,12 +206,14 @@ extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES];
|
||||||
|
|
||||||
static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
|
static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
|
||||||
{
|
{
|
||||||
|
WARN_ON(hwdev->pm_suspended);
|
||||||
return readl(hwdev->regs + reg);
|
return readl(hwdev->regs + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void malidp_hw_write(struct malidp_hw_device *hwdev,
|
static inline void malidp_hw_write(struct malidp_hw_device *hwdev,
|
||||||
u32 value, u32 reg)
|
u32 value, u32 reg)
|
||||||
{
|
{
|
||||||
|
WARN_ON(hwdev->pm_suspended);
|
||||||
writel(value, hwdev->regs + reg);
|
writel(value, hwdev->regs + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,6 +278,47 @@ static inline bool malidp_hw_pitch_valid(struct malidp_hw_device *hwdev,
|
||||||
return !(pitch & (hwdev->map.bus_align_bytes - 1));
|
return !(pitch & (hwdev->map.bus_align_bytes - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* U16.16 */
|
||||||
|
#define FP_1_00000 0x00010000 /* 1.0 */
|
||||||
|
#define FP_0_66667 0x0000AAAA /* 0.6667 = 1/1.5 */
|
||||||
|
#define FP_0_50000 0x00008000 /* 0.5 = 1/2 */
|
||||||
|
#define FP_0_36363 0x00005D17 /* 0.36363 = 1/2.75 */
|
||||||
|
#define FP_0_25000 0x00004000 /* 0.25 = 1/4 */
|
||||||
|
|
||||||
|
static inline enum malidp_scaling_coeff_set
|
||||||
|
malidp_se_select_coeffs(u32 upscale_factor)
|
||||||
|
{
|
||||||
|
return (upscale_factor >= FP_1_00000) ? MALIDP_UPSCALING_COEFFS :
|
||||||
|
(upscale_factor >= FP_0_66667) ? MALIDP_DOWNSCALING_1_5_COEFFS :
|
||||||
|
(upscale_factor >= FP_0_50000) ? MALIDP_DOWNSCALING_2_COEFFS :
|
||||||
|
(upscale_factor >= FP_0_36363) ? MALIDP_DOWNSCALING_2_75_COEFFS :
|
||||||
|
MALIDP_DOWNSCALING_4_COEFFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef FP_0_25000
|
||||||
|
#undef FP_0_36363
|
||||||
|
#undef FP_0_50000
|
||||||
|
#undef FP_0_66667
|
||||||
|
#undef FP_1_00000
|
||||||
|
|
||||||
|
static inline void malidp_se_set_enh_coeffs(struct malidp_hw_device *hwdev)
|
||||||
|
{
|
||||||
|
static const s32 enhancer_coeffs[] = {
|
||||||
|
-8, -8, -8, -8, 128, -8, -8, -8, -8
|
||||||
|
};
|
||||||
|
u32 val = MALIDP_SE_SET_ENH_LIMIT_LOW(MALIDP_SE_ENH_LOW_LEVEL) |
|
||||||
|
MALIDP_SE_SET_ENH_LIMIT_HIGH(MALIDP_SE_ENH_HIGH_LEVEL);
|
||||||
|
u32 image_enh = hwdev->map.se_base +
|
||||||
|
((hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ?
|
||||||
|
0x10 : 0xC) + MALIDP_SE_IMAGE_ENH;
|
||||||
|
u32 enh_coeffs = image_enh + MALIDP_SE_ENH_COEFF0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
malidp_hw_write(hwdev, val, image_enh);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(enhancer_coeffs); ++i)
|
||||||
|
malidp_hw_write(hwdev, enhancer_coeffs[i], enh_coeffs + i * 4);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* background color components are defined as 12bits values,
|
* background color components are defined as 12bits values,
|
||||||
* they will be shifted right when stored on hardware that
|
* they will be shifted right when stored on hardware that
|
||||||
|
@ -252,4 +328,9 @@ static inline bool malidp_hw_pitch_valid(struct malidp_hw_device *hwdev,
|
||||||
#define MALIDP_BGND_COLOR_G 0x000
|
#define MALIDP_BGND_COLOR_G 0x000
|
||||||
#define MALIDP_BGND_COLOR_B 0x000
|
#define MALIDP_BGND_COLOR_B 0x000
|
||||||
|
|
||||||
|
#define MALIDP_COLORADJ_NUM_COEFFS 12
|
||||||
|
#define MALIDP_COEFFTAB_NUM_COEFFS 64
|
||||||
|
|
||||||
|
#define MALIDP_GAMMA_LUT_SIZE 4096
|
||||||
|
|
||||||
#endif /* __MALIDP_HW_H__ */
|
#endif /* __MALIDP_HW_H__ */
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <drm/drm_fb_cma_helper.h>
|
#include <drm/drm_fb_cma_helper.h>
|
||||||
#include <drm/drm_gem_cma_helper.h>
|
#include <drm/drm_gem_cma_helper.h>
|
||||||
#include <drm/drm_plane_helper.h>
|
#include <drm/drm_plane_helper.h>
|
||||||
|
#include <drm/drm_print.h>
|
||||||
|
|
||||||
#include "malidp_hw.h"
|
#include "malidp_hw.h"
|
||||||
#include "malidp_drv.h"
|
#include "malidp_drv.h"
|
||||||
|
@ -24,6 +25,9 @@
|
||||||
#define MALIDP_LAYER_FORMAT 0x000
|
#define MALIDP_LAYER_FORMAT 0x000
|
||||||
#define MALIDP_LAYER_CONTROL 0x004
|
#define MALIDP_LAYER_CONTROL 0x004
|
||||||
#define LAYER_ENABLE (1 << 0)
|
#define LAYER_ENABLE (1 << 0)
|
||||||
|
#define LAYER_FLOWCFG_MASK 7
|
||||||
|
#define LAYER_FLOWCFG(x) (((x) & LAYER_FLOWCFG_MASK) << 1)
|
||||||
|
#define LAYER_FLOWCFG_SCALE_SE 3
|
||||||
#define LAYER_ROT_OFFSET 8
|
#define LAYER_ROT_OFFSET 8
|
||||||
#define LAYER_H_FLIP (1 << 10)
|
#define LAYER_H_FLIP (1 << 10)
|
||||||
#define LAYER_V_FLIP (1 << 11)
|
#define LAYER_V_FLIP (1 << 11)
|
||||||
|
@ -60,6 +64,27 @@ static void malidp_de_plane_destroy(struct drm_plane *plane)
|
||||||
devm_kfree(plane->dev->dev, mp);
|
devm_kfree(plane->dev->dev, mp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replicate what the default ->reset hook does: free the state pointer and
|
||||||
|
* allocate a new empty object. We just need enough space to store
|
||||||
|
* a malidp_plane_state instead of a drm_plane_state.
|
||||||
|
*/
|
||||||
|
static void malidp_plane_reset(struct drm_plane *plane)
|
||||||
|
{
|
||||||
|
struct malidp_plane_state *state = to_malidp_plane_state(plane->state);
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
__drm_atomic_helper_plane_destroy_state(&state->base);
|
||||||
|
kfree(state);
|
||||||
|
plane->state = NULL;
|
||||||
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||||
|
if (state) {
|
||||||
|
state->base.plane = plane;
|
||||||
|
state->base.rotation = DRM_ROTATE_0;
|
||||||
|
plane->state = &state->base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct
|
static struct
|
||||||
drm_plane_state *malidp_duplicate_plane_state(struct drm_plane *plane)
|
drm_plane_state *malidp_duplicate_plane_state(struct drm_plane *plane)
|
||||||
{
|
{
|
||||||
|
@ -90,26 +115,71 @@ static void malidp_destroy_plane_state(struct drm_plane *plane,
|
||||||
kfree(m_state);
|
kfree(m_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void malidp_plane_atomic_print_state(struct drm_printer *p,
|
||||||
|
const struct drm_plane_state *state)
|
||||||
|
{
|
||||||
|
struct malidp_plane_state *ms = to_malidp_plane_state(state);
|
||||||
|
|
||||||
|
drm_printf(p, "\trotmem_size=%u\n", ms->rotmem_size);
|
||||||
|
drm_printf(p, "\tformat_id=%u\n", ms->format);
|
||||||
|
drm_printf(p, "\tn_planes=%u\n", ms->n_planes);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct drm_plane_funcs malidp_de_plane_funcs = {
|
static const struct drm_plane_funcs malidp_de_plane_funcs = {
|
||||||
.update_plane = drm_atomic_helper_update_plane,
|
.update_plane = drm_atomic_helper_update_plane,
|
||||||
.disable_plane = drm_atomic_helper_disable_plane,
|
.disable_plane = drm_atomic_helper_disable_plane,
|
||||||
.set_property = drm_atomic_helper_plane_set_property,
|
.set_property = drm_atomic_helper_plane_set_property,
|
||||||
.destroy = malidp_de_plane_destroy,
|
.destroy = malidp_de_plane_destroy,
|
||||||
.reset = drm_atomic_helper_plane_reset,
|
.reset = malidp_plane_reset,
|
||||||
.atomic_duplicate_state = malidp_duplicate_plane_state,
|
.atomic_duplicate_state = malidp_duplicate_plane_state,
|
||||||
.atomic_destroy_state = malidp_destroy_plane_state,
|
.atomic_destroy_state = malidp_destroy_plane_state,
|
||||||
|
.atomic_print_state = malidp_plane_atomic_print_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int malidp_se_check_scaling(struct malidp_plane *mp,
|
||||||
|
struct drm_plane_state *state)
|
||||||
|
{
|
||||||
|
struct drm_crtc_state *crtc_state =
|
||||||
|
drm_atomic_get_existing_crtc_state(state->state, state->crtc);
|
||||||
|
struct malidp_crtc_state *mc;
|
||||||
|
struct drm_rect clip = { 0 };
|
||||||
|
u32 src_w, src_h;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!crtc_state)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||||
|
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||||
|
ret = drm_plane_helper_check_state(state, &clip, 0, INT_MAX, true, true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
src_w = state->src_w >> 16;
|
||||||
|
src_h = state->src_h >> 16;
|
||||||
|
if ((state->crtc_w == src_w) && (state->crtc_h == src_h)) {
|
||||||
|
/* Scaling not necessary for this plane. */
|
||||||
|
mc->scaled_planes_mask &= ~(mp->layer->id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mp->layer->id & (DE_SMART | DE_GRAPHICS2))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mc = to_malidp_crtc_state(crtc_state);
|
||||||
|
|
||||||
|
mc->scaled_planes_mask |= mp->layer->id;
|
||||||
|
/* Defer scaling requirements calculation to the crtc check. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int malidp_de_plane_check(struct drm_plane *plane,
|
static int malidp_de_plane_check(struct drm_plane *plane,
|
||||||
struct drm_plane_state *state)
|
struct drm_plane_state *state)
|
||||||
{
|
{
|
||||||
struct malidp_plane *mp = to_malidp_plane(plane);
|
struct malidp_plane *mp = to_malidp_plane(plane);
|
||||||
struct malidp_plane_state *ms = to_malidp_plane_state(state);
|
struct malidp_plane_state *ms = to_malidp_plane_state(state);
|
||||||
struct drm_crtc_state *crtc_state;
|
|
||||||
struct drm_framebuffer *fb;
|
struct drm_framebuffer *fb;
|
||||||
struct drm_rect clip = { 0 };
|
|
||||||
int i, ret;
|
int i, ret;
|
||||||
u32 src_w, src_h;
|
|
||||||
|
|
||||||
if (!state->crtc || !state->fb)
|
if (!state->crtc || !state->fb)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -130,9 +200,6 @@ static int malidp_de_plane_check(struct drm_plane *plane,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
src_w = state->src_w >> 16;
|
|
||||||
src_h = state->src_h >> 16;
|
|
||||||
|
|
||||||
if ((state->crtc_w > mp->hwdev->max_line_size) ||
|
if ((state->crtc_w > mp->hwdev->max_line_size) ||
|
||||||
(state->crtc_h > mp->hwdev->max_line_size) ||
|
(state->crtc_h > mp->hwdev->max_line_size) ||
|
||||||
(state->crtc_w < mp->hwdev->min_line_size) ||
|
(state->crtc_w < mp->hwdev->min_line_size) ||
|
||||||
|
@ -149,22 +216,16 @@ static int malidp_de_plane_check(struct drm_plane *plane,
|
||||||
(state->fb->pitches[1] != state->fb->pitches[2]))
|
(state->fb->pitches[1] != state->fb->pitches[2]))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = malidp_se_check_scaling(mp, state);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* packed RGB888 / BGR888 can't be rotated or flipped */
|
/* packed RGB888 / BGR888 can't be rotated or flipped */
|
||||||
if (state->rotation != DRM_ROTATE_0 &&
|
if (state->rotation != DRM_ROTATE_0 &&
|
||||||
(fb->format->format == DRM_FORMAT_RGB888 ||
|
(fb->format->format == DRM_FORMAT_RGB888 ||
|
||||||
fb->format->format == DRM_FORMAT_BGR888))
|
fb->format->format == DRM_FORMAT_BGR888))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
|
|
||||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
|
||||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
|
||||||
ret = drm_plane_helper_check_state(state, &clip,
|
|
||||||
DRM_PLANE_HELPER_NO_SCALING,
|
|
||||||
DRM_PLANE_HELPER_NO_SCALING,
|
|
||||||
true, true);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ms->rotmem_size = 0;
|
ms->rotmem_size = 0;
|
||||||
if (state->rotation & MALIDP_ROTATED_MASK) {
|
if (state->rotation & MALIDP_ROTATED_MASK) {
|
||||||
int val;
|
int val;
|
||||||
|
@ -269,6 +330,16 @@ static void malidp_de_plane_update(struct drm_plane *plane,
|
||||||
val &= ~LAYER_COMP_MASK;
|
val &= ~LAYER_COMP_MASK;
|
||||||
val |= LAYER_COMP_PIXEL;
|
val |= LAYER_COMP_PIXEL;
|
||||||
|
|
||||||
|
val &= ~LAYER_FLOWCFG(LAYER_FLOWCFG_MASK);
|
||||||
|
if (plane->state->crtc) {
|
||||||
|
struct malidp_crtc_state *m =
|
||||||
|
to_malidp_crtc_state(plane->state->crtc->state);
|
||||||
|
|
||||||
|
if (m->scaler_config.scale_enable &&
|
||||||
|
m->scaler_config.plane_src_id == mp->layer->id)
|
||||||
|
val |= LAYER_FLOWCFG(LAYER_FLOWCFG_SCALE_SE);
|
||||||
|
}
|
||||||
|
|
||||||
/* set the 'enable layer' bit */
|
/* set the 'enable layer' bit */
|
||||||
val |= LAYER_ENABLE;
|
val |= LAYER_ENABLE;
|
||||||
|
|
||||||
|
@ -281,7 +352,8 @@ static void malidp_de_plane_disable(struct drm_plane *plane,
|
||||||
{
|
{
|
||||||
struct malidp_plane *mp = to_malidp_plane(plane);
|
struct malidp_plane *mp = to_malidp_plane(plane);
|
||||||
|
|
||||||
malidp_hw_clearbits(mp->hwdev, LAYER_ENABLE,
|
malidp_hw_clearbits(mp->hwdev,
|
||||||
|
LAYER_ENABLE | LAYER_FLOWCFG(LAYER_FLOWCFG_MASK),
|
||||||
mp->layer->base + MALIDP_LAYER_CONTROL);
|
mp->layer->base + MALIDP_LAYER_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@
|
||||||
|
|
||||||
/* bit masks that are common between products */
|
/* bit masks that are common between products */
|
||||||
#define MALIDP_CFG_VALID (1 << 0)
|
#define MALIDP_CFG_VALID (1 << 0)
|
||||||
|
#define MALIDP_DISP_FUNC_GAMMA (1 << 0)
|
||||||
|
#define MALIDP_DISP_FUNC_CADJ (1 << 4)
|
||||||
#define MALIDP_DISP_FUNC_ILACED (1 << 8)
|
#define MALIDP_DISP_FUNC_ILACED (1 << 8)
|
||||||
|
|
||||||
/* register offsets for IRQ management */
|
/* register offsets for IRQ management */
|
||||||
|
@ -99,6 +101,58 @@
|
||||||
|
|
||||||
#define MALIDP_PRODUCT_ID(__core_id) ((u32)(__core_id) >> 16)
|
#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
|
||||||
|
|
||||||
|
/* Scaling engine registers and masks. */
|
||||||
|
#define MALIDP_SE_SCALING_EN (1 << 0)
|
||||||
|
#define MALIDP_SE_ALPHA_EN (1 << 1)
|
||||||
|
#define MALIDP_SE_ENH_MASK 3
|
||||||
|
#define MALIDP_SE_ENH(x) (((x) & MALIDP_SE_ENH_MASK) << 2)
|
||||||
|
#define MALIDP_SE_RGBO_IF_EN (1 << 4)
|
||||||
|
#define MALIDP550_SE_CTL_SEL_MASK 7
|
||||||
|
#define MALIDP550_SE_CTL_VCSEL(x) \
|
||||||
|
(((x) & MALIDP550_SE_CTL_SEL_MASK) << 20)
|
||||||
|
#define MALIDP550_SE_CTL_HCSEL(x) \
|
||||||
|
(((x) & MALIDP550_SE_CTL_SEL_MASK) << 16)
|
||||||
|
|
||||||
|
/* Blocks with offsets from SE_CONTROL register. */
|
||||||
|
#define MALIDP_SE_LAYER_CONTROL 0x14
|
||||||
|
#define MALIDP_SE_L0_IN_SIZE 0x00
|
||||||
|
#define MALIDP_SE_L0_OUT_SIZE 0x04
|
||||||
|
#define MALIDP_SE_SET_V_SIZE(x) (((x) & 0x1fff) << 16)
|
||||||
|
#define MALIDP_SE_SET_H_SIZE(x) (((x) & 0x1fff) << 0)
|
||||||
|
#define MALIDP_SE_SCALING_CONTROL 0x24
|
||||||
|
#define MALIDP_SE_H_INIT_PH 0x00
|
||||||
|
#define MALIDP_SE_H_DELTA_PH 0x04
|
||||||
|
#define MALIDP_SE_V_INIT_PH 0x08
|
||||||
|
#define MALIDP_SE_V_DELTA_PH 0x0c
|
||||||
|
#define MALIDP_SE_COEFFTAB_ADDR 0x10
|
||||||
|
#define MALIDP_SE_COEFFTAB_ADDR_MASK 0x7f
|
||||||
|
#define MALIDP_SE_V_COEFFTAB (1 << 8)
|
||||||
|
#define MALIDP_SE_H_COEFFTAB (1 << 9)
|
||||||
|
#define MALIDP_SE_SET_V_COEFFTAB_ADDR(x) \
|
||||||
|
(MALIDP_SE_V_COEFFTAB | ((x) & MALIDP_SE_COEFFTAB_ADDR_MASK))
|
||||||
|
#define MALIDP_SE_SET_H_COEFFTAB_ADDR(x) \
|
||||||
|
(MALIDP_SE_H_COEFFTAB | ((x) & MALIDP_SE_COEFFTAB_ADDR_MASK))
|
||||||
|
#define MALIDP_SE_COEFFTAB_DATA 0x14
|
||||||
|
#define MALIDP_SE_COEFFTAB_DATA_MASK 0x3fff
|
||||||
|
#define MALIDP_SE_SET_COEFFTAB_DATA(x) \
|
||||||
|
((x) & MALIDP_SE_COEFFTAB_DATA_MASK)
|
||||||
|
/* Enhance coeffents reigster offset */
|
||||||
|
#define MALIDP_SE_IMAGE_ENH 0x3C
|
||||||
|
/* ENH_LIMITS offset 0x0 */
|
||||||
|
#define MALIDP_SE_ENH_LOW_LEVEL 24
|
||||||
|
#define MALIDP_SE_ENH_HIGH_LEVEL 63
|
||||||
|
#define MALIDP_SE_ENH_LIMIT_MASK 0xfff
|
||||||
|
#define MALIDP_SE_SET_ENH_LIMIT_LOW(x) \
|
||||||
|
((x) & MALIDP_SE_ENH_LIMIT_MASK)
|
||||||
|
#define MALIDP_SE_SET_ENH_LIMIT_HIGH(x) \
|
||||||
|
(((x) & MALIDP_SE_ENH_LIMIT_MASK) << 16)
|
||||||
|
#define MALIDP_SE_ENH_COEFF0 0x04
|
||||||
|
|
||||||
/* register offsets and bits specific to DP500 */
|
/* register offsets and bits specific to DP500 */
|
||||||
#define MALIDP500_ADDR_SPACE_SIZE 0x01000
|
#define MALIDP500_ADDR_SPACE_SIZE 0x01000
|
||||||
#define MALIDP500_DC_BASE 0x00000
|
#define MALIDP500_DC_BASE 0x00000
|
||||||
|
@ -120,6 +174,18 @@
|
||||||
#define MALIDP500_COLOR_ADJ_COEF 0x00078
|
#define MALIDP500_COLOR_ADJ_COEF 0x00078
|
||||||
#define MALIDP500_COEF_TABLE_ADDR 0x000a8
|
#define MALIDP500_COEF_TABLE_ADDR 0x000a8
|
||||||
#define MALIDP500_COEF_TABLE_DATA 0x000ac
|
#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_BASE 0x00100
|
||||||
#define MALIDP500_DE_LV_PTR_BASE 0x00124
|
#define MALIDP500_DE_LV_PTR_BASE 0x00124
|
||||||
#define MALIDP500_DE_LG1_BASE 0x00200
|
#define MALIDP500_DE_LG1_BASE 0x00200
|
||||||
|
@ -127,6 +193,7 @@
|
||||||
#define MALIDP500_DE_LG2_BASE 0x00300
|
#define MALIDP500_DE_LG2_BASE 0x00300
|
||||||
#define MALIDP500_DE_LG2_PTR_BASE 0x0031c
|
#define MALIDP500_DE_LG2_PTR_BASE 0x0031c
|
||||||
#define MALIDP500_SE_BASE 0x00c00
|
#define MALIDP500_SE_BASE 0x00c00
|
||||||
|
#define MALIDP500_SE_CONTROL 0x00c0c
|
||||||
#define MALIDP500_SE_PTR_BASE 0x00e0c
|
#define MALIDP500_SE_PTR_BASE 0x00e0c
|
||||||
#define MALIDP500_DC_IRQ_BASE 0x00f00
|
#define MALIDP500_DC_IRQ_BASE 0x00f00
|
||||||
#define MALIDP500_CONFIG_VALID 0x00f00
|
#define MALIDP500_CONFIG_VALID 0x00f00
|
||||||
|
@ -145,9 +212,7 @@
|
||||||
#define MALIDP550_DE_DISP_SIDEBAND 0x00040
|
#define MALIDP550_DE_DISP_SIDEBAND 0x00040
|
||||||
#define MALIDP550_DE_BGND_COLOR 0x00044
|
#define MALIDP550_DE_BGND_COLOR 0x00044
|
||||||
#define MALIDP550_DE_OUTPUT_DEPTH 0x0004c
|
#define MALIDP550_DE_OUTPUT_DEPTH 0x0004c
|
||||||
#define MALIDP550_DE_COLOR_COEF 0x00050
|
#define MALIDP550_COEFFS_BASE 0x00050
|
||||||
#define MALIDP550_DE_COEF_TABLE_ADDR 0x00080
|
|
||||||
#define MALIDP550_DE_COEF_TABLE_DATA 0x00084
|
|
||||||
#define MALIDP550_DE_LV1_BASE 0x00100
|
#define MALIDP550_DE_LV1_BASE 0x00100
|
||||||
#define MALIDP550_DE_LV1_PTR_BASE 0x00124
|
#define MALIDP550_DE_LV1_PTR_BASE 0x00124
|
||||||
#define MALIDP550_DE_LV2_BASE 0x00200
|
#define MALIDP550_DE_LV2_BASE 0x00200
|
||||||
|
@ -158,6 +223,7 @@
|
||||||
#define MALIDP550_DE_LS_PTR_BASE 0x0042c
|
#define MALIDP550_DE_LS_PTR_BASE 0x0042c
|
||||||
#define MALIDP550_DE_PERF_BASE 0x00500
|
#define MALIDP550_DE_PERF_BASE 0x00500
|
||||||
#define MALIDP550_SE_BASE 0x08000
|
#define MALIDP550_SE_BASE 0x08000
|
||||||
|
#define MALIDP550_SE_CONTROL 0x08010
|
||||||
#define MALIDP550_DC_BASE 0x0c000
|
#define MALIDP550_DC_BASE 0x0c000
|
||||||
#define MALIDP550_DC_CONTROL 0x0c010
|
#define MALIDP550_DC_CONTROL 0x0c010
|
||||||
#define MALIDP550_DC_CONFIG_REQ (1 << 16)
|
#define MALIDP550_DC_CONFIG_REQ (1 << 16)
|
||||||
|
|
Loading…
Reference in New Issue