summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/meson
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/meson')
-rw-r--r--drivers/gpu/drm/meson/meson_crtc.c118
-rw-r--r--drivers/gpu/drm/meson/meson_drv.h16
-rw-r--r--drivers/gpu/drm/meson/meson_dw_hdmi.c34
-rw-r--r--drivers/gpu/drm/meson/meson_overlay.c293
-rw-r--r--drivers/gpu/drm/meson/meson_registers.h28
-rw-r--r--drivers/gpu/drm/meson/meson_viu.c11
6 files changed, 437 insertions, 63 deletions
diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c
index e66b6271ff58..2854272dc2d9 100644
--- a/drivers/gpu/drm/meson/meson_crtc.c
+++ b/drivers/gpu/drm/meson/meson_crtc.c
@@ -291,6 +291,10 @@ static void meson_crtc_enable_vd1(struct meson_drm *priv)
VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND |
VPP_COLOR_MNG_ENABLE,
priv->io_base + _REG(VPP_MISC));
+
+ writel_bits_relaxed(VIU_CTRL0_AFBC_TO_VD1,
+ priv->viu.vd1_afbc ? VIU_CTRL0_AFBC_TO_VD1 : 0,
+ priv->io_base + _REG(VIU_MISC_CTRL0));
}
static void meson_g12a_crtc_enable_vd1(struct meson_drm *priv)
@@ -300,6 +304,10 @@ static void meson_g12a_crtc_enable_vd1(struct meson_drm *priv)
VD_BLEND_POSTBLD_SRC_VD1 |
VD_BLEND_POSTBLD_PREMULT_EN,
priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
+
+ writel_relaxed(priv->viu.vd1_afbc ?
+ (VD1_AXI_SEL_AFBC | AFBC_VD1_SEL) : 0,
+ priv->io_base + _REG(VD1_AFBCD0_MISC_CTRL));
}
void meson_crtc_irq(struct meson_drm *priv)
@@ -383,36 +391,86 @@ void meson_crtc_irq(struct meson_drm *priv)
/* Update the VD1 registers */
if (priv->viu.vd1_enabled && priv->viu.vd1_commit) {
- switch (priv->viu.vd1_planes) {
- case 3:
- meson_canvas_config(priv->canvas,
- priv->canvas_id_vd1_2,
- priv->viu.vd1_addr2,
- priv->viu.vd1_stride2,
- priv->viu.vd1_height2,
- MESON_CANVAS_WRAP_NONE,
- MESON_CANVAS_BLKMODE_LINEAR,
- MESON_CANVAS_ENDIAN_SWAP64);
- /* fallthrough */
- case 2:
- meson_canvas_config(priv->canvas,
- priv->canvas_id_vd1_1,
- priv->viu.vd1_addr1,
- priv->viu.vd1_stride1,
- priv->viu.vd1_height1,
- MESON_CANVAS_WRAP_NONE,
- MESON_CANVAS_BLKMODE_LINEAR,
- MESON_CANVAS_ENDIAN_SWAP64);
- /* fallthrough */
- case 1:
- meson_canvas_config(priv->canvas,
- priv->canvas_id_vd1_0,
- priv->viu.vd1_addr0,
- priv->viu.vd1_stride0,
- priv->viu.vd1_height0,
- MESON_CANVAS_WRAP_NONE,
- MESON_CANVAS_BLKMODE_LINEAR,
- MESON_CANVAS_ENDIAN_SWAP64);
+ if (priv->viu.vd1_afbc) {
+ writel_relaxed(priv->viu.vd1_afbc_head_addr,
+ priv->io_base +
+ _REG(AFBC_HEAD_BADDR));
+ writel_relaxed(priv->viu.vd1_afbc_body_addr,
+ priv->io_base +
+ _REG(AFBC_BODY_BADDR));
+ writel_relaxed(priv->viu.vd1_afbc_en,
+ priv->io_base +
+ _REG(AFBC_ENABLE));
+ writel_relaxed(priv->viu.vd1_afbc_mode,
+ priv->io_base +
+ _REG(AFBC_MODE));
+ writel_relaxed(priv->viu.vd1_afbc_size_in,
+ priv->io_base +
+ _REG(AFBC_SIZE_IN));
+ writel_relaxed(priv->viu.vd1_afbc_dec_def_color,
+ priv->io_base +
+ _REG(AFBC_DEC_DEF_COLOR));
+ writel_relaxed(priv->viu.vd1_afbc_conv_ctrl,
+ priv->io_base +
+ _REG(AFBC_CONV_CTRL));
+ writel_relaxed(priv->viu.vd1_afbc_size_out,
+ priv->io_base +
+ _REG(AFBC_SIZE_OUT));
+ writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_ctrl,
+ priv->io_base +
+ _REG(AFBC_VD_CFMT_CTRL));
+ writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_w,
+ priv->io_base +
+ _REG(AFBC_VD_CFMT_W));
+ writel_relaxed(priv->viu.vd1_afbc_mif_hor_scope,
+ priv->io_base +
+ _REG(AFBC_MIF_HOR_SCOPE));
+ writel_relaxed(priv->viu.vd1_afbc_mif_ver_scope,
+ priv->io_base +
+ _REG(AFBC_MIF_VER_SCOPE));
+ writel_relaxed(priv->viu.vd1_afbc_pixel_hor_scope,
+ priv->io_base+
+ _REG(AFBC_PIXEL_HOR_SCOPE));
+ writel_relaxed(priv->viu.vd1_afbc_pixel_ver_scope,
+ priv->io_base +
+ _REG(AFBC_PIXEL_VER_SCOPE));
+ writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_h,
+ priv->io_base +
+ _REG(AFBC_VD_CFMT_H));
+ } else {
+ switch (priv->viu.vd1_planes) {
+ case 3:
+ meson_canvas_config(priv->canvas,
+ priv->canvas_id_vd1_2,
+ priv->viu.vd1_addr2,
+ priv->viu.vd1_stride2,
+ priv->viu.vd1_height2,
+ MESON_CANVAS_WRAP_NONE,
+ MESON_CANVAS_BLKMODE_LINEAR,
+ MESON_CANVAS_ENDIAN_SWAP64);
+ fallthrough;
+ case 2:
+ meson_canvas_config(priv->canvas,
+ priv->canvas_id_vd1_1,
+ priv->viu.vd1_addr1,
+ priv->viu.vd1_stride1,
+ priv->viu.vd1_height1,
+ MESON_CANVAS_WRAP_NONE,
+ MESON_CANVAS_BLKMODE_LINEAR,
+ MESON_CANVAS_ENDIAN_SWAP64);
+ fallthrough;
+ case 1:
+ meson_canvas_config(priv->canvas,
+ priv->canvas_id_vd1_0,
+ priv->viu.vd1_addr0,
+ priv->viu.vd1_stride0,
+ priv->viu.vd1_height0,
+ MESON_CANVAS_WRAP_NONE,
+ MESON_CANVAS_BLKMODE_LINEAR,
+ MESON_CANVAS_ENDIAN_SWAP64);
+ }
+
+ writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE));
}
writel_relaxed(priv->viu.vd1_if0_gen_reg,
diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h
index 5b23704a80d6..177dac3ca3be 100644
--- a/drivers/gpu/drm/meson/meson_drv.h
+++ b/drivers/gpu/drm/meson/meson_drv.h
@@ -86,6 +86,7 @@ struct meson_drm {
bool vd1_enabled;
bool vd1_commit;
+ bool vd1_afbc;
unsigned int vd1_planes;
uint32_t vd1_if0_gen_reg;
uint32_t vd1_if0_luma_x0;
@@ -111,6 +112,21 @@ struct meson_drm {
uint32_t vd1_height0;
uint32_t vd1_height1;
uint32_t vd1_height2;
+ uint32_t vd1_afbc_mode;
+ uint32_t vd1_afbc_en;
+ uint32_t vd1_afbc_head_addr;
+ uint32_t vd1_afbc_body_addr;
+ uint32_t vd1_afbc_conv_ctrl;
+ uint32_t vd1_afbc_dec_def_color;
+ uint32_t vd1_afbc_vd_cfmt_ctrl;
+ uint32_t vd1_afbc_vd_cfmt_w;
+ uint32_t vd1_afbc_vd_cfmt_h;
+ uint32_t vd1_afbc_mif_hor_scope;
+ uint32_t vd1_afbc_mif_ver_scope;
+ uint32_t vd1_afbc_size_out;
+ uint32_t vd1_afbc_pixel_hor_scope;
+ uint32_t vd1_afbc_pixel_ver_scope;
+ uint32_t vd1_afbc_size_in;
uint32_t vpp_pic_in_height;
uint32_t vpp_postblend_vd1_h_start_end;
uint32_t vpp_postblend_vd1_v_start_end;
diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 24a12c453095..29a8ff41595d 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -297,7 +297,7 @@ static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi,
/* Setup PHY bandwidth modes */
static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct meson_drm *priv = dw_hdmi->priv;
unsigned int pixel_clock = mode->clock;
@@ -427,7 +427,8 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
}
static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
- struct drm_display_mode *mode)
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
{
struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
struct meson_drm *priv = dw_hdmi->priv;
@@ -496,7 +497,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
/* Disable clock, fifo, fifo_wr */
regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0);
- dw_hdmi_set_high_tmds_clock_ratio(hdmi);
+ dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
msleep(100);
@@ -630,11 +631,13 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
}
static enum drm_mode_status
-dw_hdmi_mode_valid(struct drm_connector *connector,
+dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *display_info,
const struct drm_display_mode *mode)
{
- struct meson_drm *priv = connector->dev->dev_private;
- bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported;
+ struct meson_dw_hdmi *dw_hdmi = data;
+ struct meson_drm *priv = dw_hdmi->priv;
+ bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
unsigned int phy_freq;
unsigned int vclk_freq;
unsigned int venc_freq;
@@ -645,10 +648,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
DRM_DEBUG_DRIVER("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
/* If sink does not support 540MHz, reject the non-420 HDMI2 modes */
- if (connector->display_info.max_tmds_clock &&
- mode->clock > connector->display_info.max_tmds_clock &&
- !drm_mode_is_420_only(&connector->display_info, mode) &&
- !drm_mode_is_420_also(&connector->display_info, mode))
+ if (display_info->max_tmds_clock &&
+ mode->clock > display_info->max_tmds_clock &&
+ !drm_mode_is_420_only(display_info, mode) &&
+ !drm_mode_is_420_also(display_info, mode))
return MODE_BAD;
/* Check against non-VIC supported modes */
@@ -665,9 +668,9 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
vclk_freq = mode->clock;
/* For 420, pixel clock is half unlike venc clock */
- if (drm_mode_is_420_only(&connector->display_info, mode) ||
+ if (drm_mode_is_420_only(display_info, mode) ||
(!is_hdmi2_sink &&
- drm_mode_is_420_also(&connector->display_info, mode)))
+ drm_mode_is_420_also(display_info, mode)))
vclk_freq /= 2;
/* TMDS clock is pixel_clock * 10 */
@@ -682,9 +685,9 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
/* VENC double pixels for 1080i, 720p and YUV420 modes */
if (meson_venc_hdmi_venc_repeat(vic) ||
- drm_mode_is_420_only(&connector->display_info, mode) ||
+ drm_mode_is_420_only(display_info, mode) ||
(!is_hdmi2_sink &&
- drm_mode_is_420_also(&connector->display_info, mode)))
+ drm_mode_is_420_also(display_info, mode)))
venc_freq *= 2;
vclk_freq = max(venc_freq, hdmi_freq);
@@ -692,7 +695,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
venc_freq /= 2;
- dev_dbg(connector->dev->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
+ dev_dbg(dw_hdmi->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
__func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq);
@@ -1065,6 +1068,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
/* Bridge / Connector */
+ dw_plat_data->priv_data = meson_dw_hdmi;
dw_plat_data->mode_valid = dw_hdmi_mode_valid;
dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops;
dw_plat_data->phy_name = "meson_dw_hdmi_phy";
diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c
index 2468b0212d52..a8bcc70644df 100644
--- a/drivers/gpu/drm/meson/meson_overlay.c
+++ b/drivers/gpu/drm/meson/meson_overlay.c
@@ -58,7 +58,8 @@
/* VPP_POSTBLEND_VD1_H_START_END */
#define VD_H_END(value) FIELD_PREP(GENMASK(11, 0), value)
-#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), value)
+#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), \
+ ((value) & GENMASK(13, 0)))
/* VPP_POSTBLEND_VD1_V_START_END */
#define VD_V_END(value) FIELD_PREP(GENMASK(11, 0), value)
@@ -76,6 +77,85 @@
#define VD_REGION24_START(value) FIELD_PREP(GENMASK(11, 0), value)
#define VD_REGION13_END(value) FIELD_PREP(GENMASK(27, 16), value)
+/* AFBC_ENABLE */
+#define AFBC_DEC_ENABLE BIT(8)
+#define AFBC_FRM_START BIT(0)
+
+/* AFBC_MODE */
+#define AFBC_HORZ_SKIP_UV(value) FIELD_PREP(GENMASK(1, 0), value)
+#define AFBC_VERT_SKIP_UV(value) FIELD_PREP(GENMASK(3, 2), value)
+#define AFBC_HORZ_SKIP_Y(value) FIELD_PREP(GENMASK(5, 4), value)
+#define AFBC_VERT_SKIP_Y(value) FIELD_PREP(GENMASK(7, 6), value)
+#define AFBC_COMPBITS_YUV(value) FIELD_PREP(GENMASK(13, 8), value)
+#define AFBC_COMPBITS_8BIT 0
+#define AFBC_COMPBITS_10BIT (2 | (2 << 2) | (2 << 4))
+#define AFBC_BURST_LEN(value) FIELD_PREP(GENMASK(15, 14), value)
+#define AFBC_HOLD_LINE_NUM(value) FIELD_PREP(GENMASK(22, 16), value)
+#define AFBC_MIF_URGENT(value) FIELD_PREP(GENMASK(25, 24), value)
+#define AFBC_REV_MODE(value) FIELD_PREP(GENMASK(27, 26), value)
+#define AFBC_BLK_MEM_MODE BIT(28)
+#define AFBC_SCATTER_MODE BIT(29)
+#define AFBC_SOFT_RESET BIT(31)
+
+/* AFBC_SIZE_IN */
+#define AFBC_HSIZE_IN(value) FIELD_PREP(GENMASK(28, 16), value)
+#define AFBC_VSIZE_IN(value) FIELD_PREP(GENMASK(12, 0), value)
+
+/* AFBC_DEC_DEF_COLOR */
+#define AFBC_DEF_COLOR_Y(value) FIELD_PREP(GENMASK(29, 20), value)
+#define AFBC_DEF_COLOR_U(value) FIELD_PREP(GENMASK(19, 10), value)
+#define AFBC_DEF_COLOR_V(value) FIELD_PREP(GENMASK(9, 0), value)
+
+/* AFBC_CONV_CTRL */
+#define AFBC_CONV_LBUF_LEN(value) FIELD_PREP(GENMASK(11, 0), value)
+
+/* AFBC_LBUF_DEPTH */
+#define AFBC_DEC_LBUF_DEPTH(value) FIELD_PREP(GENMASK(27, 16), value)
+#define AFBC_MIF_LBUF_DEPTH(value) FIELD_PREP(GENMASK(11, 0), value)
+
+/* AFBC_OUT_XSCOPE/AFBC_SIZE_OUT */
+#define AFBC_HSIZE_OUT(value) FIELD_PREP(GENMASK(28, 16), value)
+#define AFBC_VSIZE_OUT(value) FIELD_PREP(GENMASK(12, 0), value)
+#define AFBC_OUT_HORZ_BGN(value) FIELD_PREP(GENMASK(28, 16), value)
+#define AFBC_OUT_HORZ_END(value) FIELD_PREP(GENMASK(12, 0), value)
+
+/* AFBC_OUT_YSCOPE */
+#define AFBC_OUT_VERT_BGN(value) FIELD_PREP(GENMASK(28, 16), value)
+#define AFBC_OUT_VERT_END(value) FIELD_PREP(GENMASK(12, 0), value)
+
+/* AFBC_VD_CFMT_CTRL */
+#define AFBC_HORZ_RPT_PIXEL0 BIT(23)
+#define AFBC_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value)
+#define AFBC_HORZ_FMT_EN BIT(20)
+#define AFBC_VERT_RPT_LINE0 BIT(16)
+#define AFBC_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value)
+#define AFBC_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value)
+#define AFBC_VERT_FMT_EN BIT(0)
+
+/* AFBC_VD_CFMT_W */
+#define AFBC_VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value)
+#define AFBC_VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value)
+
+/* AFBC_MIF_HOR_SCOPE */
+#define AFBC_MIF_BLK_BGN_H(value) FIELD_PREP(GENMASK(25, 16), value)
+#define AFBC_MIF_BLK_END_H(value) FIELD_PREP(GENMASK(9, 0), value)
+
+/* AFBC_MIF_VER_SCOPE */
+#define AFBC_MIF_BLK_BGN_V(value) FIELD_PREP(GENMASK(27, 16), value)
+#define AFBC_MIF_BLK_END_V(value) FIELD_PREP(GENMASK(11, 0), value)
+
+/* AFBC_PIXEL_HOR_SCOPE */
+#define AFBC_DEC_PIXEL_BGN_H(value) FIELD_PREP(GENMASK(28, 16), \
+ ((value) & GENMASK(12, 0)))
+#define AFBC_DEC_PIXEL_END_H(value) FIELD_PREP(GENMASK(12, 0), value)
+
+/* AFBC_PIXEL_VER_SCOPE */
+#define AFBC_DEC_PIXEL_BGN_V(value) FIELD_PREP(GENMASK(28, 16), value)
+#define AFBC_DEC_PIXEL_END_V(value) FIELD_PREP(GENMASK(12, 0), value)
+
+/* AFBC_VD_CFMT_H */
+#define AFBC_VD_HEIGHT(value) FIELD_PREP(GENMASK(12, 0), value)
+
struct meson_overlay {
struct drm_plane base;
struct meson_drm *priv;
@@ -157,6 +237,9 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
unsigned int ratio_x, ratio_y;
int temp_height, temp_width;
unsigned int w_in, h_in;
+ int afbc_left, afbc_right;
+ int afbc_top_src, afbc_bottom_src;
+ int afbc_top, afbc_bottom;
int temp, start, end;
if (!crtc_state) {
@@ -169,7 +252,7 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
w_in = fixed16_to_int(state->src_w);
h_in = fixed16_to_int(state->src_h);
- crop_top = fixed16_to_int(state->src_x);
+ crop_top = fixed16_to_int(state->src_y);
crop_left = fixed16_to_int(state->src_x);
video_top = state->crtc_y;
@@ -243,6 +326,14 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n",
vsc_startp, vsc_endp, vd_start_lines, vd_end_lines);
+ afbc_top = round_down(vd_start_lines, 4);
+ afbc_bottom = round_up(vd_end_lines + 1, 4);
+ afbc_top_src = 0;
+ afbc_bottom_src = round_up(h_in + 1, 4);
+
+ DRM_DEBUG("afbc top %d (src %d) bottom %d (src %d)\n",
+ afbc_top, afbc_top_src, afbc_bottom, afbc_bottom_src);
+
/* Horizontal */
start = video_left + video_width / 2 - ((w_in << 17) / ratio_x);
@@ -278,6 +369,16 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n",
hsc_startp, hsc_endp, hd_start_lines, hd_end_lines);
+ if (hd_start_lines > 0 || (hd_end_lines < w_in)) {
+ afbc_left = 0;
+ afbc_right = round_up(w_in, 32);
+ } else {
+ afbc_left = round_down(hd_start_lines, 32);
+ afbc_right = round_up(hd_end_lines + 1, 32);
+ }
+
+ DRM_DEBUG("afbc left %d right %d\n", afbc_left, afbc_right);
+
priv->viu.vpp_vsc_start_phase_step = ratio_y << 6;
priv->viu.vpp_vsc_ini_phase = vphase << 8;
@@ -293,6 +394,35 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
VD_H_WIDTH(hd_end_lines - hd_start_lines + 1) |
VD_V_WIDTH(hd_end_lines/2 - hd_start_lines/2 + 1);
+ priv->viu.vd1_afbc_vd_cfmt_w =
+ AFBC_VD_H_WIDTH(afbc_right - afbc_left) |
+ AFBC_VD_V_WIDTH(afbc_right / 2 - afbc_left / 2);
+
+ priv->viu.vd1_afbc_vd_cfmt_h =
+ AFBC_VD_HEIGHT((afbc_bottom - afbc_top) / 2);
+
+ priv->viu.vd1_afbc_mif_hor_scope = AFBC_MIF_BLK_BGN_H(afbc_left / 32) |
+ AFBC_MIF_BLK_END_H((afbc_right / 32) - 1);
+
+ priv->viu.vd1_afbc_mif_ver_scope = AFBC_MIF_BLK_BGN_V(afbc_top / 4) |
+ AFBC_MIF_BLK_END_H((afbc_bottom / 4) - 1);
+
+ priv->viu.vd1_afbc_size_out =
+ AFBC_HSIZE_OUT(afbc_right - afbc_left) |
+ AFBC_VSIZE_OUT(afbc_bottom - afbc_top);
+
+ priv->viu.vd1_afbc_pixel_hor_scope =
+ AFBC_DEC_PIXEL_BGN_H(hd_start_lines - afbc_left) |
+ AFBC_DEC_PIXEL_END_H(hd_end_lines - afbc_left);
+
+ priv->viu.vd1_afbc_pixel_ver_scope =
+ AFBC_DEC_PIXEL_BGN_V(vd_start_lines - afbc_top) |
+ AFBC_DEC_PIXEL_END_V(vd_end_lines - afbc_top);
+
+ priv->viu.vd1_afbc_size_in =
+ AFBC_HSIZE_IN(afbc_right - afbc_left) |
+ AFBC_VSIZE_IN(afbc_bottom_src - afbc_top_src);
+
priv->viu.vd1_if0_luma_y0 = VD_Y_START(vd_start_lines) |
VD_Y_END(vd_end_lines);
@@ -350,11 +480,65 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
spin_lock_irqsave(&priv->drm->event_lock, flags);
- priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA |
- VD_URGENT_LUMA |
- VD_HOLD_LINES(9) |
- VD_CHRO_RPT_LASTL_CTRL |
- VD_ENABLE;
+ if ((fb->modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) ==
+ DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) {
+ priv->viu.vd1_afbc = true;
+
+ priv->viu.vd1_afbc_mode = AFBC_MIF_URGENT(3) |
+ AFBC_HOLD_LINE_NUM(8) |
+ AFBC_BURST_LEN(2);
+
+ if (fb->modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0,
+ AMLOGIC_FBC_OPTION_MEM_SAVING))
+ priv->viu.vd1_afbc_mode |= AFBC_BLK_MEM_MODE;
+
+ if ((fb->modifier & __fourcc_mod_amlogic_layout_mask) ==
+ AMLOGIC_FBC_LAYOUT_SCATTER)
+ priv->viu.vd1_afbc_mode |= AFBC_SCATTER_MODE;
+
+ priv->viu.vd1_afbc_en = 0x1600 | AFBC_DEC_ENABLE;
+
+ priv->viu.vd1_afbc_conv_ctrl = AFBC_CONV_LBUF_LEN(256);
+
+ priv->viu.vd1_afbc_dec_def_color = AFBC_DEF_COLOR_Y(1023);
+
+ /* 420: horizontal / 2, vertical / 4 */
+ priv->viu.vd1_afbc_vd_cfmt_ctrl = AFBC_HORZ_RPT_PIXEL0 |
+ AFBC_HORZ_Y_C_RATIO(1) |
+ AFBC_HORZ_FMT_EN |
+ AFBC_VERT_RPT_LINE0 |
+ AFBC_VERT_INITIAL_PHASE(12) |
+ AFBC_VERT_PHASE_STEP(8) |
+ AFBC_VERT_FMT_EN;
+
+ switch (fb->format->format) {
+ /* AFBC Only formats */
+ case DRM_FORMAT_YUV420_10BIT:
+ priv->viu.vd1_afbc_mode |=
+ AFBC_COMPBITS_YUV(AFBC_COMPBITS_10BIT);
+ priv->viu.vd1_afbc_dec_def_color |=
+ AFBC_DEF_COLOR_U(512) |
+ AFBC_DEF_COLOR_V(512);
+ break;
+ case DRM_FORMAT_YUV420_8BIT:
+ priv->viu.vd1_afbc_dec_def_color |=
+ AFBC_DEF_COLOR_U(128) |
+ AFBC_DEF_COLOR_V(128);
+ break;
+ }
+
+ priv->viu.vd1_if0_gen_reg = 0;
+ priv->viu.vd1_if0_canvas0 = 0;
+ priv->viu.viu_vd1_fmt_ctrl = 0;
+ } else {
+ priv->viu.vd1_afbc = false;
+
+ priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA |
+ VD_URGENT_LUMA |
+ VD_HOLD_LINES(9) |
+ VD_CHRO_RPT_LASTL_CTRL |
+ VD_ENABLE;
+ }
/* Setup scaler params */
meson_overlay_setup_scaler_params(priv, plane, interlace_mode);
@@ -370,6 +554,7 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
priv->viu.vd1_if0_gen_reg2 = 0;
priv->viu.viu_vd1_fmt_ctrl = 0;
+ /* None will match for AFBC Only formats */
switch (fb->format->format) {
/* TOFIX DRM_FORMAT_RGB888 should be supported */
case DRM_FORMAT_YUYV:
@@ -488,13 +673,42 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
priv->viu.vd1_stride0 = fb->pitches[0];
priv->viu.vd1_height0 =
drm_format_info_plane_height(fb->format,
- fb->height, 0);
+ fb->height, 0);
DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n",
priv->viu.vd1_addr0,
priv->viu.vd1_stride0,
priv->viu.vd1_height0);
}
+ if (priv->viu.vd1_afbc) {
+ if (priv->viu.vd1_afbc_mode & AFBC_SCATTER_MODE) {
+ /*
+ * In Scatter mode, the header contains the physical
+ * body content layout, thus the body content
+ * size isn't needed.
+ */
+ priv->viu.vd1_afbc_head_addr = priv->viu.vd1_addr0 >> 4;
+ priv->viu.vd1_afbc_body_addr = 0;
+ } else {
+ /* Default mode is 4k per superblock */
+ unsigned long block_size = 4096;
+ unsigned long body_size;
+
+ /* 8bit mem saving mode is 3072bytes per superblock */
+ if (priv->viu.vd1_afbc_mode & AFBC_BLK_MEM_MODE)
+ block_size = 3072;
+
+ body_size = (ALIGN(priv->viu.vd1_stride0, 64) / 64) *
+ (ALIGN(priv->viu.vd1_height0, 32) / 32) *
+ block_size;
+
+ priv->viu.vd1_afbc_body_addr = priv->viu.vd1_addr0 >> 4;
+ /* Header is after body content */
+ priv->viu.vd1_afbc_head_addr = (priv->viu.vd1_addr0 +
+ body_size) >> 4;
+ }
+ }
+
priv->viu.vd1_enabled = true;
spin_unlock_irqrestore(&priv->drm->event_lock, flags);
@@ -531,6 +745,53 @@ static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = {
.prepare_fb = drm_gem_fb_prepare_fb,
};
+static bool meson_overlay_format_mod_supported(struct drm_plane *plane,
+ u32 format, u64 modifier)
+{
+ if (modifier == DRM_FORMAT_MOD_LINEAR &&
+ format != DRM_FORMAT_YUV420_8BIT &&
+ format != DRM_FORMAT_YUV420_10BIT)
+ return true;
+
+ if ((modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) ==
+ DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) {
+ unsigned int layout = modifier &
+ DRM_FORMAT_MOD_AMLOGIC_FBC(
+ __fourcc_mod_amlogic_layout_mask, 0);
+ unsigned int options =
+ (modifier >> __fourcc_mod_amlogic_options_shift) &
+ __fourcc_mod_amlogic_options_mask;
+
+ if (format != DRM_FORMAT_YUV420_8BIT &&
+ format != DRM_FORMAT_YUV420_10BIT) {
+ DRM_DEBUG_KMS("%llx invalid format 0x%08x\n",
+ modifier, format);
+ return false;
+ }
+
+ if (layout != AMLOGIC_FBC_LAYOUT_BASIC &&
+ layout != AMLOGIC_FBC_LAYOUT_SCATTER) {
+ DRM_DEBUG_KMS("%llx invalid layout %x\n",
+ modifier, layout);
+ return false;
+ }
+
+ if (options &&
+ options != AMLOGIC_FBC_OPTION_MEM_SAVING) {
+ DRM_DEBUG_KMS("%llx invalid layout %x\n",
+ modifier, layout);
+ return false;
+ }
+
+ return true;
+ }
+
+ DRM_DEBUG_KMS("invalid modifier %llx for format 0x%08x\n",
+ modifier, format);
+
+ return false;
+}
+
static const struct drm_plane_funcs meson_overlay_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
@@ -538,6 +799,7 @@ static const struct drm_plane_funcs meson_overlay_funcs = {
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .format_mod_supported = meson_overlay_format_mod_supported,
};
static const uint32_t supported_drm_formats[] = {
@@ -549,6 +811,19 @@ static const uint32_t supported_drm_formats[] = {
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV411,
DRM_FORMAT_YUV410,
+ DRM_FORMAT_YUV420_8BIT, /* Amlogic FBC Only */
+ DRM_FORMAT_YUV420_10BIT, /* Amlogic FBC Only */
+};
+
+static const uint64_t format_modifiers[] = {
+ DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER,
+ AMLOGIC_FBC_OPTION_MEM_SAVING),
+ DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC,
+ AMLOGIC_FBC_OPTION_MEM_SAVING),
+ DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, 0),
+ DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, 0),
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID,
};
int meson_overlay_create(struct meson_drm *priv)
@@ -570,7 +845,7 @@ int meson_overlay_create(struct meson_drm *priv)
&meson_overlay_funcs,
supported_drm_formats,
ARRAY_SIZE(supported_drm_formats),
- NULL,
+ format_modifiers,
DRM_PLANE_TYPE_OVERLAY, "meson_overlay_plane");
drm_plane_helper_add(plane, &meson_overlay_helper_funcs);
diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h
index 8ea00546cd4e..446e7961da48 100644
--- a/drivers/gpu/drm/meson/meson_registers.h
+++ b/drivers/gpu/drm/meson/meson_registers.h
@@ -144,10 +144,15 @@
#define VIU_SW_RESET_OSD1 BIT(0)
#define VIU_MISC_CTRL0 0x1a06
#define VIU_CTRL0_VD1_AFBC_MASK 0x170000
+#define VIU_CTRL0_AFBC_TO_VD1 BIT(20)
#define VIU_MISC_CTRL1 0x1a07
#define MALI_AFBC_MISC GENMASK(15, 8)
#define D2D3_INTF_LENGTH 0x1a08
#define D2D3_INTF_CTRL0 0x1a09
+#define VD1_AFBCD0_MISC_CTRL 0x1a0a
+#define VD1_AXI_SEL_AFBC (1 << 12)
+#define AFBC_VD1_SEL (1 << 10)
+#define VD2_AFBCD1_MISC_CTRL 0x1a0b
#define VIU_OSD1_CTRL_STAT 0x1a10
#define VIU_OSD1_OSD_BLK_ENABLE BIT(0)
#define VIU_OSD1_OSD_MEM_MODE_LINEAR BIT(2)
@@ -261,6 +266,12 @@
#define VIU_OSD_FIFO_DEPTH_VAL(val) ((val & 0x7f) << 12)
#define VIU_OSD_WORDS_PER_BURST(words) (((words & 0x4) >> 1) << 22)
#define VIU_OSD_FIFO_LIMITS(size) ((size & 0xf) << 24)
+#define VIU_OSD_BURST_LENGTH_24 (0x0 << 31 | 0x0 << 10)
+#define VIU_OSD_BURST_LENGTH_32 (0x0 << 31 | 0x1 << 10)
+#define VIU_OSD_BURST_LENGTH_48 (0x0 << 31 | 0x2 << 10)
+#define VIU_OSD_BURST_LENGTH_64 (0x0 << 31 | 0x3 << 10)
+#define VIU_OSD_BURST_LENGTH_96 (0x1 << 31 | 0x0 << 10)
+#define VIU_OSD_BURST_LENGTH_128 (0x1 << 31 | 0x1 << 10)
#define VD1_IF0_GEN_REG 0x1a50
#define VD1_IF0_CANVAS0 0x1a51
@@ -365,6 +376,23 @@
#define VIU_OSD1_OETF_LUT_ADDR_PORT 0x1add
#define VIU_OSD1_OETF_LUT_DATA_PORT 0x1ade
#define AFBC_ENABLE 0x1ae0
+#define AFBC_MODE 0x1ae1
+#define AFBC_SIZE_IN 0x1ae2
+#define AFBC_DEC_DEF_COLOR 0x1ae3
+#define AFBC_CONV_CTRL 0x1ae4
+#define AFBC_LBUF_DEPTH 0x1ae5
+#define AFBC_HEAD_BADDR 0x1ae6
+#define AFBC_BODY_BADDR 0x1ae7
+#define AFBC_SIZE_OUT 0x1ae8
+#define AFBC_OUT_YSCOPE 0x1ae9
+#define AFBC_STAT 0x1aea
+#define AFBC_VD_CFMT_CTRL 0x1aeb
+#define AFBC_VD_CFMT_W 0x1aec
+#define AFBC_MIF_HOR_SCOPE 0x1aed
+#define AFBC_MIF_VER_SCOPE 0x1aee
+#define AFBC_PIXEL_HOR_SCOPE 0x1aef
+#define AFBC_PIXEL_VER_SCOPE 0x1af0
+#define AFBC_VD_CFMT_H 0x1af1
/* vpp */
#define VPP_DUMMY_DATA 0x1d00
diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c
index 304f8ff1339c..aede0c67a57f 100644
--- a/drivers/gpu/drm/meson/meson_viu.c
+++ b/drivers/gpu/drm/meson/meson_viu.c
@@ -411,13 +411,6 @@ void meson_viu_gxm_disable_osd1_afbc(struct meson_drm *priv)
priv->io_base + _REG(VIU_MISC_CTRL1));
}
-static inline uint32_t meson_viu_osd_burst_length_reg(uint32_t length)
-{
- uint32_t val = (((length & 0x80) % 24) / 12);
-
- return (((val & 0x3) << 10) | (((val & 0x4) >> 2) << 31));
-}
-
void meson_viu_init(struct meson_drm *priv)
{
uint32_t reg;
@@ -444,9 +437,9 @@ void meson_viu_init(struct meson_drm *priv)
VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
- reg |= meson_viu_osd_burst_length_reg(32);
+ reg |= VIU_OSD_BURST_LENGTH_32;
else
- reg |= meson_viu_osd_burst_length_reg(64);
+ reg |= VIU_OSD_BURST_LENGTH_64;
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));