From: Huang Jiachai Date: Fri, 25 Mar 2016 09:01:52 +0000 (+0800) Subject: video: rockchip: vop: 3399: add support rk3399 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=ac5406c5aa6938b6acb08290faa3faffcb26ab6f;p=firefly-linux-kernel-4.4.55.git video: rockchip: vop: 3399: add support rk3399 Change-Id: Icbccfdd4fb841df67c0ade1cfd141fb574d837f0 Signed-off-by: Huang Jiachai --- diff --git a/drivers/video/rockchip/lcdc/rk322x_lcdc.c b/drivers/video/rockchip/lcdc/rk322x_lcdc.c index 1694c9158435..c8a3021cf62c 100644 --- a/drivers/video/rockchip/lcdc/rk322x_lcdc.c +++ b/drivers/video/rockchip/lcdc/rk322x_lcdc.c @@ -24,12 +24,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -38,6 +38,7 @@ #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); @@ -47,112 +48,156 @@ 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); @@ -172,16 +217,105 @@ static void vop_load_csc_table(struct vop_device *vop_dev, u32 offset, 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); @@ -200,8 +334,10 @@ static int vop_clk_disable(struct vop_device *vop_dev) 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; @@ -274,6 +410,8 @@ static int win##id##_enable(struct vop_device *vop_dev, int en) \ 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, @@ -285,6 +423,10 @@ 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; @@ -390,17 +532,22 @@ static int vop_pre_init(struct rk_lcdc_driver *dev_drv) 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); @@ -441,28 +588,33 @@ static int vop_pre_init(struct rk_lcdc_driver *dev_drv) 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 { @@ -471,6 +623,76 @@ static void vop_win_csc_mode(struct vop_device *vop_dev, } } +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 @@ -500,112 +722,131 @@ static void vop_win_csc_mode(struct vop_device *vop_dev, * * 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) @@ -616,10 +857,14 @@ static void vop_post_csc_cfg(struct rk_lcdc_driver *dev_drv) 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) @@ -703,7 +948,7 @@ 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 + @@ -770,6 +1015,12 @@ static int vop_clr_key_cfg(struct rk_lcdc_driver *dev_drv) 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); @@ -906,6 +1157,14 @@ static int vop_alpha_cfg(struct rk_lcdc_driver *dev_drv, int win_id) 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; @@ -958,13 +1217,17 @@ static int vop_axi_gather_cfg(struct vop_device *vop_dev, 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); @@ -978,7 +1241,7 @@ static int vop_win_0_1_reg_update(struct rk_lcdc_driver *dev_drv, int win_id) 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; @@ -1085,6 +1348,138 @@ static int vop_win_0_1_reg_update(struct rk_lcdc_driver *dev_drv, int win_id) 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 = @@ -1143,9 +1538,11 @@ static int vop_layer_update_regs(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); } @@ -1337,7 +1734,7 @@ static int vop_get_dspbuf_info(struct rk_lcdc_driver *dev_drv, u16 *xact, 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; @@ -1363,7 +1760,7 @@ static int vop_post_dspbuf(struct rk_lcdc_driver *dev_drv, u32 rgb_mst, 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)); @@ -1383,38 +1780,6 @@ static int vop_post_dspbuf(struct rk_lcdc_driver *dev_drv, u32 rgb_mst, 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; @@ -1435,49 +1800,58 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) 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", @@ -1503,6 +1877,17 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) 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); @@ -1558,8 +1943,8 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) } 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; @@ -1663,11 +2048,11 @@ static int vop_open(struct rk_lcdc_driver *dev_drv, int win_id, 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); @@ -1703,6 +2088,33 @@ static int win_0_1_display(struct vop_device *vop_dev, 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; @@ -1741,6 +2153,10 @@ static int vop_pan_display(struct rk_lcdc_driver *dev_drv, int win_id) } 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); @@ -1964,8 +2380,9 @@ static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen) 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", @@ -2349,6 +2766,95 @@ static int win_0_1_set_par(struct vop_device *vop_dev, 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) { @@ -2395,7 +2901,7 @@ static int hwc_set_par(struct vop_device *vop_dev, 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", @@ -2418,6 +2924,7 @@ static int vop_set_par(struct rk_lcdc_driver *dev_drv, int win_id) return 0; } win = dev_drv->win[win_id]; + if (win) switch (win_id) { case 0: win_0_1_set_par(vop_dev, screen, win); @@ -2426,6 +2933,12 @@ static int vop_set_par(struct rk_lcdc_driver *dev_drv, int win_id) 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: @@ -2435,6 +2948,84 @@ static int vop_set_par(struct rk_lcdc_driver *dev_drv, int win_id) 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) { @@ -2452,8 +3043,7 @@ static int vop_ioctl(struct rk_lcdc_driver *dev_drv, unsigned int cmd, 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, @@ -2521,6 +3111,7 @@ static int vop_early_suspend(struct rk_lcdc_driver *dev_drv) 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); @@ -2559,8 +3150,12 @@ static int vop_early_resume(struct rk_lcdc_driver *dev_drv) 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)); @@ -2619,6 +3214,34 @@ static int vop_get_win_state(struct rk_lcdc_driver *dev_drv, 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: @@ -2634,9 +3257,17 @@ static int vop_get_win_state(struct rk_lcdc_driver *dev_drv, 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; } @@ -2743,6 +3374,7 @@ static char *vop_format_to_string(int format, char *fmt) } return fmt; } + static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, char *buf, int win_id) { @@ -2858,7 +3490,7 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, 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)); @@ -2884,7 +3516,7 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, 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)); @@ -2998,6 +3630,18 @@ static int vop_config_done(struct rk_lcdc_driver *dev_drv) 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; @@ -3092,34 +3736,44 @@ static int vop_get_dsp_addr(struct rk_lcdc_driver *dev_drv, 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; @@ -3142,7 +3796,74 @@ static int vop_get_bcsh_hue(struct rk_lcdc_driver *dev_drv, bcsh_hue_mode mode) 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; } @@ -3370,6 +4091,7 @@ static struct rk_lcdc_drv_ops lcdc_drv_ops = { .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, @@ -3388,6 +4110,9 @@ static struct rk_lcdc_drv_ops lcdc_drv_ops = { .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, @@ -3421,6 +4146,18 @@ static irqreturn_t vop_isr(int irq, void *dev_id) 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; @@ -3547,6 +4284,7 @@ static int vop_probe(struct platform_device *pdev) { 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; @@ -3564,7 +4302,10 @@ static int vop_probe(struct platform_device *pdev) 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); @@ -3574,25 +4315,35 @@ static int vop_probe(struct platform_device *pdev) 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", @@ -3601,20 +4352,17 @@ static int vop_probe(struct platform_device *pdev) } 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; @@ -3635,24 +4383,16 @@ static void vop_shutdown(struct platform_device *pdev) 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, diff --git a/drivers/video/rockchip/lcdc/rk322x_lcdc.h b/drivers/video/rockchip/lcdc/rk322x_lcdc.h index c26473ce8e92..7a5a203ed642 100644 --- a/drivers/video/rockchip/lcdc/rk322x_lcdc.h +++ b/drivers/video/rockchip/lcdc/rk322x_lcdc.h @@ -169,10 +169,14 @@ * 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 @@ -201,7 +205,7 @@ * 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 @@ -250,10 +254,14 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -280,7 +288,10 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -297,6 +308,8 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -374,6 +387,8 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -469,6 +484,8 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -551,7 +568,7 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -643,7 +660,7 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -738,7 +755,7 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -859,6 +876,8 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -992,6 +1011,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1010,6 +1032,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1028,6 +1053,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1045,6 +1073,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1080,6 +1111,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1094,6 +1128,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1108,6 +1145,9 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1241,6 +1281,18 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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 @@ -1265,7 +1317,10 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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) @@ -1296,17 +1351,47 @@ static inline u64 val_mask(int val, u64 msk, int shift) #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; @@ -1317,6 +1402,8 @@ struct vop_device { 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; @@ -1391,7 +1478,7 @@ static inline void vop_clr_bit(struct vop_device *vop_dev, u32 offset, u64 v) 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; @@ -1401,6 +1488,15 @@ static inline void vop_msk_reg(struct vop_device *vop_dev, u32 offset, u64 v) 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) {