1a67719d1SMark Yao /* 2a67719d1SMark Yao * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3a67719d1SMark Yao * Author:Mark Yao <mark.yao@rock-chips.com> 4a67719d1SMark Yao * 5a67719d1SMark Yao * This software is licensed under the terms of the GNU General Public 6a67719d1SMark Yao * License version 2, as published by the Free Software Foundation, and 7a67719d1SMark Yao * may be copied, distributed, and modified under those terms. 8a67719d1SMark Yao * 9a67719d1SMark Yao * This program is distributed in the hope that it will be useful, 10a67719d1SMark Yao * but WITHOUT ANY WARRANTY; without even the implied warranty of 11a67719d1SMark Yao * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12a67719d1SMark Yao * GNU General Public License for more details. 13a67719d1SMark Yao */ 14a67719d1SMark Yao 15a67719d1SMark Yao #include <drm/drmP.h> 16a67719d1SMark Yao 17a67719d1SMark Yao #include <linux/kernel.h> 18a67719d1SMark Yao #include <linux/component.h> 19a67719d1SMark Yao 20a67719d1SMark Yao #include "rockchip_drm_vop.h" 21a67719d1SMark Yao #include "rockchip_vop_reg.h" 22a67719d1SMark Yao 23a67719d1SMark Yao #define VOP_REG(off, _mask, s) \ 24a67719d1SMark Yao {.offset = off, \ 25a67719d1SMark Yao .mask = _mask, \ 26a67719d1SMark Yao .shift = s,} 27a67719d1SMark Yao 28a67719d1SMark Yao static const uint32_t formats_01[] = { 29a67719d1SMark Yao DRM_FORMAT_XRGB8888, 30a67719d1SMark Yao DRM_FORMAT_ARGB8888, 31a67719d1SMark Yao DRM_FORMAT_XBGR8888, 32a67719d1SMark Yao DRM_FORMAT_ABGR8888, 33a67719d1SMark Yao DRM_FORMAT_RGB888, 34a67719d1SMark Yao DRM_FORMAT_BGR888, 35a67719d1SMark Yao DRM_FORMAT_RGB565, 36a67719d1SMark Yao DRM_FORMAT_BGR565, 37a67719d1SMark Yao DRM_FORMAT_NV12, 38a67719d1SMark Yao DRM_FORMAT_NV16, 39a67719d1SMark Yao DRM_FORMAT_NV24, 40a67719d1SMark Yao }; 41a67719d1SMark Yao 42a67719d1SMark Yao static const uint32_t formats_234[] = { 43a67719d1SMark Yao DRM_FORMAT_XRGB8888, 44a67719d1SMark Yao DRM_FORMAT_ARGB8888, 45a67719d1SMark Yao DRM_FORMAT_XBGR8888, 46a67719d1SMark Yao DRM_FORMAT_ABGR8888, 47a67719d1SMark Yao DRM_FORMAT_RGB888, 48a67719d1SMark Yao DRM_FORMAT_BGR888, 49a67719d1SMark Yao DRM_FORMAT_RGB565, 50a67719d1SMark Yao DRM_FORMAT_BGR565, 51a67719d1SMark Yao }; 52a67719d1SMark Yao 53a67719d1SMark Yao static const struct vop_scl_regs win_full_scl = { 54a67719d1SMark Yao .cbcr_vsd_mode = VOP_REG(WIN0_CTRL1, 0x1, 31), 55a67719d1SMark Yao .cbcr_vsu_mode = VOP_REG(WIN0_CTRL1, 0x1, 30), 56a67719d1SMark Yao .cbcr_hsd_mode = VOP_REG(WIN0_CTRL1, 0x3, 28), 57a67719d1SMark Yao .cbcr_ver_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 26), 58a67719d1SMark Yao .cbcr_hor_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 24), 59a67719d1SMark Yao .yrgb_vsd_mode = VOP_REG(WIN0_CTRL1, 0x1, 23), 60a67719d1SMark Yao .yrgb_vsu_mode = VOP_REG(WIN0_CTRL1, 0x1, 22), 61a67719d1SMark Yao .yrgb_hsd_mode = VOP_REG(WIN0_CTRL1, 0x3, 20), 62a67719d1SMark Yao .yrgb_ver_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 18), 63a67719d1SMark Yao .yrgb_hor_scl_mode = VOP_REG(WIN0_CTRL1, 0x3, 16), 64a67719d1SMark Yao .line_load_mode = VOP_REG(WIN0_CTRL1, 0x1, 15), 65a67719d1SMark Yao .cbcr_axi_gather_num = VOP_REG(WIN0_CTRL1, 0x7, 12), 66a67719d1SMark Yao .yrgb_axi_gather_num = VOP_REG(WIN0_CTRL1, 0xf, 8), 67a67719d1SMark Yao .vsd_cbcr_gt2 = VOP_REG(WIN0_CTRL1, 0x1, 7), 68a67719d1SMark Yao .vsd_cbcr_gt4 = VOP_REG(WIN0_CTRL1, 0x1, 6), 69a67719d1SMark Yao .vsd_yrgb_gt2 = VOP_REG(WIN0_CTRL1, 0x1, 5), 70a67719d1SMark Yao .vsd_yrgb_gt4 = VOP_REG(WIN0_CTRL1, 0x1, 4), 71a67719d1SMark Yao .bic_coe_sel = VOP_REG(WIN0_CTRL1, 0x3, 2), 72a67719d1SMark Yao .cbcr_axi_gather_en = VOP_REG(WIN0_CTRL1, 0x1, 1), 73a67719d1SMark Yao .yrgb_axi_gather_en = VOP_REG(WIN0_CTRL1, 0x1, 0), 74a67719d1SMark Yao .lb_mode = VOP_REG(WIN0_CTRL0, 0x7, 5), 75a67719d1SMark Yao .scale_yrgb_x = VOP_REG(WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), 76a67719d1SMark Yao .scale_yrgb_y = VOP_REG(WIN0_SCL_FACTOR_YRGB, 0xffff, 16), 77a67719d1SMark Yao .scale_cbcr_x = VOP_REG(WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), 78a67719d1SMark Yao .scale_cbcr_y = VOP_REG(WIN0_SCL_FACTOR_CBR, 0xffff, 16), 79a67719d1SMark Yao }; 80a67719d1SMark Yao 81a67719d1SMark Yao static const struct vop_win_phy win01_data = { 82a67719d1SMark Yao .scl = &win_full_scl, 83a67719d1SMark Yao .data_formats = formats_01, 84a67719d1SMark Yao .nformats = ARRAY_SIZE(formats_01), 85a67719d1SMark Yao .enable = VOP_REG(WIN0_CTRL0, 0x1, 0), 86a67719d1SMark Yao .format = VOP_REG(WIN0_CTRL0, 0x7, 1), 87a67719d1SMark Yao .rb_swap = VOP_REG(WIN0_CTRL0, 0x1, 12), 88a67719d1SMark Yao .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0), 89a67719d1SMark Yao .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x0fff0fff, 0), 90a67719d1SMark Yao .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0), 91a67719d1SMark Yao .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0), 92a67719d1SMark Yao .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0), 93a67719d1SMark Yao .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0), 94a67719d1SMark Yao .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16), 95a67719d1SMark Yao .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0), 96a67719d1SMark Yao .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0), 97a67719d1SMark Yao }; 98a67719d1SMark Yao 99a67719d1SMark Yao static const struct vop_win_phy win23_data = { 100a67719d1SMark Yao .data_formats = formats_234, 101a67719d1SMark Yao .nformats = ARRAY_SIZE(formats_234), 102a67719d1SMark Yao .enable = VOP_REG(WIN2_CTRL0, 0x1, 0), 103a67719d1SMark Yao .format = VOP_REG(WIN2_CTRL0, 0x7, 1), 104a67719d1SMark Yao .rb_swap = VOP_REG(WIN2_CTRL0, 0x1, 12), 105a67719d1SMark Yao .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0), 106a67719d1SMark Yao .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0), 107a67719d1SMark Yao .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0), 108a67719d1SMark Yao .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0), 109a67719d1SMark Yao .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0), 110a67719d1SMark Yao .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0), 111a67719d1SMark Yao }; 112a67719d1SMark Yao 113a67719d1SMark Yao static const struct vop_ctrl ctrl_data = { 114a67719d1SMark Yao .standby = VOP_REG(SYS_CTRL, 0x1, 22), 115a67719d1SMark Yao .gate_en = VOP_REG(SYS_CTRL, 0x1, 23), 116a67719d1SMark Yao .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20), 117a67719d1SMark Yao .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12), 118a67719d1SMark Yao .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13), 119a67719d1SMark Yao .edp_en = VOP_REG(SYS_CTRL, 0x1, 14), 120a67719d1SMark Yao .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15), 121a67719d1SMark Yao .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1), 122a67719d1SMark Yao .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6), 123a67719d1SMark Yao .data_blank = VOP_REG(DSP_CTRL0, 0x1, 19), 124a67719d1SMark Yao .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0), 125a67719d1SMark Yao .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4), 126a67719d1SMark Yao .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0), 127a67719d1SMark Yao .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0), 128a67719d1SMark Yao .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0), 129a67719d1SMark Yao .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0), 130a67719d1SMark Yao .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0), 131a67719d1SMark Yao .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0), 132a67719d1SMark Yao .cfg_done = VOP_REG(REG_CFG_DONE, 0x1, 0), 133a67719d1SMark Yao }; 134a67719d1SMark Yao 135a67719d1SMark Yao static const struct vop_reg_data vop_init_reg_table[] = { 136a67719d1SMark Yao {SYS_CTRL, 0x00c00000}, 137a67719d1SMark Yao {DSP_CTRL0, 0x00000000}, 138a67719d1SMark Yao {WIN0_CTRL0, 0x00000080}, 139a67719d1SMark Yao {WIN1_CTRL0, 0x00000080}, 140a67719d1SMark Yao /* TODO: Win2/3 support multiple area function, but we haven't found 141a67719d1SMark Yao * a suitable way to use it yet, so let's just use them as other windows 142a67719d1SMark Yao * with only area 0 enabled. 143a67719d1SMark Yao */ 144a67719d1SMark Yao {WIN2_CTRL0, 0x00000010}, 145a67719d1SMark Yao {WIN3_CTRL0, 0x00000010}, 146a67719d1SMark Yao }; 147a67719d1SMark Yao 148a67719d1SMark Yao /* 149a67719d1SMark Yao * Note: rk3288 has a dedicated 'cursor' window, however, that window requires 150a67719d1SMark Yao * special support to get alpha blending working. For now, just use overlay 151a67719d1SMark Yao * window 3 for the drm cursor. 152a67719d1SMark Yao * 153a67719d1SMark Yao */ 154a67719d1SMark Yao static const struct vop_win_data rk3288_vop_win_data[] = { 155a67719d1SMark Yao { .base = 0x00, .phy = &win01_data, .type = DRM_PLANE_TYPE_PRIMARY }, 156a67719d1SMark Yao { .base = 0x40, .phy = &win01_data, .type = DRM_PLANE_TYPE_OVERLAY }, 157a67719d1SMark Yao { .base = 0x00, .phy = &win23_data, .type = DRM_PLANE_TYPE_OVERLAY }, 158a67719d1SMark Yao { .base = 0x50, .phy = &win23_data, .type = DRM_PLANE_TYPE_CURSOR }, 159a67719d1SMark Yao }; 160a67719d1SMark Yao 161a67719d1SMark Yao static const int rk3288_vop_intrs[] = { 162a67719d1SMark Yao DSP_HOLD_VALID_INTR, 163a67719d1SMark Yao FS_INTR, 164a67719d1SMark Yao LINE_FLAG_INTR, 165a67719d1SMark Yao BUS_ERROR_INTR, 166a67719d1SMark Yao }; 167a67719d1SMark Yao 168a67719d1SMark Yao static const struct vop_intr rk3288_vop_intr = { 169a67719d1SMark Yao .intrs = rk3288_vop_intrs, 170a67719d1SMark Yao .nintrs = ARRAY_SIZE(rk3288_vop_intrs), 171a67719d1SMark Yao .status = VOP_REG(INTR_CTRL0, 0xf, 0), 172a67719d1SMark Yao .enable = VOP_REG(INTR_CTRL0, 0xf, 4), 173a67719d1SMark Yao .clear = VOP_REG(INTR_CTRL0, 0xf, 8), 174a67719d1SMark Yao }; 175a67719d1SMark Yao 176a67719d1SMark Yao static const struct vop_data rk3288_vop = { 177a67719d1SMark Yao .init_table = vop_init_reg_table, 178a67719d1SMark Yao .intr = &rk3288_vop_intr, 179a67719d1SMark Yao .table_size = ARRAY_SIZE(vop_init_reg_table), 180a67719d1SMark Yao .ctrl = &ctrl_data, 181a67719d1SMark Yao .win = rk3288_vop_win_data, 182a67719d1SMark Yao .win_size = ARRAY_SIZE(rk3288_vop_win_data), 183a67719d1SMark Yao }; 184a67719d1SMark Yao 185a67719d1SMark Yao static const struct of_device_id vop_driver_dt_match[] = { 186a67719d1SMark Yao { .compatible = "rockchip,rk3288-vop", 187a67719d1SMark Yao .data = &rk3288_vop }, 188a67719d1SMark Yao {}, 189a67719d1SMark Yao }; 190a67719d1SMark Yao MODULE_DEVICE_TABLE(of, vop_driver_dt_match); 191a67719d1SMark Yao 192a67719d1SMark Yao static int vop_probe(struct platform_device *pdev) 193a67719d1SMark Yao { 194a67719d1SMark Yao struct device *dev = &pdev->dev; 195a67719d1SMark Yao 196a67719d1SMark Yao if (!dev->of_node) { 197a67719d1SMark Yao dev_err(dev, "can't find vop devices\n"); 198a67719d1SMark Yao return -ENODEV; 199a67719d1SMark Yao } 200a67719d1SMark Yao 201a67719d1SMark Yao return component_add(dev, &vop_component_ops); 202a67719d1SMark Yao } 203a67719d1SMark Yao 204a67719d1SMark Yao static int vop_remove(struct platform_device *pdev) 205a67719d1SMark Yao { 206a67719d1SMark Yao component_del(&pdev->dev, &vop_component_ops); 207a67719d1SMark Yao 208a67719d1SMark Yao return 0; 209a67719d1SMark Yao } 210a67719d1SMark Yao 211a67719d1SMark Yao struct platform_driver vop_platform_driver = { 212a67719d1SMark Yao .probe = vop_probe, 213a67719d1SMark Yao .remove = vop_remove, 214a67719d1SMark Yao .driver = { 215a67719d1SMark Yao .name = "rockchip-vop", 216a67719d1SMark Yao .owner = THIS_MODULE, 217a67719d1SMark Yao .of_match_table = of_match_ptr(vop_driver_dt_match), 218a67719d1SMark Yao }, 219a67719d1SMark Yao }; 220a67719d1SMark Yao 221a67719d1SMark Yao module_platform_driver(vop_platform_driver); 222a67719d1SMark Yao 223a67719d1SMark Yao MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); 224a67719d1SMark Yao MODULE_DESCRIPTION("ROCKCHIP VOP Driver"); 225a67719d1SMark Yao MODULE_LICENSE("GPL v2"); 226