1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io> 4 * 5 * Based on sun4i_backend.c, which is: 6 * Copyright (C) 2015 Free Electrons 7 * Copyright (C) 2015 NextThing Co 8 */ 9 10 #include <linux/component.h> 11 #include <linux/dma-mapping.h> 12 #include <linux/module.h> 13 #include <linux/of_device.h> 14 #include <linux/of_graph.h> 15 #include <linux/reset.h> 16 17 #include <drm/drm_atomic_helper.h> 18 #include <drm/drm_crtc.h> 19 #include <drm/drm_fb_cma_helper.h> 20 #include <drm/drm_framebuffer.h> 21 #include <drm/drm_gem_cma_helper.h> 22 #include <drm/drm_plane_helper.h> 23 #include <drm/drm_probe_helper.h> 24 25 #include "sun4i_drv.h" 26 #include "sun8i_mixer.h" 27 #include "sun8i_ui_layer.h" 28 #include "sun8i_vi_layer.h" 29 #include "sunxi_engine.h" 30 31 struct de2_fmt_info { 32 u32 drm_fmt; 33 u32 de2_fmt; 34 }; 35 36 static const struct de2_fmt_info de2_formats[] = { 37 { 38 .drm_fmt = DRM_FORMAT_ARGB8888, 39 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888, 40 }, 41 { 42 .drm_fmt = DRM_FORMAT_ABGR8888, 43 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888, 44 }, 45 { 46 .drm_fmt = DRM_FORMAT_RGBA8888, 47 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888, 48 }, 49 { 50 .drm_fmt = DRM_FORMAT_BGRA8888, 51 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888, 52 }, 53 { 54 .drm_fmt = DRM_FORMAT_XRGB8888, 55 .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888, 56 }, 57 { 58 .drm_fmt = DRM_FORMAT_XBGR8888, 59 .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888, 60 }, 61 { 62 .drm_fmt = DRM_FORMAT_RGBX8888, 63 .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888, 64 }, 65 { 66 .drm_fmt = DRM_FORMAT_BGRX8888, 67 .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888, 68 }, 69 { 70 .drm_fmt = DRM_FORMAT_RGB888, 71 .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, 72 }, 73 { 74 .drm_fmt = DRM_FORMAT_BGR888, 75 .de2_fmt = SUN8I_MIXER_FBFMT_BGR888, 76 }, 77 { 78 .drm_fmt = DRM_FORMAT_RGB565, 79 .de2_fmt = SUN8I_MIXER_FBFMT_RGB565, 80 }, 81 { 82 .drm_fmt = DRM_FORMAT_BGR565, 83 .de2_fmt = SUN8I_MIXER_FBFMT_BGR565, 84 }, 85 { 86 .drm_fmt = DRM_FORMAT_ARGB4444, 87 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, 88 }, 89 { 90 /* for DE2 VI layer which ignores alpha */ 91 .drm_fmt = DRM_FORMAT_XRGB4444, 92 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, 93 }, 94 { 95 .drm_fmt = DRM_FORMAT_ABGR4444, 96 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, 97 }, 98 { 99 /* for DE2 VI layer which ignores alpha */ 100 .drm_fmt = DRM_FORMAT_XBGR4444, 101 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, 102 }, 103 { 104 .drm_fmt = DRM_FORMAT_RGBA4444, 105 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, 106 }, 107 { 108 /* for DE2 VI layer which ignores alpha */ 109 .drm_fmt = DRM_FORMAT_RGBX4444, 110 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, 111 }, 112 { 113 .drm_fmt = DRM_FORMAT_BGRA4444, 114 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, 115 }, 116 { 117 /* for DE2 VI layer which ignores alpha */ 118 .drm_fmt = DRM_FORMAT_BGRX4444, 119 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, 120 }, 121 { 122 .drm_fmt = DRM_FORMAT_ARGB1555, 123 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, 124 }, 125 { 126 /* for DE2 VI layer which ignores alpha */ 127 .drm_fmt = DRM_FORMAT_XRGB1555, 128 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, 129 }, 130 { 131 .drm_fmt = DRM_FORMAT_ABGR1555, 132 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, 133 }, 134 { 135 /* for DE2 VI layer which ignores alpha */ 136 .drm_fmt = DRM_FORMAT_XBGR1555, 137 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, 138 }, 139 { 140 .drm_fmt = DRM_FORMAT_RGBA5551, 141 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, 142 }, 143 { 144 /* for DE2 VI layer which ignores alpha */ 145 .drm_fmt = DRM_FORMAT_RGBX5551, 146 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, 147 }, 148 { 149 .drm_fmt = DRM_FORMAT_BGRA5551, 150 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, 151 }, 152 { 153 /* for DE2 VI layer which ignores alpha */ 154 .drm_fmt = DRM_FORMAT_BGRX5551, 155 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, 156 }, 157 { 158 .drm_fmt = DRM_FORMAT_ARGB2101010, 159 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB2101010, 160 }, 161 { 162 .drm_fmt = DRM_FORMAT_ABGR2101010, 163 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR2101010, 164 }, 165 { 166 .drm_fmt = DRM_FORMAT_RGBA1010102, 167 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA1010102, 168 }, 169 { 170 .drm_fmt = DRM_FORMAT_BGRA1010102, 171 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA1010102, 172 }, 173 { 174 .drm_fmt = DRM_FORMAT_UYVY, 175 .de2_fmt = SUN8I_MIXER_FBFMT_UYVY, 176 }, 177 { 178 .drm_fmt = DRM_FORMAT_VYUY, 179 .de2_fmt = SUN8I_MIXER_FBFMT_VYUY, 180 }, 181 { 182 .drm_fmt = DRM_FORMAT_YUYV, 183 .de2_fmt = SUN8I_MIXER_FBFMT_YUYV, 184 }, 185 { 186 .drm_fmt = DRM_FORMAT_YVYU, 187 .de2_fmt = SUN8I_MIXER_FBFMT_YVYU, 188 }, 189 { 190 .drm_fmt = DRM_FORMAT_NV16, 191 .de2_fmt = SUN8I_MIXER_FBFMT_NV16, 192 }, 193 { 194 .drm_fmt = DRM_FORMAT_NV61, 195 .de2_fmt = SUN8I_MIXER_FBFMT_NV61, 196 }, 197 { 198 .drm_fmt = DRM_FORMAT_NV12, 199 .de2_fmt = SUN8I_MIXER_FBFMT_NV12, 200 }, 201 { 202 .drm_fmt = DRM_FORMAT_NV21, 203 .de2_fmt = SUN8I_MIXER_FBFMT_NV21, 204 }, 205 { 206 .drm_fmt = DRM_FORMAT_YUV422, 207 .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, 208 }, 209 { 210 .drm_fmt = DRM_FORMAT_YUV420, 211 .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, 212 }, 213 { 214 .drm_fmt = DRM_FORMAT_YUV411, 215 .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, 216 }, 217 { 218 .drm_fmt = DRM_FORMAT_YVU422, 219 .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, 220 }, 221 { 222 .drm_fmt = DRM_FORMAT_YVU420, 223 .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, 224 }, 225 { 226 .drm_fmt = DRM_FORMAT_YVU411, 227 .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, 228 }, 229 { 230 .drm_fmt = DRM_FORMAT_P010, 231 .de2_fmt = SUN8I_MIXER_FBFMT_P010_YUV, 232 }, 233 { 234 .drm_fmt = DRM_FORMAT_P210, 235 .de2_fmt = SUN8I_MIXER_FBFMT_P210_YUV, 236 }, 237 }; 238 239 int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format) 240 { 241 unsigned int i; 242 243 for (i = 0; i < ARRAY_SIZE(de2_formats); ++i) 244 if (de2_formats[i].drm_fmt == format) { 245 *hw_format = de2_formats[i].de2_fmt; 246 return 0; 247 } 248 249 return -EINVAL; 250 } 251 252 static void sun8i_mixer_commit(struct sunxi_engine *engine) 253 { 254 DRM_DEBUG_DRIVER("Committing changes\n"); 255 256 regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, 257 SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); 258 } 259 260 static struct drm_plane **sun8i_layers_init(struct drm_device *drm, 261 struct sunxi_engine *engine) 262 { 263 struct drm_plane **planes; 264 struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); 265 int i; 266 267 planes = devm_kcalloc(drm->dev, 268 mixer->cfg->vi_num + mixer->cfg->ui_num + 1, 269 sizeof(*planes), GFP_KERNEL); 270 if (!planes) 271 return ERR_PTR(-ENOMEM); 272 273 for (i = 0; i < mixer->cfg->vi_num; i++) { 274 struct sun8i_vi_layer *layer; 275 276 layer = sun8i_vi_layer_init_one(drm, mixer, i); 277 if (IS_ERR(layer)) { 278 dev_err(drm->dev, 279 "Couldn't initialize overlay plane\n"); 280 return ERR_CAST(layer); 281 } 282 283 planes[i] = &layer->plane; 284 } 285 286 for (i = 0; i < mixer->cfg->ui_num; i++) { 287 struct sun8i_ui_layer *layer; 288 289 layer = sun8i_ui_layer_init_one(drm, mixer, i); 290 if (IS_ERR(layer)) { 291 dev_err(drm->dev, "Couldn't initialize %s plane\n", 292 i ? "overlay" : "primary"); 293 return ERR_CAST(layer); 294 } 295 296 planes[mixer->cfg->vi_num + i] = &layer->plane; 297 } 298 299 return planes; 300 } 301 302 static void sun8i_mixer_mode_set(struct sunxi_engine *engine, 303 const struct drm_display_mode *mode) 304 { 305 struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); 306 u32 bld_base, size, val; 307 bool interlaced; 308 309 bld_base = sun8i_blender_base(mixer); 310 interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 311 size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay); 312 313 DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n", 314 mode->hdisplay, mode->vdisplay); 315 316 regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size); 317 regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size); 318 319 if (interlaced) 320 val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED; 321 else 322 val = 0; 323 324 regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base), 325 SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val); 326 327 DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", 328 interlaced ? "on" : "off"); 329 } 330 331 static const struct sunxi_engine_ops sun8i_engine_ops = { 332 .commit = sun8i_mixer_commit, 333 .layers_init = sun8i_layers_init, 334 .mode_set = sun8i_mixer_mode_set, 335 }; 336 337 static const struct regmap_config sun8i_mixer_regmap_config = { 338 .reg_bits = 32, 339 .val_bits = 32, 340 .reg_stride = 4, 341 .max_register = 0xffffc, /* guessed */ 342 }; 343 344 static int sun8i_mixer_of_get_id(struct device_node *node) 345 { 346 struct device_node *ep, *remote; 347 struct of_endpoint of_ep; 348 349 /* Output port is 1, and we want the first endpoint. */ 350 ep = of_graph_get_endpoint_by_regs(node, 1, -1); 351 if (!ep) 352 return -EINVAL; 353 354 remote = of_graph_get_remote_endpoint(ep); 355 of_node_put(ep); 356 if (!remote) 357 return -EINVAL; 358 359 of_graph_parse_endpoint(remote, &of_ep); 360 of_node_put(remote); 361 return of_ep.id; 362 } 363 364 static int sun8i_mixer_bind(struct device *dev, struct device *master, 365 void *data) 366 { 367 struct platform_device *pdev = to_platform_device(dev); 368 struct drm_device *drm = data; 369 struct sun4i_drv *drv = drm->dev_private; 370 struct sun8i_mixer *mixer; 371 void __iomem *regs; 372 unsigned int base; 373 int plane_cnt; 374 int i, ret; 375 376 /* 377 * The mixer uses single 32-bit register to store memory 378 * addresses, so that it cannot deal with 64-bit memory 379 * addresses. 380 * Restrict the DMA mask so that the mixer won't be 381 * allocated some memory that is too high. 382 */ 383 ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 384 if (ret) { 385 dev_err(dev, "Cannot do 32-bit DMA.\n"); 386 return ret; 387 } 388 389 mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); 390 if (!mixer) 391 return -ENOMEM; 392 dev_set_drvdata(dev, mixer); 393 mixer->engine.ops = &sun8i_engine_ops; 394 mixer->engine.node = dev->of_node; 395 396 if (of_find_property(dev->of_node, "iommus", NULL)) { 397 /* 398 * This assume we have the same DMA constraints for 399 * all our the mixers in our pipeline. This sounds 400 * bad, but it has always been the case for us, and 401 * DRM doesn't do per-device allocation either, so we 402 * would need to fix DRM first... 403 */ 404 ret = of_dma_configure(drm->dev, dev->of_node, true); 405 if (ret) 406 return ret; 407 } 408 409 /* 410 * While this function can fail, we shouldn't do anything 411 * if this happens. Some early DE2 DT entries don't provide 412 * mixer id but work nevertheless because matching between 413 * TCON and mixer is done by comparing node pointers (old 414 * way) instead comparing ids. If this function fails and 415 * id is needed, it will fail during id matching anyway. 416 */ 417 mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node); 418 419 mixer->cfg = of_device_get_match_data(dev); 420 if (!mixer->cfg) 421 return -EINVAL; 422 423 regs = devm_platform_ioremap_resource(pdev, 0); 424 if (IS_ERR(regs)) 425 return PTR_ERR(regs); 426 427 mixer->engine.regs = devm_regmap_init_mmio(dev, regs, 428 &sun8i_mixer_regmap_config); 429 if (IS_ERR(mixer->engine.regs)) { 430 dev_err(dev, "Couldn't create the mixer regmap\n"); 431 return PTR_ERR(mixer->engine.regs); 432 } 433 434 mixer->reset = devm_reset_control_get(dev, NULL); 435 if (IS_ERR(mixer->reset)) { 436 dev_err(dev, "Couldn't get our reset line\n"); 437 return PTR_ERR(mixer->reset); 438 } 439 440 ret = reset_control_deassert(mixer->reset); 441 if (ret) { 442 dev_err(dev, "Couldn't deassert our reset line\n"); 443 return ret; 444 } 445 446 mixer->bus_clk = devm_clk_get(dev, "bus"); 447 if (IS_ERR(mixer->bus_clk)) { 448 dev_err(dev, "Couldn't get the mixer bus clock\n"); 449 ret = PTR_ERR(mixer->bus_clk); 450 goto err_assert_reset; 451 } 452 clk_prepare_enable(mixer->bus_clk); 453 454 mixer->mod_clk = devm_clk_get(dev, "mod"); 455 if (IS_ERR(mixer->mod_clk)) { 456 dev_err(dev, "Couldn't get the mixer module clock\n"); 457 ret = PTR_ERR(mixer->mod_clk); 458 goto err_disable_bus_clk; 459 } 460 461 /* 462 * It seems that we need to enforce that rate for whatever 463 * reason for the mixer to be functional. Make sure it's the 464 * case. 465 */ 466 if (mixer->cfg->mod_rate) 467 clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); 468 469 clk_prepare_enable(mixer->mod_clk); 470 471 list_add_tail(&mixer->engine.list, &drv->engine_list); 472 473 base = sun8i_blender_base(mixer); 474 475 /* Reset registers and disable unused sub-engines */ 476 if (mixer->cfg->is_de3) { 477 for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) 478 regmap_write(mixer->engine.regs, i, 0); 479 480 regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0); 481 regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0); 482 regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0); 483 regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0); 484 regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0); 485 regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0); 486 regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0); 487 regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0); 488 regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0); 489 regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0); 490 } else { 491 for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4) 492 regmap_write(mixer->engine.regs, i, 0); 493 494 regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0); 495 regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0); 496 regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0); 497 regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0); 498 regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0); 499 regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0); 500 regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0); 501 } 502 503 /* Enable the mixer */ 504 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, 505 SUN8I_MIXER_GLOBAL_CTL_RT_EN); 506 507 /* Set background color to black */ 508 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), 509 SUN8I_MIXER_BLEND_COLOR_BLACK); 510 511 /* 512 * Set fill color of bottom plane to black. Generally not needed 513 * except when VI plane is at bottom (zpos = 0) and enabled. 514 */ 515 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), 516 SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); 517 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), 518 SUN8I_MIXER_BLEND_COLOR_BLACK); 519 520 plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; 521 for (i = 0; i < plane_cnt; i++) 522 regmap_write(mixer->engine.regs, 523 SUN8I_MIXER_BLEND_MODE(base, i), 524 SUN8I_MIXER_BLEND_MODE_DEF); 525 526 regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), 527 SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); 528 529 return 0; 530 531 err_disable_bus_clk: 532 clk_disable_unprepare(mixer->bus_clk); 533 err_assert_reset: 534 reset_control_assert(mixer->reset); 535 return ret; 536 } 537 538 static void sun8i_mixer_unbind(struct device *dev, struct device *master, 539 void *data) 540 { 541 struct sun8i_mixer *mixer = dev_get_drvdata(dev); 542 543 list_del(&mixer->engine.list); 544 545 clk_disable_unprepare(mixer->mod_clk); 546 clk_disable_unprepare(mixer->bus_clk); 547 reset_control_assert(mixer->reset); 548 } 549 550 static const struct component_ops sun8i_mixer_ops = { 551 .bind = sun8i_mixer_bind, 552 .unbind = sun8i_mixer_unbind, 553 }; 554 555 static int sun8i_mixer_probe(struct platform_device *pdev) 556 { 557 return component_add(&pdev->dev, &sun8i_mixer_ops); 558 } 559 560 static int sun8i_mixer_remove(struct platform_device *pdev) 561 { 562 component_del(&pdev->dev, &sun8i_mixer_ops); 563 564 return 0; 565 } 566 567 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { 568 .ccsc = CCSC_MIXER0_LAYOUT, 569 .scaler_mask = 0xf, 570 .scanline_yuv = 2048, 571 .ui_num = 3, 572 .vi_num = 1, 573 }; 574 575 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { 576 .ccsc = CCSC_MIXER1_LAYOUT, 577 .scaler_mask = 0x3, 578 .scanline_yuv = 2048, 579 .ui_num = 1, 580 .vi_num = 1, 581 }; 582 583 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { 584 .ccsc = CCSC_MIXER0_LAYOUT, 585 .mod_rate = 432000000, 586 .scaler_mask = 0xf, 587 .scanline_yuv = 2048, 588 .ui_num = 3, 589 .vi_num = 1, 590 }; 591 592 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { 593 .ccsc = CCSC_MIXER0_LAYOUT, 594 .mod_rate = 297000000, 595 .scaler_mask = 0xf, 596 .scanline_yuv = 2048, 597 .ui_num = 3, 598 .vi_num = 1, 599 }; 600 601 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { 602 .ccsc = CCSC_MIXER1_LAYOUT, 603 .mod_rate = 297000000, 604 .scaler_mask = 0x3, 605 .scanline_yuv = 2048, 606 .ui_num = 1, 607 .vi_num = 1, 608 }; 609 610 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { 611 .vi_num = 2, 612 .ui_num = 1, 613 .scaler_mask = 0x3, 614 .scanline_yuv = 2048, 615 .ccsc = CCSC_MIXER0_LAYOUT, 616 .mod_rate = 150000000, 617 }; 618 619 static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = { 620 .ccsc = CCSC_D1_MIXER0_LAYOUT, 621 .mod_rate = 297000000, 622 .scaler_mask = 0x3, 623 .scanline_yuv = 2048, 624 .ui_num = 1, 625 .vi_num = 1, 626 }; 627 628 static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = { 629 .ccsc = CCSC_MIXER1_LAYOUT, 630 .mod_rate = 297000000, 631 .scaler_mask = 0x1, 632 .scanline_yuv = 1024, 633 .ui_num = 0, 634 .vi_num = 1, 635 }; 636 637 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { 638 .ccsc = CCSC_MIXER0_LAYOUT, 639 .mod_rate = 297000000, 640 .scaler_mask = 0xf, 641 .scanline_yuv = 4096, 642 .ui_num = 3, 643 .vi_num = 1, 644 }; 645 646 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { 647 .ccsc = CCSC_MIXER1_LAYOUT, 648 .mod_rate = 297000000, 649 .scaler_mask = 0x3, 650 .scanline_yuv = 2048, 651 .ui_num = 1, 652 .vi_num = 1, 653 }; 654 655 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { 656 .ccsc = CCSC_MIXER0_LAYOUT, 657 .is_de3 = true, 658 .mod_rate = 600000000, 659 .scaler_mask = 0xf, 660 .scanline_yuv = 4096, 661 .ui_num = 3, 662 .vi_num = 1, 663 }; 664 665 static const struct of_device_id sun8i_mixer_of_table[] = { 666 { 667 .compatible = "allwinner,sun8i-a83t-de2-mixer-0", 668 .data = &sun8i_a83t_mixer0_cfg, 669 }, 670 { 671 .compatible = "allwinner,sun8i-a83t-de2-mixer-1", 672 .data = &sun8i_a83t_mixer1_cfg, 673 }, 674 { 675 .compatible = "allwinner,sun8i-h3-de2-mixer-0", 676 .data = &sun8i_h3_mixer0_cfg, 677 }, 678 { 679 .compatible = "allwinner,sun8i-r40-de2-mixer-0", 680 .data = &sun8i_r40_mixer0_cfg, 681 }, 682 { 683 .compatible = "allwinner,sun8i-r40-de2-mixer-1", 684 .data = &sun8i_r40_mixer1_cfg, 685 }, 686 { 687 .compatible = "allwinner,sun8i-v3s-de2-mixer", 688 .data = &sun8i_v3s_mixer_cfg, 689 }, 690 { 691 .compatible = "allwinner,sun20i-d1-de2-mixer-0", 692 .data = &sun20i_d1_mixer0_cfg, 693 }, 694 { 695 .compatible = "allwinner,sun20i-d1-de2-mixer-1", 696 .data = &sun20i_d1_mixer1_cfg, 697 }, 698 { 699 .compatible = "allwinner,sun50i-a64-de2-mixer-0", 700 .data = &sun50i_a64_mixer0_cfg, 701 }, 702 { 703 .compatible = "allwinner,sun50i-a64-de2-mixer-1", 704 .data = &sun50i_a64_mixer1_cfg, 705 }, 706 { 707 .compatible = "allwinner,sun50i-h6-de3-mixer-0", 708 .data = &sun50i_h6_mixer0_cfg, 709 }, 710 { } 711 }; 712 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); 713 714 static struct platform_driver sun8i_mixer_platform_driver = { 715 .probe = sun8i_mixer_probe, 716 .remove = sun8i_mixer_remove, 717 .driver = { 718 .name = "sun8i-mixer", 719 .of_match_table = sun8i_mixer_of_table, 720 }, 721 }; 722 module_platform_driver(sun8i_mixer_platform_driver); 723 724 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); 725 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver"); 726 MODULE_LICENSE("GPL"); 727