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 int plane_cnt; 372 int i, ret; 373 374 /* 375 * The mixer uses single 32-bit register to store memory 376 * addresses, so that it cannot deal with 64-bit memory 377 * addresses. 378 * Restrict the DMA mask so that the mixer won't be 379 * allocated some memory that is too high. 380 */ 381 ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 382 if (ret) { 383 dev_err(dev, "Cannot do 32-bit DMA.\n"); 384 return ret; 385 } 386 387 mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); 388 if (!mixer) 389 return -ENOMEM; 390 dev_set_drvdata(dev, mixer); 391 mixer->engine.ops = &sun8i_engine_ops; 392 mixer->engine.node = dev->of_node; 393 394 /* 395 * While this function can fail, we shouldn't do anything 396 * if this happens. Some early DE2 DT entries don't provide 397 * mixer id but work nevertheless because matching between 398 * TCON and mixer is done by comparing node pointers (old 399 * way) instead comparing ids. If this function fails and 400 * id is needed, it will fail during id matching anyway. 401 */ 402 mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node); 403 404 mixer->cfg = of_device_get_match_data(dev); 405 if (!mixer->cfg) 406 return -EINVAL; 407 408 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 409 regs = devm_ioremap_resource(dev, res); 410 if (IS_ERR(regs)) 411 return PTR_ERR(regs); 412 413 mixer->engine.regs = devm_regmap_init_mmio(dev, regs, 414 &sun8i_mixer_regmap_config); 415 if (IS_ERR(mixer->engine.regs)) { 416 dev_err(dev, "Couldn't create the mixer regmap\n"); 417 return PTR_ERR(mixer->engine.regs); 418 } 419 420 mixer->reset = devm_reset_control_get(dev, NULL); 421 if (IS_ERR(mixer->reset)) { 422 dev_err(dev, "Couldn't get our reset line\n"); 423 return PTR_ERR(mixer->reset); 424 } 425 426 ret = reset_control_deassert(mixer->reset); 427 if (ret) { 428 dev_err(dev, "Couldn't deassert our reset line\n"); 429 return ret; 430 } 431 432 mixer->bus_clk = devm_clk_get(dev, "bus"); 433 if (IS_ERR(mixer->bus_clk)) { 434 dev_err(dev, "Couldn't get the mixer bus clock\n"); 435 ret = PTR_ERR(mixer->bus_clk); 436 goto err_assert_reset; 437 } 438 clk_prepare_enable(mixer->bus_clk); 439 440 mixer->mod_clk = devm_clk_get(dev, "mod"); 441 if (IS_ERR(mixer->mod_clk)) { 442 dev_err(dev, "Couldn't get the mixer module clock\n"); 443 ret = PTR_ERR(mixer->mod_clk); 444 goto err_disable_bus_clk; 445 } 446 447 /* 448 * It seems that we need to enforce that rate for whatever 449 * reason for the mixer to be functional. Make sure it's the 450 * case. 451 */ 452 if (mixer->cfg->mod_rate) 453 clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); 454 455 clk_prepare_enable(mixer->mod_clk); 456 457 list_add_tail(&mixer->engine.list, &drv->engine_list); 458 459 /* Reset the registers */ 460 for (i = 0x0; i < 0x20000; i += 4) 461 regmap_write(mixer->engine.regs, i, 0); 462 463 /* Enable the mixer */ 464 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, 465 SUN8I_MIXER_GLOBAL_CTL_RT_EN); 466 467 /* Set background color to black */ 468 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR, 469 SUN8I_MIXER_BLEND_COLOR_BLACK); 470 471 /* 472 * Set fill color of bottom plane to black. Generally not needed 473 * except when VI plane is at bottom (zpos = 0) and enabled. 474 */ 475 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, 476 SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); 477 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), 478 SUN8I_MIXER_BLEND_COLOR_BLACK); 479 480 plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; 481 for (i = 0; i < plane_cnt; i++) 482 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(i), 483 SUN8I_MIXER_BLEND_MODE_DEF); 484 485 regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, 486 SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); 487 488 return 0; 489 490 err_disable_bus_clk: 491 clk_disable_unprepare(mixer->bus_clk); 492 err_assert_reset: 493 reset_control_assert(mixer->reset); 494 return ret; 495 } 496 497 static void sun8i_mixer_unbind(struct device *dev, struct device *master, 498 void *data) 499 { 500 struct sun8i_mixer *mixer = dev_get_drvdata(dev); 501 502 list_del(&mixer->engine.list); 503 504 clk_disable_unprepare(mixer->mod_clk); 505 clk_disable_unprepare(mixer->bus_clk); 506 reset_control_assert(mixer->reset); 507 } 508 509 static const struct component_ops sun8i_mixer_ops = { 510 .bind = sun8i_mixer_bind, 511 .unbind = sun8i_mixer_unbind, 512 }; 513 514 static int sun8i_mixer_probe(struct platform_device *pdev) 515 { 516 return component_add(&pdev->dev, &sun8i_mixer_ops); 517 } 518 519 static int sun8i_mixer_remove(struct platform_device *pdev) 520 { 521 component_del(&pdev->dev, &sun8i_mixer_ops); 522 523 return 0; 524 } 525 526 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { 527 .ccsc = 0, 528 .scaler_mask = 0xf, 529 .ui_num = 3, 530 .vi_num = 1, 531 }; 532 533 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { 534 .ccsc = 1, 535 .scaler_mask = 0x3, 536 .ui_num = 1, 537 .vi_num = 1, 538 }; 539 540 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { 541 .ccsc = 0, 542 .mod_rate = 432000000, 543 .scaler_mask = 0xf, 544 .ui_num = 3, 545 .vi_num = 1, 546 }; 547 548 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { 549 .vi_num = 2, 550 .ui_num = 1, 551 .scaler_mask = 0x3, 552 .ccsc = 0, 553 .mod_rate = 150000000, 554 }; 555 556 static const struct of_device_id sun8i_mixer_of_table[] = { 557 { 558 .compatible = "allwinner,sun8i-a83t-de2-mixer-0", 559 .data = &sun8i_a83t_mixer0_cfg, 560 }, 561 { 562 .compatible = "allwinner,sun8i-a83t-de2-mixer-1", 563 .data = &sun8i_a83t_mixer1_cfg, 564 }, 565 { 566 .compatible = "allwinner,sun8i-h3-de2-mixer-0", 567 .data = &sun8i_h3_mixer0_cfg, 568 }, 569 { 570 .compatible = "allwinner,sun8i-v3s-de2-mixer", 571 .data = &sun8i_v3s_mixer_cfg, 572 }, 573 { } 574 }; 575 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); 576 577 static struct platform_driver sun8i_mixer_platform_driver = { 578 .probe = sun8i_mixer_probe, 579 .remove = sun8i_mixer_remove, 580 .driver = { 581 .name = "sun8i-mixer", 582 .of_match_table = sun8i_mixer_of_table, 583 }, 584 }; 585 module_platform_driver(sun8i_mixer_platform_driver); 586 587 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); 588 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver"); 589 MODULE_LICENSE("GPL"); 590