summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/stm/ltdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/stm/ltdc.c')
-rw-r--r--drivers/gpu/drm/stm/ltdc.c118
1 files changed, 106 insertions, 12 deletions
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 6dc5d4ec4e17..1a3277e483d5 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -175,6 +175,8 @@
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
+#define CLUT_SIZE 256
+
#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */
#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */
#define BF1_CA 0x400 /* Constant Alpha */
@@ -326,6 +328,26 @@ static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
}
}
+static inline u32 get_pixelformat_without_alpha(u32 drm)
+{
+ switch (drm) {
+ case DRM_FORMAT_ARGB4444:
+ return DRM_FORMAT_XRGB4444;
+ case DRM_FORMAT_RGBA4444:
+ return DRM_FORMAT_RGBX4444;
+ case DRM_FORMAT_ARGB1555:
+ return DRM_FORMAT_XRGB1555;
+ case DRM_FORMAT_RGBA5551:
+ return DRM_FORMAT_RGBX5551;
+ case DRM_FORMAT_ARGB8888:
+ return DRM_FORMAT_XRGB8888;
+ case DRM_FORMAT_RGBA8888:
+ return DRM_FORMAT_RGBX8888;
+ default:
+ return 0;
+ }
+}
+
static irqreturn_t ltdc_irq_thread(int irq, void *arg)
{
struct drm_device *ddev = arg;
@@ -363,6 +385,28 @@ static irqreturn_t ltdc_irq(int irq, void *arg)
* DRM_CRTC
*/
+static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ struct drm_color_lut *lut;
+ u32 val;
+ int i;
+
+ if (!crtc || !crtc->state)
+ return;
+
+ if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
+ return;
+
+ lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
+
+ for (i = 0; i < CLUT_SIZE; i++, lut++) {
+ val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
+ (lut->blue >> 8) | (i << 24);
+ reg_write(ldev->regs, LTDC_L1CLUTWR, val);
+ }
+}
+
static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
@@ -404,12 +448,35 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
}
+static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ int rate = mode->clock * 1000;
+
+ /*
+ * TODO clk_round_rate() does not work yet. When ready, it can
+ * be used instead of clk_set_rate() then clk_get_rate().
+ */
+
+ clk_disable(ldev->pixel_clk);
+ if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
+ DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
+ return false;
+ }
+ clk_enable(ldev->pixel_clk);
+
+ adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
+
+ return true;
+}
+
static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct videomode vm;
- int rate = mode->clock * 1000;
u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
u32 total_width, total_height;
u32 val;
@@ -432,15 +499,6 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
total_width = accum_act_w + vm.hfront_porch;
total_height = accum_act_h + vm.vfront_porch;
- clk_disable(ldev->pixel_clk);
-
- if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
- DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
- return;
- }
-
- clk_enable(ldev->pixel_clk);
-
/* Configures the HS, VS, DE and PC polarities. Default Active Low */
val = 0;
@@ -486,6 +544,8 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
DRM_DEBUG_ATOMIC("\n");
+ ltdc_crtc_update_clut(crtc);
+
/* Commit shadow registers = update planes at next vblank */
reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
@@ -502,6 +562,7 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
}
static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
+ .mode_fixup = ltdc_crtc_mode_fixup,
.mode_set_nofb = ltdc_crtc_mode_set_nofb,
.atomic_flush = ltdc_crtc_atomic_flush,
.atomic_enable = ltdc_crtc_atomic_enable,
@@ -533,6 +594,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .gamma_set = drm_atomic_helper_legacy_gamma_set,
};
/*
@@ -638,6 +700,14 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
/* Specifies the blending factors */
val = BF1_PAXCA | BF2_1PAXCA;
+ if (!fb->format->has_alpha)
+ val = BF1_CA | BF2_1CA;
+
+ /* Manage hw-specific capabilities */
+ if (ldev->caps.non_alpha_only_l1 &&
+ plane->type != DRM_PLANE_TYPE_PRIMARY)
+ val = BF1_PAXCA | BF2_1PAXCA;
+
reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
LXBFCR_BF2 | LXBFCR_BF1, val);
@@ -705,8 +775,8 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
struct device *dev = ddev->dev;
struct drm_plane *plane;
unsigned int i, nb_fmt = 0;
- u32 formats[NB_PF];
- u32 drm_fmt;
+ u32 formats[NB_PF * 2];
+ u32 drm_fmt, drm_fmt_no_alpha;
int ret;
/* Get supported pixel formats */
@@ -715,6 +785,18 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
if (!drm_fmt)
continue;
formats[nb_fmt++] = drm_fmt;
+
+ /* Add the no-alpha related format if any & supported */
+ drm_fmt_no_alpha = get_pixelformat_without_alpha(drm_fmt);
+ if (!drm_fmt_no_alpha)
+ continue;
+
+ /* Manage hw-specific capabilities */
+ if (ldev->caps.non_alpha_only_l1 &&
+ type != DRM_PLANE_TYPE_PRIMARY)
+ continue;
+
+ formats[nb_fmt++] = drm_fmt_no_alpha;
}
plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
@@ -765,6 +847,9 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
+ drm_mode_crtc_set_gamma_size(crtc, CLUT_SIZE);
+ drm_crtc_enable_color_mgmt(crtc, 0, false, CLUT_SIZE);
+
DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
/* Add planes. Note : the first layer is used by primary plane */
@@ -839,10 +924,19 @@ static int ltdc_get_caps(struct drm_device *ddev)
case HWVER_10300:
ldev->caps.reg_ofs = REG_OFS_NONE;
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+ /*
+ * Hw older versions support non-alpha color formats derived
+ * from native alpha color formats only on the primary layer.
+ * For instance, RG16 native format without alpha works fine
+ * on 2nd layer but XR24 (derived color format from AR24)
+ * does not work on 2nd layer.
+ */
+ ldev->caps.non_alpha_only_l1 = true;
break;
case HWVER_20101:
ldev->caps.reg_ofs = REG_OFS_4;
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+ ldev->caps.non_alpha_only_l1 = false;
break;
default:
return -ENODEV;