linux/drivers/gpu/drm/xilinx/xilinx_drm_plane.c
<<
>>
Prefs
   1/*
   2 * Xilinx DRM plane driver for Xilinx
   3 *
   4 *  Copyright (C) 2013 Xilinx, Inc.
   5 *
   6 *  Author: Hyun Woo Kwon <hyunk@xilinx.com>
   7 *
   8 * This software is licensed under the terms of the GNU General Public
   9 * License version 2, as published by the Free Software Foundation, and
  10 * may be copied, distributed, and modified under those terms.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include <drm/drmP.h>
  19#include <drm/drm_crtc.h>
  20#include <drm/drm_fb_cma_helper.h>
  21#include <drm/drm_gem_cma_helper.h>
  22
  23#include <linux/device.h>
  24#include <linux/dmaengine.h>
  25#include <linux/dma/xilinx_dma.h>
  26#include <linux/dma/xilinx_frmbuf.h>
  27#include <linux/of_dma.h>
  28#include <linux/platform_device.h>
  29
  30#include "xilinx_drm_dp_sub.h"
  31#include "xilinx_drm_drv.h"
  32#include "xilinx_drm_fb.h"
  33#include "xilinx_drm_plane.h"
  34
  35#include "xilinx_cresample.h"
  36#include "xilinx_osd.h"
  37#include "xilinx_rgb2yuv.h"
  38
  39#define MAX_NUM_SUB_PLANES      4
  40
  41/**
  42 * struct xilinx_drm_plane_dma - Xilinx drm plane VDMA object
  43 *
  44 * @chan: dma channel
  45 * @xt: dma interleaved configuration template
  46 * @sgl: data chunk for dma_interleaved_template
  47 * @is_active: flag if the DMA is active
  48 */
  49struct xilinx_drm_plane_dma {
  50        struct dma_chan *chan;
  51        struct dma_interleaved_template xt;
  52        struct data_chunk sgl[1];
  53        bool is_active;
  54};
  55
  56/**
  57 * struct xilinx_drm_plane - Xilinx drm plane object
  58 *
  59 * @base: base drm plane object
  60 * @id: plane id
  61 * @dpms: current dpms level
  62 * @zpos: user requested z-position value
  63 * @prio: actual layer priority
  64 * @alpha: alpha value
  65 * @alpha_enable: alpha enable value
  66 * @primary: flag for primary plane
  67 * @format: pixel format
  68 * @dma: dma object
  69 * @rgb2yuv: rgb2yuv instance
  70 * @cresample: cresample instance
  71 * @osd_layer: osd layer
  72 * @dp_layer: DisplayPort subsystem layer
  73 * @manager: plane manager
  74 */
  75struct xilinx_drm_plane {
  76        struct drm_plane base;
  77        int id;
  78        int dpms;
  79        unsigned int zpos;
  80        unsigned int prio;
  81        unsigned int alpha;
  82        unsigned int alpha_enable;
  83        bool primary;
  84        u32 format;
  85        struct xilinx_drm_plane_dma dma[MAX_NUM_SUB_PLANES];
  86        struct xilinx_rgb2yuv *rgb2yuv;
  87        struct xilinx_cresample *cresample;
  88        struct xilinx_osd_layer *osd_layer;
  89        struct xilinx_drm_dp_sub_layer *dp_layer;
  90        struct xilinx_drm_plane_manager *manager;
  91};
  92
  93#define MAX_PLANES 8
  94
  95/**
  96 * struct xilinx_drm_plane_manager - Xilinx drm plane manager object
  97 *
  98 * @drm: drm device
  99 * @node: plane device node
 100 * @osd: osd instance
 101 * @dp_sub: DisplayPort subsystem instance
 102 * @num_planes: number of available planes
 103 * @format: video format
 104 * @max_width: maximum width
 105 * @zpos_prop: z-position(priority) property
 106 * @alpha_prop: alpha value property
 107 * @alpha_enable_prop: alpha enable property
 108 * @default_alpha: default alpha value
 109 * @planes: xilinx drm planes
 110 */
 111struct xilinx_drm_plane_manager {
 112        struct drm_device *drm;
 113        struct device_node *node;
 114        struct xilinx_osd *osd;
 115        struct xilinx_drm_dp_sub *dp_sub;
 116        int num_planes;
 117        u32 format;
 118        int max_width;
 119        struct drm_property *zpos_prop;
 120        struct drm_property *alpha_prop;
 121        struct drm_property *alpha_enable_prop;
 122        unsigned int default_alpha;
 123        struct xilinx_drm_plane *planes[MAX_PLANES];
 124};
 125
 126#define to_xilinx_plane(x)      container_of(x, struct xilinx_drm_plane, base)
 127
 128void xilinx_drm_plane_commit(struct drm_plane *base_plane)
 129{
 130        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 131        struct dma_async_tx_descriptor *desc;
 132        enum dma_ctrl_flags flags;
 133        unsigned int i;
 134
 135        /* for xilinx video framebuffer dma, if used */
 136        xilinx_xdma_drm_config(plane->dma[0].chan, plane->format);
 137
 138        DRM_DEBUG_KMS("plane->id: %d\n", plane->id);
 139
 140        for (i = 0; i < MAX_NUM_SUB_PLANES; i++) {
 141                struct xilinx_drm_plane_dma *dma = &plane->dma[i];
 142
 143                if (dma->chan && dma->is_active) {
 144                        flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
 145                        desc = dmaengine_prep_interleaved_dma(dma->chan,
 146                                                              &dma->xt,
 147                                                              flags);
 148                        if (!desc) {
 149                                DRM_ERROR("failed to prepare DMA descriptor\n");
 150                                return;
 151                        }
 152
 153                        dmaengine_submit(desc);
 154
 155                        dma_async_issue_pending(dma->chan);
 156                }
 157        }
 158}
 159
 160/* set plane dpms */
 161void xilinx_drm_plane_dpms(struct drm_plane *base_plane, int dpms)
 162{
 163        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 164        struct xilinx_drm_plane_manager *manager = plane->manager;
 165        unsigned int i;
 166
 167        DRM_DEBUG_KMS("plane->id: %d\n", plane->id);
 168        DRM_DEBUG_KMS("dpms: %d -> %d\n", plane->dpms, dpms);
 169
 170        if (plane->dpms == dpms)
 171                return;
 172
 173        plane->dpms = dpms;
 174        switch (dpms) {
 175        case DRM_MODE_DPMS_ON:
 176                if (manager->dp_sub) {
 177                        if (plane->primary) {
 178                                xilinx_drm_dp_sub_enable_alpha(manager->dp_sub,
 179                                                               plane->alpha_enable);
 180                                xilinx_drm_dp_sub_set_alpha(manager->dp_sub,
 181                                                            plane->alpha);
 182                        }
 183                        xilinx_drm_dp_sub_layer_enable(manager->dp_sub,
 184                                                       plane->dp_layer);
 185                }
 186
 187                if (plane->rgb2yuv)
 188                        xilinx_rgb2yuv_enable(plane->rgb2yuv);
 189
 190                if (plane->cresample)
 191                        xilinx_cresample_enable(plane->cresample);
 192
 193                /* enable osd */
 194                if (manager->osd) {
 195                        xilinx_osd_disable_rue(manager->osd);
 196
 197                        xilinx_osd_layer_set_priority(plane->osd_layer,
 198                                                      plane->prio);
 199                        xilinx_osd_layer_enable_alpha(plane->osd_layer,
 200                                                      plane->alpha_enable);
 201                        xilinx_osd_layer_set_alpha(plane->osd_layer,
 202                                                   plane->alpha);
 203                        xilinx_osd_layer_enable(plane->osd_layer);
 204
 205                        xilinx_osd_enable_rue(manager->osd);
 206                }
 207
 208                xilinx_drm_plane_commit(base_plane);
 209                break;
 210        default:
 211                /* disable/reset osd */
 212                if (manager->osd) {
 213                        xilinx_osd_disable_rue(manager->osd);
 214
 215                        xilinx_osd_layer_set_dimension(plane->osd_layer,
 216                                                       0, 0, 0, 0);
 217                        xilinx_osd_layer_disable(plane->osd_layer);
 218
 219                        xilinx_osd_enable_rue(manager->osd);
 220                }
 221
 222                if (plane->cresample) {
 223                        xilinx_cresample_disable(plane->cresample);
 224                        xilinx_cresample_reset(plane->cresample);
 225                }
 226
 227                if (plane->rgb2yuv) {
 228                        xilinx_rgb2yuv_disable(plane->rgb2yuv);
 229                        xilinx_rgb2yuv_reset(plane->rgb2yuv);
 230                }
 231
 232                /* stop dma engine and release descriptors */
 233                for (i = 0; i < MAX_NUM_SUB_PLANES; i++) {
 234                        if (plane->dma[i].chan && plane->dma[i].is_active)
 235                                dmaengine_terminate_all(plane->dma[i].chan);
 236                }
 237
 238                if (manager->dp_sub)
 239                        xilinx_drm_dp_sub_layer_disable(manager->dp_sub,
 240                                                        plane->dp_layer);
 241
 242                break;
 243        }
 244}
 245
 246/* mode set a plane */
 247int xilinx_drm_plane_mode_set(struct drm_plane *base_plane,
 248                              struct drm_framebuffer *fb,
 249                              int crtc_x, int crtc_y,
 250                              unsigned int crtc_w, unsigned int crtc_h,
 251                              u32 src_x, u32 src_y,
 252                              u32 src_w, u32 src_h)
 253{
 254        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 255        struct drm_gem_cma_object *obj;
 256        size_t offset;
 257        unsigned int hsub, vsub, i;
 258
 259        DRM_DEBUG_KMS("plane->id: %d\n", plane->id);
 260
 261        /* configure cresample */
 262        if (plane->cresample)
 263                xilinx_cresample_configure(plane->cresample, crtc_w, crtc_h);
 264
 265        /* configure rgb2yuv */
 266        if (plane->rgb2yuv)
 267                xilinx_rgb2yuv_configure(plane->rgb2yuv, crtc_w, crtc_h);
 268
 269        DRM_DEBUG_KMS("h: %d(%d), v: %d(%d)\n",
 270                      src_w, crtc_x, src_h, crtc_y);
 271        DRM_DEBUG_KMS("bpp: %d\n", fb->bits_per_pixel / 8);
 272
 273        hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
 274        vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
 275
 276        for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
 277                unsigned int width = src_w / (i ? hsub : 1);
 278                unsigned int height = src_h / (i ? vsub : 1);
 279                unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, i);
 280
 281                if (!cpp)
 282                        cpp = xilinx_drm_format_bpp(fb->pixel_format) >> 3;
 283
 284                obj = xilinx_drm_fb_get_gem_obj(fb, i);
 285                if (!obj) {
 286                        DRM_ERROR("failed to get a gem obj for fb\n");
 287                        return -EINVAL;
 288                }
 289
 290                plane->dma[i].xt.numf = height;
 291                plane->dma[i].sgl[0].size = width * cpp;
 292                plane->dma[i].sgl[0].icg = fb->pitches[i] -
 293                                           plane->dma[i].sgl[0].size;
 294                offset = src_x * cpp + src_y * fb->pitches[i];
 295                offset += fb->offsets[i];
 296                plane->dma[i].xt.src_start = obj->paddr + offset;
 297                plane->dma[i].xt.frame_size = 1;
 298                plane->dma[i].xt.dir = DMA_MEM_TO_DEV;
 299                plane->dma[i].xt.src_sgl = true;
 300                plane->dma[i].xt.dst_sgl = false;
 301                plane->dma[i].is_active = true;
 302        }
 303
 304        for (; i < MAX_NUM_SUB_PLANES; i++)
 305                plane->dma[i].is_active = false;
 306
 307        /* set OSD dimensions */
 308        if (plane->manager->osd) {
 309                xilinx_osd_disable_rue(plane->manager->osd);
 310
 311                xilinx_osd_layer_set_dimension(plane->osd_layer, crtc_x, crtc_y,
 312                                               src_w, src_h);
 313
 314                xilinx_osd_enable_rue(plane->manager->osd);
 315        }
 316
 317        if (plane->manager->dp_sub) {
 318                int ret;
 319
 320                ret = xilinx_drm_dp_sub_layer_check_size(plane->manager->dp_sub,
 321                                                         plane->dp_layer,
 322                                                         src_w, src_h);
 323                if (ret)
 324                        return ret;
 325
 326                ret = xilinx_drm_dp_sub_layer_set_fmt(plane->manager->dp_sub,
 327                                                      plane->dp_layer,
 328                                                      fb->pixel_format);
 329                if (ret) {
 330                        DRM_ERROR("failed to set dp_sub layer fmt\n");
 331                        return ret;
 332                }
 333        }
 334
 335        return 0;
 336}
 337
 338/* update a plane. just call mode_set() with bit-shifted values */
 339static int xilinx_drm_plane_update(struct drm_plane *base_plane,
 340                                   struct drm_crtc *crtc,
 341                                   struct drm_framebuffer *fb,
 342                                   int crtc_x, int crtc_y,
 343                                   unsigned int crtc_w, unsigned int crtc_h,
 344                                   u32 src_x, u32 src_y,
 345                                   u32 src_w, u32 src_h)
 346{
 347        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 348        int ret;
 349
 350        ret = xilinx_drm_plane_mode_set(base_plane, fb,
 351                                        crtc_x, crtc_y, crtc_w, crtc_h,
 352                                        src_x >> 16, src_y >> 16,
 353                                        src_w >> 16, src_h >> 16);
 354        if (ret) {
 355                DRM_ERROR("failed to mode-set a plane\n");
 356                return ret;
 357        }
 358
 359        /* make sure a plane is on */
 360        if (plane->dpms != DRM_MODE_DPMS_ON)
 361                xilinx_drm_plane_dpms(base_plane, DRM_MODE_DPMS_ON);
 362        else
 363                xilinx_drm_plane_commit(base_plane);
 364
 365        return 0;
 366}
 367
 368/* disable a plane */
 369static int xilinx_drm_plane_disable(struct drm_plane *base_plane)
 370{
 371        xilinx_drm_plane_dpms(base_plane, DRM_MODE_DPMS_OFF);
 372
 373        return 0;
 374}
 375
 376/* destroy a plane */
 377static void xilinx_drm_plane_destroy(struct drm_plane *base_plane)
 378{
 379        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 380        unsigned int i;
 381
 382        xilinx_drm_plane_dpms(base_plane, DRM_MODE_DPMS_OFF);
 383
 384        plane->manager->planes[plane->id] = NULL;
 385
 386        drm_plane_cleanup(base_plane);
 387
 388        for (i = 0; i < MAX_NUM_SUB_PLANES; i++)
 389                if (plane->dma[i].chan)
 390                        dma_release_channel(plane->dma[i].chan);
 391
 392        if (plane->manager->osd) {
 393                xilinx_osd_layer_disable(plane->osd_layer);
 394                xilinx_osd_layer_put(plane->osd_layer);
 395        }
 396
 397        if (plane->manager->dp_sub) {
 398                xilinx_drm_dp_sub_layer_disable(plane->manager->dp_sub,
 399                                                plane->dp_layer);
 400                xilinx_drm_dp_sub_layer_put(plane->manager->dp_sub,
 401                                            plane->dp_layer);
 402        }
 403}
 404
 405/**
 406 * xilinx_drm_plane_update_prio - Configure plane priorities based on zpos
 407 * @manager: the plane manager
 408 *
 409 * Z-position values are user requested position of planes. The priority is
 410 * the actual position of planes in hardware. Some hardware doesn't allow
 411 * any duplicate priority, so this function needs to be called when a duplicate
 412 * priority is found. Then planes are sorted by zpos value, and the priorities
 413 * are reconfigured. A plane with lower plane ID gets assigned to the lower
 414 * priority when planes have the same zpos value.
 415 */
 416static void
 417xilinx_drm_plane_update_prio(struct xilinx_drm_plane_manager *manager)
 418{
 419        struct xilinx_drm_plane *planes[MAX_PLANES];
 420        struct xilinx_drm_plane *plane;
 421        unsigned int i, j;
 422
 423        /* sort planes by zpos */
 424        for (i = 0; i < manager->num_planes; i++) {
 425                plane = manager->planes[i];
 426
 427                for (j = i; j > 0; --j) {
 428                        if (planes[j - 1]->zpos <= plane->zpos)
 429                                break;
 430                        planes[j] = planes[j - 1];
 431                }
 432
 433                planes[j] = plane;
 434        }
 435
 436        xilinx_osd_disable_rue(manager->osd);
 437
 438        /* remove duplicates by reassigning priority */
 439        for (i = 0; i < manager->num_planes; i++) {
 440                planes[i]->prio = i;
 441                xilinx_osd_layer_set_priority(planes[i]->osd_layer,
 442                                              planes[i]->prio);
 443        }
 444
 445        xilinx_osd_enable_rue(manager->osd);
 446}
 447
 448static void xilinx_drm_plane_set_zpos(struct drm_plane *base_plane,
 449                                      unsigned int zpos)
 450{
 451        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 452        struct xilinx_drm_plane_manager *manager = plane->manager;
 453        bool update = false;
 454        int i;
 455
 456        for (i = 0; i < manager->num_planes; i++) {
 457                if (manager->planes[i] != plane &&
 458                    manager->planes[i]->prio == zpos) {
 459                        update = true;
 460                        break;
 461                }
 462        }
 463
 464        plane->zpos = zpos;
 465
 466        if (update) {
 467                xilinx_drm_plane_update_prio(manager);
 468        } else {
 469                plane->prio = zpos;
 470                xilinx_osd_layer_set_priority(plane->osd_layer, plane->prio);
 471        }
 472}
 473
 474static void xilinx_drm_plane_set_alpha(struct drm_plane *base_plane,
 475                                       unsigned int alpha)
 476{
 477        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 478        struct xilinx_drm_plane_manager *manager = plane->manager;
 479
 480        plane->alpha = alpha;
 481
 482        if (plane->osd_layer)
 483                xilinx_osd_layer_set_alpha(plane->osd_layer, plane->alpha);
 484        else if (manager->dp_sub)
 485                xilinx_drm_dp_sub_set_alpha(manager->dp_sub, plane->alpha);
 486}
 487
 488static void xilinx_drm_plane_enable_alpha(struct drm_plane *base_plane,
 489                                          bool enable)
 490{
 491        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 492        struct xilinx_drm_plane_manager *manager = plane->manager;
 493
 494        plane->alpha_enable = enable;
 495
 496        if (plane->osd_layer)
 497                xilinx_osd_layer_enable_alpha(plane->osd_layer, enable);
 498        else if (manager->dp_sub)
 499                xilinx_drm_dp_sub_enable_alpha(manager->dp_sub, enable);
 500}
 501
 502/* set property of a plane */
 503static int xilinx_drm_plane_set_property(struct drm_plane *base_plane,
 504                                         struct drm_property *property,
 505                                         uint64_t val)
 506{
 507        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 508        struct xilinx_drm_plane_manager *manager = plane->manager;
 509
 510        if (property == manager->zpos_prop)
 511                xilinx_drm_plane_set_zpos(base_plane, val);
 512        else if (property == manager->alpha_prop)
 513                xilinx_drm_plane_set_alpha(base_plane, val);
 514        else if (property == manager->alpha_enable_prop)
 515                xilinx_drm_plane_enable_alpha(base_plane, val);
 516        else
 517                return -EINVAL;
 518
 519        drm_object_property_set_value(&base_plane->base, property, val);
 520
 521        return 0;
 522}
 523
 524static struct drm_plane_funcs xilinx_drm_plane_funcs = {
 525        .update_plane   = xilinx_drm_plane_update,
 526        .disable_plane  = xilinx_drm_plane_disable,
 527        .destroy        = xilinx_drm_plane_destroy,
 528        .set_property   = xilinx_drm_plane_set_property,
 529};
 530
 531/* get a plane max width */
 532int xilinx_drm_plane_get_max_width(struct drm_plane *base_plane)
 533{
 534        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 535
 536        return plane->manager->max_width;
 537}
 538
 539/* check if format is supported */
 540bool xilinx_drm_plane_check_format(struct xilinx_drm_plane_manager *manager,
 541                                   u32 format)
 542{
 543        int i;
 544
 545        for (i = 0; i < MAX_PLANES; i++)
 546                if (manager->planes[i] &&
 547                    (manager->planes[i]->format == format))
 548                        return true;
 549
 550        return false;
 551}
 552
 553/* get the number of planes */
 554int xilinx_drm_plane_get_num_planes(struct xilinx_drm_plane_manager *manager)
 555{
 556        return manager->num_planes;
 557}
 558
 559/**
 560 * xilinx_drm_plane_restore - Restore the plane states
 561 * @manager: the plane manager
 562 *
 563 * Restore the plane states to the default ones. Any state that needs to be
 564 * restored should be here. This improves consistency as applications see
 565 * the same default values, and removes mismatch between software and hardware
 566 * values as software values are updated as hardware values are reset.
 567 */
 568void xilinx_drm_plane_restore(struct xilinx_drm_plane_manager *manager)
 569{
 570        struct xilinx_drm_plane *plane;
 571        unsigned int i;
 572
 573        /*
 574         * Reinitialize property default values as they get reset by DPMS OFF
 575         * operation. User will read the correct default values later, and
 576         * planes will be initialized with default values.
 577         */
 578        for (i = 0; i < manager->num_planes; i++) {
 579                plane = manager->planes[i];
 580
 581                plane->prio = plane->id;
 582                plane->zpos = plane->id;
 583                if (manager->zpos_prop)
 584                        drm_object_property_set_value(&plane->base.base,
 585                                                      manager->zpos_prop,
 586                                                      plane->prio);
 587
 588                plane->alpha = manager->default_alpha;
 589                if (manager->alpha_prop)
 590                        drm_object_property_set_value(&plane->base.base,
 591                                                      manager->alpha_prop,
 592                                                      plane->alpha);
 593
 594                plane->alpha_enable = true;
 595                if (manager->alpha_enable_prop)
 596                        drm_object_property_set_value(&plane->base.base,
 597                                                      manager->alpha_enable_prop, true);
 598        }
 599}
 600
 601/* get the plane format */
 602u32 xilinx_drm_plane_get_format(struct drm_plane *base_plane)
 603{
 604        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 605
 606        return plane->format;
 607}
 608
 609/**
 610 * xilinx_drm_plane_get_align - Get the alignment value for pitch
 611 * @base_plane: Base drm plane object
 612 *
 613 * Get the alignment value for pitch from the dma device
 614 *
 615 * Return: The alignment value if successful, or the error code.
 616 */
 617unsigned int xilinx_drm_plane_get_align(struct drm_plane *base_plane)
 618{
 619        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 620
 621        return 1 << plane->dma[0].chan->device->copy_align;
 622}
 623
 624/* create plane properties */
 625static void
 626xilinx_drm_plane_create_property(struct xilinx_drm_plane_manager *manager)
 627{
 628        if (manager->osd)
 629                manager->zpos_prop = drm_property_create_range(manager->drm, 0,
 630                                "zpos", 0, manager->num_planes - 1);
 631
 632        if (manager->osd || manager->dp_sub) {
 633                manager->alpha_prop = drm_property_create_range(manager->drm, 0,
 634                                "alpha", 0, manager->default_alpha);
 635                manager->alpha_enable_prop =
 636                        drm_property_create_bool(manager->drm, 0,
 637                                                 "global alpha enable");
 638        }
 639}
 640
 641/* attach plane properties */
 642static void xilinx_drm_plane_attach_property(struct drm_plane *base_plane)
 643{
 644        struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane);
 645        struct xilinx_drm_plane_manager *manager = plane->manager;
 646
 647        if (manager->zpos_prop)
 648                drm_object_attach_property(&base_plane->base,
 649                                           manager->zpos_prop,
 650                                           plane->id);
 651
 652        if (manager->alpha_prop) {
 653                if (manager->dp_sub && !plane->primary)
 654                        return;
 655
 656                drm_object_attach_property(&base_plane->base,
 657                                           manager->alpha_prop,
 658                                           manager->default_alpha);
 659                drm_object_attach_property(&base_plane->base,
 660                                           manager->alpha_enable_prop, false);
 661        }
 662
 663        plane->alpha_enable = true;
 664}
 665
 666/**
 667 * xilinx_drm_plane_manager_dpms - Set DPMS for the Xilinx plane manager
 668 * @manager: Xilinx plane manager object
 669 * @dpms: requested DPMS
 670 *
 671 * Set the Xilinx plane manager to the given DPMS state. This function is
 672 * usually called from the CRTC driver with calling xilinx_drm_plane_dpms().
 673 */
 674void xilinx_drm_plane_manager_dpms(struct xilinx_drm_plane_manager *manager,
 675                                   int dpms)
 676{
 677        switch (dpms) {
 678        case DRM_MODE_DPMS_ON:
 679                if (manager->dp_sub) {
 680                        xilinx_drm_dp_sub_set_bg_color(manager->dp_sub,
 681                                                       0, 0, 0);
 682                        xilinx_drm_dp_sub_enable(manager->dp_sub);
 683                }
 684
 685                if (manager->osd) {
 686                        xilinx_osd_disable_rue(manager->osd);
 687                        xilinx_osd_enable(manager->osd);
 688                        xilinx_osd_enable_rue(manager->osd);
 689                }
 690
 691                break;
 692        default:
 693                if (manager->osd)
 694                        xilinx_osd_reset(manager->osd);
 695
 696                if (manager->dp_sub)
 697                        xilinx_drm_dp_sub_disable(manager->dp_sub);
 698
 699                break;
 700        }
 701}
 702
 703/**
 704 * xilinx_drm_plane_manager_mode_set - Set the mode to the Xilinx plane manager
 705 * @manager: Xilinx plane manager object
 706 * @crtc_w: CRTC width
 707 * @crtc_h: CRTC height
 708 *
 709 * Set the width and height of the Xilinx plane manager. This function is uaully
 710 * called from the CRTC driver before calling the xilinx_drm_plane_mode_set().
 711 */
 712void xilinx_drm_plane_manager_mode_set(struct xilinx_drm_plane_manager *manager,
 713                                       unsigned int crtc_w, unsigned int crtc_h)
 714{
 715        if (manager->osd)
 716                xilinx_osd_set_dimension(manager->osd, crtc_w, crtc_h);
 717}
 718
 719/* create a plane */
 720static struct xilinx_drm_plane *
 721xilinx_drm_plane_create(struct xilinx_drm_plane_manager *manager,
 722                        unsigned int possible_crtcs, bool primary)
 723{
 724        struct xilinx_drm_plane *plane;
 725        struct device *dev = manager->drm->dev;
 726        char plane_name[16];
 727        struct device_node *plane_node;
 728        struct device_node *sub_node;
 729        struct property *prop;
 730        const char *dma_name;
 731        enum drm_plane_type type;
 732        u32 fmt_in = 0;
 733        u32 fmt_out = 0;
 734        const char *fmt;
 735        int i;
 736        int ret;
 737        u32 *fmts = NULL;
 738        unsigned int num_fmts = 0;
 739
 740        for (i = 0; i < manager->num_planes; i++)
 741                if (!manager->planes[i])
 742                        break;
 743
 744        if (i >= manager->num_planes) {
 745                DRM_ERROR("failed to allocate plane\n");
 746                return ERR_PTR(-ENODEV);
 747        }
 748
 749        snprintf(plane_name, sizeof(plane_name), "plane%d", i);
 750        plane_node = of_get_child_by_name(manager->node, plane_name);
 751        if (!plane_node) {
 752                DRM_ERROR("failed to find a plane node\n");
 753                return ERR_PTR(-ENODEV);
 754        }
 755
 756        plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
 757        if (!plane) {
 758                ret = -ENOMEM;
 759                goto err_out;
 760        }
 761
 762        plane->primary = primary;
 763        plane->id = i;
 764        plane->prio = i;
 765        plane->zpos = i;
 766        plane->alpha = manager->default_alpha;
 767        plane->dpms = DRM_MODE_DPMS_OFF;
 768        plane->format = 0;
 769        DRM_DEBUG_KMS("plane->id: %d\n", plane->id);
 770
 771        i = 0;
 772        of_property_for_each_string(plane_node, "dma-names", prop, dma_name) {
 773                if (i >= MAX_NUM_SUB_PLANES) {
 774                        DRM_WARN("%s contains too many sub-planes (dma-names), indexes %d and above ignored\n",
 775                                 of_node_full_name(plane_node),
 776                                 MAX_NUM_SUB_PLANES);
 777                        break;
 778                }
 779                plane->dma[i].chan = of_dma_request_slave_channel(plane_node,
 780                                                                  dma_name);
 781                if (IS_ERR(plane->dma[i].chan)) {
 782                        ret = PTR_ERR(plane->dma[i].chan);
 783                        DRM_ERROR("failed to request dma channel \"%s\" for plane %s (err:%d)\n",
 784                                  dma_name, of_node_full_name(plane_node), ret);
 785                        plane->dma[i].chan = NULL;
 786                        goto err_dma;
 787                }
 788                ++i;
 789        }
 790
 791        if (i == 0) {
 792                DRM_ERROR("plane \"%s\" doesn't have any dma channels (dma-names)\n",
 793                          of_node_full_name(plane_node));
 794                ret = -EINVAL;
 795                goto err_out;
 796        }
 797
 798        /* probe color space converter */
 799        sub_node = of_parse_phandle(plane_node, "xlnx,rgb2yuv", i);
 800        if (sub_node) {
 801                plane->rgb2yuv = xilinx_rgb2yuv_probe(dev, sub_node);
 802                of_node_put(sub_node);
 803                if (IS_ERR(plane->rgb2yuv)) {
 804                        DRM_ERROR("failed to probe a rgb2yuv\n");
 805                        ret = PTR_ERR(plane->rgb2yuv);
 806                        goto err_dma;
 807                }
 808
 809                /* rgb2yuv input format */
 810                plane->format = DRM_FORMAT_XRGB8888;
 811
 812                /* rgb2yuv output format */
 813                fmt_out = DRM_FORMAT_YUV444;
 814        }
 815
 816        /* probe chroma resampler */
 817        sub_node = of_parse_phandle(plane_node, "xlnx,cresample", i);
 818        if (sub_node) {
 819                plane->cresample = xilinx_cresample_probe(dev, sub_node);
 820                of_node_put(sub_node);
 821                if (IS_ERR(plane->cresample)) {
 822                        DRM_ERROR("failed to probe a cresample\n");
 823                        ret = PTR_ERR(plane->cresample);
 824                        goto err_dma;
 825                }
 826
 827                /* cresample input format */
 828                fmt = xilinx_cresample_get_input_format_name(plane->cresample);
 829                ret = xilinx_drm_format_by_name(fmt, &fmt_in);
 830                if (ret)
 831                        goto err_dma;
 832
 833                /* format sanity check */
 834                if ((fmt_out != 0) && (fmt_out != fmt_in)) {
 835                        DRM_ERROR("input/output format mismatch\n");
 836                        ret = -EINVAL;
 837                        goto err_dma;
 838                }
 839
 840                if (plane->format == 0)
 841                        plane->format = fmt_in;
 842
 843                /* cresample output format */
 844                fmt = xilinx_cresample_get_output_format_name(plane->cresample);
 845                ret = xilinx_drm_format_by_name(fmt, &fmt_out);
 846                if (ret)
 847                        goto err_dma;
 848        }
 849
 850        /* create an OSD layer when OSD is available */
 851        if (manager->osd) {
 852                /* format sanity check */
 853                if ((fmt_out != 0) && (fmt_out != manager->format)) {
 854                        DRM_ERROR("input/output format mismatch\n");
 855                        ret = -EINVAL;
 856                        goto err_dma;
 857                }
 858
 859                /* create an osd layer */
 860                plane->osd_layer = xilinx_osd_layer_get(manager->osd);
 861                if (IS_ERR(plane->osd_layer)) {
 862                        DRM_ERROR("failed to create a osd layer\n");
 863                        ret = PTR_ERR(plane->osd_layer);
 864                        plane->osd_layer = NULL;
 865                        goto err_dma;
 866                }
 867
 868                if (plane->format == 0)
 869                        plane->format = manager->format;
 870        }
 871
 872        if (manager->dp_sub) {
 873                plane->dp_layer = xilinx_drm_dp_sub_layer_get(manager->dp_sub,
 874                                                              primary);
 875                if (IS_ERR(plane->dp_layer)) {
 876                        DRM_ERROR("failed to create a dp_sub layer\n");
 877                        ret = PTR_ERR(plane->dp_layer);
 878                        plane->dp_layer = NULL;
 879                        goto err_dma;
 880                }
 881
 882                if (primary) {
 883                        ret = xilinx_drm_dp_sub_layer_set_fmt(manager->dp_sub,
 884                                                              plane->dp_layer,
 885                                                              manager->format);
 886                        if (ret) {
 887                                DRM_ERROR("failed to set dp_sub layer fmt\n");
 888                                goto err_dma;
 889                        }
 890                }
 891
 892                plane->format =
 893                        xilinx_drm_dp_sub_layer_get_fmt(manager->dp_sub,
 894                                                        plane->dp_layer);
 895                xilinx_drm_dp_sub_layer_get_fmts(manager->dp_sub,
 896                                                 plane->dp_layer, &fmts,
 897                                                 &num_fmts);
 898        }
 899
 900        /* If there's no IP other than VDMA, pick the manager's format */
 901        if (plane->format == 0)
 902                plane->format = manager->format;
 903
 904        /* initialize drm plane */
 905        type = primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
 906        ret = drm_universal_plane_init(manager->drm, &plane->base,
 907                                       possible_crtcs, &xilinx_drm_plane_funcs,
 908                                       fmts ? fmts : &plane->format,
 909                                       num_fmts ? num_fmts : 1, type, NULL);
 910        if (ret) {
 911                DRM_ERROR("failed to initialize plane\n");
 912                goto err_init;
 913        }
 914        plane->manager = manager;
 915        manager->planes[plane->id] = plane;
 916
 917        xilinx_drm_plane_attach_property(&plane->base);
 918
 919        of_node_put(plane_node);
 920
 921        return plane;
 922
 923err_init:
 924        if (manager->dp_sub) {
 925                xilinx_drm_dp_sub_layer_disable(manager->dp_sub,
 926                                                plane->dp_layer);
 927                xilinx_drm_dp_sub_layer_put(plane->manager->dp_sub,
 928                                            plane->dp_layer);
 929        }
 930        if (manager->osd) {
 931                xilinx_osd_layer_disable(plane->osd_layer);
 932                xilinx_osd_layer_put(plane->osd_layer);
 933        }
 934err_dma:
 935        for (i = 0; i < MAX_NUM_SUB_PLANES; i++)
 936                if (plane->dma[i].chan)
 937                        dma_release_channel(plane->dma[i].chan);
 938err_out:
 939        of_node_put(plane_node);
 940        return ERR_PTR(ret);
 941}
 942
 943/* create a primary plane */
 944struct drm_plane *
 945xilinx_drm_plane_create_primary(struct xilinx_drm_plane_manager *manager,
 946                                unsigned int possible_crtcs)
 947{
 948        struct xilinx_drm_plane *plane;
 949
 950        plane = xilinx_drm_plane_create(manager, possible_crtcs, true);
 951        if (IS_ERR(plane)) {
 952                DRM_ERROR("failed to allocate a primary plane\n");
 953                return ERR_CAST(plane);
 954        }
 955
 956        return &plane->base;
 957}
 958
 959/* create extra planes */
 960int xilinx_drm_plane_create_planes(struct xilinx_drm_plane_manager *manager,
 961                                   unsigned int possible_crtcs)
 962{
 963        struct xilinx_drm_plane *plane;
 964        int i;
 965
 966        /* find if there any available plane, and create if available */
 967        for (i = 0; i < manager->num_planes; i++) {
 968                if (manager->planes[i])
 969                        continue;
 970
 971                plane = xilinx_drm_plane_create(manager, possible_crtcs, false);
 972                if (IS_ERR(plane)) {
 973                        DRM_ERROR("failed to allocate a plane\n");
 974                        return PTR_ERR(plane);
 975                }
 976
 977                manager->planes[i] = plane;
 978        }
 979
 980        return 0;
 981}
 982
 983/* initialize a plane manager: num_planes, format, max_width */
 984static int
 985xilinx_drm_plane_init_manager(struct xilinx_drm_plane_manager *manager)
 986{
 987        unsigned int format;
 988        u32 drm_format;
 989        int ret = 0;
 990
 991        if (manager->osd) {
 992                manager->num_planes = xilinx_osd_get_num_layers(manager->osd);
 993                manager->max_width = xilinx_osd_get_max_width(manager->osd);
 994
 995                format = xilinx_osd_get_format(manager->osd);
 996                ret = xilinx_drm_format_by_code(format, &drm_format);
 997                if (drm_format != manager->format)
 998                        ret = -EINVAL;
 999        } else if (manager->dp_sub) {
1000                manager->num_planes = XILINX_DRM_DP_SUB_NUM_LAYERS;
1001                manager->max_width = XILINX_DRM_DP_SUB_MAX_WIDTH;
1002        } else {
1003                /* without osd, only one plane is supported */
1004                manager->num_planes = 1;
1005                manager->max_width = 4096;
1006        }
1007
1008        return ret;
1009}
1010
1011struct xilinx_drm_plane_manager *
1012xilinx_drm_plane_probe_manager(struct drm_device *drm)
1013{
1014        struct xilinx_drm_plane_manager *manager;
1015        struct device *dev = drm->dev;
1016        struct device_node *sub_node;
1017        const char *format;
1018        int ret;
1019
1020        manager = devm_kzalloc(dev, sizeof(*manager), GFP_KERNEL);
1021        if (!manager)
1022                return ERR_PTR(-ENOMEM);
1023
1024        /* this node is used to create a plane */
1025        manager->node = of_get_child_by_name(dev->of_node, "planes");
1026        if (!manager->node) {
1027                DRM_ERROR("failed to get a planes node\n");
1028                return ERR_PTR(-EINVAL);
1029        }
1030
1031        /* check the base pixel format of plane manager */
1032        ret = of_property_read_string(manager->node, "xlnx,pixel-format",
1033                                      &format);
1034        if (ret < 0) {
1035                DRM_ERROR("failed to get a plane manager format\n");
1036                return ERR_PTR(ret);
1037        }
1038
1039        ret = xilinx_drm_format_by_name(format, &manager->format);
1040        if (ret < 0) {
1041                DRM_ERROR("invalid plane manager format\n");
1042                return ERR_PTR(ret);
1043        }
1044
1045        manager->drm = drm;
1046
1047        /* probe an OSD. proceed even if there's no OSD */
1048        sub_node = of_parse_phandle(dev->of_node, "xlnx,osd", 0);
1049        if (sub_node) {
1050                manager->osd = xilinx_osd_probe(dev, sub_node);
1051                of_node_put(sub_node);
1052                if (IS_ERR(manager->osd)) {
1053                        of_node_put(manager->node);
1054                        DRM_ERROR("failed to probe an osd\n");
1055                        return ERR_CAST(manager->osd);
1056                }
1057                manager->default_alpha = OSD_MAX_ALPHA;
1058        }
1059
1060        manager->dp_sub = xilinx_drm_dp_sub_of_get(drm->dev->of_node);
1061        if (IS_ERR(manager->dp_sub)) {
1062                DRM_DEBUG_KMS("failed to get a dp_sub\n");
1063                return ERR_CAST(manager->dp_sub);
1064        } else if (manager->dp_sub) {
1065                manager->default_alpha = XILINX_DRM_DP_SUB_MAX_ALPHA;
1066        }
1067
1068        ret = xilinx_drm_plane_init_manager(manager);
1069        if (ret) {
1070                DRM_ERROR("failed to init a plane manager\n");
1071                return ERR_PTR(ret);
1072        }
1073
1074        xilinx_drm_plane_create_property(manager);
1075
1076        return manager;
1077}
1078
1079void xilinx_drm_plane_remove_manager(struct xilinx_drm_plane_manager *manager)
1080{
1081        xilinx_drm_dp_sub_put(manager->dp_sub);
1082        of_node_put(manager->node);
1083}
1084