X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fvideo%2Frockchip%2Flcdc%2Frk322x_lcdc.c;h=9e321afce5ad18d289e564eb616b367b5f3b8cc9;hb=0b2165ea0545a93007ffbee5d81ec65575d0447b;hp=1694c9158435cc8efa45b97ebaeaf28626a10313;hpb=5b1f5a99409a92d32917104d2b5f1112bb3779e0;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/video/rockchip/lcdc/rk322x_lcdc.c b/drivers/video/rockchip/lcdc/rk322x_lcdc.c index 1694c9158435..9e321afce5ad 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,204 @@ 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, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_SCALE | SUPPORT_YUV | + SUPPORT_YUV10BIT, + .property.max_input_x = 4096, + .property.max_input_y = 2304}, + { .name = "win1", + .id = VOP_WIN1, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_SCALE | SUPPORT_YUV | + SUPPORT_YUV10BIT, + .property.max_input_x = 4096, + .property.max_input_y = 2304}, + { + .name = "hwc", + .id = VOP_HWC, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_HWC_LAYER, + .property.max_input_x = 128, + .property.max_input_y = 128 + } +}; + +static struct rk_lcdc_win rk3399_vop_win[] = { + { .name = "win0", + .id = VOP_WIN0, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_SCALE | SUPPORT_YUV | + SUPPORT_YUV10BIT, + .property.max_input_x = 4096, + .property.max_input_y = 2304}, + { .name = "win1", + .id = VOP_WIN1, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_SCALE | SUPPORT_YUV | + SUPPORT_YUV10BIT, + .property.max_input_x = 4096, + .property.max_input_y = 2304}, + { .name = "win2", + .id = VOP_WIN2, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_MULTI_AREA, + .property.max_input_x = 4096, + .property.max_input_y = 2304}, + { .name = "win3", + .id = VOP_WIN3, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_MULTI_AREA, + .property.max_input_x = 4096, + .property.max_input_y = 2304}, + { + .name = "hwc", + .id = VOP_HWC, + .property.feature = SUPPORT_WIN_IDENTIFY | SUPPORT_HW_EXIST | + SUPPORT_HWC_LAYER, + .property.max_input_x = 128, + .property.max_input_y = 128 + } +}; + +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 +265,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 +382,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 +458,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 +471,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 +580,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 +636,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 +671,79 @@ 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) || win->area[0].yuyv_fmt)) + 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, R2R, csc_r2r_bt2020to709, i); + } + } else if (output_color == COLOR_YCBCR || + output_color == COLOR_YCBCR_BT709) { + if (!(IS_YUV(win->area[0].fmt_cfg) || + win->area[0].yuyv_fmt)) { + val |= V_WIN0_YUV2YUV_R2Y_EN(1); + LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full_10, 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, Y2R, csc_y2r_bt2020, i); + LOAD_CSC(vop_dev, R2R, csc_r2r_bt2020to709, i); + LOAD_CSC(vop_dev, R2Y, csc_r2y_bt709_full_10, i); + } + } else if (output_color == COLOR_YCBCR_BT2020) { + if (!(IS_YUV(win->area[0].fmt_cfg) || + win->area[0].yuyv_fmt)) { + val |= V_WIN0_YUV2YUV_R2Y_EN(1) | + V_WIN0_YUV2YUV_EN(1); + LOAD_CSC(vop_dev, R2R, 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, Y2R, csc_y2r_bt709_full_10, i); + LOAD_CSC(vop_dev, R2R, 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 +773,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 +908,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 +999,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 +1066,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 +1208,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; @@ -936,10 +1246,14 @@ static int vop_axi_gather_cfg(struct vop_device *vop_dev, case ARGB888: case XBGR888: case ABGR888: + case FBDC_ARGB_888: + case FBDC_RGBX_888: + case FBDC_ABGR_888: yrgb_gather_num = 3; break; case RGB888: case RGB565: + case FBDC_RGB_565: yrgb_gather_num = 2; break; case YUV444: @@ -949,22 +1263,33 @@ static int vop_axi_gather_cfg(struct vop_device *vop_dev, case YUV422_A: case YUV444_A: case YUV420_NV21: + case YUYV420: + case UYVY420: yrgb_gather_num = 1; cbcr_gather_num = 2; break; + case YUYV422: + case UYVY422: + yrgb_gather_num = 2; + cbcr_gather_num = 2; + break; default: dev_err(vop_dev->driver.dev, "%s:un supported format[%d]\n", __func__, win->area[0].format); 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); @@ -972,20 +1297,69 @@ static int vop_axi_gather_cfg(struct vop_device *vop_dev, return 0; } +static int vop_fbdc_reg_update(struct vop_device *vop_dev, int win_id) +{ + struct rk_lcdc_win *win = vop_dev->driver.win[win_id]; + u32 val; + + val = V_VOP_FBDC_WIN_SEL(win_id) | + V_AFBCD_HREG_PIXEL_PACKING_FMT(win->area[0].fbdc_fmt_cfg) | + V_AFBCD_HREG_BLOCK_SPLIT(win->area[0].fbdc_cor_en); + vop_msk_reg(vop_dev, AFBCD0_CTRL, val); + + val = V_AFBCD_HREG_PIC_WIDTH(win->area[0].fbdc_mb_width - 1) | + V_AFBCD_HREG_PIC_HEIGHT(win->area[0].fbdc_mb_height - 1); + vop_msk_reg(vop_dev, AFBCD0_PIC_SIZE, val); + + return 0; +} + +static int vop_init_fbdc_config(struct vop_device *vop_dev, int win_id) +{ + struct rk_lcdc_driver *vop_drv = &vop_dev->driver; + struct rk_lcdc_win *win = vop_drv->win[win_id]; + struct rk_screen *screen = vop_drv->cur_screen; + + if (screen->mode.flag & FB_VMODE_INTERLACED) { + dev_err(vop_dev->dev, "unsupport fbdc+interlace!\n"); + return 0; + } + + if (VOP_CHIP(vop_dev) != VOP_RK3399) { + pr_err("soc: 0x%08x not support FBDC\n", VOP_CHIP(vop_dev)); + return 0; + } + + win->area[0].fbdc_mb_width = win->area[0].xact; + win->area[0].fbdc_mb_height = win->area[0].yact; + win->area[0].fbdc_cor_en = 0; /* hreg_block_split */ + win->area[0].fbdc_fmt_cfg |= 0 << 4; + + return 0; +} + static int vop_win_0_1_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]; u64 val; - uint32_t off; + u32 off; int format; + struct rk_win_property *win_property = + &dev_drv->win[win_id]->property; off = win_id * 0x40; if (win->state == 1) { + if (!(win_property->feature & SUPPORT_HW_EXIST)) { + pr_err("vop[%d] win[%d] hardware unsupport\n", + vop_dev->id, win_id); + return 0; + } vop_axi_gather_cfg(vop_dev, win); - + if (win->area[0].fbdc_en) + vop_fbdc_reg_update(vop_dev, win_id); /* * rk322x have a bug on windows 0 and 1: * @@ -1031,6 +1405,8 @@ static int vop_win_0_1_reg_update(struct rk_lcdc_driver *dev_drv, int win_id) V_WIN0_X_MIR_EN(win->xmirror) | V_WIN0_Y_MIR_EN(win->ymirror) | V_WIN0_UV_SWAP(win->area[0].swap_uv); + if (VOP_CHIP(vop_dev) == VOP_RK3399) + val |= V_WIN0_YUYV(win->area[0].yuyv_fmt); vop_msk_reg(vop_dev, WIN0_CTRL0 + off, val); val = V_WIN0_BIC_COE_SEL(win->bic_coe_el) | V_WIN0_VSD_YRGB_GT4(win->vsd_yrgb_gt4) | @@ -1085,6 +1461,147 @@ 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; + struct rk_win_property *win_property = + &dev_drv->win[win_id]->property; + + off = (win_id - 2) * 0x50; + area_xst(win, win->area_num); + + if (win->state == 1) { + if (!(win_property->feature & SUPPORT_HW_EXIST)) { + pr_err("vop[%d] win[%d] hardware unsupport\n", + vop_dev->id, win_id); + return 0; + } + vop_axi_gather_cfg(vop_dev, win); + if (win->area[0].fbdc_en) + vop_fbdc_reg_update(vop_dev, win_id); + 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 = @@ -1093,27 +1610,27 @@ static int vop_hwc_reg_update(struct rk_lcdc_driver *dev_drv, int win_id) unsigned int hwc_size = 0; u64 val; + if ((win->area[0].xsize == 32) && (win->area[0].ysize == 32)) { + hwc_size = 0; + } else if ((win->area[0].xsize == 64) && (win->area[0].ysize == 64)) { + hwc_size = 1; + } else if ((win->area[0].xsize == 96) && (win->area[0].ysize == 96)) { + hwc_size = 2; + } else if ((win->area[0].xsize == 128) && + (win->area[0].ysize == 128)) { + hwc_size = 3; + } else { + dev_err(vop_dev->dev, "un supported hwc size[%dx%d]!\n", + win->area[0].xsize, win->area[0].ysize); + return -EINVAL; + } + if (win->state == 1) { vop_axi_gather_cfg(vop_dev, win); val = V_HWC_EN(1) | V_HWC_DATA_FMT(win->area[0].fmt_cfg) | V_HWC_RB_SWAP(win->area[0].swap_rb); vop_msk_reg(vop_dev, HWC_CTRL0, val); - if ((win->area[0].xsize == 32) && (win->area[0].ysize == 32)) - hwc_size = 0; - else if ((win->area[0].xsize == 64) && - (win->area[0].ysize == 64)) - hwc_size = 1; - else if ((win->area[0].xsize == 96) && - (win->area[0].ysize == 96)) - hwc_size = 2; - else if ((win->area[0].xsize == 128) && - (win->area[0].ysize == 128)) - hwc_size = 3; - else - dev_err(vop_dev->dev, "un supported hwc size[%dx%d]!\n", - win->area[0].xsize, win->area[0].ysize); - val = V_HWC_SIZE(hwc_size); vop_msk_reg(vop_dev, HWC_CTRL0, val); @@ -1143,9 +1660,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 +1856,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 +1882,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 +1902,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 +1922,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", @@ -1500,9 +1996,23 @@ static int vop_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) vop_msk_reg(vop_dev, SYS_CTRL, val); break; case SCREEN_HDMI: + if ((screen->face == OUT_P888) || + (screen->face == OUT_P101010)) + face = OUT_P101010; /*RGB 101010 output*/ 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 +2068,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 +2173,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); @@ -1697,12 +2207,45 @@ static int win_0_1_display(struct vop_device *vop_dev, win->area[0].uv_addr = uv_addr; vop_writel(vop_dev, WIN0_YRGB_MST + off, win->area[0].y_addr); vop_writel(vop_dev, WIN0_CBR_MST + off, win->area[0].uv_addr); + if (win->area[0].fbdc_en == 1) + vop_writel(vop_dev, AFBCD0_HDR_PTR, + win->area[0].y_addr); } spin_unlock(&vop_dev->reg_lock); 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); + if (win->area[0].fbdc_en == 1) + vop_writel(vop_dev, AFBCD0_HDR_PTR, + win->area[0].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 +2284,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); @@ -1816,6 +2363,8 @@ static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen) /*cbcr scl mode */ switch (win->area[0].format) { case YUV422: + case YUYV422: + case UYVY422: case YUV422_A: cbcr_srcW = srcW / 2; cbcr_dstW = dstW; @@ -1824,6 +2373,8 @@ static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen) yuv_fmt = 1; break; case YUV420: + case YUYV420: + case UYVY420: case YUV420_A: case YUV420_NV21: cbcr_srcW = srcW / 2; @@ -1874,6 +2425,10 @@ static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen) /* line buffer mode */ if ((win->area[0].format == YUV422) || (win->area[0].format == YUV420) || + (win->area[0].format == YUYV422) || + (win->area[0].format == YUYV420) || + (win->area[0].format == UYVY422) || + (win->area[0].format == UYVY420) || (win->area[0].format == YUV420_NV21) || (win->area[0].format == YUV422_A) || (win->area[0].format == YUV420_A)) { @@ -1930,6 +2485,66 @@ static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen) win->cbr_hsd_mode = SCALE_DOWN_BIL; /*not to specify */ win->yrgb_vsd_mode = SCALE_DOWN_BIL; /*not to specify */ win->cbr_vsd_mode = SCALE_DOWN_BIL; /*not to specify */ + + /* if (VOP_CHIP(vop_dev) == VOP_RK3399) { */ + if ((win->area[0].format == YUYV422) || + (win->area[0].format == YUYV420) || + (win->area[0].format == UYVY422) || + (win->area[0].format == UYVY420)) { + yrgb_vscalednmult = + vop_get_hard_ware_vskiplines(yrgb_srcH, yrgb_dstH); + if (yrgb_vscalednmult == 4) { + yrgb_vsd_bil_gt4 = 1; + yrgb_vsd_bil_gt2 = 0; + } else if (yrgb_vscalednmult == 2) { + yrgb_vsd_bil_gt4 = 0; + yrgb_vsd_bil_gt2 = 1; + } else { + yrgb_vsd_bil_gt4 = 0; + yrgb_vsd_bil_gt2 = 0; + } + if ((win->area[0].format == YUYV420) || + (win->area[0].format == UYVY420)) { + if ((yrgb_vsd_bil_gt4 == 1) || (yrgb_vsd_bil_gt2 == 1)) + win->yrgb_vsd_mode = SCALE_DOWN_AVG; + } + + cbcr_vscalednmult = + vop_get_hard_ware_vskiplines(cbcr_srcH, cbcr_dstH); + if (cbcr_vscalednmult == 4) { + cbcr_vsd_bil_gt4 = 1; + cbcr_vsd_bil_gt2 = 0; + } else if (cbcr_vscalednmult == 2) { + cbcr_vsd_bil_gt4 = 0; + cbcr_vsd_bil_gt2 = 1; + } else { + cbcr_vsd_bil_gt4 = 0; + cbcr_vsd_bil_gt2 = 0; + } + if ((win->area[0].format == YUYV420) || + (win->area[0].format == UYVY420)) { + if ((cbcr_vsd_bil_gt4 == 1) || (cbcr_vsd_bil_gt2 == 1)) + win->cbr_vsd_mode = SCALE_DOWN_AVG; + } + /* CBCR vsd_mode must same to YRGB for YUYV when gt2 or gt4 */ + if ((cbcr_vsd_bil_gt4 == 1) || (cbcr_vsd_bil_gt2 == 1)) { + if (win->yrgb_vsd_mode != win->cbr_vsd_mode) + win->cbr_vsd_mode = win->yrgb_vsd_mode; + } + } + /* 3399 yuyv support*/ + if (win->ymirror == 1) { + if (win->yrgb_vsd_mode == SCALE_DOWN_AVG) + pr_info("y_mirror enable, y-vsd AVG mode unsupprot\n"); + win->yrgb_vsd_mode = SCALE_DOWN_BIL; + } + if (screen->mode.vmode & FB_VMODE_INTERLACED) { + if (win->yrgb_vsd_mode == SCALE_DOWN_AVG) + pr_info("interlace mode, y-vsd AVG mode unsupprot\n"); + /* interlace mode must bill */ + win->yrgb_vsd_mode = SCALE_DOWN_BIL; + win->cbr_vsd_mode = SCALE_DOWN_BIL; + } switch (win->win_lb_mode) { case LB_YUV_3840X5: case LB_YUV_2560X8: @@ -1954,18 +2569,12 @@ static int vop_cal_scl_fac(struct rk_lcdc_win *win, struct rk_screen *screen) break; } - if (win->ymirror == 1) - win->yrgb_vsd_mode = SCALE_DOWN_BIL; - if (screen->mode.vmode & FB_VMODE_INTERLACED) { - /* interlace mode must bill */ - win->yrgb_vsd_mode = SCALE_DOWN_BIL; - win->cbr_vsd_mode = SCALE_DOWN_BIL; - } 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", @@ -2240,25 +2849,25 @@ static int win_0_1_set_par(struct vop_device *vop_dev, fmt_cfg = 2; swap_rb = 0; win->fmt_10 = 0; - win->area[0].fbdc_fmt_cfg = 0x05; + win->area[0].fbdc_fmt_cfg = AFBDC_FMT_RGB565; break; case FBDC_ARGB_888: fmt_cfg = 0; - swap_rb = 0; + swap_rb = 1; win->fmt_10 = 0; - win->area[0].fbdc_fmt_cfg = 0x0c; + win->area[0].fbdc_fmt_cfg = AFBDC_FMT_U8U8U8U8; break; case FBDC_ABGR_888: fmt_cfg = 0; - swap_rb = 1; + swap_rb = 0; win->fmt_10 = 0; - win->area[0].fbdc_fmt_cfg = 0x0c; + win->area[0].fbdc_fmt_cfg = AFBDC_FMT_U8U8U8U8; break; case FBDC_RGBX_888: fmt_cfg = 0; swap_rb = 0; win->fmt_10 = 0; - win->area[0].fbdc_fmt_cfg = 0x3a; + win->area[0].fbdc_fmt_cfg = AFBDC_FMT_U8U8U8U8; break; case ARGB888: fmt_cfg = 0; @@ -2322,6 +2931,30 @@ static int win_0_1_set_par(struct vop_device *vop_dev, swap_rb = 0; win->fmt_10 = 1; break; + case YUYV422: + fmt_cfg = 0; + swap_rb = 0; + win->fmt_10 = 0; + win->area[0].yuyv_fmt = 1; + break; + case YUYV420: + fmt_cfg = 1; + swap_rb = 0; + win->fmt_10 = 0; + win->area[0].yuyv_fmt = 1; + break; + case UYVY422: + fmt_cfg = 2; + swap_rb = 0; + win->fmt_10 = 0; + win->area[0].yuyv_fmt = 1; + break; + case UYVY420: + fmt_cfg = 3; + swap_rb = 0; + win->fmt_10 = 0; + win->area[0].yuyv_fmt = 1; + break; default: dev_err(vop_dev->dev, "%s:unsupport format[%d]!\n", __func__, win->area[0].format); @@ -2337,6 +2970,8 @@ static int win_0_1_set_par(struct vop_device *vop_dev, xvir = win->area[0].xvir; yvir = win->area[0].yvir; } + if (win->area[0].fbdc_en) + vop_init_fbdc_config(vop_dev, win->id); vop_win_0_1_reg_update(&vop_dev->driver, win->id); spin_unlock(&vop_dev->reg_lock); @@ -2349,6 +2984,97 @@ 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); + } + } + if (win->area[0].fbdc_en) + vop_init_fbdc_config(vop_dev, win->id); + 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 +3121,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 +3144,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 +3153,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 +3168,91 @@ 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; + if ((wb_data->xsize == 0) || (wb_data->ysize == 0)) + return 0; + + 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: + case ABGR888: + case XRGB888: + case XBGR888: + fmt_cfg = 0; + break; + case RGB888: + case BGR888: + fmt_cfg = 1; + break; + case RGB565: + case BGR565: + 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 +3270,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 +3338,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 +3377,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 +3441,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 +3484,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; } @@ -2736,6 +3594,17 @@ static char *vop_format_to_string(int format, char *fmt) break; case 6: strcpy(fmt, "YCbCr444"); + case 8: + strcpy(fmt, "YUYV422"); + break; + case 9: + strcpy(fmt, "YUYV420"); + break; + case 10: + strcpy(fmt, "UYVY422"); + break; + case 11: + strcpy(fmt, "UYVY420"); break; default: strcpy(fmt, "invalid\n"); @@ -2743,6 +3612,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) { @@ -2758,11 +3628,21 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, u32 fmt_id; char format_w0[9] = "NULL"; char format_w1[9] = "NULL"; + char format_w2_0[9] = "NULL"; + char format_w2_1[9] = "NULL"; + char format_w2_2[9] = "NULL"; + char format_w2_3[9] = "NULL"; + char format_w3_0[9] = "NULL"; + char format_w3_1[9] = "NULL"; + char format_w3_2[9] = "NULL"; + char format_w3_3[9] = "NULL"; char dsp_buf[100]; u32 win_ctrl, zorder, vir_info, act_info, dsp_info, dsp_st; u32 y_factor, uv_factor; - u8 layer0_sel, layer1_sel; - u8 w0_state, w1_state; + u8 layer0_sel, layer1_sel, layer2_sel, layer3_sel; + u8 w0_state, w1_state, w2_state, w3_state; + u8 w2_0_state, w2_1_state, w2_2_state, w2_3_state; + u8 w3_0_state, w3_1_state, w3_2_state, w3_3_state; u32 w0_vir_y, w0_vir_uv, w0_act_x, w0_act_y, w0_dsp_x, w0_dsp_y; u32 w0_st_x = h_pw_bp, w0_st_y = v_pw_bp; @@ -2771,6 +3651,21 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, u32 w0_y_h_fac, w0_y_v_fac, w0_uv_h_fac, w0_uv_v_fac; u32 w1_y_h_fac, w1_y_v_fac, w1_uv_h_fac, w1_uv_v_fac; + u32 w2_0_vir_y, w2_1_vir_y, w2_2_vir_y, w2_3_vir_y; + u32 w2_0_dsp_x, w2_1_dsp_x, w2_2_dsp_x, w2_3_dsp_x; + u32 w2_0_dsp_y, w2_1_dsp_y, w2_2_dsp_y, w2_3_dsp_y; + u32 w2_0_st_x = h_pw_bp, w2_1_st_x = h_pw_bp; + u32 w2_2_st_x = h_pw_bp, w2_3_st_x = h_pw_bp; + u32 w2_0_st_y = v_pw_bp, w2_1_st_y = v_pw_bp; + u32 w2_2_st_y = v_pw_bp, w2_3_st_y = v_pw_bp; + + u32 w3_0_vir_y, w3_1_vir_y, w3_2_vir_y, w3_3_vir_y; + u32 w3_0_dsp_x, w3_1_dsp_x, w3_2_dsp_x, w3_3_dsp_x; + u32 w3_0_dsp_y, w3_1_dsp_y, w3_2_dsp_y, w3_3_dsp_y; + u32 w3_0_st_x = h_pw_bp, w3_1_st_x = h_pw_bp; + u32 w3_2_st_x = h_pw_bp, w3_3_st_x = h_pw_bp; + u32 w3_0_st_y = v_pw_bp, w3_1_st_y = v_pw_bp; + u32 w3_2_st_y = v_pw_bp, w3_3_st_y = v_pw_bp; u32 dclk_freq; int size = 0; @@ -2782,10 +3677,13 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, zorder = vop_readl(vop_dev, DSP_CTRL1); layer0_sel = (zorder & MASK(DSP_LAYER0_SEL)) >> 8; layer1_sel = (zorder & MASK(DSP_LAYER1_SEL)) >> 10; + layer2_sel = (zorder & MASK(DSP_LAYER2_SEL)) >> 12; + layer3_sel = (zorder & MASK(DSP_LAYER3_SEL)) >> 14; /* WIN0 */ win_ctrl = vop_readl(vop_dev, WIN0_CTRL0); w0_state = win_ctrl & MASK(WIN0_EN); fmt_id = (win_ctrl & MASK(WIN0_DATA_FMT)) >> 1; + fmt_id |= (win_ctrl & MASK(WIN0_YUYV)) >> 14; /* yuyv*/ vop_format_to_string(fmt_id, format_w0); vir_info = vop_readl(vop_dev, WIN0_VIR); act_info = vop_readl(vop_dev, WIN0_ACT_INFO); @@ -2812,6 +3710,7 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, win_ctrl = vop_readl(vop_dev, WIN1_CTRL0); w1_state = win_ctrl & MASK(WIN1_EN); fmt_id = (win_ctrl & MASK(WIN1_DATA_FMT)) >> 1; + fmt_id |= (win_ctrl & MASK(WIN1_YUYV)) >> 14; /* yuyv*/ vop_format_to_string(fmt_id, format_w1); vir_info = vop_readl(vop_dev, WIN1_VIR); act_info = vop_readl(vop_dev, WIN1_ACT_INFO); @@ -2833,14 +3732,126 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, w1_y_v_fac = (y_factor & MASK(WIN1_VS_FACTOR_YRGB)) >> 16; w1_uv_h_fac = uv_factor & MASK(WIN1_HS_FACTOR_CBR); w1_uv_v_fac = (uv_factor & MASK(WIN1_VS_FACTOR_CBR)) >> 16; + + /*WIN2 */ + win_ctrl = vop_readl(vop_dev, WIN2_CTRL0); + w2_state = win_ctrl & MASK(WIN2_EN); + w2_0_state = (win_ctrl & 0x10) >> 4; + w2_1_state = (win_ctrl & 0x100) >> 8; + w2_2_state = (win_ctrl & 0x1000) >> 12; + w2_3_state = (win_ctrl & 0x10000) >> 16; + vir_info = vop_readl(vop_dev, WIN2_VIR0_1); + w2_0_vir_y = vir_info & MASK(WIN2_VIR_STRIDE0); + w2_1_vir_y = (vir_info & MASK(WIN2_VIR_STRIDE1)) >> 16; + vir_info = vop_readl(vop_dev, WIN2_VIR2_3); + w2_2_vir_y = vir_info & MASK(WIN2_VIR_STRIDE2); + w2_3_vir_y = (vir_info & MASK(WIN2_VIR_STRIDE3)) >> 16; + + fmt_id = (win_ctrl & MASK(WIN2_DATA_FMT0)) >> 5; + vop_format_to_string(fmt_id, format_w2_0); + fmt_id = (win_ctrl & MASK(WIN2_DATA_FMT1)) >> 9; + vop_format_to_string(fmt_id, format_w2_1); + fmt_id = (win_ctrl & MASK(WIN2_DATA_FMT2)) >> 13; + vop_format_to_string(fmt_id, format_w2_2); + fmt_id = (win_ctrl & MASK(WIN2_DATA_FMT3)) >> 17; + vop_format_to_string(fmt_id, format_w2_3); + + dsp_info = vop_readl(vop_dev, WIN2_DSP_INFO0); + dsp_st = vop_readl(vop_dev, WIN2_DSP_ST0); + w2_0_dsp_x = (dsp_info & MASK(WIN2_DSP_WIDTH0)) + 1; + w2_0_dsp_y = ((dsp_info & MASK(WIN2_DSP_HEIGHT0)) >> 16) + 1; + if (w2_0_state) { + w2_0_st_x = dsp_st & MASK(WIN2_DSP_XST0); + w2_0_st_y = (dsp_st & MASK(WIN2_DSP_YST0)) >> 16; + } + dsp_info = vop_readl(vop_dev, WIN2_DSP_INFO1); + dsp_st = vop_readl(vop_dev, WIN2_DSP_ST1); + w2_1_dsp_x = (dsp_info & MASK(WIN2_DSP_WIDTH1)) + 1; + w2_1_dsp_y = ((dsp_info & MASK(WIN2_DSP_HEIGHT1)) >> 16) + 1; + if (w2_1_state) { + w2_1_st_x = dsp_st & MASK(WIN2_DSP_XST1); + w2_1_st_y = (dsp_st & MASK(WIN2_DSP_YST1)) >> 16; + } + dsp_info = vop_readl(vop_dev, WIN2_DSP_INFO2); + dsp_st = vop_readl(vop_dev, WIN2_DSP_ST2); + w2_2_dsp_x = (dsp_info & MASK(WIN2_DSP_WIDTH2)) + 1; + w2_2_dsp_y = ((dsp_info & MASK(WIN2_DSP_HEIGHT2)) >> 16) + 1; + if (w2_2_state) { + w2_2_st_x = dsp_st & MASK(WIN2_DSP_XST2); + w2_2_st_y = (dsp_st & MASK(WIN2_DSP_YST2)) >> 16; + } + dsp_info = vop_readl(vop_dev, WIN2_DSP_INFO3); + dsp_st = vop_readl(vop_dev, WIN2_DSP_ST3); + w2_3_dsp_x = (dsp_info & MASK(WIN2_DSP_WIDTH3)) + 1; + w2_3_dsp_y = ((dsp_info & MASK(WIN2_DSP_HEIGHT3)) >> 16) + 1; + if (w2_3_state) { + w2_3_st_x = dsp_st & MASK(WIN2_DSP_XST3); + w2_3_st_y = (dsp_st & MASK(WIN2_DSP_YST3)) >> 16; + } + + /*WIN3 */ + win_ctrl = vop_readl(vop_dev, WIN3_CTRL0); + w3_state = win_ctrl & MASK(WIN3_EN); + w3_0_state = (win_ctrl & 0x10) >> 4; + w3_1_state = (win_ctrl & 0x100) >> 8; + w3_2_state = (win_ctrl & 0x1000) >> 12; + w3_3_state = (win_ctrl & 0x10000) >> 16; + vir_info = vop_readl(vop_dev, WIN3_VIR0_1); + w3_0_vir_y = vir_info & MASK(WIN3_VIR_STRIDE0); + w3_1_vir_y = (vir_info & MASK(WIN3_VIR_STRIDE1)) >> 16; + vir_info = vop_readl(vop_dev, WIN3_VIR2_3); + w3_2_vir_y = vir_info & MASK(WIN3_VIR_STRIDE2); + w3_3_vir_y = (vir_info & MASK(WIN3_VIR_STRIDE3)) >> 16; + + fmt_id = (win_ctrl & MASK(WIN3_DATA_FMT0)) >> 5; + vop_format_to_string(fmt_id, format_w3_0); + fmt_id = (win_ctrl & MASK(WIN3_DATA_FMT1)) >> 9; + vop_format_to_string(fmt_id, format_w3_1); + fmt_id = (win_ctrl & MASK(WIN3_DATA_FMT2)) >> 13; + vop_format_to_string(fmt_id, format_w3_2); + fmt_id = (win_ctrl & MASK(WIN3_DATA_FMT3)) >> 17; + vop_format_to_string(fmt_id, format_w3_3); + + dsp_info = vop_readl(vop_dev, WIN3_DSP_INFO0); + dsp_st = vop_readl(vop_dev, WIN3_DSP_ST0); + w3_0_dsp_x = (dsp_info & MASK(WIN3_DSP_WIDTH0)) + 1; + w3_0_dsp_y = ((dsp_info & MASK(WIN3_DSP_HEIGHT0)) >> 16) + 1; + if (w3_0_state) { + w3_0_st_x = dsp_st & MASK(WIN3_DSP_XST0); + w3_0_st_y = (dsp_st & MASK(WIN3_DSP_YST0)) >> 16; + } + dsp_info = vop_readl(vop_dev, WIN3_DSP_INFO1); + dsp_st = vop_readl(vop_dev, WIN3_DSP_ST1); + w3_1_dsp_x = (dsp_info & MASK(WIN3_DSP_WIDTH1)) + 1; + w3_1_dsp_y = ((dsp_info & MASK(WIN3_DSP_HEIGHT1)) >> 16) + 1; + if (w3_1_state) { + w3_1_st_x = dsp_st & MASK(WIN3_DSP_XST1); + w3_1_st_y = (dsp_st & MASK(WIN3_DSP_YST1)) >> 16; + } + dsp_info = vop_readl(vop_dev, WIN3_DSP_INFO2); + dsp_st = vop_readl(vop_dev, WIN3_DSP_ST2); + w3_2_dsp_x = (dsp_info & MASK(WIN3_DSP_WIDTH2)) + 1; + w3_2_dsp_y = ((dsp_info & MASK(WIN3_DSP_HEIGHT2)) >> 16) + 1; + if (w3_2_state) { + w3_2_st_x = dsp_st & MASK(WIN3_DSP_XST2); + w3_2_st_y = (dsp_st & MASK(WIN3_DSP_YST2)) >> 16; + } + dsp_info = vop_readl(vop_dev, WIN3_DSP_INFO3); + dsp_st = vop_readl(vop_dev, WIN3_DSP_ST3); + w3_3_dsp_x = (dsp_info & MASK(WIN3_DSP_WIDTH3)) + 1; + w3_3_dsp_y = ((dsp_info & MASK(WIN3_DSP_HEIGHT3)) >> 16) + 1; + if (w3_3_state) { + w3_3_st_x = dsp_st & MASK(WIN3_DSP_XST3); + w3_3_st_y = (dsp_st & MASK(WIN3_DSP_YST3)) >> 16; + } } else { spin_unlock(&vop_dev->reg_lock); return -EPERM; } spin_unlock(&vop_dev->reg_lock); size += snprintf(dsp_buf, 80, - "z-order:\n win[%d]\n win[%d]\n", - layer1_sel, layer0_sel); + "z-order:\n win[%d]\n win[%d]\n win[%d]\n win[%d]\n", + layer1_sel, layer0_sel, layer2_sel, layer3_sel); strcat(buf, dsp_buf); memset(dsp_buf, 0, sizeof(dsp_buf)); /* win0 */ @@ -2858,7 +3869,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 +3895,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)); @@ -2895,6 +3906,122 @@ static ssize_t vop_get_disp_info(struct rk_lcdc_driver *dev_drv, strcat(buf, dsp_buf); memset(dsp_buf, 0, sizeof(dsp_buf)); + /*win2*/ + size += snprintf(dsp_buf, 80, + "win2:\n state:%d\n", + w2_state); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + /*area 0*/ + size += snprintf(dsp_buf, 80, + " area0: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w2_0_state, format_w2_0, w2_0_dsp_x, w2_0_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w2_0_st_x - h_pw_bp, w2_0_st_y - v_pw_bp, + vop_readl(vop_dev, WIN2_MST0)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*area 1*/ + size += snprintf(dsp_buf, 80, + " area1: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w2_1_state, format_w2_1, w2_1_dsp_x, w2_1_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w2_1_st_x - h_pw_bp, w2_1_st_y - v_pw_bp, + vop_readl(vop_dev, WIN2_MST1)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*area 2*/ + size += snprintf(dsp_buf, 80, + " area2: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w2_2_state, format_w2_2, w2_2_dsp_x, w2_2_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w2_2_st_x - h_pw_bp, w2_2_st_y - v_pw_bp, + vop_readl(vop_dev, WIN2_MST2)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*area 3*/ + size += snprintf(dsp_buf, 80, + " area3: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w2_3_state, format_w2_3, w2_3_dsp_x, w2_3_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w2_3_st_x - h_pw_bp, w2_3_st_y - v_pw_bp, + vop_readl(vop_dev, WIN2_MST3)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*win3*/ + size += snprintf(dsp_buf, 80, + "win3:\n state:%d\n", + w3_state); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + /*area 0*/ + size += snprintf(dsp_buf, 80, + " area0: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w3_0_state, format_w3_0, w3_0_dsp_x, w3_0_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w3_0_st_x - h_pw_bp, w3_0_st_y - v_pw_bp, + vop_readl(vop_dev, WIN3_MST0)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*area 1*/ + size += snprintf(dsp_buf, 80, + " area1: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w3_1_state, format_w3_1, w3_1_dsp_x, w3_1_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w3_1_st_x - h_pw_bp, w3_1_st_y - v_pw_bp, + vop_readl(vop_dev, WIN3_MST1)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*area 2*/ + size += snprintf(dsp_buf, 80, + " area2: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w3_2_state, format_w3_2, w3_2_dsp_x, w3_2_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w3_2_st_x - h_pw_bp, w3_2_st_y - v_pw_bp, + vop_readl(vop_dev, WIN3_MST2)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + + /*area 3*/ + size += snprintf(dsp_buf, 80, + " area3: state:%d, fmt:%7s, dsp_x:%4d, dsp_y:%4d,", + w3_3_state, format_w3_3, w3_3_dsp_x, w3_3_dsp_y); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + size += snprintf(dsp_buf, 80, + " x_st:%4d, y_st:%4d, y_addr:0x%08x\n", + w3_3_st_x - h_pw_bp, w3_3_st_y - v_pw_bp, + vop_readl(vop_dev, WIN3_MST3)); + strcat(buf, dsp_buf); + memset(dsp_buf, 0, sizeof(dsp_buf)); + return size; } @@ -2978,7 +4105,7 @@ static int vop_config_done(struct rk_lcdc_driver *dev_drv) { struct vop_device *vop_dev = container_of(dev_drv, struct vop_device, driver); - int i; + int i, fbdc_en = 0; u64 val; struct rk_lcdc_win *win = NULL; @@ -2987,6 +4114,7 @@ static int vop_config_done(struct rk_lcdc_driver *dev_drv) vop_msk_reg(vop_dev, SYS_CTRL, V_VOP_STANDBY_EN(vop_dev->standby)); for (i = 0; i < dev_drv->lcdc_win_num; i++) { win = dev_drv->win[i]; + fbdc_en |= win->area[0].fbdc_en; if ((win->state == 0) && (win->last_state == 1)) { switch (win->id) { case 0: @@ -2998,6 +4126,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; @@ -3007,6 +4147,10 @@ static int vop_config_done(struct rk_lcdc_driver *dev_drv) } win->last_state = win->state; } + if (VOP_CHIP(vop_dev) == VOP_RK3399) { + val = V_VOP_FBDC_EN(fbdc_en); + vop_msk_reg(vop_dev, AFBCD0_CTRL, val); + } vop_cfg_done(vop_dev); spin_unlock(&vop_dev->reg_lock); return 0; @@ -3092,34 +4236,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 +4296,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 +4591,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 +4610,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 +4646,19 @@ static irqreturn_t vop_isr(int irq, void *dev_id) if (intr_status & INTR_FS) { timestamp = ktime_get(); + if (vop_dev->driver.wb_data.state) { + 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_clr_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 +4785,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,35 +4803,49 @@ 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); vop_dev->reg_phy_base = res->start; vop_dev->len = resource_size(res); - vop_dev->regs = devm_ioremap_resource(dev, res); + vop_dev->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(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 +4854,40 @@ 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) { + if (VOP_CHIP(vop_dev) == VOP_RK322X) { + strcpy(dev_drv->mmu_dts_name, + VOP_IOMMU_COMPATIBLE_NAME); + } else { + if (vop_dev->id == 0) + strcpy(dev_drv->mmu_dts_name, + VOPB_IOMMU_COMPATIBLE_NAME); + else + strcpy(dev_drv->mmu_dts_name, + VOPL_IOMMU_COMPATIBLE_NAME); + } + } + if (VOP_CHIP(vop_dev) == VOP_RK3399) + dev_drv->property.feature |= SUPPORT_WRITE_BACK | SUPPORT_AFBDC; + dev_drv->property.feature |= SUPPORT_VOP_IDENTIFY | + SUPPORT_YUV420_OUTPUT; + dev_drv->property.max_output_x = 4096; + dev_drv->property.max_output_y = 2160; + + if ((VOP_CHIP(vop_dev) == VOP_RK3399) && (vop_dev->id == 1)) { + vop_dev->data->win[1].property.feature &= ~SUPPORT_HW_EXIST; + vop_dev->data->win[3].property.feature &= ~SUPPORT_HW_EXIST; + } - 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 +4908,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,