#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/rockchip-iovmm.h>
#include <asm/div64.h>
#include <linux/uaccess.h>
-#include <linux/rockchip/cpu.h>
#include <linux/rockchip/iomap.h>
#include <linux/rockchip/grf.h>
#include <linux/rockchip/common.h>
#include "rk322x_lcdc.h"
/*#define CONFIG_RK_FPGA 1*/
+#define VOP_CHIP(dev) (dev->data->chip_type)
static int dbg_thresd;
module_param(dbg_thresd, int, S_IRUGO | S_IWUSR);
pr_info(x);\
} while (0)
-static const uint32_t csc_y2r_bt601_limit[12] = {
+static struct rk_lcdc_win rk322x_vop_win[] = {
+ { .name = "win0", .id = VOP_WIN0},
+ { .name = "win1", .id = VOP_WIN1},
+ { .name = "hwc", .id = VOP_HWC}
+};
+
+static struct rk_lcdc_win rk3399_vop_win[] = {
+ { .name = "win0", .id = VOP_WIN0},
+ { .name = "win1", .id = VOP_WIN1},
+ { .name = "win2", .id = VOP_WIN2},
+ { .name = "win3", .id = VOP_WIN3},
+ { .name = "hwc", .id = VOP_HWC}
+};
+
+static const struct vop_data rk322x_data = {
+ .chip_type = VOP_RK322X,
+ .win = rk322x_vop_win,
+ .n_wins = ARRAY_SIZE(rk322x_vop_win),
+};
+
+static const struct vop_data rk3399_data = {
+ .chip_type = VOP_RK3399,
+ .win = rk3399_vop_win,
+ .n_wins = ARRAY_SIZE(rk3399_vop_win),
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id vop_dt_ids[] = {
+ {.compatible = "rockchip,rk322x-lcdc",
+ .data = &rk322x_data, },
+ {.compatible = "rockchip,rk3399-lcdc",
+ .data = &rk3399_data, },
+ {}
+};
+#endif
+
+static const u32 csc_y2r_bt601_limit[12] = {
0x04a8, 0, 0x0662, 0xfffc8654,
0x04a8, 0xfe6f, 0xfcbf, 0x00022056,
0x04a8, 0x0812, 0, 0xfffbaeac,
};
-static const uint32_t csc_y2r_bt709_full[12] = {
+static const u32 csc_y2r_bt709_full[12] = {
0x04a8, 0, 0x072c, 0xfffc219e,
0x04a8, 0xff26, 0xfdde, 0x0001357b,
0x04a8, 0x0873, 0, 0xfffb7dee,
};
-static const uint32_t csc_y2r_bt601_full[12] = {
+static const u32 csc_y2r_bt601_full[12] = {
0x0400, 0, 0x059c, 0xfffd342d,
0x0400, 0xfea0, 0xfd25, 0x00021fcc,
0x0400, 0x0717, 0, 0xfffc76bc,
};
-static const uint32_t csc_y2r_bt601_limit_10[12] = {
+static const u32 csc_y2r_bt601_limit_10[12] = {
0x04a8, 0, 0x0662, 0xfff2134e,
0x04a8, 0xfe6f, 0xfcbf, 0x00087b58,
0x04a8, 0x0812, 0, 0xffeeb4b0,
};
-static const uint32_t csc_y2r_bt709_full_10[12] = {
+static const u32 csc_y2r_bt709_full_10[12] = {
0x04a8, 0, 0x072c, 0xfff08077,
0x04a8, 0xff26, 0xfdde, 0x0004cfed,
0x04a8, 0x0873, 0, 0xffedf1b8,
};
-static const uint32_t csc_y2r_bt601_full_10[12] = {
+static const u32 csc_y2r_bt601_full_10[12] = {
0x0400, 0, 0x059c, 0xfff4cab4,
0x0400, 0xfea0, 0xfd25, 0x00087932,
0x0400, 0x0717, 0, 0xfff1d4f2,
};
-static const uint32_t csc_y2r_bt2020[12] = {
+static const u32 csc_y2r_bt2020[12] = {
0x04a8, 0, 0x06b6, 0xfff16bfc,
0x04a8, 0xff40, 0xfd66, 0x58ae9,
0x04a8, 0x0890, 0, 0xffedb828,
};
-static const uint32_t csc_r2y_bt601_limit[12] = {
+static const u32 csc_r2y_bt601_limit[12] = {
0x0107, 0x0204, 0x0064, 0x04200,
0xff68, 0xfed6, 0x01c2, 0x20200,
0x01c2, 0xfe87, 0xffb7, 0x20200,
};
-static const uint32_t csc_r2y_bt709_full[12] = {
+static const u32 csc_r2y_bt709_full[12] = {
0x00bb, 0x0275, 0x003f, 0x04200,
0xff99, 0xfea5, 0x01c2, 0x20200,
0x01c2, 0xfe68, 0xffd7, 0x20200,
};
-static const uint32_t csc_r2y_bt601_full[12] = {
+static const u32 csc_r2y_bt601_full[12] = {
0x0132, 0x0259, 0x0075, 0x200,
0xff53, 0xfead, 0x0200, 0x20200,
0x0200, 0xfe53, 0xffad, 0x20200,
};
-static const uint32_t csc_r2y_bt601_limit_10[12] = {
+static const u32 csc_r2y_bt601_limit_10[12] = {
0x0107, 0x0204, 0x0064, 0x10200,
0xff68, 0xfed6, 0x01c2, 0x80200,
0x01c2, 0xfe87, 0xffb7, 0x80200,
};
-static const uint32_t csc_r2y_bt709_full_10[12] = {
+static const u32 csc_r2y_bt709_full_10[12] = {
0x00bb, 0x0275, 0x003f, 0x10200,
0xff99, 0xfea5, 0x01c2, 0x80200,
0x01c2, 0xfe68, 0xffd7, 0x80200,
};
-static const uint32_t csc_r2y_bt601_full_10[12] = {
+static const u32 csc_r2y_bt601_full_10[12] = {
0x0132, 0x0259, 0x0075, 0x200,
0xff53, 0xfead, 0x0200, 0x80200,
0x0200, 0xfe53, 0xffad, 0x80200,
};
-static const uint32_t csc_r2y_bt2020[12] = {
+static const u32 csc_r2y_bt2020[12] = {
0x00e6, 0x0253, 0x0034, 0x10200,
0xff83, 0xfebd, 0x01c1, 0x80200,
0x01c1, 0xfe64, 0xffdc, 0x80200,
};
-static const uint32_t csc_r2r_bt2020to709[12] = {
+static const u32 csc_r2r_bt2020to709[12] = {
0x06a4, 0xfda6, 0xffb5, 0x200,
0xff80, 0x0488, 0xfff8, 0x200,
0xffed, 0xff99, 0x047a, 0x200,
};
-static const uint32_t csc_r2r_bt709to2020[12] = {
+static const u32 csc_r2r_bt709to2020[12] = {
0x282, 0x151, 0x02c, 0x200,
0x047, 0x3ae, 0x00c, 0x200,
0x011, 0x05a, 0x395, 0x200,
};
-static struct rk_lcdc_win vop_win[] = {
- { .name = "win0", .id = 0},
- { .name = "win1", .id = 1},
- { .name = "hwc", .id = 2}
-};
+static int vop_get_id(struct vop_device *vop_dev, u32 phy_base)
+{
+ if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+ if (phy_base == 0xff900000) /* vop big */
+ return 0;
+ else if (phy_base == 0xff8f0000) /* vop lit */
+ return 1;
+ else
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
static void vop_load_csc_table(struct vop_device *vop_dev, u32 offset,
- const uint32_t *table)
+ const u32 *table)
{
- uint32_t csc_val;
+ u32 csc_val;
csc_val = table[1] << 16 | table[0];
vop_writel(vop_dev, offset, csc_val);
vop_writel(vop_dev, offset + 0x1c, csc_val);
}
+#define LOAD_CSC(dev, mode, table, win_id) \
+ vop_load_csc_table(dev, \
+ WIN0_YUV2YUV_##mode + 0x60 * win_id, \
+ table)
+
static int vop_set_bcsh(struct rk_lcdc_driver *dev_drv, bool enable);
+static int vop_set_lut(struct rk_lcdc_driver *dev_drv, int *dsp_lut)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ int i, j;
+
+ if (!vop_dev->dsp_lut_addr_base) {
+ dev_warn(vop_dev->dev, "not support dsp lut config\n");
+ return 0;
+ }
+
+ if (!dsp_lut) {
+ dev_err(vop_dev->dev, "dsp lut table is null\n");
+ return -EINVAL;
+ }
+
+ spin_lock(&vop_dev->reg_lock);
+ for (i = 0; i < 256; i++) {
+ u32 v, r, g, b;
+ int __iomem *c;
+
+ v = dsp_lut[i];
+ c = vop_dev->dsp_lut_addr_base + (i << 2);
+ b = (v & 0xff) << 2;
+ g = (v & 0xff00) << 4;
+ r = (v & 0xff0000) << 6;
+ v = r + g + b;
+ for (j = 0; j < 4; j++) {
+ writel_relaxed(v, c);
+ v += (1 + (1 << 10) + (1 << 20));
+ c++;
+ }
+ }
+ vop_msk_reg(vop_dev, DSP_CTRL1, V_DSP_LUT_EN(1));
+ /*
+ * update_gamma value auto clean to 0 by HW, should not
+ * bakeup it.
+ */
+ vop_msk_reg_nobak(vop_dev, DSP_CTRL1, V_UPDATE_GAMMA_LUT(1));
+
+ vop_cfg_done(vop_dev);
+ spin_unlock(&vop_dev->reg_lock);
+
+ return 0;
+}
+
+static int vop_set_cabc(struct rk_lcdc_driver *dev_drv, int *cabc_lut)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ int i;
+
+ if (!vop_dev->cabc_lut_addr_base) {
+ dev_warn(vop_dev->dev, "not support cabc config\n");
+ return 0;
+ }
+
+ if (!cabc_lut) {
+ dev_err(vop_dev->dev, "cabc lut table is null\n");
+ return -EINVAL;
+ }
+ spin_lock(&vop_dev->reg_lock);
+ vop_msk_reg(vop_dev, CABC_CTRL1, V_CABC_LUT_EN(0));
+ vop_cfg_done(vop_dev);
+ spin_unlock(&vop_dev->reg_lock);
+
+ mdelay(25);
+
+ spin_lock(&vop_dev->reg_lock);
+ for (i = 0; i < 128; i++) {
+ u32 v;
+
+ v = cabc_lut[i];
+
+ writel_relaxed(v, vop_dev->cabc_lut_addr_base + i);
+ }
+ vop_msk_reg(vop_dev, CABC_CTRL1, V_CABC_LUT_EN(1));
+ spin_unlock(&vop_dev->reg_lock);
+
+ return 0;
+}
+
static int vop_clk_enable(struct vop_device *vop_dev)
{
if (!vop_dev->clk_on) {
clk_prepare_enable(vop_dev->hclk);
clk_prepare_enable(vop_dev->dclk);
clk_prepare_enable(vop_dev->aclk);
- clk_prepare_enable(vop_dev->hclk_noc);
- clk_prepare_enable(vop_dev->aclk_noc);
+ if (vop_dev->hclk_noc)
+ clk_prepare_enable(vop_dev->hclk_noc);
+ if (vop_dev->aclk_noc)
+ clk_prepare_enable(vop_dev->aclk_noc);
spin_lock(&vop_dev->reg_lock);
vop_dev->clk_on = 1;
spin_unlock(&vop_dev->reg_lock);
clk_disable_unprepare(vop_dev->dclk);
clk_disable_unprepare(vop_dev->hclk);
clk_disable_unprepare(vop_dev->aclk);
- clk_disable_unprepare(vop_dev->hclk_noc);
- clk_disable_unprepare(vop_dev->aclk_noc);
+ if (vop_dev->hclk_noc)
+ clk_disable_unprepare(vop_dev->hclk_noc);
+ if (vop_dev->aclk_noc)
+ clk_disable_unprepare(vop_dev->aclk_noc);
}
return 0;
WIN_EN(0);
WIN_EN(1);
+WIN_EN(2);
+WIN_EN(3);
/*enable/disable win directly*/
static int vop_win_direct_en(struct rk_lcdc_driver *drv,
win0_enable(vop_dev, en);
else if (win_id == 1)
win1_enable(vop_dev, en);
+ else if (win_id == 2)
+ win2_enable(vop_dev, en);
+ else if (win_id == 3)
+ win3_enable(vop_dev, en);
else
dev_err(vop_dev->dev, "invalid win number:%d\n", win_id);
return 0;
container_of(dev_drv, struct vop_device, driver);
if (vop_dev->pre_init)
return 0;
-
- vop_dev->hclk = devm_clk_get(vop_dev->dev, "hclk_vop");
- vop_dev->aclk = devm_clk_get(vop_dev->dev, "aclk_vop");
- vop_dev->dclk = devm_clk_get(vop_dev->dev, "dclk_vop");
+ vop_dev->hclk = devm_clk_get(vop_dev->dev, "hclk_lcdc");
+ vop_dev->aclk = devm_clk_get(vop_dev->dev, "aclk_lcdc");
+ vop_dev->dclk = devm_clk_get(vop_dev->dev, "dclk_lcdc");
+ if (IS_ERR(vop_dev->aclk) || IS_ERR(vop_dev->dclk) ||
+ IS_ERR(vop_dev->hclk))
+ dev_err(vop_dev->dev, "failed to get clk source\n");
vop_dev->hclk_noc = devm_clk_get(vop_dev->dev, "hclk_vop_noc");
+ if (IS_ERR(vop_dev->hclk_noc)) {
+ vop_dev->hclk_noc = NULL;
+ dev_err(vop_dev->dev, "failed to get clk source\n");
+ }
vop_dev->aclk_noc = devm_clk_get(vop_dev->dev, "aclk_vop_noc");
-
- if (IS_ERR(vop_dev->aclk) || IS_ERR(vop_dev->dclk) ||
- IS_ERR(vop_dev->hclk) || IS_ERR(vop_dev->hclk_noc) ||
- IS_ERR(vop_dev->aclk_noc))
+ if (IS_ERR(vop_dev->aclk_noc)) {
+ vop_dev->aclk_noc = NULL;
dev_err(vop_dev->dev, "failed to get clk source\n");
+ }
if (!support_uboot_display())
rk_disp_pwr_enable(dev_drv);
vop_clk_enable(vop_dev);
static void vop_deint(struct vop_device *vop_dev)
{
if (vop_dev->clk_on) {
+ u64 val;
+
vop_disable_irq(vop_dev);
spin_lock(&vop_dev->reg_lock);
vop_msk_reg(vop_dev, WIN0_CTRL0, V_WIN0_EN(0));
vop_msk_reg(vop_dev, WIN1_CTRL0, V_WIN0_EN(0));
+ val = V_WIN2_EN(0) | V_WIN2_MST0_EN(0) | V_WIN2_MST1_EN(0) |
+ V_WIN2_MST2_EN(0) | V_WIN2_MST3_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0, val);
+ vop_msk_reg(vop_dev, WIN3_CTRL0, val);
vop_cfg_done(vop_dev);
spin_unlock(&vop_dev->reg_lock);
mdelay(50);
}
}
-
static void vop_win_csc_mode(struct vop_device *vop_dev,
struct rk_lcdc_win *win,
int csc_mode)
{
u64 val;
- if (win->id == 0) {
+ if (win->id == VOP_WIN0) {
val = V_WIN0_CSC_MODE(csc_mode);
vop_msk_reg(vop_dev, WIN0_CTRL0, val);
- } else if (win->id == 1) {
+ } else if (win->id == VOP_WIN1) {
val = V_WIN1_CSC_MODE(csc_mode);
vop_msk_reg(vop_dev, WIN1_CTRL0, val);
} else {
}
}
+static int rk3399_vop_win_csc_cfg(struct rk_lcdc_driver *dev_drv)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ int output_color = dev_drv->output_color;
+ int i;
+
+ for (i = 0; i < dev_drv->lcdc_win_num && i <= 4; i++) {
+ struct rk_lcdc_win *win = dev_drv->win[i];
+ int shift = i * 8;
+ u64 val = V_WIN0_YUV2YUV_EN(0) | V_WIN0_YUV2YUV_R2Y_EN(0) |
+ V_WIN0_YUV2YUV_Y2R_EN(0);
+
+ if (!win->state)
+ continue;
+ if (output_color == COLOR_RGB && !IS_YUV(win->area[0].fmt_cfg))
+ goto post;
+
+ if (output_color == COLOR_RGB) {
+ val |= V_WIN0_YUV2YUV_Y2R_EN(1);
+ if (win->colorspace == CSC_BT601) {
+ /*
+ * Win Y2Y moudle always use 10bit mode.
+ */
+ LOAD_CSC(vop_dev, Y2R,
+ csc_y2r_bt601_full_10, i);
+ } else if (win->colorspace == CSC_BT709) {
+ LOAD_CSC(vop_dev, Y2R,
+ csc_y2r_bt709_full_10, i);
+ } else if (win->colorspace == CSC_BT2020) {
+ val |= V_WIN0_YUV2YUV_EN(1);
+ LOAD_CSC(vop_dev, Y2R, csc_y2r_bt2020, i);
+ LOAD_CSC(vop_dev, 3x3, csc_r2r_bt2020to709, i);
+ }
+ } else if (output_color == COLOR_YCBCR ||
+ output_color == COLOR_YCBCR_BT709) {
+ if (!IS_YUV(win->area[0].fmt_cfg)) {
+ val |= V_WIN0_YUV2YUV_R2Y_EN(1);
+ LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full, i);
+ } else if (win->colorspace == CSC_BT2020) {
+ val |= V_WIN0_YUV2YUV_EN(1) |
+ V_WIN0_YUV2YUV_Y2R_EN(1) |
+ V_WIN0_YUV2YUV_R2Y_EN(1);
+ LOAD_CSC(vop_dev, R2Y, csc_y2r_bt2020, i);
+ LOAD_CSC(vop_dev, R2Y, csc_r2r_bt2020to709, i);
+ LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full, i);
+ }
+ } else if (output_color == COLOR_YCBCR_BT2020) {
+ if (!IS_YUV(win->area[0].fmt_cfg)) {
+ val |= V_WIN0_YUV2YUV_R2Y_EN(1) |
+ V_WIN0_YUV2YUV_EN(1);
+ LOAD_CSC(vop_dev, R2Y, csc_r2r_bt709to2020, i);
+ LOAD_CSC(vop_dev, R2Y, csc_r2y_bt2020, i);
+ } else if (win->colorspace == CSC_BT601 ||
+ win->colorspace == CSC_BT709) {
+ val |= V_WIN0_YUV2YUV_Y2R_EN(1) |
+ V_WIN0_YUV2YUV_R2Y_EN(1) |
+ V_WIN0_YUV2YUV_EN(1);
+ LOAD_CSC(vop_dev, R2Y, csc_y2r_bt709_full, i);
+ LOAD_CSC(vop_dev, R2Y, csc_r2r_bt709to2020, i);
+ LOAD_CSC(vop_dev, R2Y, csc_r2y_bt2020, i);
+ }
+ }
+post:
+ vop_msk_reg(vop_dev, YUV2YUV_WIN, val << shift);
+ }
+
+ return output_color;
+}
+
/*
* colorspace path:
* Input Win csc Post csc Output
*
* 9. RGB --> bypass ---> 709To2020->R2Y --> YUV_OUTPUT(2020)
*
- * 10. RGB --> R2Y(709) ---> Y2R --> YUV_OUTPUT(709)
+ * 10. RGB --> R2Y(709) ---> bypass --> YUV_OUTPUT(709)
*
- * 11. RGB --> R2Y(601) ---> Y2R --> YUV_OUTPUT(601)
+ * 11. RGB --> R2Y(601) ---> bypass --> YUV_OUTPUT(601)
*
* 12. RGB --> bypass ---> bypass --> RGB_OUTPUT(709)
*/
-
-static void vop_post_csc_cfg(struct rk_lcdc_driver *dev_drv)
+static int rk3228_vop_win_csc_cfg(struct rk_lcdc_driver *dev_drv)
{
struct vop_device *vop_dev =
container_of(dev_drv, struct vop_device, driver);
struct rk_lcdc_win *win;
int output_color = dev_drv->output_color;
- int i, r2y_mode;
- int overlay_mode;
int win_csc = COLOR_RGB;
- u64 val;
-
- if (output_color == COLOR_RGB)
- overlay_mode = VOP_RGB_DOMAIN;
- else
- overlay_mode = VOP_YUV_DOMAIN;
-
- if (output_color == COLOR_YCBCR)
- r2y_mode = VOP_R2Y_CSC_BT601;
- else
- r2y_mode = VOP_R2Y_CSC_BT709;
+ int r2y_mode = VOP_R2Y_CSC_BT709;
+ int i;
for (i = 0; i < dev_drv->lcdc_win_num; i++) {
win = dev_drv->win[i];
if (!win->state)
continue;
- /*
- * force use yuv domain when there is a windows's csc is bt2020.
- */
- if (win->colorspace == CSC_BT2020) {
- overlay_mode = VOP_YUV_DOMAIN;
- r2y_mode = VOP_R2Y_CSC_BT709;
- win_csc = COLOR_YCBCR_BT2020;
- break;
+
+ if (IS_YUV(win->area[0].fmt_cfg)) {
+ if (win->colorspace == CSC_BT2020 &&
+ win_csc < COLOR_YCBCR_BT2020) {
+ r2y_mode = VOP_R2Y_CSC_BT709;
+ win_csc = COLOR_YCBCR_BT2020;
+ }
+
+ if (win->colorspace == CSC_BT709 &&
+ win_csc < COLOR_YCBCR_BT709) {
+ r2y_mode = VOP_R2Y_CSC_BT709;
+ win_csc = COLOR_YCBCR_BT709;
+ }
+
+ if (win->colorspace == CSC_BT601 &&
+ win_csc < COLOR_YCBCR) {
+ r2y_mode = VOP_R2Y_CSC_BT709;
+ win_csc = COLOR_YCBCR;
+ }
}
- if (IS_YUV(win->area[0].fmt_cfg))
+ }
+
+ if (win_csc == COLOR_RGB) {
+ if (output_color == COLOR_YCBCR_BT709) {
+ r2y_mode = VOP_R2Y_CSC_BT709;
+ win_csc = COLOR_YCBCR_BT709;
+ } else if (output_color == COLOR_YCBCR) {
+ r2y_mode = VOP_R2Y_CSC_BT601;
win_csc = COLOR_YCBCR;
+ }
}
for (i = 0; i < dev_drv->lcdc_win_num; i++) {
win = dev_drv->win[i];
if (!win->state)
continue;
- if (overlay_mode == VOP_YUV_DOMAIN &&
- !IS_YUV(win->area[0].fmt_cfg))
+
+ if (win_csc != COLOR_RGB && !IS_YUV(win->area[0].fmt_cfg))
vop_win_csc_mode(vop_dev, win, r2y_mode);
- if (overlay_mode == VOP_RGB_DOMAIN &&
- IS_YUV(win->area[0].fmt_cfg)) {
- if (win->colorspace == CSC_BT709)
- vop_win_csc_mode(vop_dev, win, VOP_Y2R_CSC_HD);
- else if (win->colorspace == CSC_BT601)
+
+ if (IS_YUV(win->area[0].fmt_cfg)) {
+ if (win_csc == COLOR_YCBCR)
vop_win_csc_mode(vop_dev, win,
VOP_Y2R_CSC_MPEG);
- else
- pr_err("Error Y2R path, colorspace=%d\n",
- win->colorspace);
+ else if (win_csc == COLOR_YCBCR_BT709)
+ vop_win_csc_mode(vop_dev, win, VOP_Y2R_CSC_HD);
}
}
- if (win_csc == COLOR_RGB && overlay_mode == VOP_YUV_DOMAIN)
- win_csc = COLOR_YCBCR;
- else if (IS_YUV_COLOR(win_csc) && overlay_mode == VOP_RGB_DOMAIN)
- win_csc = COLOR_RGB;
+ return win_csc;
+}
+
+static int vop_post_csc_cfg(struct rk_lcdc_driver *dev_drv)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ int output_color = dev_drv->output_color;
+ int win_csc, overlay_mode;
+ u64 val;
+
+ if (VOP_CHIP(vop_dev) == VOP_RK322X) {
+ win_csc = rk3228_vop_win_csc_cfg(dev_drv);
+ } else if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+ win_csc = rk3399_vop_win_csc_cfg(dev_drv);
+
+ /*
+ * RK3399 not support post csc config.
+ */
+ goto done;
+ }
val = V_YUV2YUV_POST_Y2R_EN(0) | V_YUV2YUV_POST_EN(0) |
V_YUV2YUV_POST_R2Y_EN(0);
/* Y2R */
if (win_csc == COLOR_YCBCR && output_color == COLOR_YCBCR_BT2020) {
- win_csc = COLOR_RGB;
val |= V_YUV2YUV_POST_Y2R_EN(1);
vop_load_csc_table(vop_dev, POST_YUV2YUV_Y2R_COE,
csc_y2r_bt709_full);
}
if (win_csc == COLOR_YCBCR_BT2020 &&
output_color != COLOR_YCBCR_BT2020) {
- win_csc = COLOR_RGB_BT2020;
val |= V_YUV2YUV_POST_Y2R_EN(1);
vop_load_csc_table(vop_dev, POST_YUV2YUV_Y2R_COE,
csc_y2r_bt2020);
}
/* R2R */
- if (win_csc == COLOR_RGB && output_color == COLOR_YCBCR_BT2020) {
- win_csc = COLOR_RGB_BT2020;
+ if ((win_csc == COLOR_YCBCR ||
+ win_csc == COLOR_YCBCR_BT709 ||
+ win_csc == COLOR_RGB) && output_color == COLOR_YCBCR_BT2020) {
val |= V_YUV2YUV_POST_EN(1);
vop_load_csc_table(vop_dev, POST_YUV2YUV_3x3_COE,
csc_r2r_bt709to2020);
}
- if (win_csc == COLOR_RGB_BT2020 &&
+ if (win_csc == COLOR_YCBCR_BT2020 &&
(output_color == COLOR_YCBCR ||
output_color == COLOR_YCBCR_BT709 ||
output_color == COLOR_RGB)) {
- win_csc = COLOR_RGB;
val |= V_YUV2YUV_POST_EN(1);
vop_load_csc_table(vop_dev, POST_YUV2YUV_3x3_COE,
csc_r2r_bt2020to709);
}
- /* R2Y */
- if (!IS_YUV_COLOR(win_csc) && IS_YUV_COLOR(output_color)) {
+ /* Y2R */
+ if (output_color != COLOR_RGB) {
val |= V_YUV2YUV_POST_R2Y_EN(1);
if (output_color == COLOR_YCBCR_BT2020)
csc_r2y_bt709_full);
}
- DBG(1, "win_csc=%d output_color=%d val=%llx overlay_mode=%d\n",
- win_csc, output_color, val, overlay_mode);
- vop_msk_reg(vop_dev, SYS_CTRL, V_OVERLAY_MODE(overlay_mode));
+ DBG(1, "win_csc=%d output_color=%d val=%llx\n",
+ win_csc, output_color, val);
vop_msk_reg(vop_dev, YUV2YUV_POST, val);
+done:
+ overlay_mode = (win_csc != COLOR_RGB) ? VOP_YUV_DOMAIN : VOP_RGB_DOMAIN;
+ vop_msk_reg(vop_dev, SYS_CTRL, V_OVERLAY_MODE(overlay_mode));
+
+ return 0;
}
static int vop_post_cfg(struct rk_lcdc_driver *dev_drv)
screen->post_dsp_sty / 2 +
1;
post_dsp_vact_end_f1 = post_dsp_vact_st_f1 +
- screen->post_ysize/2;
+ screen->post_ysize / 2;
} else {
if (screen->y_mirror == 0) {
post_dsp_vact_st = screen->post_dsp_sty +
case 1:
vop_writel(vop_dev, WIN1_COLOR_KEY, key_val);
break;
+ case 2:
+ vop_writel(vop_dev, WIN2_COLOR_KEY, key_val);
+ break;
+ case 3:
+ vop_writel(vop_dev, WIN3_COLOR_KEY, key_val);
+ break;
default:
pr_info("%s:un support win num:%d\n",
__func__, i);
dst_alpha_ctl = 0xa4;
break;
case 2:
+ src_alpha_ctl = 0xdc;
+ dst_alpha_ctl = 0xec;
+ break;
+ case 3:
+ src_alpha_ctl = 0x12c;
+ dst_alpha_ctl = 0x13c;
+ break;
+ case 4:
src_alpha_ctl = 0x160;
dst_alpha_ctl = 0x164;
break;
return -EINVAL;
}
- if ((win->id == 0) || (win->id == 1)) {
+ if ((win->id == VOP_WIN0) || (win->id == VOP_WIN1)) {
val = V_WIN0_YRGB_AXI_GATHER_EN(1) |
V_WIN0_CBR_AXI_GATHER_EN(1) |
V_WIN0_YRGB_AXI_GATHER_NUM(yrgb_gather_num) |
V_WIN0_CBR_AXI_GATHER_NUM(cbcr_gather_num);
vop_msk_reg(vop_dev, WIN0_CTRL1 + (win->id * 0x40), val);
- } else if (win->id == 2) {
+ } else if ((win->id == VOP_WIN2) || (win->id == VOP_WIN3)) {
+ val = V_WIN2_AXI_GATHER_EN(1) |
+ V_WIN2_AXI_GATHER_NUM(yrgb_gather_num);
+ vop_msk_reg(vop_dev, WIN2_CTRL1 + ((win->id - 2) * 0x50), val);
+ } else if (win->id == VOP_HWC) {
val = V_HWC_AXI_GATHER_EN(1) |
V_HWC_AXI_GATHER_NUM(yrgb_gather_num);
vop_msk_reg(vop_dev, HWC_CTRL1, val);
container_of(dev_drv, struct vop_device, driver);
struct rk_lcdc_win *win = dev_drv->win[win_id];
u64 val;
- uint32_t off;
+ u32 off;
int format;
off = win_id * 0x40;
return 0;
}
+static int area_xst(struct rk_lcdc_win *win, int area_num)
+{
+ struct rk_lcdc_win_area area_temp;
+ int i, j;
+
+ for (i = 0; i < area_num; i++) {
+ for (j = i + 1; j < area_num; j++) {
+ if (win->area[i].dsp_stx > win->area[j].dsp_stx) {
+ memcpy(&area_temp, &win->area[i],
+ sizeof(struct rk_lcdc_win_area));
+ memcpy(&win->area[i], &win->area[j],
+ sizeof(struct rk_lcdc_win_area));
+ memcpy(&win->area[j], &area_temp,
+ sizeof(struct rk_lcdc_win_area));
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int vop_win_2_3_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ struct rk_lcdc_win *win = dev_drv->win[win_id];
+ unsigned int off;
+ u64 val;
+
+ off = (win_id - 2) * 0x50;
+ area_xst(win, win->area_num);
+
+ if (win->state == 1) {
+ vop_axi_gather_cfg(vop_dev, win);
+ val = V_WIN2_EN(1) | V_WIN1_CSC_MODE(win->csc_mode);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+ /* area 0 */
+ if (win->area[0].state == 1) {
+ val = V_WIN2_MST0_EN(win->area[0].state) |
+ V_WIN2_DATA_FMT0(win->area[0].fmt_cfg) |
+ V_WIN2_RB_SWAP0(win->area[0].swap_rb);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+
+ val = V_WIN2_VIR_STRIDE0(win->area[0].y_vir_stride);
+ vop_msk_reg(vop_dev, WIN2_VIR0_1 + off, val);
+
+ val = V_WIN2_DSP_WIDTH0(win->area[0].xsize) |
+ V_WIN2_DSP_HEIGHT0(win->area[0].ysize);
+ vop_writel(vop_dev, WIN2_DSP_INFO0 + off, val);
+ val = V_WIN2_DSP_XST0(win->area[0].dsp_stx) |
+ V_WIN2_DSP_YST0(win->area[0].dsp_sty);
+ vop_writel(vop_dev, WIN2_DSP_ST0 + off, val);
+ } else {
+ val = V_WIN2_MST0_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+ }
+ /* area 1 */
+ if (win->area[1].state == 1) {
+ val = V_WIN2_MST1_EN(win->area[1].state) |
+ V_WIN2_DATA_FMT1(win->area[1].fmt_cfg) |
+ V_WIN2_RB_SWAP1(win->area[1].swap_rb);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+
+ val = V_WIN2_VIR_STRIDE1(win->area[1].y_vir_stride);
+ vop_msk_reg(vop_dev, WIN2_VIR0_1 + off, val);
+
+ val = V_WIN2_DSP_WIDTH1(win->area[1].xsize) |
+ V_WIN2_DSP_HEIGHT1(win->area[1].ysize);
+ vop_writel(vop_dev, WIN2_DSP_INFO1 + off, val);
+ val = V_WIN2_DSP_XST1(win->area[1].dsp_stx) |
+ V_WIN2_DSP_YST1(win->area[1].dsp_sty);
+ vop_writel(vop_dev, WIN2_DSP_ST1 + off, val);
+ } else {
+ val = V_WIN2_MST1_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+ }
+ /* area 2 */
+ if (win->area[2].state == 1) {
+ val = V_WIN2_MST2_EN(win->area[2].state) |
+ V_WIN2_DATA_FMT2(win->area[2].fmt_cfg) |
+ V_WIN2_RB_SWAP2(win->area[2].swap_rb);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+
+ val = V_WIN2_VIR_STRIDE2(win->area[2].y_vir_stride);
+ vop_msk_reg(vop_dev, WIN2_VIR2_3 + off, val);
+
+ val = V_WIN2_DSP_WIDTH2(win->area[2].xsize) |
+ V_WIN2_DSP_HEIGHT2(win->area[2].ysize);
+ vop_writel(vop_dev, WIN2_DSP_INFO2 + off, val);
+ val = V_WIN2_DSP_XST2(win->area[2].dsp_stx) |
+ V_WIN2_DSP_YST2(win->area[2].dsp_sty);
+ vop_writel(vop_dev, WIN2_DSP_ST2 + off, val);
+ } else {
+ val = V_WIN2_MST2_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+ }
+ /* area 3 */
+ if (win->area[3].state == 1) {
+ val = V_WIN2_MST3_EN(win->area[3].state) |
+ V_WIN2_DATA_FMT3(win->area[3].fmt_cfg) |
+ V_WIN2_RB_SWAP3(win->area[3].swap_rb);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+
+ val = V_WIN2_VIR_STRIDE3(win->area[3].y_vir_stride);
+ vop_msk_reg(vop_dev, WIN2_VIR2_3 + off, val);
+
+ val = V_WIN2_DSP_WIDTH3(win->area[3].xsize) |
+ V_WIN2_DSP_HEIGHT3(win->area[3].ysize);
+ vop_writel(vop_dev, WIN2_DSP_INFO3 + off, val);
+ val = V_WIN2_DSP_XST3(win->area[3].dsp_stx) |
+ V_WIN2_DSP_YST3(win->area[3].dsp_sty);
+ vop_writel(vop_dev, WIN2_DSP_ST3 + off, val);
+ } else {
+ val = V_WIN2_MST3_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+ }
+
+ if (win->alpha_en == 1) {
+ vop_alpha_cfg(dev_drv, win_id);
+ } else {
+ val = V_WIN2_SRC_ALPHA_EN(0);
+ vop_msk_reg(vop_dev, WIN2_SRC_ALPHA_CTRL + off, val);
+ }
+ } else {
+ val = V_WIN2_EN(win->state) | V_WIN2_MST0_EN(0) |
+ V_WIN2_MST1_EN(0) | V_WIN2_MST2_EN(0) | V_WIN2_MST3_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0 + off, val);
+ }
+
+ return 0;
+}
+
static int vop_hwc_reg_update(struct rk_lcdc_driver *dev_drv, int win_id)
{
struct vop_device *vop_dev =
if (likely(vop_dev->clk_on)) {
vop_msk_reg(vop_dev, SYS_CTRL,
V_VOP_STANDBY_EN(vop_dev->standby));
- if ((win->id == 0) || (win->id == 1))
+ if ((win->id == VOP_WIN0) || (win->id == VOP_WIN1))
vop_win_0_1_reg_update(dev_drv, win->id);
- else if (win->id == 2)
+ else if ((win->id == VOP_WIN2) || (win->id == VOP_WIN3))
+ vop_win_2_3_reg_update(dev_drv, win->id);
+ else if (win->id == VOP_HWC)
vop_hwc_reg_update(dev_drv, win->id);
vop_cfg_done(vop_dev);
}
val = vop_readl(vop_dev, WIN0_ACT_INFO);
*xact = (val & MASK(WIN0_ACT_WIDTH)) + 1;
- *yact = ((val & MASK(WIN0_ACT_HEIGHT))>>16) + 1;
+ *yact = ((val & MASK(WIN0_ACT_HEIGHT)) >> 16) + 1;
val = vop_readl(vop_dev, WIN0_CTRL0);
*format = (val & MASK(WIN0_DATA_FMT)) >> 1;
V_WIN0_Y_MIR_EN(ymirror);
vop_msk_reg(vop_dev, WIN0_CTRL0, val);
- vop_msk_reg(vop_dev, WIN0_VIR, V_WIN0_VIR_STRIDE(xvir));
+ vop_msk_reg(vop_dev, WIN0_VIR, V_WIN0_VIR_STRIDE(xvir));
vop_writel(vop_dev, WIN0_ACT_INFO, V_WIN0_ACT_WIDTH(xact - 1) |
V_WIN0_ACT_HEIGHT(yact - 1));
return 0;
}
-/*
-static int lcdc_reset(struct rk_lcdc_driver *dev_drv, bool initscreen)
-{
- struct vop_device *vop_dev =
- container_of(dev_drv, struct vop_device, driver);
- u64 val;
- u32 __maybe_unused v;
-
- if (!vop_dev->standby && initscreen && (dev_drv->first_frame != 1)) {
- mdelay(150);
- val = V_WIN0_EN(0);
- vop_msk_reg(vop_dev, WIN0_CTRL0, val);
- vop_msk_reg(vop_dev, WIN1_CTRL0, val);
-
- val = V_WIN2_EN(0) | V_WIN2_MST0_EN(0) |
- V_WIN2_MST1_EN(0) |
- V_WIN2_MST2_EN(0) | V_WIN2_MST3_EN(0);
- vop_msk_reg(vop_dev, WIN2_CTRL0, val);
- vop_msk_reg(vop_dev, WIN3_CTRL0, val);
- val = V_HDMI_OUT_EN(0);
- vop_msk_reg(vop_dev, SYS_CTRL, val);
- vop_cfg_done(vop_dev);
- mdelay(50);
- vop_msk_reg(vop_dev, SYS_CTRL, V_VOP_STANDBY_EN(1));
- writel_relaxed(0, vop_dev->regs + REG_CFG_DONE);
- mdelay(50);
- }
-
- return 0;
-}
-*/
-
static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
{
u16 face = 0;
spin_lock(&vop_dev->reg_lock);
if (likely(vop_dev->clk_on)) {
switch (screen->face) {
+ case OUT_P565:
+ face = OUT_P565;
+ val = V_DITHER_DOWN_EN(1) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(1) |
+ V_DITHER_DOWN_SEL(1) | V_DITHER_DOWN_MODE(0);
+ break;
+ case OUT_P666:
+ face = OUT_P666;
+ val = V_DITHER_DOWN_EN(1) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(1) |
+ V_DITHER_DOWN_SEL(1) | V_DITHER_DOWN_MODE(1);
+ break;
+ case OUT_D888_P565:
+ face = OUT_P888;
+ val = V_DITHER_DOWN_EN(1) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(1) |
+ V_DITHER_DOWN_SEL(1) | V_DITHER_DOWN_MODE(0);
+ break;
+ case OUT_D888_P666:
+ face = OUT_P888;
+ val = V_DITHER_DOWN_EN(1) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(1) |
+ V_DITHER_DOWN_SEL(1) | V_DITHER_DOWN_MODE(1);
+ break;
case OUT_P888:
- if (rockchip_get_cpu_version())
- face = OUT_P101010;
- else
- face = OUT_P888;
-
- val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(0)
- | V_PRE_DITHER_DOWN_EN(1);
+ face = OUT_P888;
+ val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1)
+ | V_PRE_DITHER_DOWN_EN(1) |
+ V_DITHER_DOWN_SEL(0) | V_DITHER_DOWN_MODE(0);
break;
case OUT_YUV_420:
- if (rockchip_get_cpu_version()) {
- face = OUT_YUV_420;
- dclk_ddr = 1;
- val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(0)
- | V_PRE_DITHER_DOWN_EN(1);
- break;
- }
- dev_err(vop_dev->dev,
- "This chip can't supported screen face[%d]\n",
- screen->face);
+ face = OUT_YUV_420;
+ dclk_ddr = 1;
+ val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(1) |
+ V_DITHER_DOWN_SEL(0) |
+ V_DITHER_DOWN_MODE(0);
break;
case OUT_YUV_420_10BIT:
- if (rockchip_get_cpu_version()) {
- face = OUT_YUV_420;
- dclk_ddr = 1;
- val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1)
- | V_PRE_DITHER_DOWN_EN(0);
- break;
- }
- dev_err(vop_dev->dev,
- "This chip can't supported screen face[%d]\n",
- screen->face);
+ face = OUT_YUV_420;
+ dclk_ddr = 1;
+ val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(0) |
+ V_DITHER_DOWN_SEL(0) |
+ V_DITHER_DOWN_MODE(0);
break;
case OUT_P101010:
- if (rockchip_get_cpu_version()) {
- face = OUT_P101010;
- val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1)
- | V_PRE_DITHER_DOWN_EN(0);
- break;
- }
- dev_err(vop_dev->dev,
- "This chip can't supported screen face[%d]\n",
- screen->face);
+ face = OUT_P101010;
+ val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(1) |
+ V_PRE_DITHER_DOWN_EN(0) |
+ V_DITHER_DOWN_SEL(0) |
+ V_DITHER_DOWN_MODE(0);
break;
default:
dev_err(vop_dev->dev, "un supported screen face[%d]!\n",
val = V_HDMI_OUT_EN(1) | V_SW_UV_OFFSET_EN(0);
vop_msk_reg(vop_dev, SYS_CTRL, val);
break;
+ case SCREEN_RGB:
+ case SCREEN_LVDS:
+ val = V_RGB_OUT_EN(1);
+ vop_msk_reg(vop_dev, SYS_CTRL, val);
+ case SCREEN_MIPI:
+ val = V_MIPI_OUT_EN(1);
+ vop_msk_reg(vop_dev, SYS_CTRL, val);
+ case SCREEN_EDP:
+ val = V_EDP_OUT_EN(1);
+ vop_msk_reg(vop_dev, SYS_CTRL, val);
+ break;
default:
dev_err(vop_dev->dev, "un supported interface[%d]!\n",
screen->type);
} else {
val = V_DSP_OUT_RGB_YUV(0);
vop_msk_reg(vop_dev, POST_SCL_CTRL, val);
- val = V_DSP_BG_BLUE(0) | V_DSP_BG_GREEN(0) |
- V_DSP_BG_RED(0);
+ val = V_DSP_BG_BLUE(0x55) | V_DSP_BG_GREEN(0x55) |
+ V_DSP_BG_RED(0x55);
vop_msk_reg(vop_dev, DSP_BG, val);
}
dev_drv->output_color = screen->color_mode;
vop_load_screen(dev_drv, 1);
if (dev_drv->bcsh.enable)
vop_set_bcsh(dev_drv, 1);
- spin_lock(&vop_dev->reg_lock);
- spin_unlock(&vop_dev->reg_lock);
+ vop_set_lut(dev_drv, dev_drv->cur_screen->dsp_lut);
+ vop_set_cabc(dev_drv, dev_drv->cur_screen->cabc_lut);
}
- if (win_id < ARRAY_SIZE(vop_win))
+ if (win_id < dev_drv->lcdc_win_num)
vop_layer_enable(vop_dev, win_id, open);
else
dev_err(vop_dev->dev, "invalid win id:%d\n", win_id);
return 0;
}
+static int win_2_3_display(struct vop_device *vop_dev,
+ struct rk_lcdc_win *win)
+{
+ u32 i, y_addr;
+ unsigned int off;
+
+ off = (win->id - 2) * 0x50;
+ y_addr = win->area[0].smem_start + win->area[0].y_offset;
+ DBG(2, "lcdc[%d]:win[%d]:", vop_dev->id, win->id);
+
+ if (likely(vop_dev->clk_on)) {
+ for (i = 0; i < win->area_num; i++) {
+ DBG(2, "area[%d]:yaddr:0x%x>>offset:0x%x>>\n",
+ i, win->area[i].y_addr, win->area[i].y_offset);
+ win->area[i].y_addr =
+ win->area[i].smem_start + win->area[i].y_offset;
+ }
+ spin_lock(&vop_dev->reg_lock);
+ vop_writel(vop_dev, WIN2_MST0 + off, win->area[0].y_addr);
+ vop_writel(vop_dev, WIN2_MST1 + off, win->area[1].y_addr);
+ vop_writel(vop_dev, WIN2_MST2 + off, win->area[2].y_addr);
+ vop_writel(vop_dev, WIN2_MST3 + off, win->area[3].y_addr);
+ spin_unlock(&vop_dev->reg_lock);
+ }
+ return 0;
+}
+
static int hwc_display(struct vop_device *vop_dev, struct rk_lcdc_win *win)
{
u32 y_addr;
} else if (win_id == 1) {
win_0_1_display(vop_dev, win);
} else if (win_id == 2) {
+ win_2_3_display(vop_dev, win);
+ } else if (win_id == 3) {
+ win_2_3_display(vop_dev, win);
+ } else if (win_id == 4) {
hwc_display(vop_dev, win);
} else {
dev_err(dev_drv->dev, "invalid win number:%d!\n", win_id);
if ((win->yrgb_ver_scl_mode == SCALE_DOWN) &&
(win->area[0].fbdc_en == 1)) {
/* in this pattern,use bil mode,not support souble scd,
- use avg mode, support double scd, but aclk should be
- bigger than dclk,aclk>>dclk */
+ * use avg mode, support double scd, but aclk should be
+ * bigger than dclk.
+ */
if (yrgb_srcH >= 2 * yrgb_dstH) {
pr_err("ERROR : fbdc mode,not support y scale down:");
pr_err("srcH[%d] > 2 *dstH[%d]\n",
return 0;
}
+static int win_2_3_set_par(struct vop_device *vop_dev,
+ struct rk_screen *screen, struct rk_lcdc_win *win)
+{
+ int i;
+ u8 fmt_cfg, swap_rb;
+ char fmt[9] = "NULL";
+
+ if (VOP_CHIP(vop_dev) == VOP_RK322X) {
+ pr_err("rk3228 not support win2/3 set par\n");
+ return -EINVAL;
+ }
+ if (win->ymirror) {
+ pr_err("win[%d] not support y mirror\n", win->id);
+ return -EINVAL;
+ }
+ spin_lock(&vop_dev->reg_lock);
+ if (likely(vop_dev->clk_on)) {
+ DBG(2, "lcdc[%d]:win[%d]>>\n>\n", vop_dev->id, win->id);
+ for (i = 0; i < win->area_num; i++) {
+ switch (win->area[i].format) {
+ case FBDC_RGB_565:
+ fmt_cfg = 2;
+ swap_rb = 0;
+ win->fmt_10 = 0;
+ win->area[0].fbdc_fmt_cfg = 0x05;
+ break;
+ case FBDC_ARGB_888:
+ fmt_cfg = 0;
+ swap_rb = 0;
+ win->fmt_10 = 0;
+ win->area[0].fbdc_fmt_cfg = 0x0c;
+ break;
+ case FBDC_RGBX_888:
+ fmt_cfg = 0;
+ swap_rb = 0;
+ win->fmt_10 = 0;
+ win->area[0].fbdc_fmt_cfg = 0x3a;
+ break;
+ case ARGB888:
+ fmt_cfg = 0;
+ swap_rb = 0;
+ break;
+ case XBGR888:
+ case ABGR888:
+ fmt_cfg = 0;
+ swap_rb = 1;
+ break;
+ case RGB888:
+ fmt_cfg = 1;
+ swap_rb = 0;
+ break;
+ case RGB565:
+ fmt_cfg = 2;
+ swap_rb = 0;
+ break;
+ default:
+ dev_err(vop_dev->driver.dev,
+ "%s:un supported format!\n", __func__);
+ spin_unlock(&vop_dev->reg_lock);
+ return -EINVAL;
+ }
+ win->area[i].fmt_cfg = fmt_cfg;
+ win->area[i].swap_rb = swap_rb;
+ win->area[i].dsp_stx = dsp_x_pos(win->xmirror, screen,
+ &win->area[i]);
+ win->area[i].dsp_sty = dsp_y_pos(win->ymirror, screen,
+ &win->area[i]);
+ if (((win->area[i].xact != win->area[i].xsize) ||
+ (win->area[i].yact != win->area[i].ysize)) &&
+ (screen->mode.vmode == FB_VMODE_NONINTERLACED)) {
+ pr_err("win[%d]->area[%d],not support scale\n",
+ win->id, i);
+ pr_err("xact=%d,yact=%d,xsize=%d,ysize=%d\n",
+ win->area[i].xact, win->area[i].yact,
+ win->area[i].xsize, win->area[i].ysize);
+ win->area[i].xsize = win->area[i].xact;
+ win->area[i].ysize = win->area[i].yact;
+ }
+ DBG(2, "fmt:%s:xsize:%d>>ysize:%d>>xpos:%d>>ypos:%d\n",
+ get_format_string(win->area[i].format, fmt),
+ win->area[i].xsize, win->area[i].ysize,
+ win->area[i].xpos, win->area[i].ypos);
+ }
+ }
+ vop_win_2_3_reg_update(&vop_dev->driver, win->id);
+ spin_unlock(&vop_dev->reg_lock);
+ return 0;
+}
+
static int hwc_set_par(struct vop_device *vop_dev,
struct rk_screen *screen, struct rk_lcdc_win *win)
{
xvir = win->area[0].xvir;
yvir = win->area[0].yvir;
}
- vop_hwc_reg_update(&vop_dev->driver, 2);
+ vop_hwc_reg_update(&vop_dev->driver, 4);
spin_unlock(&vop_dev->reg_lock);
DBG(1, "lcdc[%d]:hwc>>%s\n>>format:%s>>>xact:%d>>yact:%d>>xsize:%d",
return 0;
}
win = dev_drv->win[win_id];
+ if (win)
switch (win_id) {
case 0:
win_0_1_set_par(vop_dev, screen, win);
win_0_1_set_par(vop_dev, screen, win);
break;
case 2:
+ win_2_3_set_par(vop_dev, screen, win);
+ break;
+ case 3:
+ win_2_3_set_par(vop_dev, screen, win);
+ break;
+ case 4:
hwc_set_par(vop_dev, screen, win);
break;
default:
return 0;
}
+static int vop_set_writeback(struct rk_lcdc_driver *dev_drv)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ int output_color = dev_drv->output_color;
+ struct rk_screen *screen = dev_drv->cur_screen;
+ struct rk_fb_reg_wb_data *wb_data;
+ int xact = screen->mode.xres;
+ int yact = screen->mode.yres;
+ u32 fmt_cfg;
+ int xsize, ysize;
+ u64 v;
+
+ if (unlikely(!vop_dev->clk_on)) {
+ pr_info("%s,clk_on = %d\n", __func__, vop_dev->clk_on);
+ return 0;
+ }
+ wb_data = &dev_drv->wb_data;
+
+ xsize = wb_data->xsize;
+ ysize = wb_data->ysize;
+
+ /*
+ * RGB overlay mode support ARGB888, RGB888, RGB565, NV12,
+ * but YUV overlay mode only support NV12, it's hard to judge RGB
+ * or YUV overlay mode by userspace, so here force only support
+ * NV12 mode.
+ */
+ if (wb_data->data_format != YUV420 && output_color != COLOR_RGB) {
+ pr_err("writeback only support NV12 when overlay is not RGB\n");
+ return -EINVAL;
+ }
+
+ if (ysize != yact && ysize != (yact / 2)) {
+ pr_err("WriteBack only support yact=%d, ysize=%d\n",
+ yact, ysize);
+ return -EINVAL;
+ }
+
+ switch (wb_data->data_format) {
+ case ARGB888:
+ fmt_cfg = 0;
+ break;
+ case RGB888:
+ fmt_cfg = 1;
+ break;
+ case RGB565:
+ fmt_cfg = 2;
+ break;
+ case YUV420:
+ fmt_cfg = 8;
+ break;
+ default:
+ pr_info("unsupport fmt: %d\n", wb_data->data_format);
+ return -EINVAL;
+ }
+
+ v = V_WB_EN(wb_data->state) | V_WB_FMT(fmt_cfg) | V_WB_RGB2YUV_MODE(1) |
+ V_WB_XPSD_BIL_EN(xact != xsize) |
+ V_WB_YTHROW_EN(ysize == (yact / 2)) |
+ V_WB_YTHROW_MODE(0);
+
+ v |= V_WB_RGB2YUV_EN((output_color == COLOR_RGB) &&
+ (wb_data->data_format == YUV420));
+
+ vop_msk_reg(vop_dev, WB_CTRL0, v);
+
+ v = V_WB_WIDTH(xsize) | V_WB_XPSD_BIL_FACTOR((xact << 12) / xsize);
+
+ vop_msk_reg(vop_dev, WB_CTRL1, v);
+
+ vop_writel(vop_dev, WB_YRGB_MST, wb_data->smem_start);
+ if (wb_data->data_format == YUV420)
+ vop_writel(vop_dev, WB_CBR_MST, wb_data->smem_start);
+
+ return 0;
+}
+
static int vop_ioctl(struct rk_lcdc_driver *dev_drv, unsigned int cmd,
unsigned long arg, int win_id)
{
return -EFAULT;
break;
case RK_FBIOPUT_COLOR_KEY_CFG:
- if (copy_from_user(&clr_key_cfg, argp,
- sizeof(struct color_key_cfg)))
+ if (copy_from_user(&clr_key_cfg, argp, sizeof(clr_key_cfg)))
return -EFAULT;
vop_clr_key_cfg(dev_drv);
vop_writel(vop_dev, WIN0_COLOR_KEY,
return 0;
dev_drv->suspend_flag = 1;
+ /* ensure suspend_flag take effect on multi process */
smp_wmb();
flush_kthread_worker(&dev_drv->update_regs_worker);
rk_disp_pwr_enable(dev_drv);
vop_clk_enable(vop_dev);
+ spin_lock(&vop_dev->reg_lock);
memcpy(vop_dev->regs, vop_dev->regsbak, vop_dev->len);
+ spin_unlock(&vop_dev->reg_lock);
+ vop_set_lut(dev_drv, dev_drv->cur_screen->dsp_lut);
+ vop_set_cabc(dev_drv, dev_drv->cur_screen->cabc_lut);
spin_lock(&vop_dev->reg_lock);
vop_msk_reg(vop_dev, DSP_CTRL0, V_DSP_OUT_ZERO(0));
area_status = vop_read_bit(vop_dev, WIN1_CTRL0, V_WIN1_EN(0));
break;
case 2:
+ if (area_id == 0)
+ area_status = vop_read_bit(vop_dev, WIN2_CTRL0,
+ V_WIN2_MST0_EN(0));
+ if (area_id == 1)
+ area_status = vop_read_bit(vop_dev, WIN2_CTRL0,
+ V_WIN2_MST1_EN(0));
+ if (area_id == 2)
+ area_status = vop_read_bit(vop_dev, WIN2_CTRL0,
+ V_WIN2_MST2_EN(0));
+ if (area_id == 3)
+ area_status = vop_read_bit(vop_dev, WIN2_CTRL0,
+ V_WIN2_MST3_EN(0));
+ break;
+ case 3:
+ if (area_id == 0)
+ area_status = vop_read_bit(vop_dev, WIN3_CTRL0,
+ V_WIN3_MST0_EN(0));
+ if (area_id == 1)
+ area_status = vop_read_bit(vop_dev, WIN3_CTRL0,
+ V_WIN3_MST1_EN(0));
+ if (area_id == 2)
+ area_status = vop_read_bit(vop_dev, WIN3_CTRL0,
+ V_WIN3_MST2_EN(0));
+ if (area_id == 3)
+ area_status = vop_read_bit(vop_dev, WIN3_CTRL0,
+ V_WIN3_MST3_EN(0));
+ break;
+ case 4:
area_status = vop_read_bit(vop_dev, HWC_CTRL0, V_HWC_EN(0));
break;
default:
static int vop_get_area_num(struct rk_lcdc_driver *dev_drv,
unsigned int *area_support)
{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+
area_support[0] = 1;
area_support[1] = 1;
+ if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+ area_support[2] = 4;
+ area_support[3] = 4;
+ }
+
return 0;
}
}
return fmt;
}
+
static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv,
char *buf, int win_id)
{
size += snprintf(dsp_buf, 80,
" x_st :%4d, y_st :%4d, y_h_fac:%5d, y_v_fac:%5d, ",
- w0_st_x-h_pw_bp, w0_st_y-v_pw_bp, w0_y_h_fac, w0_y_v_fac);
+ w0_st_x - h_pw_bp, w0_st_y - v_pw_bp, w0_y_h_fac, w0_y_v_fac);
strcat(buf, dsp_buf);
memset(dsp_buf, 0, sizeof(dsp_buf));
size += snprintf(dsp_buf, 80,
" x_st :%4d, y_st :%4d, y_h_fac:%5d, y_v_fac:%5d, ",
- w1_st_x-h_pw_bp, w1_st_y-v_pw_bp, w1_y_h_fac, w1_y_v_fac);
+ w1_st_x - h_pw_bp, w1_st_y - v_pw_bp, w1_y_h_fac, w1_y_v_fac);
strcat(buf, dsp_buf);
memset(dsp_buf, 0, sizeof(dsp_buf));
vop_msk_reg(vop_dev, WIN1_CTRL0, val);
break;
case 2:
+ val = V_WIN2_EN(0) | V_WIN2_MST0_EN(0) |
+ V_WIN2_MST1_EN(0) |
+ V_WIN2_MST2_EN(0) | V_WIN2_MST3_EN(0);
+ vop_msk_reg(vop_dev, WIN2_CTRL0, val);
+ break;
+ case 3:
+ val = V_WIN3_EN(0) | V_WIN3_MST0_EN(0) |
+ V_WIN3_MST1_EN(0) |
+ V_WIN3_MST2_EN(0) | V_WIN3_MST3_EN(0);
+ vop_msk_reg(vop_dev, WIN3_CTRL0, val);
+ break;
+ case 4:
val = V_HWC_EN(0);
vop_msk_reg(vop_dev, HWC_CTRL0, val);
break;
if (vop_dev->clk_on) {
dsp_addr[0][0] = vop_readl(vop_dev, WIN0_YRGB_MST);
dsp_addr[1][0] = vop_readl(vop_dev, WIN1_YRGB_MST);
- dsp_addr[2][0] = vop_readl(vop_dev, HWC_MST);
+ dsp_addr[2][0] = vop_readl(vop_dev, WIN2_MST0);
+ dsp_addr[2][1] = vop_readl(vop_dev, WIN2_MST1);
+ dsp_addr[2][2] = vop_readl(vop_dev, WIN2_MST2);
+ dsp_addr[2][3] = vop_readl(vop_dev, WIN2_MST3);
+ dsp_addr[3][0] = vop_readl(vop_dev, WIN3_MST0);
+ dsp_addr[3][1] = vop_readl(vop_dev, WIN3_MST1);
+ dsp_addr[3][2] = vop_readl(vop_dev, WIN3_MST2);
+ dsp_addr[3][3] = vop_readl(vop_dev, WIN3_MST3);
+ dsp_addr[4][0] = vop_readl(vop_dev, HWC_MST);
}
spin_unlock(&vop_dev->reg_lock);
return 0;
}
-static u32 pwm_period_hpr, pwm_duty_lpr;
int vop_update_pwm(int bl_pwm_period, int bl_pwm_duty)
{
- pwm_period_hpr = bl_pwm_period;
- pwm_duty_lpr = bl_pwm_duty;
- /*pr_info("bl_pwm_period_hpr = 0x%x, bl_pwm_duty_lpr = 0x%x\n",
- bl_pwm_period, bl_pwm_duty);*/
+ /*
+ * TODO:
+ * pwm_period_hpr = bl_pwm_period;
+ * pwm_duty_lpr = bl_pwm_duty;
+ * pr_info("bl_pwm_period_hpr = 0x%x, bl_pwm_duty_lpr = 0x%x\n",
+ * bl_pwm_period, bl_pwm_duty);
+ */
+
return 0;
}
/*
- a:[-30~0]:
- sin_hue = sin(a)*256 +0x100;
- cos_hue = cos(a)*256;
- a:[0~30]
- sin_hue = sin(a)*256;
- cos_hue = cos(a)*256;
-*/
+ * a:[-30~0]:
+ * sin_hue = sin(a)*256 +0x100;
+ * cos_hue = cos(a)*256;
+ * a:[0~30]
+ * sin_hue = sin(a)*256;
+ * cos_hue = cos(a)*256;
+ */
static int vop_get_bcsh_hue(struct rk_lcdc_driver *dev_drv, bcsh_hue_mode mode)
{
-#if 1
struct vop_device *vop_dev =
container_of(dev_drv, struct vop_device, driver);
u32 val;
spin_unlock(&vop_dev->reg_lock);
return val;
-#endif
+}
+
+static int vop_set_dsp_cabc(struct rk_lcdc_driver *dev_drv, int mode,
+ int calc, int up, int down, int global)
+{
+ struct vop_device *vop_dev =
+ container_of(dev_drv, struct vop_device, driver);
+ struct rk_screen *screen = dev_drv->cur_screen;
+ u32 total_pixel, calc_pixel, stage_up, stage_down;
+ u32 pixel_num, global_dn;
+
+ if (!vop_dev->cabc_lut_addr_base) {
+ pr_err("vop chip[%d] not supoort cabc\n", VOP_CHIP(vop_dev));
+ return 0;
+ }
+
+ if (!screen->cabc_lut) {
+ pr_err("screen cabc lut not config, so not open cabc\n");
+ return 0;
+ }
+
+ dev_drv->cabc_mode = mode;
+ if (!dev_drv->cabc_mode) {
+ spin_lock(&vop_dev->reg_lock);
+ if (vop_dev->clk_on) {
+ vop_msk_reg(vop_dev, CABC_CTRL0,
+ V_CABC_EN(0) | V_CABC_HANDLE_EN(0));
+ vop_cfg_done(vop_dev);
+ }
+ pr_info("mode = 0, close cabc\n");
+ spin_unlock(&vop_dev->reg_lock);
+ return 0;
+ }
+
+ total_pixel = screen->mode.xres * screen->mode.yres;
+ pixel_num = 1000 - calc;
+ calc_pixel = (total_pixel * pixel_num) / 1000;
+ stage_up = up;
+ stage_down = down;
+ global_dn = global;
+ pr_info("enable cabc:mode=%d, calc=%d, up=%d, down=%d, global=%d\n",
+ mode, calc, stage_up, stage_down, global_dn);
+
+ spin_lock(&vop_dev->reg_lock);
+ if (vop_dev->clk_on) {
+ u64 val = 0;
+
+ val = V_CABC_EN(1) | V_CABC_HANDLE_EN(1) |
+ V_PWM_CONFIG_MODE(STAGE_BY_STAGE) |
+ V_CABC_CALC_PIXEL_NUM(calc_pixel);
+ vop_msk_reg(vop_dev, CABC_CTRL0, val);
+
+ val = V_CABC_LUT_EN(1) | V_CABC_TOTAL_NUM(total_pixel);
+ vop_msk_reg(vop_dev, CABC_CTRL1, val);
+
+ val = V_CABC_STAGE_DOWN(stage_down) |
+ V_CABC_STAGE_UP(stage_up) |
+ V_CABC_STAGE_UP_MODE(0) | V_MAX_SCALE_CFG_VALUE(1) |
+ V_MAX_SCALE_CFG_ENABLE(0);
+ vop_msk_reg(vop_dev, CABC_CTRL2, val);
+
+ val = V_CABC_GLOBAL_DN(global_dn) |
+ V_CABC_GLOBAL_DN_LIMIT_EN(1);
+ vop_msk_reg(vop_dev, CABC_CTRL3, val);
+ vop_cfg_done(vop_dev);
+ }
+ spin_unlock(&vop_dev->reg_lock);
+
return 0;
}
.post_dspbuf = vop_post_dspbuf,
.set_par = vop_set_par,
.pan_display = vop_pan_display,
+ .set_wb = vop_set_writeback,
.direct_set_addr = vop_direct_set_win_addr,
/*.lcdc_reg_update = vop_reg_update,*/
.blank = vop_blank,
.dpi_win_sel = vop_dpi_win_sel,
.dpi_status = vop_dpi_status,
.get_dsp_addr = vop_get_dsp_addr,
+ .set_dsp_lut = vop_set_lut,
+ .set_cabc_lut = vop_set_cabc,
+ .set_dsp_cabc = vop_set_dsp_cabc,
.set_dsp_bcsh_hue = vop_set_bcsh_hue,
.set_dsp_bcsh_bcs = vop_set_bcsh_bcs,
.get_dsp_bcsh_hue = vop_get_bcsh_hue,
if (intr_status & INTR_FS) {
timestamp = ktime_get();
+ if (vop_dev->wb_on) {
+ u32 wb_status;
+
+ spin_lock_irqsave(&vop_dev->irq_lock, flags);
+ wb_status = vop_read_bit(vop_dev, WB_CTRL0, V_WB_EN(0));
+ if (wb_status)
+ vop_set_bit(vop_dev, WB_CTRL0, V_WB_EN(0));
+
+ vop_cfg_done(vop_dev);
+ vop_dev->driver.wb_data.state = 0;
+ spin_unlock_irqrestore(&vop_dev->irq_lock, flags);
+ }
vop_dev->driver.vsync_info.timestamp = timestamp;
wake_up_interruptible_all(&vop_dev->driver.vsync_info.wait);
intr_status &= ~INTR_FS;
{
struct vop_device *vop_dev = NULL;
struct rk_lcdc_driver *dev_drv;
+ const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
vop_dev = devm_kzalloc(dev, sizeof(struct vop_device), GFP_KERNEL);
if (!vop_dev)
return -ENOMEM;
-
+ of_id = of_match_device(vop_dt_ids, dev);
+ vop_dev->data = of_id->data;
+ if (VOP_CHIP(vop_dev) != VOP_RK322X && VOP_CHIP(vop_dev) != VOP_RK3399)
+ return -ENODEV;
platform_set_drvdata(pdev, vop_dev);
vop_dev->dev = dev;
vop_parse_dt(vop_dev);
vop_dev->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(vop_dev->regs))
return PTR_ERR(vop_dev->regs);
- else
- dev_info(dev, "vop_dev->regs=0x%lx\n", (long)vop_dev->regs);
+
+ dev_info(dev, "vop_dev->regs=0x%lx\n", (long)vop_dev->regs);
vop_dev->regsbak = devm_kzalloc(dev, vop_dev->len, GFP_KERNEL);
if (IS_ERR(vop_dev->regsbak))
return PTR_ERR(vop_dev->regsbak);
+ if (VOP_CHIP(vop_dev) == VOP_RK3399) {
+ vop_dev->dsp_lut_addr_base = vop_dev->regs + GAMMA_LUT_ADDR;
+ vop_dev->cabc_lut_addr_base = vop_dev->regs +
+ CABC_GAMMA_LUT_ADDR;
+ }
+ vop_dev->grf_base =
+ syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(vop_dev->grf_base)) {
+ dev_err(&pdev->dev, "can't find lcdc grf property\n");
+ vop_dev->grf_base = NULL;
+ }
- vop_dev->id = 0;
+ vop_dev->id = vop_get_id(vop_dev, vop_dev->reg_phy_base);
dev_set_name(vop_dev->dev, "vop%d", vop_dev->id);
dev_drv = &vop_dev->driver;
dev_drv->dev = dev;
dev_drv->prop = prop;
dev_drv->id = vop_dev->id;
dev_drv->ops = &lcdc_drv_ops;
- dev_drv->lcdc_win_num = ARRAY_SIZE(vop_win);
+ dev_drv->lcdc_win_num = vop_dev->data->n_wins;
dev_drv->reserved_fb = 0;
spin_lock_init(&vop_dev->reg_lock);
spin_lock_init(&vop_dev->irq_lock);
-
vop_dev->irq = platform_get_irq(pdev, 0);
if (vop_dev->irq < 0) {
dev_err(&pdev->dev, "cannot find IRQ for lcdc%d\n",
}
ret = devm_request_irq(dev, vop_dev->irq, vop_isr,
- IRQF_DISABLED | IRQF_SHARED,
- dev_name(dev), vop_dev);
+ IRQF_SHARED, dev_name(dev), vop_dev);
if (ret) {
dev_err(&pdev->dev, "cannot requeset irq %d - err %d\n",
vop_dev->irq, ret);
return ret;
}
-
if (dev_drv->iommu_enabled)
strcpy(dev_drv->mmu_dts_name, VOP_IOMMU_COMPATIBLE_NAME);
-
- ret = rk_fb_register(dev_drv, vop_win, vop_dev->id);
+ ret = rk_fb_register(dev_drv, vop_dev->data->win, vop_dev->id);
if (ret < 0) {
- dev_err(dev, "register fb for failed!\n");
+ dev_err(dev, "register fb for lcdc%d failed!\n", vop_dev->id);
return ret;
}
vop_dev->screen = dev_drv->screen0;
struct rk_lcdc_driver *dev_drv = &vop_dev->driver;
dev_drv->suspend_flag = 1;
+ /* ensure suspend_flag take effect on multi process */
smp_wmb();
flush_kthread_worker(&dev_drv->update_regs_worker);
kthread_stop(dev_drv->update_regs_thread);
vop_deint(vop_dev);
- /*if (dev_drv->trsm_ops && dev_drv->trsm_ops->disable)
- dev_drv->trsm_ops->disable();*/
vop_clk_disable(vop_dev);
rk_disp_pwr_disable(dev_drv);
}
-#if defined(CONFIG_OF)
-static const struct of_device_id vop_dt_ids[] = {
- {.compatible = "rockchip,rk322x-lcdc",},
- {}
-};
-#endif
-
static struct platform_driver vop_driver = {
.probe = vop_probe,
.remove = vop_remove,
* LINE_FLAG: Line flag config register
* VOP_STATUS: vop status register
* BLANKING_VALUE: Register0000 Abstract
+ * MCU_BYPASS_PORT: Mcu bypass value
* WIN0_DSP_BG: Win0 layer background color
* WIN1_DSP_BG: Win1 layer background color
* WIN2_DSP_BG: Win2 layer background color
* WIN3_DSP_BG: Win3 layer background color
+ * YUV2YUV_WIN: YUV to YUV win
+ * YUV2YUV_POST: Post YUV to YUV
+ * AUTO_GATING_EN: Auto gating enable
* DBG_PERF_LATENCY_CTRL0: Axi performance latency module contrl register0
* DBG_PERF_RD_MAX_LATENCY_NUM0: Read max latency number
* DBG_PERF_RD_LATENCY_THR_NUM0: The number of bigger than configed
* DBG_PRE_REG0: Vop debug pre register0
* DBG_PRE_RESERVED: Vop debug pre register1 reserved
* DBG_POST_REG0: Vop debug post register0
- * DBG_POST_RESERVED: Vop debug post register1 reserved
+ * DBG_POST_REG1: Vop debug
* DBG_DATAO: debug data output path
* DBG_DATAO_2: debug data output path 2
* WIN2_LUT_ADDR: Win2 lut base address
#define V_EDP_OUT_EN(x) VAL_MASK(x, 1, 14)
#define V_MIPI_OUT_EN(x) VAL_MASK(x, 1, 15)
#define V_OVERLAY_MODE(x) VAL_MASK(x, 1, 16)
+/* rk322x only */
#define V_FS_SAME_ADDR_MASK_EN(x) VAL_MASK(x, 1, 17)
#define V_POST_LB_MODE(x) VAL_MASK(x, 1, 18)
#define V_WIN23_PRI_OPT_MODE(x) VAL_MASK(x, 1, 19)
+/* rk322x only */
#define V_VOP_MMU_EN(x) VAL_MASK(x, 1, 20)
+/* rk3399 only */
+#define V_VOP_FIELD_TVE_TIMING_POL(x) VAL_MASK(x, 1, 20)
#define V_VOP_DMA_STOP(x) VAL_MASK(x, 1, 21)
#define V_VOP_STANDBY_EN(x) VAL_MASK(x, 1, 22)
#define V_AUTO_GATING_EN(x) VAL_MASK(x, 1, 23)
#define DSP_CTRL0 0x00000010
#define V_DSP_OUT_MODE(x) VAL_MASK(x, 4, 0)
#define V_SW_CORE_DCLK_SEL(x) VAL_MASK(x, 1, 4)
+/* rk322x */
#define V_SW_HDMI_CLK_I_SEL(x) VAL_MASK(x, 1, 5)
+/* rk3399 */
+#define V_P2I_EN(x) VAL_MASK(x, 1, 5)
#define V_DSP_DCLK_DDR(x) VAL_MASK(x, 1, 8)
#define V_DSP_DDR_PHASE(x) VAL_MASK(x, 1, 9)
#define V_DSP_INTERLACE(x) VAL_MASK(x, 1, 10)
#define V_DSP_YUV_CLIP(x) VAL_MASK(x, 1, 21)
#define V_DSP_X_MIR_EN(x) VAL_MASK(x, 1, 22)
#define V_DSP_Y_MIR_EN(x) VAL_MASK(x, 1, 23)
+/* rk3399 only */
+#define V_SW_TVE_OUTPUT_SEL(x) VAL_MASK(x, 1, 25)
#define V_DSP_FIELD(x) VAL_MASK(x, 1, 31)
#define DSP_CTRL1 0x00000014
#define V_DSP_LUT_EN(x) VAL_MASK(x, 1, 0)
#define V_WIN0_MID_SWAP(x) VAL_MASK(x, 1, 14)
#define V_WIN0_UV_SWAP(x) VAL_MASK(x, 1, 15)
#define V_WIN0_HW_PRE_MUL_EN(x) VAL_MASK(x, 1, 16)
+/* rk3399 only */
+#define V_WIN0_YUYV(x) VAL_MASK(x, 1, 17)
#define V_WIN0_YRGB_DEFLICK(x) VAL_MASK(x, 1, 18)
#define V_WIN0_CBR_DEFLICK(x) VAL_MASK(x, 1, 19)
#define V_WIN0_YUV_CLIP(x) VAL_MASK(x, 1, 20)
#define V_WIN1_MID_SWAP(x) VAL_MASK(x, 1, 14)
#define V_WIN1_UV_SWAP(x) VAL_MASK(x, 1, 15)
#define V_WIN1_HW_PRE_MUL_EN(x) VAL_MASK(x, 1, 16)
+/* rk3399 only */
+#define V_WIN1_YUYV(x) VAL_MASK(x, 1, 17)
#define V_WIN1_YRGB_DEFLICK(x) VAL_MASK(x, 1, 18)
#define V_WIN1_CBR_DEFLICK(x) VAL_MASK(x, 1, 19)
#define V_WIN1_YUV_CLIP(x) VAL_MASK(x, 1, 20)
#define WIN2_CTRL0 0x000000b0
#define V_WIN2_EN(x) VAL_MASK(x, 1, 0)
#define V_WIN2_INTERLACE_READ(x) VAL_MASK(x, 1, 1)
-#define V_WIN2_CSC_MODE(x) VAL_MASK(x, 1, 2)
+#define V_WIN2_CSC_MODE(x) VAL_MASK(x, 2, 2)
#define V_WIN2_MST0_EN(x) VAL_MASK(x, 1, 4)
#define V_WIN2_DATA_FMT0(x) VAL_MASK(x, 2, 5)
#define V_WIN2_MST1_EN(x) VAL_MASK(x, 1, 8)
#define WIN3_CTRL0 0x00000100
#define V_WIN3_EN(x) VAL_MASK(x, 1, 0)
#define V_WIN3_INTERLACE_READ(x) VAL_MASK(x, 1, 1)
-#define V_WIN3_CSC_MODE(x) VAL_MASK(x, 1, 2)
+#define V_WIN3_CSC_MODE(x) VAL_MASK(x, 2, 2)
#define V_WIN3_MST0_EN(x) VAL_MASK(x, 1, 4)
#define V_WIN3_DATA_FMT0(x) VAL_MASK(x, 2, 5)
#define V_WIN3_MST1_EN(x) VAL_MASK(x, 1, 8)
#define V_HWC_MODE(x) VAL_MASK(x, 1, 4)
#define V_HWC_SIZE(x) VAL_MASK(x, 2, 5)
#define V_HWC_INTERLACE_READ(x) VAL_MASK(x, 1, 8)
-#define V_HWC_CSC_MODE(x) VAL_MASK(x, 1, 10)
+#define V_HWC_CSC_MODE(x) VAL_MASK(x, 2, 10)
#define V_HWC_RB_SWAP(x) VAL_MASK(x, 1, 12)
#define V_HWC_ALPHA_SWAP(x) VAL_MASK(x, 1, 13)
#define V_HWC_ENDIAN_SWAP(x) VAL_MASK(x, 1, 14)
#define V_CABC_STAGE_DOWN(x) VAL_MASK(x, 8, 0)
#define V_CABC_STAGE_UP(x) VAL_MASK(x, 9, 8)
#define V_CABC_STAGE_UP_MODE(x) VAL_MASK(x, 1, 19)
+#define V_MAX_SCALE_CFG_VALUE(x) VAL_MASK(x, 9, 20)
+#define V_MAX_SCALE_CFG_ENABLE(x) VAL_MASK(x, 1, 31)
#define CABC_CTRL3 0x000001cc
#define V_CABC_GLOBAL_DN(x) VAL_MASK(x, 8, 0)
#define V_CABC_GLOBAL_DN_LIMIT_EN(x) VAL_MASK(x, 1, 8)
#define V_INTR_EN_WIN3_EMPTY(x) VAL_MASK(x, 1, 9)
#define V_INTR_EN_HWC_EMPTY(x) VAL_MASK(x, 1, 10)
#define V_INTR_EN_POST_BUF_EMPTY(x) VAL_MASK(x, 1, 11)
+/* rk3399 only */
+#define V_INTR_EN_FS_FIELD(x) VAL_MASK(x, 1, 12)
+/* rk322x only */
#define V_INTR_EN_PWM_GEN(x) VAL_MASK(x, 1, 12)
#define V_INTR_EN_DSP_HOLD_VALID(x) VAL_MASK(x, 1, 13)
#define V_INTR_EN_MMU(x) VAL_MASK(x, 1, 14)
#define V_INT_CLR_WIN3_EMPTY(x) VAL_MASK(x, 1, 9)
#define V_INT_CLR_HWC_EMPTY(x) VAL_MASK(x, 1, 10)
#define V_INT_CLR_POST_BUF_EMPTY(x) VAL_MASK(x, 1, 11)
+/* rk3399 only */
+#define V_INT_CLR_FS_FIELD(x) VAL_MASK(x, 1, 12)
+/* rk322x only */
#define V_INT_CLR_PWM_GEN(x) VAL_MASK(x, 1, 12)
#define V_INT_CLR_DSP_HOLD_VALID(x) VAL_MASK(x, 1, 13)
#define V_INT_CLR_MMU(x) VAL_MASK(x, 1, 14)
#define V_INT_STATUS_WIN3_EMPTY(x) VAL_MASK(x, 1, 9)
#define V_INT_STATUS_HWC_EMPTY(x) VAL_MASK(x, 1, 10)
#define V_INT_STATUS_POST_BUF_EMPTY(x) VAL_MASK(x, 1, 11)
+/* rk3399 only */
+#define V_INT_STATUS_FS_FIELD(x) VAL_MASK(x, 1, 12)
+/* rk322x only */
#define V_INT_STATUS_PWM_GEN(x) VAL_MASK(x, 1, 12)
#define V_INT_STATUS_DSP_HOLD_VALID(x) VAL_MASK(x, 1, 13)
#define V_INT_STATUS_MMU(x) VAL_MASK(x, 1, 14)
#define V_INT_RAW_STATUS_WIN3_EMPTY(x) VAL_MASK(x, 1, 9)
#define V_INT_RAW_STATUS_HWC_EMPTY(x) VAL_MASK(x, 1, 10)
#define V_INT_RAW_STATUS_POST_BUF_EMPTY(x) VAL_MASK(x, 1, 11)
+/* rk3399 only */
+#define V_INT_RAW_STATUS_FS_FIELD(x) VAL_MASK(x, 1, 12)
+/* rk322x only */
#define V_INT_RAW_STATUS_PWM_GEN(x) VAL_MASK(x, 1, 12)
#define V_INT_RAW_STATUS_DSP_HOLD_VALID(x) VAL_MASK(x, 1, 13)
#define V_INT_RAW_STATUS_MMU(x) VAL_MASK(x, 1, 14)
#define V_INT_CLR_AFBCD2_HREG_AXI_RRESP(x) VAL_MASK(x, 1, 9)
#define V_INT_CLR_AFBCD3_HREG_DEC_RESP(x) VAL_MASK(x, 1, 10)
#define V_INT_CLR_AFBCD3_HREG_AXI_RRESP(x) VAL_MASK(x, 1, 11)
+#define V_INT_CLR_WB_YRGB_FIFO_FULL(x) VAL_MASK(x, 1, 12)
+#define V_INT_CLR_WB_UV_FIFO_FULL(x) VAL_MASK(x, 1, 13)
+#define V_INT_CLR_WB_DMA_FINISH(x) VAL_MASK(x, 1, 14)
#define V_INT_CLR_VFP(x) VAL_MASK(x, 1, 15)
#define INTR_STATUS1 0x00000298
#define V_INT_STATUS_FBCD0(x) VAL_MASK(x, 1, 0)
#define V_INT_STATUS_AFBCD2_HREG_AXI_RRESP(x) VAL_MASK(x, 1, 9)
#define V_INT_STATUS_AFBCD3_HREG_DEC_RESP(x) VAL_MASK(x, 1, 10)
#define V_INT_STATUS_AFBCD4_HREG_DEC_RESP(x) VAL_MASK(x, 1, 11)
+#define V_INT_STATUS_WB_YRGB_FIFO_FULL(x) VAL_MASK(x, 1, 12)
+#define V_INT_STATUS_WB_UV_FIFO_FULL(x) VAL_MASK(x, 1, 13)
+#define V_INT_STATUS_WB_DMA_FINISH(x) VAL_MASK(x, 1, 14)
#define V_INT_STATUS_VFP(x) VAL_MASK(x, 1, 15)
#define INTR_RAW_STATUS1 0x0000029c
#define V_INT_RAW_STATUS_FBCD0(x) VAL_MASK(x, 1, 0)
#define V_INT_RAW_STATUS_AFBCD2_HREG_AXI_RRESP(x) VAL_MASK(x, 1, 9)
#define V_INT_RAW_STATUS_AFBCD3_HREG_DEC_RESP(x) VAL_MASK(x, 1, 10)
#define V_INT_RAW_STATUS_AFBCD3_HREG_AXI_RRESP(x) VAL_MASK(x, 1, 11)
+#define V_INT_RAW_STATUS_WB_YRGB_FIFO_FULL(x) VAL_MASK(x, 1, 12)
+#define V_INT_RAW_STATUS_WB_UV_FIFO_FULL(x) VAL_MASK(x, 1, 13)
+#define V_INT_RAW_STATUS_WB_DMA_FINISH(x) VAL_MASK(x, 1, 14)
#define V_INT_RAW_STATUS_VFP(x) VAL_MASK(x, 1, 15)
#define LINE_FLAG 0x000002a0
#define V_DSP_LINE_FLAG_NUM_0(x) VAL_MASK(x, 13, 0)
#define POST_YUV2YUV_Y2R_COE 0x00000480
#define POST_YUV2YUV_3x3_COE 0x000004a0
#define POST_YUV2YUV_R2Y_COE 0x000004c0
+#define WIN0_YUV2YUV_Y2R 0x000004e0
+#define WIN0_YUV2YUV_3x3 0x00000500
+#define WIN0_YUV2YUV_R2Y 0x00000520
+#define WIN1_YUV2YUV_Y2R 0x00000540
+#define WIN1_YUV2YUV_3x3 0x00000560
+#define WIN1_YUV2YUV_R2Y 0x00000580
+#define WIN2_YUV2YUV_Y2R 0x000005a0
+#define WIN2_YUV2YUV_3x3 0x000005c0
+#define WIN2_YUV2YUV_R2Y 0x000005e0
+#define WIN3_YUV2YUV_Y2R 0x00000600
+#define WIN3_YUV2YUV_3x3 0x00000620
+#define WIN3_YUV2YUV_R2Y 0x00000640
#define WIN2_LUT_ADDR 0x00001000
#define V_WIN2_LUT_ADDR(x) VAL_MASK(x, 32, 0)
#define WIN3_LUT_ADDR 0x00001400
#define INTR_WIN3_EMPTY (1 << 9)
#define INTR_HWC_EMPTY (1 << 10)
#define INTR_POST_BUF_EMPTY (1 << 11)
+/* rk322x */
#define INTR_PWM_GEN (1 << 12)
+/* rk3399 */
+#define INTR_FS_FIELD (1 << 12)
#define INTR_DSP_HOLD_VALID (1 << 13)
#define INTR_MMU (1 << 14)
#define INTR_DMA_FINISH (1 << 15)
#define OUT_CCIR656_MODE_1 6
#define OUT_CCIR656_MODE_2 7
+enum cabc_stage_mode {
+ LAST_FRAME_PWM_VAL = 0x0,
+ CUR_FRAME_PWM_VAL = 0x1,
+ STAGE_BY_STAGE = 0x2
+};
+
+enum {
+ VOP_RK322X,
+ VOP_RK3399,
+};
+
+enum {
+ VOP_WIN0,
+ VOP_WIN1,
+ VOP_WIN2,
+ VOP_WIN3,
+ VOP_HWC,
+ VOP_WIN_MAX,
+};
+
+struct vop_data {
+ int chip_type;
+ struct rk_lcdc_win *win;
+ int n_wins;
+};
+
struct vop_device {
int id;
+ const struct vop_data *data;
struct rk_lcdc_driver driver;
struct device *dev;
struct rk_screen *screen;
+ struct regmap *grf_base;
void __iomem *regs;
void *regsbak;
u32 reg_phy_base;
u32 len;
+ int __iomem *dsp_lut_addr_base;
+ int __iomem *cabc_lut_addr_base;
/* one time only one process allowed to config the register */
spinlock_t reg_lock;
bool clk_on;
/*active layer counter,when atv_layer_cnt = 0,disable lcdc*/
u8 atv_layer_cnt;
+ /* point write back status */
+ bool wb_on;
unsigned int irq;
writel_relaxed(*_pv, vop_dev->regs + offset);
}
-static inline void vop_msk_reg(struct vop_device *vop_dev, u32 offset, u64 v)
+static inline void vop_msk_reg(struct vop_device *vop_dev, u32 offset, u64 v)
{
u32 *_pv = (u32 *)vop_dev->regsbak;
writel_relaxed(*_pv, vop_dev->regs + offset);
}
+static inline void vop_msk_reg_nobak(struct vop_device *vop_dev,
+ u32 offset, u64 v)
+{
+ u32 *_pv = (u32 *)vop_dev->regsbak;
+
+ _pv += (offset >> 2);
+ writel_relaxed((*_pv & (~(v >> 32))) | (u32)v, vop_dev->regs + offset);
+}
+
static inline void vop_mask_writel(struct vop_device *vop_dev, u32 offset,
u32 mask, u32 v)
{