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 .write_mask = false,} 28 29 #define VOP_REG_MASK(off, _mask, s) \ 30 {.offset = off, \ 31 .mask = _mask, \ 32 .shift = s, \ 33 .write_mask = true,} 34 35 static const uint32_t formats_win_full[] = { 36 DRM_FORMAT_XRGB8888, 37 DRM_FORMAT_ARGB8888, 38 DRM_FORMAT_XBGR8888, 39 DRM_FORMAT_ABGR8888, 40 DRM_FORMAT_RGB888, 41 DRM_FORMAT_BGR888, 42 DRM_FORMAT_RGB565, 43 DRM_FORMAT_BGR565, 44 DRM_FORMAT_NV12, 45 DRM_FORMAT_NV16, 46 DRM_FORMAT_NV24, 47 }; 48 49 static const uint32_t formats_win_lite[] = { 50 DRM_FORMAT_XRGB8888, 51 DRM_FORMAT_ARGB8888, 52 DRM_FORMAT_XBGR8888, 53 DRM_FORMAT_ABGR8888, 54 DRM_FORMAT_RGB888, 55 DRM_FORMAT_BGR888, 56 DRM_FORMAT_RGB565, 57 DRM_FORMAT_BGR565, 58 }; 59 60 static const struct vop_scl_regs rk3036_win_scl = { 61 .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), 62 .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), 63 .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), 64 .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16), 65 }; 66 67 static const struct vop_win_phy rk3036_win0_data = { 68 .scl = &rk3036_win_scl, 69 .data_formats = formats_win_full, 70 .nformats = ARRAY_SIZE(formats_win_full), 71 .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), 72 .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3), 73 .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15), 74 .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0), 75 .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0), 76 .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0), 77 .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), 78 .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), 79 .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), 80 .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16), 81 }; 82 83 static const struct vop_win_phy rk3036_win1_data = { 84 .data_formats = formats_win_lite, 85 .nformats = ARRAY_SIZE(formats_win_lite), 86 .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), 87 .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), 88 .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), 89 .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0), 90 .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0), 91 .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0), 92 .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0), 93 .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), 94 }; 95 96 static const struct vop_win_data rk3036_vop_win_data[] = { 97 { .base = 0x00, .phy = &rk3036_win0_data, 98 .type = DRM_PLANE_TYPE_PRIMARY }, 99 { .base = 0x00, .phy = &rk3036_win1_data, 100 .type = DRM_PLANE_TYPE_CURSOR }, 101 }; 102 103 static const int rk3036_vop_intrs[] = { 104 DSP_HOLD_VALID_INTR, 105 FS_INTR, 106 LINE_FLAG_INTR, 107 BUS_ERROR_INTR, 108 }; 109 110 static const struct vop_intr rk3036_intr = { 111 .intrs = rk3036_vop_intrs, 112 .nintrs = ARRAY_SIZE(rk3036_vop_intrs), 113 .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), 114 .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), 115 .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), 116 }; 117 118 static const struct vop_ctrl rk3036_ctrl_data = { 119 .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), 120 .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), 121 .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), 122 .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), 123 .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), 124 .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), 125 .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), 126 .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), 127 .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), 128 }; 129 130 static const struct vop_reg_data rk3036_vop_init_reg_table[] = { 131 {RK3036_DSP_CTRL1, 0x00000000}, 132 }; 133 134 static const struct vop_data rk3036_vop = { 135 .init_table = rk3036_vop_init_reg_table, 136 .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table), 137 .ctrl = &rk3036_ctrl_data, 138 .intr = &rk3036_intr, 139 .win = rk3036_vop_win_data, 140 .win_size = ARRAY_SIZE(rk3036_vop_win_data), 141 }; 142 143 static const struct vop_scl_extension rk3288_win_full_scl_ext = { 144 .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31), 145 .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30), 146 .cbcr_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 28), 147 .cbcr_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 26), 148 .cbcr_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 24), 149 .yrgb_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 23), 150 .yrgb_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 22), 151 .yrgb_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 20), 152 .yrgb_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 18), 153 .yrgb_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 16), 154 .line_load_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 15), 155 .cbcr_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0x7, 12), 156 .yrgb_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0xf, 8), 157 .vsd_cbcr_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 7), 158 .vsd_cbcr_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 6), 159 .vsd_yrgb_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 5), 160 .vsd_yrgb_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 4), 161 .bic_coe_sel = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 2), 162 .cbcr_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 1), 163 .yrgb_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 0), 164 .lb_mode = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 5), 165 }; 166 167 static const struct vop_scl_regs rk3288_win_full_scl = { 168 .ext = &rk3288_win_full_scl_ext, 169 .scale_yrgb_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), 170 .scale_yrgb_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), 171 .scale_cbcr_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), 172 .scale_cbcr_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 16), 173 }; 174 175 static const struct vop_win_phy rk3288_win01_data = { 176 .scl = &rk3288_win_full_scl, 177 .data_formats = formats_win_full, 178 .nformats = ARRAY_SIZE(formats_win_full), 179 .enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0), 180 .format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1), 181 .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), 182 .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), 183 .dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0), 184 .dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0), 185 .yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0), 186 .uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0), 187 .yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0), 188 .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16), 189 .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0), 190 .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0), 191 }; 192 193 static const struct vop_win_phy rk3288_win23_data = { 194 .data_formats = formats_win_lite, 195 .nformats = ARRAY_SIZE(formats_win_lite), 196 .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0), 197 .format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1), 198 .rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12), 199 .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0), 200 .dsp_st = VOP_REG(RK3288_WIN2_DSP_ST0, 0x1fff1fff, 0), 201 .yrgb_mst = VOP_REG(RK3288_WIN2_MST0, 0xffffffff, 0), 202 .yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 0), 203 .src_alpha_ctl = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xff, 0), 204 .dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xff, 0), 205 }; 206 207 static const struct vop_ctrl rk3288_ctrl_data = { 208 .standby = VOP_REG(RK3288_SYS_CTRL, 0x1, 22), 209 .gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), 210 .mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20), 211 .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), 212 .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), 213 .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), 214 .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), 215 .dither_down = VOP_REG(RK3288_DSP_CTRL1, 0xf, 1), 216 .dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6), 217 .data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19), 218 .out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0), 219 .pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4), 220 .htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), 221 .hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0), 222 .vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), 223 .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), 224 .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), 225 .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), 226 .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12), 227 .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0), 228 }; 229 230 static const struct vop_reg_data rk3288_init_reg_table[] = { 231 {RK3288_SYS_CTRL, 0x00c00000}, 232 {RK3288_DSP_CTRL0, 0x00000000}, 233 {RK3288_WIN0_CTRL0, 0x00000080}, 234 {RK3288_WIN1_CTRL0, 0x00000080}, 235 /* TODO: Win2/3 support multiple area function, but we haven't found 236 * a suitable way to use it yet, so let's just use them as other windows 237 * with only area 0 enabled. 238 */ 239 {RK3288_WIN2_CTRL0, 0x00000010}, 240 {RK3288_WIN3_CTRL0, 0x00000010}, 241 }; 242 243 /* 244 * Note: rk3288 has a dedicated 'cursor' window, however, that window requires 245 * special support to get alpha blending working. For now, just use overlay 246 * window 3 for the drm cursor. 247 * 248 */ 249 static const struct vop_win_data rk3288_vop_win_data[] = { 250 { .base = 0x00, .phy = &rk3288_win01_data, 251 .type = DRM_PLANE_TYPE_PRIMARY }, 252 { .base = 0x40, .phy = &rk3288_win01_data, 253 .type = DRM_PLANE_TYPE_OVERLAY }, 254 { .base = 0x00, .phy = &rk3288_win23_data, 255 .type = DRM_PLANE_TYPE_OVERLAY }, 256 { .base = 0x50, .phy = &rk3288_win23_data, 257 .type = DRM_PLANE_TYPE_CURSOR }, 258 }; 259 260 static const int rk3288_vop_intrs[] = { 261 DSP_HOLD_VALID_INTR, 262 FS_INTR, 263 LINE_FLAG_INTR, 264 BUS_ERROR_INTR, 265 }; 266 267 static const struct vop_intr rk3288_vop_intr = { 268 .intrs = rk3288_vop_intrs, 269 .nintrs = ARRAY_SIZE(rk3288_vop_intrs), 270 .status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0), 271 .enable = VOP_REG(RK3288_INTR_CTRL0, 0xf, 4), 272 .clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8), 273 }; 274 275 static const struct vop_data rk3288_vop = { 276 .init_table = rk3288_init_reg_table, 277 .table_size = ARRAY_SIZE(rk3288_init_reg_table), 278 .intr = &rk3288_vop_intr, 279 .ctrl = &rk3288_ctrl_data, 280 .win = rk3288_vop_win_data, 281 .win_size = ARRAY_SIZE(rk3288_vop_win_data), 282 }; 283 284 static const struct vop_ctrl rk3399_ctrl_data = { 285 .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22), 286 .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23), 287 .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12), 288 .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13), 289 .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14), 290 .mipi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 15), 291 .dither_down = VOP_REG(RK3399_DSP_CTRL1, 0xf, 1), 292 .dither_up = VOP_REG(RK3399_DSP_CTRL1, 0x1, 6), 293 .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19), 294 .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0), 295 .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), 296 .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20), 297 .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24), 298 .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28), 299 .htotal_pw = VOP_REG(RK3399_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), 300 .hact_st_end = VOP_REG(RK3399_DSP_HACT_ST_END, 0x1fff1fff, 0), 301 .vtotal_pw = VOP_REG(RK3399_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), 302 .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0), 303 .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0), 304 .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0), 305 .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), 306 .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), 307 .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0), 308 }; 309 310 static const int rk3399_vop_intrs[] = { 311 FS_INTR, 312 0, 0, 313 LINE_FLAG_INTR, 314 0, 315 BUS_ERROR_INTR, 316 0, 0, 0, 0, 0, 0, 0, 317 DSP_HOLD_VALID_INTR, 318 }; 319 320 static const struct vop_intr rk3399_vop_intr = { 321 .intrs = rk3399_vop_intrs, 322 .nintrs = ARRAY_SIZE(rk3399_vop_intrs), 323 .status = VOP_REG_MASK(RK3399_INTR_STATUS0, 0xffff, 0), 324 .enable = VOP_REG_MASK(RK3399_INTR_EN0, 0xffff, 0), 325 .clear = VOP_REG_MASK(RK3399_INTR_CLEAR0, 0xffff, 0), 326 }; 327 328 static const struct vop_reg_data rk3399_init_reg_table[] = { 329 {RK3399_SYS_CTRL, 0x2000f800}, 330 {RK3399_DSP_CTRL0, 0x00000000}, 331 {RK3399_WIN0_CTRL0, 0x00000080}, 332 {RK3399_WIN1_CTRL0, 0x00000080}, 333 /* TODO: Win2/3 support multiple area function, but we haven't found 334 * a suitable way to use it yet, so let's just use them as other windows 335 * with only area 0 enabled. 336 */ 337 {RK3399_WIN2_CTRL0, 0x00000010}, 338 {RK3399_WIN3_CTRL0, 0x00000010}, 339 }; 340 341 static const struct vop_data rk3399_vop_big = { 342 .init_table = rk3399_init_reg_table, 343 .table_size = ARRAY_SIZE(rk3399_init_reg_table), 344 .intr = &rk3399_vop_intr, 345 .ctrl = &rk3399_ctrl_data, 346 /* 347 * rk3399 vop big windows register layout is same as rk3288. 348 */ 349 .win = rk3288_vop_win_data, 350 .win_size = ARRAY_SIZE(rk3288_vop_win_data), 351 }; 352 353 static const struct vop_win_data rk3399_vop_lit_win_data[] = { 354 { .base = 0x00, .phy = &rk3288_win01_data, 355 .type = DRM_PLANE_TYPE_PRIMARY }, 356 { .base = 0x00, .phy = &rk3288_win23_data, 357 .type = DRM_PLANE_TYPE_CURSOR}, 358 }; 359 360 static const struct vop_data rk3399_vop_lit = { 361 .init_table = rk3399_init_reg_table, 362 .table_size = ARRAY_SIZE(rk3399_init_reg_table), 363 .intr = &rk3399_vop_intr, 364 .ctrl = &rk3399_ctrl_data, 365 /* 366 * rk3399 vop lit windows register layout is same as rk3288, 367 * but cut off the win1 and win3 windows. 368 */ 369 .win = rk3399_vop_lit_win_data, 370 .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data), 371 }; 372 373 static const struct of_device_id vop_driver_dt_match[] = { 374 { .compatible = "rockchip,rk3036-vop", 375 .data = &rk3036_vop }, 376 { .compatible = "rockchip,rk3288-vop", 377 .data = &rk3288_vop }, 378 { .compatible = "rockchip,rk3399-vop-big", 379 .data = &rk3399_vop_big }, 380 { .compatible = "rockchip,rk3399-vop-lit", 381 .data = &rk3399_vop_lit }, 382 {}, 383 }; 384 MODULE_DEVICE_TABLE(of, vop_driver_dt_match); 385 386 static int vop_probe(struct platform_device *pdev) 387 { 388 struct device *dev = &pdev->dev; 389 390 if (!dev->of_node) { 391 dev_err(dev, "can't find vop devices\n"); 392 return -ENODEV; 393 } 394 395 return component_add(dev, &vop_component_ops); 396 } 397 398 static int vop_remove(struct platform_device *pdev) 399 { 400 component_del(&pdev->dev, &vop_component_ops); 401 402 return 0; 403 } 404 405 static struct platform_driver vop_platform_driver = { 406 .probe = vop_probe, 407 .remove = vop_remove, 408 .driver = { 409 .name = "rockchip-vop", 410 .of_match_table = of_match_ptr(vop_driver_dt_match), 411 }, 412 }; 413 414 module_platform_driver(vop_platform_driver); 415 416 MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); 417 MODULE_DESCRIPTION("ROCKCHIP VOP Driver"); 418 MODULE_LICENSE("GPL v2"); 419