1 /* 2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 * Author:Mark Yao <mark.yao@rock-chips.com> 4 * 5 * This software is licensed under the terms of the GNU General Public 6 * License version 2, as published by the Free Software Foundation, and 7 * may be copied, distributed, and modified under those terms. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #include <drm/drmP.h> 16 17 #include <linux/kernel.h> 18 #include <linux/component.h> 19 20 #include "rockchip_drm_vop.h" 21 #include "rockchip_vop_reg.h" 22 23 #define VOP_REG(off, _mask, s) \ 24 {.offset = off, \ 25 .mask = _mask, \ 26 .shift = s,} 27 28 static const uint32_t formats_win_full[] = { 29 DRM_FORMAT_XRGB8888, 30 DRM_FORMAT_ARGB8888, 31 DRM_FORMAT_XBGR8888, 32 DRM_FORMAT_ABGR8888, 33 DRM_FORMAT_RGB888, 34 DRM_FORMAT_BGR888, 35 DRM_FORMAT_RGB565, 36 DRM_FORMAT_BGR565, 37 DRM_FORMAT_NV12, 38 DRM_FORMAT_NV16, 39 DRM_FORMAT_NV24, 40 }; 41 42 static const uint32_t formats_win_lite[] = { 43 DRM_FORMAT_XRGB8888, 44 DRM_FORMAT_ARGB8888, 45 DRM_FORMAT_XBGR8888, 46 DRM_FORMAT_ABGR8888, 47 DRM_FORMAT_RGB888, 48 DRM_FORMAT_BGR888, 49 DRM_FORMAT_RGB565, 50 DRM_FORMAT_BGR565, 51 }; 52 53 static const struct vop_scl_extension rk3288_win_full_scl_ext = { 54 .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31), 55 .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30), 56 .cbcr_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 28), 57 .cbcr_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 26), 58 .cbcr_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 24), 59 .yrgb_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 23), 60 .yrgb_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 22), 61 .yrgb_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 20), 62 .yrgb_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 18), 63 .yrgb_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 16), 64 .line_load_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 15), 65 .cbcr_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0x7, 12), 66 .yrgb_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0xf, 8), 67 .vsd_cbcr_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 7), 68 .vsd_cbcr_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 6), 69 .vsd_yrgb_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 5), 70 .vsd_yrgb_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 4), 71 .bic_coe_sel = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 2), 72 .cbcr_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 1), 73 .yrgb_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 0), 74 .lb_mode = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 5), 75 }; 76 77 static const struct vop_scl_regs rk3288_win_full_scl = { 78 .ext = &rk3288_win_full_scl_ext, 79 .scale_yrgb_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), 80 .scale_yrgb_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), 81 .scale_cbcr_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), 82 .scale_cbcr_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 16), 83 }; 84 85 static const struct vop_win_phy rk3288_win01_data = { 86 .scl = &rk3288_win_full_scl, 87 .data_formats = formats_win_full, 88 .nformats = ARRAY_SIZE(formats_win_full), 89 .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0), 90 .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1), 91 .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), 92 .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), 93 .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0), 94 .dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0), 95 .yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0), 96 .uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0), 97 .yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0), 98 .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16), 99 .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0), 100 .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0), 101 }; 102 103 static const struct vop_win_phy rk3288_win23_data = { 104 .data_formats = formats_win_lite, 105 .nformats = ARRAY_SIZE(formats_win_lite), 106 .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0), 107 .format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1), 108 .rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12), 109 .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0), 110 .dsp_st = VOP_REG(RK3288_WIN2_DSP_ST0, 0x1fff1fff, 0), 111 .yrgb_mst = VOP_REG(RK3288_WIN2_MST0, 0xffffffff, 0), 112 .yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 0), 113 .src_alpha_ctl = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xff, 0), 114 .dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xff, 0), 115 }; 116 117 static const struct vop_ctrl rk3288_ctrl_data = { 118 .standby = VOP_REG(RK3288_SYS_CTRL, 0x1, 22), 119 .gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), 120 .mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20), 121 .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), 122 .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), 123 .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), 124 .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), 125 .dither_down = VOP_REG(RK3288_DSP_CTRL1, 0xf, 1), 126 .dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6), 127 .data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19), 128 .out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0), 129 .pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4), 130 .htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), 131 .hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0), 132 .vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), 133 .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), 134 .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), 135 .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), 136 .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0), 137 }; 138 139 static const struct vop_reg_data rk3288_init_reg_table[] = { 140 {RK3288_SYS_CTRL, 0x00c00000}, 141 {RK3288_DSP_CTRL0, 0x00000000}, 142 {RK3288_WIN0_CTRL0, 0x00000080}, 143 {RK3288_WIN1_CTRL0, 0x00000080}, 144 /* TODO: Win2/3 support multiple area function, but we haven't found 145 * a suitable way to use it yet, so let's just use them as other windows 146 * with only area 0 enabled. 147 */ 148 {RK3288_WIN2_CTRL0, 0x00000010}, 149 {RK3288_WIN3_CTRL0, 0x00000010}, 150 }; 151 152 /* 153 * Note: rk3288 has a dedicated 'cursor' window, however, that window requires 154 * special support to get alpha blending working. For now, just use overlay 155 * window 3 for the drm cursor. 156 * 157 */ 158 static const struct vop_win_data rk3288_vop_win_data[] = { 159 { .base = 0x00, .phy = &rk3288_win01_data, 160 .type = DRM_PLANE_TYPE_PRIMARY }, 161 { .base = 0x40, .phy = &rk3288_win01_data, 162 .type = DRM_PLANE_TYPE_OVERLAY }, 163 { .base = 0x00, .phy = &rk3288_win23_data, 164 .type = DRM_PLANE_TYPE_OVERLAY }, 165 { .base = 0x50, .phy = &rk3288_win23_data, 166 .type = DRM_PLANE_TYPE_CURSOR }, 167 }; 168 169 static const int rk3288_vop_intrs[] = { 170 DSP_HOLD_VALID_INTR, 171 FS_INTR, 172 LINE_FLAG_INTR, 173 BUS_ERROR_INTR, 174 }; 175 176 static const struct vop_intr rk3288_vop_intr = { 177 .intrs = rk3288_vop_intrs, 178 .nintrs = ARRAY_SIZE(rk3288_vop_intrs), 179 .status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0), 180 .enable = VOP_REG(RK3288_INTR_CTRL0, 0xf, 4), 181 .clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8), 182 }; 183 184 static const struct vop_data rk3288_vop = { 185 .init_table = rk3288_init_reg_table, 186 .table_size = ARRAY_SIZE(rk3288_init_reg_table), 187 .intr = &rk3288_vop_intr, 188 .ctrl = &rk3288_ctrl_data, 189 .win = rk3288_vop_win_data, 190 .win_size = ARRAY_SIZE(rk3288_vop_win_data), 191 }; 192 193 static const struct vop_scl_regs rk3066_win_scl = { 194 .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), 195 .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), 196 .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), 197 .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16), 198 }; 199 200 static const struct vop_win_phy rk3036_win0_data = { 201 .scl = &rk3066_win_scl, 202 .data_formats = formats_win_full, 203 .nformats = ARRAY_SIZE(formats_win_full), 204 .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), 205 .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3), 206 .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15), 207 .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0), 208 .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0), 209 .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0), 210 .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), 211 .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), 212 .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), 213 }; 214 215 static const struct vop_win_phy rk3036_win1_data = { 216 .data_formats = formats_win_lite, 217 .nformats = ARRAY_SIZE(formats_win_lite), 218 .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), 219 .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), 220 .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), 221 .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0), 222 .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0), 223 .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0), 224 .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0), 225 .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), 226 }; 227 228 static const struct vop_win_data rk3036_vop_win_data[] = { 229 { .base = 0x00, .phy = &rk3036_win0_data, 230 .type = DRM_PLANE_TYPE_PRIMARY }, 231 { .base = 0x00, .phy = &rk3036_win1_data, 232 .type = DRM_PLANE_TYPE_CURSOR }, 233 }; 234 235 static const int rk3036_vop_intrs[] = { 236 DSP_HOLD_VALID_INTR, 237 FS_INTR, 238 LINE_FLAG_INTR, 239 BUS_ERROR_INTR, 240 }; 241 242 static const struct vop_intr rk3036_intr = { 243 .intrs = rk3036_vop_intrs, 244 .nintrs = ARRAY_SIZE(rk3036_vop_intrs), 245 .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), 246 .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), 247 .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), 248 }; 249 250 static const struct vop_ctrl rk3036_ctrl_data = { 251 .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), 252 .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), 253 .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), 254 .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), 255 .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), 256 .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), 257 .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), 258 .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), 259 }; 260 261 static const struct vop_reg_data rk3036_vop_init_reg_table[] = { 262 {RK3036_DSP_CTRL1, 0x00000000}, 263 }; 264 265 static const struct vop_data rk3036_vop = { 266 .init_table = rk3036_vop_init_reg_table, 267 .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table), 268 .ctrl = &rk3036_ctrl_data, 269 .intr = &rk3036_intr, 270 .win = rk3036_vop_win_data, 271 .win_size = ARRAY_SIZE(rk3036_vop_win_data), 272 }; 273 274 static const struct of_device_id vop_driver_dt_match[] = { 275 { .compatible = "rockchip,rk3288-vop", 276 .data = &rk3288_vop }, 277 { .compatible = "rockchip,rk3036-vop", 278 .data = &rk3036_vop }, 279 {}, 280 }; 281 MODULE_DEVICE_TABLE(of, vop_driver_dt_match); 282 283 static int vop_probe(struct platform_device *pdev) 284 { 285 struct device *dev = &pdev->dev; 286 287 if (!dev->of_node) { 288 dev_err(dev, "can't find vop devices\n"); 289 return -ENODEV; 290 } 291 292 return component_add(dev, &vop_component_ops); 293 } 294 295 static int vop_remove(struct platform_device *pdev) 296 { 297 component_del(&pdev->dev, &vop_component_ops); 298 299 return 0; 300 } 301 302 struct platform_driver vop_platform_driver = { 303 .probe = vop_probe, 304 .remove = vop_remove, 305 .driver = { 306 .name = "rockchip-vop", 307 .owner = THIS_MODULE, 308 .of_match_table = of_match_ptr(vop_driver_dt_match), 309 }, 310 }; 311 312 module_platform_driver(vop_platform_driver); 313 314 MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); 315 MODULE_DESCRIPTION("ROCKCHIP VOP Driver"); 316 MODULE_LICENSE("GPL v2"); 317