summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2019-06-25 12:49:08 +1000
committerDave Airlie <airlied@redhat.com>2019-06-25 12:56:40 +1000
commit9b75276e4fb190b47ed62693cc4d9549a45c76a2 (patch)
treecf3dbfa427c19bbc6097865a527cea91376f60b5
parent417f2544f48c19f5958790658c4aa30b0986647f (diff)
parent2cfb1981dd0d9505b59868a7f7591746f51794b0 (diff)
downloadlinux-9b75276e4fb190b47ed62693cc4d9549a45c76a2.tar.gz
linux-9b75276e4fb190b47ed62693cc4d9549a45c76a2.tar.bz2
linux-9b75276e4fb190b47ed62693cc4d9549a45c76a2.zip
Merge commit 'refs/for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next
Picking up pace on the upstreaming of Komeda driver, with quite a lot of new features added this time. On top of that we have the small cleanups and improved usage of the debugfs functions. Please pull! Signed-off-by: Dave Airlie <airlied@redhat.com> From: Liviu Dudau <Liviu.Dudau@arm.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190621095349.GI17204@e110455-lin.cambridge.arm.com
-rw-r--r--Documentation/devicetree/bindings/display/arm,komeda.txt23
-rw-r--r--drivers/gpu/drm/arm/display/include/malidp_io.h7
-rw-r--r--drivers/gpu/drm/arm/display/include/malidp_utils.h5
-rw-r--r--drivers/gpu/drm/arm/display/komeda/Makefile2
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_component.c582
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c142
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h2
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c67
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.h17
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_crtc.c154
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_dev.c59
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_dev.h13
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c58
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h24
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c175
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h13
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_kms.c130
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_kms.h71
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c66
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h111
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c679
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_plane.c191
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c154
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c199
-rw-r--r--drivers/gpu/drm/arm/malidp_drv.c11
25 files changed, 2728 insertions, 227 deletions
diff --git a/Documentation/devicetree/bindings/display/arm,komeda.txt b/Documentation/devicetree/bindings/display/arm,komeda.txt
index 02b226532ebd..8513695ee47f 100644
--- a/Documentation/devicetree/bindings/display/arm,komeda.txt
+++ b/Documentation/devicetree/bindings/display/arm,komeda.txt
@@ -7,10 +7,13 @@ Required properties:
- clocks: A list of phandle + clock-specifier pairs, one for each entry
in 'clock-names'
- clock-names: A list of clock names. It should contain:
- - "mclk": for the main processor clock
- - "pclk": for the APB interface clock
+ - "aclk": for the main processor clock
- #address-cells: Must be 1
- #size-cells: Must be 0
+- iommus: configure the stream id to IOMMU, Must be configured if want to
+ enable iommu in display. for how to configure this node please reference
+ devicetree/bindings/iommu/arm,smmu-v3.txt,
+ devicetree/bindings/iommu/iommu.txt
Required properties for sub-node: pipeline@nq
Each device contains one or two pipeline sub-nodes (at least one), each
@@ -20,7 +23,6 @@ pipeline node should provide properties:
in 'clock-names'
- clock-names: should contain:
- "pxclk": pixel clock
- - "aclk": AXI interface clock
- port: each pipeline connect to an encoder input port. The connection is
modeled using the OF graph bindings specified in
@@ -42,12 +44,15 @@ Example:
compatible = "arm,mali-d71";
reg = <0xc00000 0x20000>;
interrupts = <0 168 4>;
- clocks = <&dpu_mclk>, <&dpu_aclk>;
- clock-names = "mclk", "pclk";
+ clocks = <&dpu_aclk>;
+ clock-names = "aclk";
+ iommus = <&smmu 0>, <&smmu 1>, <&smmu 2>, <&smmu 3>,
+ <&smmu 4>, <&smmu 5>, <&smmu 6>, <&smmu 7>,
+ <&smmu 8>, <&smmu 9>;
dp0_pipe0: pipeline@0 {
- clocks = <&fpgaosc2>, <&dpu_aclk>;
- clock-names = "pxclk", "aclk";
+ clocks = <&fpgaosc2>;
+ clock-names = "pxclk";
reg = <0>;
port {
@@ -58,8 +63,8 @@ Example:
};
dp0_pipe1: pipeline@1 {
- clocks = <&fpgaosc2>, <&dpu_aclk>;
- clock-names = "pxclk", "aclk";
+ clocks = <&fpgaosc2>;
+ clock-names = "pxclk";
reg = <1>;
port {
diff --git a/drivers/gpu/drm/arm/display/include/malidp_io.h b/drivers/gpu/drm/arm/display/include/malidp_io.h
index 4fb3caf864ce..9440dff94212 100644
--- a/drivers/gpu/drm/arm/display/include/malidp_io.h
+++ b/drivers/gpu/drm/arm/display/include/malidp_io.h
@@ -22,6 +22,13 @@ malidp_write32(u32 __iomem *base, u32 offset, u32 v)
}
static inline void
+malidp_write64(u32 __iomem *base, u32 offset, u64 v)
+{
+ writel(lower_32_bits(v), (base + (offset >> 2)));
+ writel(upper_32_bits(v), (base + (offset >> 2) + 1));
+}
+
+static inline void
malidp_write32_mask(u32 __iomem *base, u32 offset, u32 m, u32 v)
{
u32 tmp = malidp_read32(base, offset);
diff --git a/drivers/gpu/drm/arm/display/include/malidp_utils.h b/drivers/gpu/drm/arm/display/include/malidp_utils.h
index 8cfd91196e15..3bc383d5bf73 100644
--- a/drivers/gpu/drm/arm/display/include/malidp_utils.h
+++ b/drivers/gpu/drm/arm/display/include/malidp_utils.h
@@ -8,6 +8,7 @@
#define _MALIDP_UTILS_
#include <linux/delay.h>
+#include <linux/errno.h>
#define has_bit(nr, mask) (BIT(nr) & (mask))
#define has_bits(bits, mask) (((bits) & (mask)) == (bits))
@@ -20,11 +21,9 @@
int num_tries = __tries; \
while (!__cond && (num_tries > 0)) { \
usleep_range(__min_range, __max_range); \
- if (__cond) \
- break; \
num_tries--; \
} \
- num_tries; \
+ (__cond) ? 0 : -ETIMEDOUT; \
})
/* the restriction of range is [start, end] */
diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile
index 412eeba8c39f..5c3900c2e764 100644
--- a/drivers/gpu/drm/arm/display/komeda/Makefile
+++ b/drivers/gpu/drm/arm/display/komeda/Makefile
@@ -8,12 +8,14 @@ komeda-y := \
komeda_drv.o \
komeda_dev.o \
komeda_format_caps.o \
+ komeda_color_mgmt.o \
komeda_pipeline.o \
komeda_pipeline_state.o \
komeda_framebuffer.o \
komeda_kms.o \
komeda_crtc.o \
komeda_plane.o \
+ komeda_wb_connector.o \
komeda_private_obj.o
komeda-y += \
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
index 6bab816ed8e7..4073a452e24a 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -10,6 +10,7 @@
#include "komeda_kms.h"
#include "malidp_io.h"
#include "komeda_framebuffer.h"
+#include "komeda_color_mgmt.h"
static void get_resources_id(u32 hw_id, u32 *pipe_id, u32 *comp_id)
{
@@ -134,11 +135,60 @@ static u32 to_rot_ctrl(u32 rot)
return lr_ctrl;
}
-static inline u32 to_d71_input_id(struct komeda_component_output *output)
+static u32 to_ad_ctrl(u64 modifier)
{
- struct komeda_component *comp = output->component;
+ u32 afbc_ctrl = AD_AEN;
- return comp ? (comp->hw_id + output->output_port) : 0;
+ if (!modifier)
+ return 0;
+
+ if ((modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) ==
+ AFBC_FORMAT_MOD_BLOCK_SIZE_32x8)
+ afbc_ctrl |= AD_WB;
+
+ if (modifier & AFBC_FORMAT_MOD_YTR)
+ afbc_ctrl |= AD_YT;
+ if (modifier & AFBC_FORMAT_MOD_SPLIT)
+ afbc_ctrl |= AD_BS;
+ if (modifier & AFBC_FORMAT_MOD_TILED)
+ afbc_ctrl |= AD_TH;
+
+ return afbc_ctrl;
+}
+
+static inline u32 to_d71_input_id(struct komeda_component_state *st, int idx)
+{
+ struct komeda_component_output *input = &st->inputs[idx];
+
+ /* if input is not active, set hw input_id(0) to disable it */
+ if (has_bit(idx, st->active_inputs))
+ return input->component->hw_id + input->output_port;
+ else
+ return 0;
+}
+
+static void d71_layer_update_fb(struct komeda_component *c,
+ struct komeda_fb *kfb,
+ dma_addr_t *addr)
+{
+ struct drm_framebuffer *fb = &kfb->base;
+ const struct drm_format_info *info = fb->format;
+ u32 __iomem *reg = c->reg;
+ int block_h;
+
+ if (info->num_planes > 2)
+ malidp_write64(reg, BLK_P2_PTR_LOW, addr[2]);
+
+ if (info->num_planes > 1) {
+ block_h = drm_format_info_block_height(info, 1);
+ malidp_write32(reg, BLK_P1_STRIDE, fb->pitches[1] * block_h);
+ malidp_write64(reg, BLK_P1_PTR_LOW, addr[1]);
+ }
+
+ block_h = drm_format_info_block_height(info, 0);
+ malidp_write32(reg, BLK_P0_STRIDE, fb->pitches[0] * block_h);
+ malidp_write64(reg, BLK_P0_PTR_LOW, addr[0]);
+ malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
}
static void d71_layer_disable(struct komeda_component *c)
@@ -156,26 +206,65 @@ static void d71_layer_update(struct komeda_component *c,
u32 __iomem *reg = c->reg;
u32 ctrl_mask = L_EN | L_ROT(L_ROT_R270) | L_HFLIP | L_VFLIP | L_TBU_EN;
u32 ctrl = L_EN | to_rot_ctrl(st->rot);
- int i;
- for (i = 0; i < fb->format->num_planes; i++) {
- malidp_write32(reg,
- BLK_P0_PTR_LOW + i * LAYER_PER_PLANE_REGS * 4,
- lower_32_bits(st->addr[i]));
- malidp_write32(reg,
- BLK_P0_PTR_HIGH + i * LAYER_PER_PLANE_REGS * 4,
- upper_32_bits(st->addr[i]));
- if (i >= 2)
+ d71_layer_update_fb(c, kfb, st->addr);
+
+ malidp_write32(reg, AD_CONTROL, to_ad_ctrl(fb->modifier));
+ if (fb->modifier) {
+ u64 addr;
+
+ malidp_write32(reg, LAYER_AD_H_CROP, HV_CROP(st->afbc_crop_l,
+ st->afbc_crop_r));
+ malidp_write32(reg, LAYER_AD_V_CROP, HV_CROP(st->afbc_crop_t,
+ st->afbc_crop_b));
+ /* afbc 1.2 wants payload, afbc 1.0/1.1 wants end_addr */
+ if (fb->modifier & AFBC_FORMAT_MOD_TILED)
+ addr = st->addr[0] + kfb->offset_payload;
+ else
+ addr = st->addr[0] + kfb->afbc_size - 1;
+
+ malidp_write32(reg, BLK_P1_PTR_LOW, lower_32_bits(addr));
+ malidp_write32(reg, BLK_P1_PTR_HIGH, upper_32_bits(addr));
+ }
+
+ if (fb->format->is_yuv) {
+ u32 upsampling = 0;
+
+ switch (kfb->format_caps->fourcc) {
+ case DRM_FORMAT_YUYV:
+ upsampling = fb->modifier ? LR_CHI422_BILINEAR :
+ LR_CHI422_REPLICATION;
+ break;
+ case DRM_FORMAT_UYVY:
+ upsampling = LR_CHI422_REPLICATION;
break;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_YUV420_8BIT:
+ case DRM_FORMAT_YUV420_10BIT:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_P010:
+ /* these fmt support MPGE/JPEG both, here perfer JPEG*/
+ upsampling = LR_CHI420_JPEG;
+ break;
+ case DRM_FORMAT_X0L2:
+ upsampling = LR_CHI420_JPEG;
+ break;
+ default:
+ break;
+ }
- malidp_write32(reg,
- BLK_P0_STRIDE + i * LAYER_PER_PLANE_REGS * 4,
- fb->pitches[i] & 0xFFFF);
+ malidp_write32(reg, LAYER_R_CONTROL, upsampling);
+ malidp_write_group(reg, LAYER_YUV_RGB_COEFF0,
+ KOMEDA_N_YUV2RGB_COEFFS,
+ komeda_select_yuv2rgb_coeffs(
+ plane_st->color_encoding,
+ plane_st->color_range));
}
- malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
+ if (kfb->is_va)
+ ctrl |= L_TBU_EN;
malidp_write32_mask(reg, BLK_CONTROL, ctrl_mask, ctrl);
}
@@ -288,10 +377,90 @@ static int d71_layer_init(struct d71_dev *d71,
return 0;
}
+static void d71_wb_layer_update(struct komeda_component *c,
+ struct komeda_component_state *state)
+{
+ struct komeda_layer_state *st = to_layer_st(state);
+ struct drm_connector_state *conn_st = state->wb_conn->state;
+ struct komeda_fb *kfb = to_kfb(conn_st->writeback_job->fb);
+ u32 ctrl = L_EN | LW_OFM, mask = L_EN | LW_OFM | LW_TBU_EN;
+ u32 __iomem *reg = c->reg;
+
+ d71_layer_update_fb(c, kfb, st->addr);
+
+ if (kfb->is_va)
+ ctrl |= LW_TBU_EN;
+
+ malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
+ malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(state, 0));
+ malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
+}
+
+static void d71_wb_layer_dump(struct komeda_component *c, struct seq_file *sf)
+{
+ u32 v[12], i;
+
+ dump_block_header(sf, c->reg);
+
+ get_values_from_reg(c->reg, 0x80, 1, v);
+ seq_printf(sf, "LW_INPUT_ID0:\t\t0x%X\n", v[0]);
+
+ get_values_from_reg(c->reg, 0xD0, 3, v);
+ seq_printf(sf, "LW_CONTROL:\t\t0x%X\n", v[0]);
+ seq_printf(sf, "LW_PROG_LINE:\t\t0x%X\n", v[1]);
+ seq_printf(sf, "LW_FORMAT:\t\t0x%X\n", v[2]);
+
+ get_values_from_reg(c->reg, 0xE0, 1, v);
+ seq_printf(sf, "LW_IN_SIZE:\t\t0x%X\n", v[0]);
+
+ for (i = 0; i < 2; i++) {
+ get_values_from_reg(c->reg, 0x100 + i * 0x10, 3, v);
+ seq_printf(sf, "LW_P%u_PTR_LOW:\t\t0x%X\n", i, v[0]);
+ seq_printf(sf, "LW_P%u_PTR_HIGH:\t\t0x%X\n", i, v[1]);
+ seq_printf(sf, "LW_P%u_STRIDE:\t\t0x%X\n", i, v[2]);
+ }
+
+ get_values_from_reg(c->reg, 0x130, 12, v);
+ for (i = 0; i < 12; i++)
+ seq_printf(sf, "LW_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
+}
+
+static void d71_wb_layer_disable(struct komeda_component *c)
+{
+ malidp_write32(c->reg, BLK_INPUT_ID0, 0);
+ malidp_write32_mask(c->reg, BLK_CONTROL, L_EN, 0);
+}
+
+static const struct komeda_component_funcs d71_wb_layer_funcs = {
+ .update = d71_wb_layer_update,
+ .disable = d71_wb_layer_disable,
+ .dump_register = d71_wb_layer_dump,
+};
+
static int d71_wb_layer_init(struct d71_dev *d71,
struct block_header *blk, u32 __iomem *reg)
{
- DRM_DEBUG("Detect D71_Wb_Layer.\n");
+ struct komeda_component *c;
+ struct komeda_layer *wb_layer;
+ u32 pipe_id, layer_id;
+
+ get_resources_id(blk->block_info, &pipe_id, &layer_id);
+
+ c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*wb_layer),
+ layer_id, BLOCK_INFO_INPUT_ID(blk->block_info),
+ &d71_wb_layer_funcs,
+ 1, get_valid_inputs(blk), 0, reg,
+ "LPU%d_LAYER_WR", pipe_id);
+ if (IS_ERR(c)) {
+ DRM_ERROR("Failed to add wb_layer component\n");
+ return PTR_ERR(c);
+ }
+
+ wb_layer = to_layer(c);
+ wb_layer->layer_type = KOMEDA_FMT_WB_LAYER;
+
+ set_range(&wb_layer->hsize_in, D71_MIN_LINE_SIZE, d71->max_line_size);
+ set_range(&wb_layer->vsize_in, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
return 0;
}
@@ -303,8 +472,18 @@ static void d71_component_disable(struct komeda_component *c)
malidp_write32(reg, BLK_CONTROL, 0);
- for (i = 0; i < c->max_active_inputs; i++)
+ for (i = 0; i < c->max_active_inputs; i++) {
malidp_write32(reg, BLK_INPUT_ID0 + (i << 2), 0);
+
+ /* Besides clearing the input ID to zero, D71 compiz also has
+ * input enable bit in CU_INPUTx_CONTROL which need to be
+ * cleared.
+ */
+ if (has_bit(c->id, KOMEDA_PIPELINE_COMPIZS))
+ malidp_write32(reg, CU_INPUT0_CONTROL +
+ i * CU_PER_INPUT_REGS * 4,
+ CU_INPUT_CTRL_ALPHA(0xFF));
+ }
}
static void compiz_enable_input(u32 __iomem *id_reg,
@@ -337,15 +516,15 @@ static void d71_compiz_update(struct komeda_component *c,
struct komeda_compiz_state *st = to_compiz_st(state);
u32 __iomem *reg = c->reg;
u32 __iomem *id_reg, *cfg_reg;
- u32 index, input_hw_id;
+ u32 index;
for_each_changed_input(state, index) {
id_reg = reg + index;
cfg_reg = reg + index * CU_PER_INPUT_REGS;
- input_hw_id = to_d71_input_id(&state->inputs[index]);
if (state->active_inputs & BIT(index)) {
compiz_enable_input(id_reg, cfg_reg,
- input_hw_id, &st->cins[index]);
+ to_d71_input_id(state, index),
+ &st->cins[index]);
} else {
malidp_write32(id_reg, BLK_INPUT_ID0, 0);
malidp_write32(cfg_reg, CU_INPUT0_CONTROL, 0);
@@ -424,18 +603,354 @@ static int d71_compiz_init(struct d71_dev *d71,
return 0;
}
+static void d71_scaler_update_filter_lut(u32 __iomem *reg, u32 hsize_in,
+ u32 vsize_in, u32 hsize_out,
+ u32 vsize_out)
+{
+ u32 val = 0;
+
+ if (hsize_in <= hsize_out)
+ val |= 0x62;
+ else if (hsize_in <= (hsize_out + hsize_out / 2))
+ val |= 0x63;
+ else if (hsize_in <= hsize_out * 2)
+ val |= 0x64;
+ else if (hsize_in <= hsize_out * 2 + (hsize_out * 3) / 4)
+ val |= 0x65;
+ else
+ val |= 0x66;
+
+ if (vsize_in <= vsize_out)
+ val |= SC_VTSEL(0x6A);
+ else if (vsize_in <= (vsize_out + vsize_out / 2))
+ val |= SC_VTSEL(0x6B);
+ else if (vsize_in <= vsize_out * 2)
+ val |= SC_VTSEL(0x6C);
+ else if (vsize_in <= vsize_out * 2 + vsize_out * 3 / 4)
+ val |= SC_VTSEL(0x6D);
+ else
+ val |= SC_VTSEL(0x6E);
+
+ malidp_write32(reg, SC_COEFFTAB, val);
+}
+
+static void d71_scaler_update(struct komeda_component *c,
+ struct komeda_component_state *state)
+{
+ struct komeda_scaler_state *st = to_scaler_st(state);
+ u32 __iomem *reg = c->reg;
+ u32 init_ph, delta_ph, ctrl;
+
+ d71_scaler_update_filter_lut(reg, st->hsize_in, st->vsize_in,
+ st->hsize_out, st->vsize_out);
+
+ malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize_in, st->vsize_in));
+ malidp_write32(reg, SC_OUT_SIZE, HV_SIZE(st->hsize_out, st->vsize_out));
+ malidp_write32(reg, SC_H_CROP, HV_CROP(st->left_crop, st->right_crop));
+
+ /* for right part, HW only sample the valid pixel which means the pixels
+ * in left_crop will be jumpped, and the first sample pixel is:
+ *
+ * dst_a = st->total_hsize_out - st->hsize_out + st->left_crop + 0.5;
+ *
+ * Then the corresponding texel in src is:
+ *
+ * h_delta_phase = st->total_hsize_in / st->total_hsize_out;
+ * src_a = dst_A * h_delta_phase;
+ *
+ * and h_init_phase is src_a deduct the real source start src_S;
+ *
+ * src_S = st->total_hsize_in - st->hsize_in;
+ * h_init_phase = src_a - src_S;
+ *
+ * And HW precision for the initial/delta_phase is 16:16 fixed point,
+ * the following is the simplified formula
+ */
+ if (st->right_part) {
+ u32 dst_a = st->total_hsize_out - st->hsize_out + st->left_crop;
+
+ if (st->en_img_enhancement)
+ dst_a -= 1;
+
+ init_ph = ((st->total_hsize_in * (2 * dst_a + 1) -
+ 2 * st->total_hsize_out * (st->total_hsize_in -
+ st->hsize_in)) << 15) / st->total_hsize_out;
+ } else {
+ init_ph = (st->total_hsize_in << 15) / st->total_hsize_out;
+ }
+
+ malidp_write32(reg, SC_H_INIT_PH, init_ph);
+
+ delta_ph = (st->total_hsize_in << 16) / st->total_hsize_out;
+ malidp_write32(reg, SC_H_DELTA_PH, delta_ph);
+
+ init_ph = (st->total_vsize_in << 15) / st->vsize_out;
+ malidp_write32(reg, SC_V_INIT_PH, init_ph);
+
+ delta_ph = (st->total_vsize_in << 16) / st->vsize_out;
+ malidp_write32(reg, SC_V_DELTA_PH, delta_ph);
+
+ ctrl = 0;
+ ctrl |= st->en_scaling ? SC_CTRL_SCL : 0;
+ ctrl |= st->en_alpha ? SC_CTRL_AP : 0;
+ ctrl |= st->en_img_enhancement ? SC_CTRL_IENH : 0;
+ /* If we use the hardware splitter we shouldn't set SC_CTRL_LS */
+ if (st->en_split &&
+ state->inputs[0].component->id != KOMEDA_COMPONENT_SPLITTER)
+ ctrl |= SC_CTRL_LS;
+
+ malidp_write32(reg, BLK_CONTROL, ctrl);
+ malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(state, 0));
+}
+
+static void d71_scaler_dump(struct komeda_component *c, struct seq_file *sf)
+{
+ u32 v[9];
+
+ dump_block_header(sf, c->reg);
+
+ get_values_from_reg(c->reg, 0x80, 1, v);
+ seq_printf(sf, "SC_INPUT_ID0:\t\t0x%X\n", v[0]);
+
+ get_values_from_reg(c->reg, 0xD0, 1, v);
+ seq_printf(sf, "SC_CONTROL:\t\t0x%X\n", v[0]);
+
+ get_values_from_reg(c->reg, 0xDC, 9, v);
+ seq_printf(sf, "SC_COEFFTAB:\t\t0x%X\n", v[0]);
+ seq_printf(sf, "SC_IN_SIZE:\t\t0x%X\n", v[1]);
+ seq_printf(sf, "SC_OUT_SIZE:\t\t0x%X\n", v[2]);
+ seq_printf(sf, "SC_H_CROP:\t\t0x%X\n", v[3]);
+ seq_printf(sf, "SC_V_CROP:\t\t0x%X\n", v[4]);
+ seq_printf(sf, "SC_H_INIT_PH:\t\t0x%X\n", v[5]);
+ seq_printf(sf, "SC_H_DELTA_PH:\t\t0x%X\n", v[6]);
+ seq_printf(sf, "SC_V_INIT_PH:\t\t0x%X\n", v[7]);
+ seq_printf(sf, "SC_V_DELTA_PH:\t\t0x%X\n", v[8]);
+}
+
+static const struct komeda_component_funcs d71_scaler_funcs = {
+ .update = d71_scaler_update,
+ .disable = d71_component_disable,
+ .dump_register = d71_scaler_dump,
+};
+
+static int d71_scaler_init(struct d71_dev *d71,
+ struct block_header *blk, u32 __iomem *reg)
+{
+ struct komeda_component *c;
+ struct komeda_scaler *scaler;
+ u32 pipe_id, comp_id;
+
+ get_resources_id(blk->block_info, &pipe_id, &comp_id);
+
+ c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*scaler),
+ comp_id, BLOCK_INFO_INPUT_ID(blk->block_info),
+ &d71_scaler_funcs,
+ 1, get_valid_inputs(blk), 1, reg,
+ "CU%d_SCALER%d",
+ pipe_id, BLOCK_INFO_BLK_ID(blk->block_info));
+
+ if (IS_ERR(c)) {
+ DRM_ERROR("Failed to initialize scaler");
+ return PTR_ERR(c);
+ }
+
+ scaler = to_scaler(c);
+ set_range(&scaler->hsize, 4, 2048);
+ set_range(&scaler->vsize, 4, 4096);
+ scaler->max_downscaling = 6;
+ scaler->max_upscaling = 64;
+ scaler->scaling_split_overlap = 8;
+ scaler->enh_split_overlap = 1;
+
+ malidp_write32(c->reg, BLK_CONTROL, 0);
+
+ return 0;
+}
+
+static int d71_downscaling_clk_check(struct komeda_pipeline *pipe,
+ struct drm_display_mode *mode,
+ unsigned long aclk_rate,
+ struct komeda_data_flow_cfg *dflow)
+{
+ u32 h_in = dflow->in_w;
+ u32 v_in = dflow->in_h;
+ u32 v_out = dflow->out_h;
+ u64 fraction, denominator;
+
+ /* D71 downscaling must satisfy the following equation
+ *
+ * ACLK h_in * v_in
+ * ------- >= ---------------------------------------------
+ * PXLCLK (h_total - (1 + 2 * v_in / v_out)) * v_out
+ *
+ * In only horizontal downscaling situation, the right side should be
+ * multiplied by (h_total - 3) / (h_active - 3), then equation becomes
+ *
+ * ACLK h_in
+ * ------- >= ----------------
+ * PXLCLK (h_active - 3)
+ *
+ * To avoid precision lost the equation 1 will be convert to:
+ *
+ * ACLK h_in * v_in
+ * ------- >= -----------------------------------
+ * PXLCLK (h_total -1 ) * v_out - 2 * v_in
+ */
+ if (v_in == v_out) {
+ fraction = h_in;
+ denominator = mode->hdisplay - 3;
+ } else {
+ fraction = h_in * v_in;
+ denominator = (mode->htotal - 1) * v_out - 2 * v_in;
+ }
+
+ return aclk_rate * denominator >= mode->clock * 1000 * fraction ?
+ 0 : -EINVAL;
+}
+
+static void d71_splitter_update(struct komeda_component *c,
+ struct komeda_component_state *state)
+{
+ struct komeda_splitter_state *st = to_splitter_st(state);
+ u32 __iomem *reg = c->reg;
+
+ malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(state, 0));
+ malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize));
+ malidp_write32(reg, SP_OVERLAP_SIZE, st->overlap & 0x1FFF);
+ malidp_write32(reg, BLK_CONTROL, BLK_CTRL_EN);
+}
+
+static void d71_splitter_dump(struct komeda_component *c, struct seq_file *sf)
+{
+ u32 v[3];
+
+ dump_block_header(sf, c->reg);
+
+ get_values_from_reg(c->reg, BLK_INPUT_ID0, 1, v);
+ seq_printf(sf, "SP_INPUT_ID0:\t\t0x%X\n", v[0]);
+
+ get_values_from_reg(c->reg, BLK_CONTROL, 3, v);
+ seq_printf(sf, "SP_CONTROL:\t\t0x%X\n", v[0]);
+ seq_printf(sf, "SP_SIZE:\t\t0x%X\n", v[1]);
+ seq_printf(sf, "SP_OVERLAP_SIZE:\t0x%X\n", v[2]);
+}
+
+static const struct komeda_component_funcs d71_splitter_funcs = {
+ .update = d71_splitter_update,
+ .disable = d71_component_disable,
+ .dump_register = d71_splitter_dump,
+};
+
+static int d71_splitter_init(struct d71_dev *d71,
+ struct block_header *blk, u32 __iomem *reg)
+{
+ struct komeda_component *c;
+ struct komeda_splitter *splitter;
+ u32 pipe_id, comp_id;
+
+ get_resources_id(blk->block_info, &pipe_id, &comp_id);
+
+ c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*splitter),
+ comp_id,
+ BLOCK_INFO_INPUT_ID(blk->block_info),
+ &d71_splitter_funcs,
+ 1, get_valid_inputs(blk), 2, reg,
+ "CU%d_SPLITTER", pipe_id);
+
+ if (IS_ERR(c)) {
+ DRM_ERROR("Failed to initialize splitter");
+ return -1;
+ }
+
+ splitter = to_splitter(c);
+
+ set_range(&splitter->hsize, 4, d71->max_line_size);
+ set_range(&splitter->vsize, 4, d71->max_vsize);
+
+ return 0;
+}
+
+static void d71_merger_update(struct komeda_component *c,
+ struct komeda_component_state *state)
+{
+ struct komeda_merger_state *st = to_merger_st(state);
+ u32 __iomem *reg = c->reg;
+ u32 index;
+
+ for_each_changed_input(state, index)
+ malidp_write32(reg, MG_INPUT_ID0 + index * 4,
+ to_d71_input_id(state, index));
+
+ malidp_write32(reg, MG_SIZE, HV_SIZE(st->hsize_merged,
+ st->vsize_merged));
+ malidp_write32(reg, BLK_CONTROL, BLK_CTRL_EN);
+}
+
+static void d71_merger_dump(struct komeda_component *c, struct seq_file *sf)
+{
+ u32 v;
+
+ dump_block_header(sf, c->reg);
+
+ get_values_from_reg(c->reg, MG_INPUT_ID0, 1, &v);
+ seq_printf(sf, "MG_INPUT_ID0:\t\t0x%X\n", v);
+
+ get_values_from_reg(c->reg, MG_INPUT_ID1, 1, &v);
+ seq_printf(sf, "MG_INPUT_ID1:\t\t0x%X\n", v);
+
+ get_values_from_reg(c->reg, BLK_CONTROL, 1, &v);
+ seq_printf(sf, "MG_CONTROL:\t\t0x%X\n", v);
+
+ get_values_from_reg(c->reg, MG_SIZE, 1, &v);
+ seq_printf(sf, "MG_SIZE:\t\t0x%X\n", v);
+}
+
+static const struct komeda_component_funcs d71_merger_funcs = {
+ .update = d71_merger_update,
+ .disable = d71_component_disable,
+ .dump_register = d71_merger_dump,
+};
+
+static int d71_merger_init(struct d71_dev *d71,
+ struct block_header *blk, u32 __iomem *reg)
+{
+ struct komeda_component *c;
+ struct komeda_merger *merger;
+ u32 pipe_id, comp_id;
+
+ get_resources_id(blk->block_info, &pipe_id, &comp_id);
+
+ c = komeda_component_add(&d71->pipes[pipe_id]->base, sizeof(*merger),
+ comp_id,
+ BLOCK_INFO_INPUT_ID(blk->block_info),
+ &d71_merger_funcs,
+ MG_NUM_INPUTS_IDS, get_valid_inputs(blk),
+ MG_NUM_OUTPUTS_IDS, reg,
+ "CU%d_MERGER", pipe_id);
+
+ if (IS_ERR(c)) {
+ DRM_ERROR("Failed to initialize merger.\n");
+ return PTR_ERR(c);
+ }
+
+ merger = to_merger(c);
+
+ set_range(&merger->hsize_merged, 4, 4032);
+ set_range(&merger->vsize_merged, 4, 4096);
+
+ return 0;
+}
+
static void d71_improc_update(struct komeda_component *c,
struct komeda_component_state *state)
{
struct komeda_improc_state *st = to_improc_st(state);
u32 __iomem *reg = c->reg;
- u32 index, input_hw_id;
+ u32 index;
- for_each_changed_input(state, index) {
- input_hw_id = state->active_inputs & BIT(index) ?
- to_d71_input_id(&state->inputs[index]) : 0;
- malidp_write32(reg, BLK_INPUT_ID0 + index * 4, input_hw_id);
- }
+ for_each_changed_input(state, index)
+ malidp_write32(reg, BLK_INPUT_ID0 + index * 4,
+ to_d71_input_id(state, index));
malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize));
}
@@ -644,9 +1159,16 @@ int d71_probe_block(struct d71_dev *d71,
err = d71_compiz_init(d71, blk, reg);
break;
- case D71_BLK_TYPE_CU_SPLITTER:
case D71_BLK_TYPE_CU_SCALER:
+ err = d71_scaler_init(d71, blk, reg);
+ break;
+
+ case D71_BLK_TYPE_CU_SPLITTER:
+ err = d71_splitter_init(d71, blk, reg);
+ break;
+
case D71_BLK_TYPE_CU_MERGER:
+ err = d71_merger_init(d71, blk, reg);
break;
case D71_BLK_TYPE_DOU:
@@ -683,3 +1205,7 @@ int d71_probe_block(struct d71_dev *d71,
return err;
}
+
+const struct komeda_pipeline_funcs d71_pipeline_funcs = {
+ .downscaling_clk_check = d71_downscaling_clk_check,
+};
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
index 3a7248d42376..d567ab7ed314 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
@@ -280,7 +280,7 @@ static int d71_change_opmode(struct komeda_dev *mdev, int new_mode)
ret = dp_wait_cond(((malidp_read32(d71->gcu_addr, BLK_CONTROL) & 0x7) == opmode),
100, 1000, 10000);
- return ret > 0 ? 0 : -ETIMEDOUT;
+ return ret;
}
static void d71_flush(struct komeda_dev *mdev,
@@ -304,7 +304,7 @@ static int d71_reset(struct d71_dev *d71)
ret = dp_wait_cond(!(malidp_read32(gcu, BLK_CONTROL) & GCU_CONTROL_SRST),
100, 1000, 10000);
- return ret > 0 ? 0 : -ETIMEDOUT;
+ return ret;
}
void d71_read_block_header