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