0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/clk.h>
0010 #include <linux/pm_runtime.h>
0011
0012 #include <video/videomode.h>
0013
0014 #include <drm/drm_atomic.h>
0015 #include <drm/drm_atomic_helper.h>
0016 #include <drm/drm_crtc.h>
0017 #include <drm/drm_framebuffer.h>
0018 #include <drm/drm_print.h>
0019 #include <drm/drm_probe_helper.h>
0020 #include <drm/drm_vblank.h>
0021
0022 #include "malidp_drv.h"
0023 #include "malidp_hw.h"
0024
0025 static enum drm_mode_status malidp_crtc_mode_valid(struct drm_crtc *crtc,
0026 const struct drm_display_mode *mode)
0027 {
0028 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0029 struct malidp_hw_device *hwdev = malidp->dev;
0030
0031
0032
0033
0034
0035 long rate, req_rate = mode->crtc_clock * 1000;
0036
0037 if (req_rate) {
0038 rate = clk_round_rate(hwdev->pxlclk, req_rate);
0039 if (rate != req_rate) {
0040 DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
0041 req_rate);
0042 return MODE_NOCLOCK;
0043 }
0044 }
0045
0046 return MODE_OK;
0047 }
0048
0049 static void malidp_crtc_atomic_enable(struct drm_crtc *crtc,
0050 struct drm_atomic_state *state)
0051 {
0052 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0053 struct malidp_hw_device *hwdev = malidp->dev;
0054 struct videomode vm;
0055 int err = pm_runtime_get_sync(crtc->dev->dev);
0056
0057 if (err < 0) {
0058 DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err);
0059 return;
0060 }
0061
0062 drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
0063 clk_prepare_enable(hwdev->pxlclk);
0064
0065
0066 clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000);
0067
0068 hwdev->hw->modeset(hwdev, &vm);
0069 hwdev->hw->leave_config_mode(hwdev);
0070 drm_crtc_vblank_on(crtc);
0071 }
0072
0073 static void malidp_crtc_atomic_disable(struct drm_crtc *crtc,
0074 struct drm_atomic_state *state)
0075 {
0076 struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
0077 crtc);
0078 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0079 struct malidp_hw_device *hwdev = malidp->dev;
0080 int err;
0081
0082
0083 drm_atomic_helper_disable_planes_on_crtc(old_state, false);
0084
0085 drm_crtc_vblank_off(crtc);
0086 hwdev->hw->enter_config_mode(hwdev);
0087
0088 clk_disable_unprepare(hwdev->pxlclk);
0089
0090 err = pm_runtime_put(crtc->dev->dev);
0091 if (err < 0) {
0092 DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err);
0093 }
0094 }
0095
0096 static const struct gamma_curve_segment {
0097 u16 start;
0098 u16 end;
0099 } segments[MALIDP_COEFFTAB_NUM_COEFFS] = {
0100
0101 { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 },
0102 { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 },
0103 { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 },
0104 { 12, 12 }, { 13, 13 }, { 14, 14 }, { 15, 15 },
0105
0106 { 16, 19 }, { 20, 23 }, { 24, 27 }, { 28, 31 },
0107
0108 { 32, 39 }, { 40, 47 }, { 48, 55 }, { 56, 63 },
0109
0110 { 64, 79 }, { 80, 95 }, { 96, 111 }, { 112, 127 },
0111
0112 { 128, 159 }, { 160, 191 }, { 192, 223 }, { 224, 255 },
0113
0114 { 256, 319 }, { 320, 383 }, { 384, 447 }, { 448, 511 },
0115
0116 { 512, 639 }, { 640, 767 }, { 768, 895 }, { 896, 1023 },
0117 { 1024, 1151 }, { 1152, 1279 }, { 1280, 1407 }, { 1408, 1535 },
0118 { 1536, 1663 }, { 1664, 1791 }, { 1792, 1919 }, { 1920, 2047 },
0119 { 2048, 2175 }, { 2176, 2303 }, { 2304, 2431 }, { 2432, 2559 },
0120 { 2560, 2687 }, { 2688, 2815 }, { 2816, 2943 }, { 2944, 3071 },
0121 { 3072, 3199 }, { 3200, 3327 }, { 3328, 3455 }, { 3456, 3583 },
0122 { 3584, 3711 }, { 3712, 3839 }, { 3840, 3967 }, { 3968, 4095 },
0123 };
0124
0125 #define DE_COEFTAB_DATA(a, b) ((((a) & 0xfff) << 16) | (((b) & 0xfff)))
0126
0127 static void malidp_generate_gamma_table(struct drm_property_blob *lut_blob,
0128 u32 coeffs[MALIDP_COEFFTAB_NUM_COEFFS])
0129 {
0130 struct drm_color_lut *lut = (struct drm_color_lut *)lut_blob->data;
0131 int i;
0132
0133 for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) {
0134 u32 a, b, delta_in, out_start, out_end;
0135
0136 delta_in = segments[i].end - segments[i].start;
0137
0138 out_start = drm_color_lut_extract(lut[segments[i].start].green,
0139 12);
0140 out_end = drm_color_lut_extract(lut[segments[i].end].green, 12);
0141 a = (delta_in == 0) ? 0 : ((out_end - out_start) * 256) / delta_in;
0142 b = out_start;
0143 coeffs[i] = DE_COEFTAB_DATA(a, b);
0144 }
0145 }
0146
0147
0148
0149
0150
0151 static int malidp_crtc_atomic_check_gamma(struct drm_crtc *crtc,
0152 struct drm_crtc_state *state)
0153 {
0154 struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
0155 struct drm_color_lut *lut;
0156 size_t lut_size;
0157 int i;
0158
0159 if (!state->color_mgmt_changed || !state->gamma_lut)
0160 return 0;
0161
0162 if (crtc->state->gamma_lut &&
0163 (crtc->state->gamma_lut->base.id == state->gamma_lut->base.id))
0164 return 0;
0165
0166 if (state->gamma_lut->length % sizeof(struct drm_color_lut))
0167 return -EINVAL;
0168
0169 lut_size = state->gamma_lut->length / sizeof(struct drm_color_lut);
0170 if (lut_size != MALIDP_GAMMA_LUT_SIZE)
0171 return -EINVAL;
0172
0173 lut = (struct drm_color_lut *)state->gamma_lut->data;
0174 for (i = 0; i < lut_size; ++i)
0175 if (!((lut[i].red == lut[i].green) &&
0176 (lut[i].red == lut[i].blue)))
0177 return -EINVAL;
0178
0179 if (!state->mode_changed) {
0180 int ret;
0181
0182 state->mode_changed = true;
0183
0184
0185
0186
0187
0188
0189 ret = drm_atomic_helper_check_modeset(crtc->dev, state->state);
0190 if (ret)
0191 return ret;
0192 }
0193
0194 malidp_generate_gamma_table(state->gamma_lut, mc->gamma_coeffs);
0195 return 0;
0196 }
0197
0198
0199
0200
0201
0202
0203
0204
0205 static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
0206 struct drm_crtc_state *state)
0207 {
0208 struct malidp_crtc_state *mc = to_malidp_crtc_state(state);
0209 struct drm_color_ctm *ctm;
0210 int i;
0211
0212 if (!state->color_mgmt_changed)
0213 return 0;
0214
0215 if (!state->ctm)
0216 return 0;
0217
0218 if (crtc->state->ctm && (crtc->state->ctm->base.id ==
0219 state->ctm->base.id))
0220 return 0;
0221
0222
0223
0224
0225
0226 ctm = (struct drm_color_ctm *)state->ctm->data;
0227 for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) {
0228
0229 s64 val = ctm->matrix[i];
0230 u32 mag = ((((u64)val) & ~BIT_ULL(63)) >> 20) &
0231 GENMASK_ULL(14, 0);
0232
0233
0234
0235
0236
0237
0238
0239
0240 if (val & BIT_ULL(63))
0241 mag = ~mag + 1;
0242 if (!!(val & BIT_ULL(63)) != !!(mag & BIT(14)))
0243 return -EINVAL;
0244 mc->coloradj_coeffs[i] = mag;
0245 }
0246
0247 return 0;
0248 }
0249
0250 static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
0251 struct drm_crtc_state *state)
0252 {
0253 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0254 struct malidp_hw_device *hwdev = malidp->dev;
0255 struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
0256 struct malidp_se_config *s = &cs->scaler_config;
0257 struct drm_plane *plane;
0258 struct videomode vm;
0259 const struct drm_plane_state *pstate;
0260 u32 h_upscale_factor = 0;
0261 u32 v_upscale_factor = 0;
0262 u8 scaling = cs->scaled_planes_mask;
0263 int ret;
0264
0265 if (!scaling) {
0266 s->scale_enable = false;
0267 goto mclk_calc;
0268 }
0269
0270
0271 if (scaling & (scaling - 1))
0272 return -EINVAL;
0273
0274 drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
0275 struct malidp_plane *mp = to_malidp_plane(plane);
0276 u32 phase;
0277
0278 if (!(mp->layer->id & scaling))
0279 continue;
0280
0281
0282
0283
0284
0285 h_upscale_factor = div_u64((u64)pstate->crtc_w << 32,
0286 pstate->src_w);
0287 v_upscale_factor = div_u64((u64)pstate->crtc_h << 32,
0288 pstate->src_h);
0289
0290 s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 ||
0291 (v_upscale_factor >> 16) >= 2);
0292
0293 if (pstate->rotation & MALIDP_ROTATED_MASK) {
0294 s->input_w = pstate->src_h >> 16;
0295 s->input_h = pstate->src_w >> 16;
0296 } else {
0297 s->input_w = pstate->src_w >> 16;
0298 s->input_h = pstate->src_h >> 16;
0299 }
0300
0301 s->output_w = pstate->crtc_w;
0302 s->output_h = pstate->crtc_h;
0303
0304 #define SE_N_PHASE 4
0305 #define SE_SHIFT_N_PHASE 12
0306
0307 phase = s->input_w;
0308 s->h_init_phase =
0309 ((phase << SE_N_PHASE) / s->output_w + 1) / 2;
0310
0311 phase = s->input_w;
0312 phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
0313 s->h_delta_phase = phase / s->output_w;
0314
0315
0316 phase = s->input_h;
0317 s->v_init_phase =
0318 ((phase << SE_N_PHASE) / s->output_h + 1) / 2;
0319
0320 phase = s->input_h;
0321 phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
0322 s->v_delta_phase = phase / s->output_h;
0323 #undef SE_N_PHASE
0324 #undef SE_SHIFT_N_PHASE
0325 s->plane_src_id = mp->layer->id;
0326 }
0327
0328 s->scale_enable = true;
0329 s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
0330 s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);
0331
0332 mclk_calc:
0333 drm_display_mode_to_videomode(&state->adjusted_mode, &vm);
0334 ret = hwdev->hw->se_calc_mclk(hwdev, s, &vm);
0335 if (ret < 0)
0336 return -EINVAL;
0337 return 0;
0338 }
0339
0340 static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
0341 struct drm_atomic_state *state)
0342 {
0343 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
0344 crtc);
0345 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0346 struct malidp_hw_device *hwdev = malidp->dev;
0347 struct drm_plane *plane;
0348 const struct drm_plane_state *pstate;
0349 u32 rot_mem_free, rot_mem_usable;
0350 int rotated_planes = 0;
0351 int ret;
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379 drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
0380 struct drm_framebuffer *fb = pstate->fb;
0381
0382 if ((pstate->rotation & MALIDP_ROTATED_MASK) || fb->modifier)
0383 rotated_planes++;
0384 }
0385
0386 rot_mem_free = hwdev->rotation_memory[0];
0387
0388
0389
0390
0391 if (rotated_planes > 1)
0392 rot_mem_free += hwdev->rotation_memory[1];
0393
0394
0395 drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
0396 struct malidp_plane *mp = to_malidp_plane(plane);
0397 struct malidp_plane_state *ms = to_malidp_plane_state(pstate);
0398 struct drm_framebuffer *fb = pstate->fb;
0399
0400 if ((pstate->rotation & MALIDP_ROTATED_MASK) || fb->modifier) {
0401
0402 rotated_planes--;
0403
0404 if (!rotated_planes) {
0405
0406 rot_mem_usable = rot_mem_free;
0407 } else {
0408 if ((mp->layer->id != DE_VIDEO1) ||
0409 (hwdev->rotation_memory[1] == 0))
0410 rot_mem_usable = rot_mem_free / 2;
0411 else
0412 rot_mem_usable = hwdev->rotation_memory[0];
0413 }
0414
0415 rot_mem_free -= rot_mem_usable;
0416
0417 if (ms->rotmem_size > rot_mem_usable)
0418 return -EINVAL;
0419 }
0420 }
0421
0422
0423 if (crtc_state->connectors_changed) {
0424 u32 old_mask = crtc->state->connector_mask;
0425 u32 new_mask = crtc_state->connector_mask;
0426
0427 if ((old_mask ^ new_mask) ==
0428 (1 << drm_connector_index(&malidp->mw_connector.base)))
0429 crtc_state->connectors_changed = false;
0430 }
0431
0432 ret = malidp_crtc_atomic_check_gamma(crtc, crtc_state);
0433 ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, crtc_state);
0434 ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, crtc_state);
0435
0436 return ret;
0437 }
0438
0439 static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
0440 .mode_valid = malidp_crtc_mode_valid,
0441 .atomic_check = malidp_crtc_atomic_check,
0442 .atomic_enable = malidp_crtc_atomic_enable,
0443 .atomic_disable = malidp_crtc_atomic_disable,
0444 };
0445
0446 static struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc)
0447 {
0448 struct malidp_crtc_state *state, *old_state;
0449
0450 if (WARN_ON(!crtc->state))
0451 return NULL;
0452
0453 old_state = to_malidp_crtc_state(crtc->state);
0454 state = kmalloc(sizeof(*state), GFP_KERNEL);
0455 if (!state)
0456 return NULL;
0457
0458 __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
0459 memcpy(state->gamma_coeffs, old_state->gamma_coeffs,
0460 sizeof(state->gamma_coeffs));
0461 memcpy(state->coloradj_coeffs, old_state->coloradj_coeffs,
0462 sizeof(state->coloradj_coeffs));
0463 memcpy(&state->scaler_config, &old_state->scaler_config,
0464 sizeof(state->scaler_config));
0465 state->scaled_planes_mask = 0;
0466
0467 return &state->base;
0468 }
0469
0470 static void malidp_crtc_destroy_state(struct drm_crtc *crtc,
0471 struct drm_crtc_state *state)
0472 {
0473 struct malidp_crtc_state *mali_state = NULL;
0474
0475 if (state) {
0476 mali_state = to_malidp_crtc_state(state);
0477 __drm_atomic_helper_crtc_destroy_state(state);
0478 }
0479
0480 kfree(mali_state);
0481 }
0482
0483 static void malidp_crtc_reset(struct drm_crtc *crtc)
0484 {
0485 struct malidp_crtc_state *state =
0486 kzalloc(sizeof(*state), GFP_KERNEL);
0487
0488 if (crtc->state)
0489 malidp_crtc_destroy_state(crtc, crtc->state);
0490
0491 if (state)
0492 __drm_atomic_helper_crtc_reset(crtc, &state->base);
0493 else
0494 __drm_atomic_helper_crtc_reset(crtc, NULL);
0495 }
0496
0497 static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
0498 {
0499 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0500 struct malidp_hw_device *hwdev = malidp->dev;
0501
0502 malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK,
0503 hwdev->hw->map.de_irq_map.vsync_irq);
0504 return 0;
0505 }
0506
0507 static void malidp_crtc_disable_vblank(struct drm_crtc *crtc)
0508 {
0509 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
0510 struct malidp_hw_device *hwdev = malidp->dev;
0511
0512 malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK,
0513 hwdev->hw->map.de_irq_map.vsync_irq);
0514 }
0515
0516 static const struct drm_crtc_funcs malidp_crtc_funcs = {
0517 .destroy = drm_crtc_cleanup,
0518 .set_config = drm_atomic_helper_set_config,
0519 .page_flip = drm_atomic_helper_page_flip,
0520 .reset = malidp_crtc_reset,
0521 .atomic_duplicate_state = malidp_crtc_duplicate_state,
0522 .atomic_destroy_state = malidp_crtc_destroy_state,
0523 .enable_vblank = malidp_crtc_enable_vblank,
0524 .disable_vblank = malidp_crtc_disable_vblank,
0525 };
0526
0527 int malidp_crtc_init(struct drm_device *drm)
0528 {
0529 struct malidp_drm *malidp = drm->dev_private;
0530 struct drm_plane *primary = NULL, *plane;
0531 int ret;
0532
0533 ret = malidp_de_planes_init(drm);
0534 if (ret < 0) {
0535 DRM_ERROR("Failed to initialise planes\n");
0536 return ret;
0537 }
0538
0539 drm_for_each_plane(plane, drm) {
0540 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
0541 primary = plane;
0542 break;
0543 }
0544 }
0545
0546 if (!primary) {
0547 DRM_ERROR("no primary plane found\n");
0548 return -EINVAL;
0549 }
0550
0551 ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
0552 &malidp_crtc_funcs, NULL);
0553 if (ret)
0554 return ret;
0555
0556 drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
0557 drm_mode_crtc_set_gamma_size(&malidp->crtc, MALIDP_GAMMA_LUT_SIZE);
0558
0559 drm_crtc_enable_color_mgmt(&malidp->crtc, 0, true, MALIDP_GAMMA_LUT_SIZE);
0560
0561 malidp_se_set_enh_coeffs(malidp->dev);
0562
0563 return 0;
0564 }