drm/msm/sde: add hardware driver support for writeback interface
Writeback block is used to output composited images to memory.
This patch adds support to the hardware driver layer such that
writeback hardware block can be controlled using hardware
abstraction api common across families of hardware.
Change-Id: I92a69f1d564ee87e3f040cbef5a0302468599440
Signed-off-by: Alan Kwong <akwong@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index ac14bad..5655a8a 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -124,6 +124,12 @@
true, 4, 0,
SDE_FETCH_LINEAR, 1),
+ INTERLEAVED_RGB_FMT(BGRX8888,
+ COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ false, 4, 0,
+ SDE_FETCH_LINEAR, 1),
+
INTERLEAVED_RGB_FMT(XRGB8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index c2d8eac..7d0e908 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -154,6 +154,7 @@
* @SDE_WB_UBWC_1_0, Writeback Universal bandwidth compression 1.0
* support
* @SDE_WB_WBWC_1_5 UBWC 1.5 support
+ * @SDE_WB_YUV_CONFIG Writeback supports output of YUV colorspace
* @SDE_WB_MAX maximum value
*/
enum {
@@ -166,6 +167,7 @@
SDE_WB_DITHER,
SDE_WB_TRAFFIC_SHAPER,
SDE_WB_UBWC_1_0,
+ SDE_WB_YUV_CONFIG,
SDE_WB_MAX
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
index e0094bc..2f8bcc0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
@@ -53,10 +53,11 @@
(BIT(SDE_WB_LINE_MODE) | BIT(SDE_WB_BLOCK_MODE) |\
BIT(SDE_WB_CSC) | BIT(SDE_WB_CHROMA_DOWN) | BIT(SDE_WB_DOWNSCALE) |\
BIT(SDE_WB_DITHER) | BIT(SDE_WB_TRAFFIC_SHAPER) |\
- BIT(SDE_WB_UBWC_1_0))
+ BIT(SDE_WB_UBWC_1_0) | BIT(SDE_WB_YUV_CONFIG))
#define WB2_17X_MASK \
- (BIT(SDE_WB_LINE_MODE) | BIT(SDE_WB_TRAFFIC_SHAPER))
+ (BIT(SDE_WB_LINE_MODE) | BIT(SDE_WB_TRAFFIC_SHAPER) |\
+ BIT(SDE_WB_YUV_CONFIG))
#define DECIMATION_17X_MAX_H 4
#define DECIMATION_17X_MAX_V 4
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 773e3d5..7528871 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -197,6 +197,21 @@
return 0;
}
+static inline int sde_hw_ctl_get_bitmask_wb(struct sde_hw_ctl *ctx,
+ u32 *flushbits, enum sde_wb wb)
+{
+ switch (wb) {
+ case WB_0:
+ case WB_1:
+ case WB_2:
+ *flushbits |= BIT(16);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static inline int sde_hw_ctl_get_bitmask_cdm(struct sde_hw_ctl *ctx,
u32 *flushbits, enum sde_cdm cdm)
{
@@ -364,6 +379,7 @@
ops->get_bitmask_dspp = sde_hw_ctl_get_bitmask_dspp;
ops->get_bitmask_intf = sde_hw_ctl_get_bitmask_intf;
ops->get_bitmask_cdm = sde_hw_ctl_get_bitmask_cdm;
+ ops->get_bitmask_wb = sde_hw_ctl_get_bitmask_wb;
};
struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index b5a675e..007cb80 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -118,6 +118,10 @@
u32 *flushbits,
enum sde_cdm blk);
+ int (*get_bitmask_wb)(struct sde_hw_ctl *ctx,
+ u32 *flushbits,
+ enum sde_wb blk);
+
void (*setup_blendstage)(struct sde_hw_ctl *ctx,
enum sde_lm lm,
struct sde_hw_stage_cfg *cfg);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index ada3aef..73cecbf 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -39,10 +39,9 @@
#define PIPES_PER_STAGE 2
#define SDE_FORMAT_FLAG_YUV (1 << 0)
-#define SDE_FORMAT_FLAG_ROTATOR (1 << 1)
#define SDE_FORMAT_IS_YUV(X) ((X)->flag & SDE_FORMAT_FLAG_YUV)
-#define SDE_FORMAT_IS_ROTATOR(X) ((X)->flag & SDE_FORMAT_FLAG_ROTATOR)
+#define SDE_FORMAT_IS_LINEAR(X) ((X)->fetch_mode == SDE_FETCH_LINEAR)
#define SDE_FORMAT_IS_UBWC(X) ((X)->fetch_mode == SDE_FETCH_UBWC)
enum sde_mdp {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 3c7d7f4..31d5cce 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -27,6 +27,11 @@
#define UPPER_PIPE_CTRL 0x3F0
#define TE_LINE_INTERVAL 0x3F4
+#define TRAFFIC_SHAPER_EN BIT(31)
+#define TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4))
+#define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4))
+#define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4
+
static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp,
struct split_pipe_cfg *cfg)
{
@@ -65,10 +70,35 @@
SDE_REG_WRITE(c, SPLIT_DISPLAY_ENABLE, cfg->en & 0x1);
}
+static void sde_hw_setup_traffic_shaper(struct sde_hw_mdp *mdp,
+ struct traffic_shaper_cfg *cfg)
+{
+ struct sde_hw_blk_reg_map *c = &mdp->hw;
+ u32 ts_control = 0;
+ u32 offset;
+ u64 bpc;
+
+ if (cfg->rd_client)
+ offset = TRAFFIC_SHAPER_RD_CLIENT(cfg->client_id);
+ else
+ offset = TRAFFIC_SHAPER_WR_CLIENT(cfg->client_id);
+
+ if (cfg->en) {
+ bpc = cfg->bpc_numer;
+ do_div(bpc, (cfg->bpc_denom >>
+ TRAFFIC_SHAPER_FIXPOINT_FACTOR));
+ ts_control = lower_32_bits(bpc) + 1;
+ ts_control |= TRAFFIC_SHAPER_EN;
+ }
+
+ SDE_REG_WRITE(c, offset, ts_control);
+}
+
static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
unsigned long cap)
{
ops->setup_split_pipe = sde_hw_setup_split_pipe_control;
+ ops->setup_traffic_shaper = sde_hw_setup_traffic_shaper;
}
static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.h b/drivers/gpu/drm/msm/sde/sde_hw_top.h
index 6c15651..a0c8bbe 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.h
@@ -20,6 +20,22 @@
struct sde_hw_mdp;
/**
+ * struct traffic_shaper_cfg: traffic shaper configuration
+ * @en : enable/disable traffic shaper
+ * @rd_client : true if read client; false if write client
+ * @client_id : client identifier
+ * @bpc_denom : denominator of byte per clk
+ * @bpc_numer : numerator of byte per clk
+ */
+struct traffic_shaper_cfg {
+ bool en;
+ bool rd_client;
+ u32 client_id;
+ u32 bpc_denom;
+ u64 bpc_numer;
+};
+
+/**
* struct split_pipe_cfg - pipe configuration for dual display panels
* @en : Enable/disable dual pipe confguration
* @mode : Panel interface mode
@@ -40,6 +56,7 @@
* struct sde_hw_mdp_ops - interface to the MDP TOP Hw driver functions
* Assumption is these functions will be called after clocks are enabled.
* @setup_split_pipe : Programs the pipe control registers
+ * @setup_traffic_shaper : programs traffic shaper control
*/
struct sde_hw_mdp_ops {
/** setup_split_pipe() : Regsiters are not double buffered, thisk
@@ -49,6 +66,14 @@
*/
void (*setup_split_pipe)(struct sde_hw_mdp *mdp,
struct split_pipe_cfg *p);
+
+ /**
+ * setup_traffic_shaper() : Setup traffic shaper control
+ * @mdp : mdp top context driver
+ * @cfg : traffic shaper configuration
+ */
+ void (*setup_traffic_shaper)(struct sde_hw_mdp *mdp,
+ struct traffic_shaper_cfg *cfg);
};
struct sde_hw_mdp {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index ef3a35a..ac6ff8c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -14,6 +14,35 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_wb.h"
+#include "sde_formats.h"
+
+#define WB_DST_FORMAT 0x000
+#define WB_DST_OP_MODE 0x004
+#define WB_DST_PACK_PATTERN 0x008
+#define WB_DST0_ADDR 0x00C
+#define WB_DST1_ADDR 0x010
+#define WB_DST2_ADDR 0x014
+#define WB_DST3_ADDR 0x018
+#define WB_DST_YSTRIDE0 0x01C
+#define WB_DST_YSTRIDE1 0x020
+#define WB_DST_YSTRIDE1 0x020
+#define WB_DST_DITHER_BITDEPTH 0x024
+#define WB_DST_MATRIX_ROW0 0x030
+#define WB_DST_MATRIX_ROW1 0x034
+#define WB_DST_MATRIX_ROW2 0x038
+#define WB_DST_MATRIX_ROW3 0x03C
+#define WB_DST_WRITE_CONFIG 0x048
+#define WB_ROTATION_DNSCALER 0x050
+#define WB_ROTATOR_PIPE_DOWNSCALER 0x054
+#define WB_N16_INIT_PHASE_X_C03 0x060
+#define WB_N16_INIT_PHASE_X_C12 0x064
+#define WB_N16_INIT_PHASE_Y_C03 0x068
+#define WB_N16_INIT_PHASE_Y_C12 0x06C
+#define WB_OUT_SIZE 0x074
+#define WB_ALPHA_X_VALUE 0x078
+#define WB_CSC_BASE 0x260
+#define WB_DST_ADDR_SW_STATUS 0x2B0
+#define WB_CDP_CTRL 0x2B4
static struct sde_wb_cfg *_wb_offset(enum sde_wb wb,
struct sde_mdss_cfg *m,
@@ -42,11 +71,93 @@
static void sde_hw_wb_setup_outaddress(struct sde_hw_wb *ctx,
struct sde_hw_wb_cfg *data)
{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+
+ SDE_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
+ SDE_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
+ SDE_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
+ SDE_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
}
static void sde_hw_wb_setup_format(struct sde_hw_wb *ctx,
struct sde_hw_wb_cfg *data)
{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ const struct sde_format *fmt = data->dest.format;
+ u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
+ u32 write_config = 0;
+ u32 opmode = 0;
+ u32 dst_addr_sw = 0;
+ u32 cdp_settings = 0x0;
+
+ chroma_samp = fmt->chroma_sample;
+
+ dst_format = (chroma_samp << 23) |
+ (fmt->fetch_planes << 19) |
+ (fmt->bits[C3_ALPHA] << 6) |
+ (fmt->bits[C2_R_Cr] << 4) |
+ (fmt->bits[C1_B_Cb] << 2) |
+ (fmt->bits[C0_G_Y] << 0);
+
+ if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
+ dst_format |= BIT(8); /* DSTC3_EN */
+ if (!fmt->alpha_enable)
+ dst_format |= BIT(14); /* DST_ALPHA_X */
+ }
+
+ if (SDE_FORMAT_IS_YUV(fmt) &&
+ (ctx->caps->features & BIT(SDE_WB_YUV_CONFIG)))
+ dst_format |= BIT(15);
+
+ pattern = (fmt->element[3] << 24) |
+ (fmt->element[2] << 16) |
+ (fmt->element[1] << 8) |
+ (fmt->element[0] << 0);
+
+ dst_format |= (fmt->unpack_align_msb << 18) |
+ (fmt->unpack_tight << 17) |
+ ((fmt->unpack_count - 1) << 12) |
+ ((fmt->bpp - 1) << 9);
+
+ ystride0 = data->dest.plane_pitch[0] |
+ (data->dest.plane_pitch[1] << 16);
+ ystride1 = data->dest.plane_pitch[2] |
+ (data->dest.plane_pitch[3] << 16);
+ outsize = (data->dest.height << 16) | data->dest.width;
+
+ if (SDE_FORMAT_IS_UBWC(fmt)) {
+ opmode |= BIT(0);
+ dst_format |= BIT(31);
+ if (ctx->highest_bank_bit)
+ write_config |= (ctx->highest_bank_bit << 8);
+ if (fmt->base.pixel_format == DRM_FORMAT_RGB565)
+ write_config |= 0x8;
+ }
+
+ if (data->is_secure)
+ dst_addr_sw |= BIT(0);
+
+ SDE_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
+ SDE_REG_WRITE(c, WB_DST_FORMAT, dst_format);
+ SDE_REG_WRITE(c, WB_DST_OP_MODE, opmode);
+ SDE_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
+ SDE_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
+ SDE_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
+ SDE_REG_WRITE(c, WB_OUT_SIZE, outsize);
+ SDE_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
+ SDE_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
+
+ /* Enable CDP */
+ cdp_settings = BIT(0);
+
+ if (!SDE_FORMAT_IS_LINEAR(fmt))
+ cdp_settings |= BIT(1);
+
+ /* Enable 64 transactions if line mode*/
+ if (data->intf_mode == INTF_MODE_WB_LINE)
+ cdp_settings |= BIT(3);
+
+ SDE_REG_WRITE(c, WB_CDP_CTRL, cdp_settings);
}
static void sde_hw_wb_setup_rotator(struct sde_hw_wb *ctx,
@@ -63,9 +174,13 @@
struct sde_hw_wb_cfg *data)
{
}
+
static void sde_hw_wb_traffic_shaper(struct sde_hw_wb *ctx,
struct sde_hw_wb_cfg *data)
{
+ if (ctx->hw_mdp && ctx->hw_mdp->ops.setup_traffic_shaper)
+ ctx->hw_mdp->ops.setup_traffic_shaper(ctx->hw_mdp,
+ &data->ts_cfg);
}
static void _setup_wb_ops(struct sde_hw_wb_ops *ops,
@@ -92,7 +207,8 @@
struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
void __iomem *addr,
- struct sde_mdss_cfg *m)
+ struct sde_mdss_cfg *m,
+ struct sde_hw_mdp *hw_mdp)
{
struct sde_hw_wb *c;
struct sde_wb_cfg *cfg;
@@ -102,7 +218,8 @@
return ERR_PTR(-ENOMEM);
cfg = _wb_offset(idx, m, addr, &c->hw);
- if (!cfg) {
+ if (IS_ERR(cfg)) {
+ WARN(1, "Unable to find wb idx=%d\n", idx);
kfree(c);
return ERR_PTR(-EINVAL);
}
@@ -111,11 +228,13 @@
c->idx = idx;
c->caps = cfg;
_setup_wb_ops(&c->ops, c->caps->features);
-
- /*
- * Perform any default initialization for the chroma down module
- */
+ c->highest_bank_bit = m->mdp[0].highest_bank_bit;
+ c->hw_mdp = hw_mdp;
return c;
}
+void sde_hw_wb_destroy(struct sde_hw_wb *hw_wb)
+{
+ kfree(hw_wb);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.h b/drivers/gpu/drm/msm/sde/sde_hw_wb.h
index 093d2fb..e01750b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.h
@@ -15,12 +15,16 @@
#include "sde_hw_catalog.h"
#include "sde_hw_mdss.h"
+#include "sde_hw_top.h"
#include "sde_hw_util.h"
struct sde_hw_wb;
struct sde_hw_wb_cfg {
struct sde_hw_fmt_layout dest;
+ enum sde_intf_mode intf_mode;
+ struct traffic_shaper_cfg ts_cfg;
+ bool is_secure;
};
/**
@@ -56,8 +60,9 @@
* @struct sde_hw_blk_reg_map *hw;
* @idx
* @wb_hw_caps
- * @mixer_hw_caps
* @ops
+ * @highest_bank_bit: GPU highest memory bank bit used
+ * @hw_mdp: MDP top level hardware block
*/
struct sde_hw_wb {
/* base */
@@ -69,17 +74,28 @@
/* ops */
struct sde_hw_wb_ops ops;
+
+ u32 highest_bank_bit;
+
+ struct sde_hw_mdp *hw_mdp;
};
/**
- * sde_hw_wb_init(): Initializes the wb_path hw driver object.
- * should be called before accessing every mixer.
+ * sde_hw_wb_init(): Initializes and return writeback hw driver object.
* @idx: wb_path index for which driver object is required
* @addr: mapped register io address of MDP
* @m : pointer to mdss catalog data
+ * @hw_mdp: pointer to mdp top hw driver object
*/
struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
void __iomem *addr,
- struct sde_mdss_cfg *m);
+ struct sde_mdss_cfg *m,
+ struct sde_hw_mdp *hw_mdp);
+
+/**
+ * sde_hw_wb_destroy(): Destroy writeback hw driver object.
+ * @hw_wb: Pointer to writeback hw driver object
+ */
+void sde_hw_wb_destroy(struct sde_hw_wb *hw_wb);
#endif /*_SDE_HW_WB_H */