linux/drivers/gpu/drm/sun4i/sun8i_mixer.c
<<
>>
Prefs
   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
  34static 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
 253const 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
 264static 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
 272static 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
 314static const struct sunxi_engine_ops sun8i_engine_ops = {
 315        .commit         = sun8i_mixer_commit,
 316        .layers_init    = sun8i_layers_init,
 317};
 318
 319static 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
 326static 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", &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
 362static 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
 490err_disable_bus_clk:
 491        clk_disable_unprepare(mixer->bus_clk);
 492err_assert_reset:
 493        reset_control_assert(mixer->reset);
 494        return ret;
 495}
 496
 497static 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
 509static const struct component_ops sun8i_mixer_ops = {
 510        .bind   = sun8i_mixer_bind,
 511        .unbind = sun8i_mixer_unbind,
 512};
 513
 514static int sun8i_mixer_probe(struct platform_device *pdev)
 515{
 516        return component_add(&pdev->dev, &sun8i_mixer_ops);
 517}
 518
 519static int sun8i_mixer_remove(struct platform_device *pdev)
 520{
 521        component_del(&pdev->dev, &sun8i_mixer_ops);
 522
 523        return 0;
 524}
 525
 526static 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
 533static 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
 540static 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
 548static 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
 556static 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};
 575MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
 576
 577static 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};
 585module_platform_driver(sun8i_mixer_platform_driver);
 586
 587MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
 588MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
 589MODULE_LICENSE("GPL");
 590