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_crtc_helper.h> 18 #include <drm/drm_fb_cma_helper.h> 19 #include <drm/drm_gem_cma_helper.h> 20 #include <drm/drm_plane_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 *port, *ep; 329 int ret = -EINVAL; 330 331 /* output is port 1 */ 332 port = of_graph_get_port_by_id(node, 1); 333 if (!port) 334 return -EINVAL; 335 336 /* try to find downstream endpoint */ 337 for_each_available_child_of_node(port, ep) { 338 struct device_node *remote; 339 u32 reg; 340 341 remote = of_graph_get_remote_endpoint(ep); 342 if (!remote) 343 continue; 344 345 ret = of_property_read_u32(remote, "reg", ®); 346 if (!ret) { 347 of_node_put(remote); 348 of_node_put(ep); 349 of_node_put(port); 350 351 return reg; 352 } 353 354 of_node_put(remote); 355 } 356 357 of_node_put(port); 358 359 return ret; 360 } 361 362 static int sun8i_mixer_bind(struct device *dev, struct device *master, 363 void *data) 364 { 365 struct platform_device *pdev = to_platform_device(dev); 366 struct drm_device *drm = data; 367 struct sun4i_drv *drv = drm->dev_private; 368 struct sun8i_mixer *mixer; 369 struct resource *res; 370 void __iomem *regs; 371 unsigned int base; 372 int plane_cnt; 373 int i, ret; 374 375 /* 376 * The mixer uses single 32-bit register to store memory 377 * addresses, so that it cannot deal with 64-bit memory 378 * addresses. 379 * Restrict the DMA mask so that the mixer won't be 380 * allocated some memory that is too high. 381 */ 382 ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 383 if (ret) { 384 dev_err(dev, "Cannot do 32-bit DMA.\n"); 385 return ret; 386 } 387 388 mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); 389 if (!mixer) 390 return -ENOMEM; 391 dev_set_drvdata(dev, mixer); 392 mixer->engine.ops = &sun8i_engine_ops; 393 mixer->engine.node = dev->of_node; 394 395 /* 396 * While this function can fail, we shouldn't do anything 397 * if this happens. Some early DE2 DT entries don't provide 398 * mixer id but work nevertheless because matching between 399 * TCON and mixer is done by comparing node pointers (old 400 * way) instead comparing ids. If this function fails and 401 * id is needed, it will fail during id matching anyway. 402 */ 403 mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node); 404 405 mixer->cfg = of_device_get_match_data(dev); 406 if (!mixer->cfg) 407 return -EINVAL; 408 409 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 410 regs = devm_ioremap_resource(dev, res); 411 if (IS_ERR(regs)) 412 return PTR_ERR(regs); 413 414 mixer->engine.regs = devm_regmap_init_mmio(dev, regs, 415 &sun8i_mixer_regmap_config); 416 if (IS_ERR(mixer->engine.regs)) { 417 dev_err(dev, "Couldn't create the mixer regmap\n"); 418 return PTR_ERR(mixer->engine.regs); 419 } 420 421 mixer->reset = devm_reset_control_get(dev, NULL); 422 if (IS_ERR(mixer->reset)) { 423 dev_err(dev, "Couldn't get our reset line\n"); 424 return PTR_ERR(mixer->reset); 425 } 426 427 ret = reset_control_deassert(mixer->reset); 428 if (ret) { 429 dev_err(dev, "Couldn't deassert our reset line\n"); 430 return ret; 431 } 432 433 mixer->bus_clk = devm_clk_get(dev, "bus"); 434 if (IS_ERR(mixer->bus_clk)) { 435 dev_err(dev, "Couldn't get the mixer bus clock\n"); 436 ret = PTR_ERR(mixer->bus_clk); 437 goto err_assert_reset; 438 } 439 clk_prepare_enable(mixer->bus_clk); 440 441 mixer->mod_clk = devm_clk_get(dev, "mod"); 442 if (IS_ERR(mixer->mod_clk)) { 443 dev_err(dev, "Couldn't get the mixer module clock\n"); 444 ret = PTR_ERR(mixer->mod_clk); 445 goto err_disable_bus_clk; 446 } 447 448 /* 449 * It seems that we need to enforce that rate for whatever 450 * reason for the mixer to be functional. Make sure it's the 451 * case. 452 */ 453 if (mixer->cfg->mod_rate) 454 clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); 455 456 clk_prepare_enable(mixer->mod_clk); 457 458 list_add_tail(&mixer->engine.list, &drv->engine_list); 459 460 base = sun8i_blender_base(mixer); 461 462 /* Reset registers and disable unused sub-engines */ 463 if (mixer->cfg->is_de3) { 464 for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) 465 regmap_write(mixer->engine.regs, i, 0); 466 467 regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0); 468 regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0); 469 regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0); 470 regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0); 471 regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0); 472 regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0); 473 regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0); 474 regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0); 475 regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0); 476 regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0); 477 } else { 478 for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4) 479 regmap_write(mixer->engine.regs, i, 0); 480 481 regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0); 482 regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0); 483 regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0); 484 regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0); 485 regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0); 486 regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0); 487 regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0); 488 } 489 490 /* Enable the mixer */ 491 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, 492 SUN8I_MIXER_GLOBAL_CTL_RT_EN); 493 494 /* Set background color to black */ 495 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), 496 SUN8I_MIXER_BLEND_COLOR_BLACK); 497 498 /* 499 * Set fill color of bottom plane to black. Generally not needed 500 * except when VI plane is at bottom (zpos = 0) and enabled. 501 */ 502 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), 503 SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); 504 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), 505 SUN8I_MIXER_BLEND_COLOR_BLACK); 506 507 plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; 508 for (i = 0; i < plane_cnt; i++) 509 regmap_write(mixer->engine.regs, 510 SUN8I_MIXER_BLEND_MODE(base, i), 511 SUN8I_MIXER_BLEND_MODE_DEF); 512 513 regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), 514 SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); 515 516 return 0; 517 518 err_disable_bus_clk: 519 clk_disable_unprepare(mixer->bus_clk); 520 err_assert_reset: 521 reset_control_assert(mixer->reset); 522 return ret; 523 } 524 525 static void sun8i_mixer_unbind(struct device *dev, struct device *master, 526 void *data) 527 { 528 struct sun8i_mixer *mixer = dev_get_drvdata(dev); 529 530 list_del(&mixer->engine.list); 531 532 clk_disable_unprepare(mixer->mod_clk); 533 clk_disable_unprepare(mixer->bus_clk); 534 reset_control_assert(mixer->reset); 535 } 536 537 static const struct component_ops sun8i_mixer_ops = { 538 .bind = sun8i_mixer_bind, 539 .unbind = sun8i_mixer_unbind, 540 }; 541 542 static int sun8i_mixer_probe(struct platform_device *pdev) 543 { 544 return component_add(&pdev->dev, &sun8i_mixer_ops); 545 } 546 547 static int sun8i_mixer_remove(struct platform_device *pdev) 548 { 549 component_del(&pdev->dev, &sun8i_mixer_ops); 550 551 return 0; 552 } 553 554 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { 555 .ccsc = 0, 556 .scaler_mask = 0xf, 557 .ui_num = 3, 558 .vi_num = 1, 559 }; 560 561 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { 562 .ccsc = 1, 563 .scaler_mask = 0x3, 564 .ui_num = 1, 565 .vi_num = 1, 566 }; 567 568 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { 569 .ccsc = 0, 570 .mod_rate = 432000000, 571 .scaler_mask = 0xf, 572 .ui_num = 3, 573 .vi_num = 1, 574 }; 575 576 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { 577 .ccsc = 0, 578 .mod_rate = 297000000, 579 .scaler_mask = 0xf, 580 .ui_num = 3, 581 .vi_num = 1, 582 }; 583 584 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { 585 .ccsc = 1, 586 .mod_rate = 297000000, 587 .scaler_mask = 0x3, 588 .ui_num = 1, 589 .vi_num = 1, 590 }; 591 592 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { 593 .vi_num = 2, 594 .ui_num = 1, 595 .scaler_mask = 0x3, 596 .ccsc = 0, 597 .mod_rate = 150000000, 598 }; 599 600 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { 601 .ccsc = 0, 602 .mod_rate = 297000000, 603 .scaler_mask = 0xf, 604 .ui_num = 3, 605 .vi_num = 1, 606 }; 607 608 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { 609 .ccsc = 1, 610 .mod_rate = 297000000, 611 .scaler_mask = 0x3, 612 .ui_num = 1, 613 .vi_num = 1, 614 }; 615 616 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { 617 .ccsc = 0, 618 .is_de3 = true, 619 .mod_rate = 600000000, 620 .scaler_mask = 0xf, 621 .ui_num = 3, 622 .vi_num = 1, 623 }; 624 625 static const struct of_device_id sun8i_mixer_of_table[] = { 626 { 627 .compatible = "allwinner,sun8i-a83t-de2-mixer-0", 628 .data = &sun8i_a83t_mixer0_cfg, 629 }, 630 { 631 .compatible = "allwinner,sun8i-a83t-de2-mixer-1", 632 .data = &sun8i_a83t_mixer1_cfg, 633 }, 634 { 635 .compatible = "allwinner,sun8i-h3-de2-mixer-0", 636 .data = &sun8i_h3_mixer0_cfg, 637 }, 638 { 639 .compatible = "allwinner,sun8i-r40-de2-mixer-0", 640 .data = &sun8i_r40_mixer0_cfg, 641 }, 642 { 643 .compatible = "allwinner,sun8i-r40-de2-mixer-1", 644 .data = &sun8i_r40_mixer1_cfg, 645 }, 646 { 647 .compatible = "allwinner,sun8i-v3s-de2-mixer", 648 .data = &sun8i_v3s_mixer_cfg, 649 }, 650 { 651 .compatible = "allwinner,sun50i-a64-de2-mixer-0", 652 .data = &sun50i_a64_mixer0_cfg, 653 }, 654 { 655 .compatible = "allwinner,sun50i-a64-de2-mixer-1", 656 .data = &sun50i_a64_mixer1_cfg, 657 }, 658 { 659 .compatible = "allwinner,sun50i-h6-de3-mixer-0", 660 .data = &sun50i_h6_mixer0_cfg, 661 }, 662 { } 663 }; 664 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); 665 666 static struct platform_driver sun8i_mixer_platform_driver = { 667 .probe = sun8i_mixer_probe, 668 .remove = sun8i_mixer_remove, 669 .driver = { 670 .name = "sun8i-mixer", 671 .of_match_table = sun8i_mixer_of_table, 672 }, 673 }; 674 module_platform_driver(sun8i_mixer_platform_driver); 675 676 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); 677 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver"); 678 MODULE_LICENSE("GPL"); 679