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/reset.h> 25 #include <linux/of_device.h> 26 27 #include "sun4i_drv.h" 28 #include "sun8i_mixer.h" 29 #include "sun8i_ui_layer.h" 30 #include "sun8i_vi_layer.h" 31 #include "sunxi_engine.h" 32 33 static const struct de2_fmt_info de2_formats[] = { 34 { 35 .drm_fmt = DRM_FORMAT_ARGB8888, 36 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888, 37 .rgb = true, 38 .csc = SUN8I_CSC_MODE_OFF, 39 }, 40 { 41 .drm_fmt = DRM_FORMAT_ABGR8888, 42 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888, 43 .rgb = true, 44 .csc = SUN8I_CSC_MODE_OFF, 45 }, 46 { 47 .drm_fmt = DRM_FORMAT_RGBA8888, 48 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888, 49 .rgb = true, 50 .csc = SUN8I_CSC_MODE_OFF, 51 }, 52 { 53 .drm_fmt = DRM_FORMAT_BGRA8888, 54 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888, 55 .rgb = true, 56 .csc = SUN8I_CSC_MODE_OFF, 57 }, 58 { 59 .drm_fmt = DRM_FORMAT_XRGB8888, 60 .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888, 61 .rgb = true, 62 .csc = SUN8I_CSC_MODE_OFF, 63 }, 64 { 65 .drm_fmt = DRM_FORMAT_XBGR8888, 66 .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888, 67 .rgb = true, 68 .csc = SUN8I_CSC_MODE_OFF, 69 }, 70 { 71 .drm_fmt = DRM_FORMAT_RGBX8888, 72 .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888, 73 .rgb = true, 74 .csc = SUN8I_CSC_MODE_OFF, 75 }, 76 { 77 .drm_fmt = DRM_FORMAT_BGRX8888, 78 .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888, 79 .rgb = true, 80 .csc = SUN8I_CSC_MODE_OFF, 81 }, 82 { 83 .drm_fmt = DRM_FORMAT_RGB888, 84 .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, 85 .rgb = true, 86 .csc = SUN8I_CSC_MODE_OFF, 87 }, 88 { 89 .drm_fmt = DRM_FORMAT_BGR888, 90 .de2_fmt = SUN8I_MIXER_FBFMT_BGR888, 91 .rgb = true, 92 .csc = SUN8I_CSC_MODE_OFF, 93 }, 94 { 95 .drm_fmt = DRM_FORMAT_RGB565, 96 .de2_fmt = SUN8I_MIXER_FBFMT_RGB565, 97 .rgb = true, 98 .csc = SUN8I_CSC_MODE_OFF, 99 }, 100 { 101 .drm_fmt = DRM_FORMAT_BGR565, 102 .de2_fmt = SUN8I_MIXER_FBFMT_BGR565, 103 .rgb = true, 104 .csc = SUN8I_CSC_MODE_OFF, 105 }, 106 { 107 .drm_fmt = DRM_FORMAT_ARGB4444, 108 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, 109 .rgb = true, 110 .csc = SUN8I_CSC_MODE_OFF, 111 }, 112 { 113 .drm_fmt = DRM_FORMAT_ABGR4444, 114 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, 115 .rgb = true, 116 .csc = SUN8I_CSC_MODE_OFF, 117 }, 118 { 119 .drm_fmt = DRM_FORMAT_RGBA4444, 120 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, 121 .rgb = true, 122 .csc = SUN8I_CSC_MODE_OFF, 123 }, 124 { 125 .drm_fmt = DRM_FORMAT_BGRA4444, 126 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, 127 .rgb = true, 128 .csc = SUN8I_CSC_MODE_OFF, 129 }, 130 { 131 .drm_fmt = DRM_FORMAT_ARGB1555, 132 .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, 133 .rgb = true, 134 .csc = SUN8I_CSC_MODE_OFF, 135 }, 136 { 137 .drm_fmt = DRM_FORMAT_ABGR1555, 138 .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, 139 .rgb = true, 140 .csc = SUN8I_CSC_MODE_OFF, 141 }, 142 { 143 .drm_fmt = DRM_FORMAT_RGBA5551, 144 .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, 145 .rgb = true, 146 .csc = SUN8I_CSC_MODE_OFF, 147 }, 148 { 149 .drm_fmt = DRM_FORMAT_BGRA5551, 150 .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, 151 .rgb = true, 152 .csc = SUN8I_CSC_MODE_OFF, 153 }, 154 { 155 .drm_fmt = DRM_FORMAT_UYVY, 156 .de2_fmt = SUN8I_MIXER_FBFMT_UYVY, 157 .rgb = false, 158 .csc = SUN8I_CSC_MODE_YUV2RGB, 159 }, 160 { 161 .drm_fmt = DRM_FORMAT_VYUY, 162 .de2_fmt = SUN8I_MIXER_FBFMT_VYUY, 163 .rgb = false, 164 .csc = SUN8I_CSC_MODE_YUV2RGB, 165 }, 166 { 167 .drm_fmt = DRM_FORMAT_YUYV, 168 .de2_fmt = SUN8I_MIXER_FBFMT_YUYV, 169 .rgb = false, 170 .csc = SUN8I_CSC_MODE_YUV2RGB, 171 }, 172 { 173 .drm_fmt = DRM_FORMAT_YVYU, 174 .de2_fmt = SUN8I_MIXER_FBFMT_YVYU, 175 .rgb = false, 176 .csc = SUN8I_CSC_MODE_YUV2RGB, 177 }, 178 { 179 .drm_fmt = DRM_FORMAT_NV16, 180 .de2_fmt = SUN8I_MIXER_FBFMT_NV16, 181 .rgb = false, 182 .csc = SUN8I_CSC_MODE_YUV2RGB, 183 }, 184 { 185 .drm_fmt = DRM_FORMAT_NV61, 186 .de2_fmt = SUN8I_MIXER_FBFMT_NV61, 187 .rgb = false, 188 .csc = SUN8I_CSC_MODE_YUV2RGB, 189 }, 190 { 191 .drm_fmt = DRM_FORMAT_NV12, 192 .de2_fmt = SUN8I_MIXER_FBFMT_NV12, 193 .rgb = false, 194 .csc = SUN8I_CSC_MODE_YUV2RGB, 195 }, 196 { 197 .drm_fmt = DRM_FORMAT_NV21, 198 .de2_fmt = SUN8I_MIXER_FBFMT_NV21, 199 .rgb = false, 200 .csc = SUN8I_CSC_MODE_YUV2RGB, 201 }, 202 { 203 .drm_fmt = DRM_FORMAT_YUV444, 204 .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, 205 .rgb = true, 206 .csc = SUN8I_CSC_MODE_YUV2RGB, 207 }, 208 { 209 .drm_fmt = DRM_FORMAT_YUV422, 210 .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, 211 .rgb = false, 212 .csc = SUN8I_CSC_MODE_YUV2RGB, 213 }, 214 { 215 .drm_fmt = DRM_FORMAT_YUV420, 216 .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, 217 .rgb = false, 218 .csc = SUN8I_CSC_MODE_YUV2RGB, 219 }, 220 { 221 .drm_fmt = DRM_FORMAT_YUV411, 222 .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, 223 .rgb = false, 224 .csc = SUN8I_CSC_MODE_YUV2RGB, 225 }, 226 { 227 .drm_fmt = DRM_FORMAT_YVU444, 228 .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, 229 .rgb = true, 230 .csc = SUN8I_CSC_MODE_YVU2RGB, 231 }, 232 { 233 .drm_fmt = DRM_FORMAT_YVU422, 234 .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, 235 .rgb = false, 236 .csc = SUN8I_CSC_MODE_YVU2RGB, 237 }, 238 { 239 .drm_fmt = DRM_FORMAT_YVU420, 240 .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, 241 .rgb = false, 242 .csc = SUN8I_CSC_MODE_YVU2RGB, 243 }, 244 { 245 .drm_fmt = DRM_FORMAT_YVU411, 246 .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, 247 .rgb = false, 248 .csc = SUN8I_CSC_MODE_YVU2RGB, 249 }, 250 }; 251 252 const struct de2_fmt_info *sun8i_mixer_format_info(u32 format) 253 { 254 unsigned int i; 255 256 for (i = 0; i < ARRAY_SIZE(de2_formats); ++i) 257 if (de2_formats[i].drm_fmt == format) 258 return &de2_formats[i]; 259 260 return NULL; 261 } 262 263 static void sun8i_mixer_commit(struct sunxi_engine *engine) 264 { 265 DRM_DEBUG_DRIVER("Committing changes\n"); 266 267 regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, 268 SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); 269 } 270 271 static struct drm_plane **sun8i_layers_init(struct drm_device *drm, 272 struct sunxi_engine *engine) 273 { 274 struct drm_plane **planes; 275 struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); 276 int i; 277 278 planes = devm_kcalloc(drm->dev, 279 mixer->cfg->vi_num + mixer->cfg->ui_num + 1, 280 sizeof(*planes), GFP_KERNEL); 281 if (!planes) 282 return ERR_PTR(-ENOMEM); 283 284 for (i = 0; i < mixer->cfg->vi_num; i++) { 285 struct sun8i_vi_layer *layer; 286 287 layer = sun8i_vi_layer_init_one(drm, mixer, i); 288 if (IS_ERR(layer)) { 289 dev_err(drm->dev, 290 "Couldn't initialize overlay plane\n"); 291 return ERR_CAST(layer); 292 }; 293 294 planes[i] = &layer->plane; 295 }; 296 297 for (i = 0; i < mixer->cfg->ui_num; i++) { 298 struct sun8i_ui_layer *layer; 299 300 layer = sun8i_ui_layer_init_one(drm, mixer, i); 301 if (IS_ERR(layer)) { 302 dev_err(drm->dev, "Couldn't initialize %s plane\n", 303 i ? "overlay" : "primary"); 304 return ERR_CAST(layer); 305 }; 306 307 planes[mixer->cfg->vi_num + i] = &layer->plane; 308 }; 309 310 return planes; 311 } 312 313 static const struct sunxi_engine_ops sun8i_engine_ops = { 314 .commit = sun8i_mixer_commit, 315 .layers_init = sun8i_layers_init, 316 }; 317 318 static struct regmap_config sun8i_mixer_regmap_config = { 319 .reg_bits = 32, 320 .val_bits = 32, 321 .reg_stride = 4, 322 .max_register = 0xbfffc, /* guessed */ 323 }; 324 325 static int sun8i_mixer_bind(struct device *dev, struct device *master, 326 void *data) 327 { 328 struct platform_device *pdev = to_platform_device(dev); 329 struct drm_device *drm = data; 330 struct sun4i_drv *drv = drm->dev_private; 331 struct sun8i_mixer *mixer; 332 struct resource *res; 333 void __iomem *regs; 334 int plane_cnt; 335 int i, ret; 336 337 /* 338 * The mixer uses single 32-bit register to store memory 339 * addresses, so that it cannot deal with 64-bit memory 340 * addresses. 341 * Restrict the DMA mask so that the mixer won't be 342 * allocated some memory that is too high. 343 */ 344 ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 345 if (ret) { 346 dev_err(dev, "Cannot do 32-bit DMA.\n"); 347 return ret; 348 } 349 350 mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); 351 if (!mixer) 352 return -ENOMEM; 353 dev_set_drvdata(dev, mixer); 354 mixer->engine.ops = &sun8i_engine_ops; 355 mixer->engine.node = dev->of_node; 356 /* The ID of the mixer currently doesn't matter */ 357 mixer->engine.id = -1; 358 359 mixer->cfg = of_device_get_match_data(dev); 360 if (!mixer->cfg) 361 return -EINVAL; 362 363 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 364 regs = devm_ioremap_resource(dev, res); 365 if (IS_ERR(regs)) 366 return PTR_ERR(regs); 367 368 mixer->engine.regs = devm_regmap_init_mmio(dev, regs, 369 &sun8i_mixer_regmap_config); 370 if (IS_ERR(mixer->engine.regs)) { 371 dev_err(dev, "Couldn't create the mixer regmap\n"); 372 return PTR_ERR(mixer->engine.regs); 373 } 374 375 mixer->reset = devm_reset_control_get(dev, NULL); 376 if (IS_ERR(mixer->reset)) { 377 dev_err(dev, "Couldn't get our reset line\n"); 378 return PTR_ERR(mixer->reset); 379 } 380 381 ret = reset_control_deassert(mixer->reset); 382 if (ret) { 383 dev_err(dev, "Couldn't deassert our reset line\n"); 384 return ret; 385 } 386 387 mixer->bus_clk = devm_clk_get(dev, "bus"); 388 if (IS_ERR(mixer->bus_clk)) { 389 dev_err(dev, "Couldn't get the mixer bus clock\n"); 390 ret = PTR_ERR(mixer->bus_clk); 391 goto err_assert_reset; 392 } 393 clk_prepare_enable(mixer->bus_clk); 394 395 mixer->mod_clk = devm_clk_get(dev, "mod"); 396 if (IS_ERR(mixer->mod_clk)) { 397 dev_err(dev, "Couldn't get the mixer module clock\n"); 398 ret = PTR_ERR(mixer->mod_clk); 399 goto err_disable_bus_clk; 400 } 401 402 /* 403 * It seems that we need to enforce that rate for whatever 404 * reason for the mixer to be functional. Make sure it's the 405 * case. 406 */ 407 if (mixer->cfg->mod_rate) 408 clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); 409 410 clk_prepare_enable(mixer->mod_clk); 411 412 list_add_tail(&mixer->engine.list, &drv->engine_list); 413 414 /* Reset the registers */ 415 for (i = 0x0; i < 0x20000; i += 4) 416 regmap_write(mixer->engine.regs, i, 0); 417 418 /* Enable the mixer */ 419 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, 420 SUN8I_MIXER_GLOBAL_CTL_RT_EN); 421 422 /* Set background color to black */ 423 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR, 424 SUN8I_MIXER_BLEND_COLOR_BLACK); 425 426 /* 427 * Set fill color of bottom plane to black. Generally not needed 428 * except when VI plane is at bottom (zpos = 0) and enabled. 429 */ 430 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL, 431 SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); 432 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), 433 SUN8I_MIXER_BLEND_COLOR_BLACK); 434 435 /* Fixed zpos for now */ 436 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 0x43210); 437 438 plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; 439 for (i = 0; i < plane_cnt; i++) 440 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(i), 441 SUN8I_MIXER_BLEND_MODE_DEF); 442 443 return 0; 444 445 err_disable_bus_clk: 446 clk_disable_unprepare(mixer->bus_clk); 447 err_assert_reset: 448 reset_control_assert(mixer->reset); 449 return ret; 450 } 451 452 static void sun8i_mixer_unbind(struct device *dev, struct device *master, 453 void *data) 454 { 455 struct sun8i_mixer *mixer = dev_get_drvdata(dev); 456 457 list_del(&mixer->engine.list); 458 459 clk_disable_unprepare(mixer->mod_clk); 460 clk_disable_unprepare(mixer->bus_clk); 461 reset_control_assert(mixer->reset); 462 } 463 464 static const struct component_ops sun8i_mixer_ops = { 465 .bind = sun8i_mixer_bind, 466 .unbind = sun8i_mixer_unbind, 467 }; 468 469 static int sun8i_mixer_probe(struct platform_device *pdev) 470 { 471 return component_add(&pdev->dev, &sun8i_mixer_ops); 472 } 473 474 static int sun8i_mixer_remove(struct platform_device *pdev) 475 { 476 component_del(&pdev->dev, &sun8i_mixer_ops); 477 478 return 0; 479 } 480 481 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { 482 .ccsc = 0, 483 .scaler_mask = 0xf, 484 .ui_num = 3, 485 .vi_num = 1, 486 }; 487 488 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { 489 .ccsc = 1, 490 .scaler_mask = 0x3, 491 .ui_num = 1, 492 .vi_num = 1, 493 }; 494 495 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { 496 .ccsc = 0, 497 .mod_rate = 432000000, 498 .scaler_mask = 0xf, 499 .ui_num = 3, 500 .vi_num = 1, 501 }; 502 503 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { 504 .vi_num = 2, 505 .ui_num = 1, 506 .scaler_mask = 0x3, 507 .ccsc = 0, 508 .mod_rate = 150000000, 509 }; 510 511 static const struct of_device_id sun8i_mixer_of_table[] = { 512 { 513 .compatible = "allwinner,sun8i-a83t-de2-mixer-0", 514 .data = &sun8i_a83t_mixer0_cfg, 515 }, 516 { 517 .compatible = "allwinner,sun8i-a83t-de2-mixer-1", 518 .data = &sun8i_a83t_mixer1_cfg, 519 }, 520 { 521 .compatible = "allwinner,sun8i-h3-de2-mixer-0", 522 .data = &sun8i_h3_mixer0_cfg, 523 }, 524 { 525 .compatible = "allwinner,sun8i-v3s-de2-mixer", 526 .data = &sun8i_v3s_mixer_cfg, 527 }, 528 { } 529 }; 530 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); 531 532 static struct platform_driver sun8i_mixer_platform_driver = { 533 .probe = sun8i_mixer_probe, 534 .remove = sun8i_mixer_remove, 535 .driver = { 536 .name = "sun8i-mixer", 537 .of_match_table = sun8i_mixer_of_table, 538 }, 539 }; 540 module_platform_driver(sun8i_mixer_platform_driver); 541 542 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); 543 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver"); 544 MODULE_LICENSE("GPL"); 545