linux/drivers/staging/imx-drm/ipuv3-crtc.c
<<
>>
Prefs
   1/*
   2 * i.MX IPUv3 Graphics driver
   3 *
   4 * Copyright (C) 2011 Sascha Hauer, Pengutronix
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version 2
   9 * of the License, or (at your option) any later version.
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  18 * MA 02110-1301, USA.
  19 */
  20#include <linux/module.h>
  21#include <linux/export.h>
  22#include <linux/device.h>
  23#include <linux/platform_device.h>
  24#include <drm/drmP.h>
  25#include <drm/drm_fb_helper.h>
  26#include <drm/drm_crtc_helper.h>
  27#include <linux/fb.h>
  28#include <linux/clk.h>
  29#include <drm/drm_gem_cma_helper.h>
  30#include <drm/drm_fb_cma_helper.h>
  31
  32#include "ipu-v3/imx-ipu-v3.h"
  33#include "imx-drm.h"
  34
  35#define DRIVER_DESC             "i.MX IPUv3 Graphics"
  36
  37struct ipu_framebuffer {
  38        struct drm_framebuffer  base;
  39        void                    *virt;
  40        dma_addr_t              phys;
  41        size_t                  len;
  42};
  43
  44struct ipu_crtc {
  45        struct drm_fb_helper    fb_helper;
  46        struct ipu_framebuffer  ifb;
  47        int                     num_crtcs;
  48        struct device           *dev;
  49        struct drm_crtc         base;
  50        struct imx_drm_crtc     *imx_crtc;
  51        struct ipuv3_channel    *ipu_ch;
  52        struct ipu_dc           *dc;
  53        struct ipu_dp           *dp;
  54        struct dmfc_channel     *dmfc;
  55        struct ipu_di           *di;
  56        int                     enabled;
  57        struct ipu_priv         *ipu_priv;
  58        struct drm_pending_vblank_event *page_flip_event;
  59        struct drm_framebuffer  *newfb;
  60        int                     irq;
  61        u32                     interface_pix_fmt;
  62        unsigned long           di_clkflags;
  63};
  64
  65#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
  66
  67static int calc_vref(struct drm_display_mode *mode)
  68{
  69        unsigned long htotal, vtotal;
  70
  71        htotal = mode->htotal;
  72        vtotal = mode->vtotal;
  73
  74        if (!htotal || !vtotal)
  75                return 60;
  76
  77        return mode->clock * 1000 / vtotal / htotal;
  78}
  79
  80static int calc_bandwidth(struct drm_display_mode *mode, unsigned int vref)
  81{
  82        return mode->hdisplay * mode->vdisplay * vref;
  83}
  84
  85static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
  86{
  87        if (ipu_crtc->enabled)
  88                return;
  89
  90        ipu_di_enable(ipu_crtc->di);
  91        ipu_dmfc_enable_channel(ipu_crtc->dmfc);
  92        ipu_idmac_enable_channel(ipu_crtc->ipu_ch);
  93        ipu_dc_enable_channel(ipu_crtc->dc);
  94        if (ipu_crtc->dp)
  95                ipu_dp_enable_channel(ipu_crtc->dp);
  96
  97        ipu_crtc->enabled = 1;
  98}
  99
 100static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
 101{
 102        if (!ipu_crtc->enabled)
 103                return;
 104
 105        if (ipu_crtc->dp)
 106                ipu_dp_disable_channel(ipu_crtc->dp);
 107        ipu_dc_disable_channel(ipu_crtc->dc);
 108        ipu_idmac_disable_channel(ipu_crtc->ipu_ch);
 109        ipu_dmfc_disable_channel(ipu_crtc->dmfc);
 110        ipu_di_disable(ipu_crtc->di);
 111
 112        ipu_crtc->enabled = 0;
 113}
 114
 115static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
 116{
 117        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 118
 119        dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode);
 120
 121        switch (mode) {
 122        case DRM_MODE_DPMS_ON:
 123                ipu_fb_enable(ipu_crtc);
 124                break;
 125        case DRM_MODE_DPMS_STANDBY:
 126        case DRM_MODE_DPMS_SUSPEND:
 127        case DRM_MODE_DPMS_OFF:
 128                ipu_fb_disable(ipu_crtc);
 129                break;
 130        }
 131}
 132
 133static int ipu_page_flip(struct drm_crtc *crtc,
 134                struct drm_framebuffer *fb,
 135                struct drm_pending_vblank_event *event)
 136{
 137        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 138        int ret;
 139
 140        if (ipu_crtc->newfb)
 141                return -EBUSY;
 142
 143        ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
 144        if (ret) {
 145                dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n");
 146                list_del(&event->base.link);
 147
 148                return ret;
 149        }
 150
 151        ipu_crtc->newfb = fb;
 152        ipu_crtc->page_flip_event = event;
 153
 154        return 0;
 155}
 156
 157static const struct drm_crtc_funcs ipu_crtc_funcs = {
 158        .set_config = drm_crtc_helper_set_config,
 159        .destroy = drm_crtc_cleanup,
 160        .page_flip = ipu_page_flip,
 161};
 162
 163static int ipu_drm_set_base(struct drm_crtc *crtc, int x, int y)
 164{
 165        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 166        struct drm_gem_cma_object *cma_obj;
 167        struct drm_framebuffer *fb = crtc->fb;
 168        unsigned long phys;
 169
 170        cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
 171        if (!cma_obj) {
 172                DRM_LOG_KMS("entry is null.\n");
 173                return -EFAULT;
 174        }
 175
 176        phys = cma_obj->paddr;
 177        phys += x * (fb->bits_per_pixel >> 3);
 178        phys += y * fb->pitches[0];
 179
 180        dev_dbg(ipu_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
 181        dev_dbg(ipu_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
 182
 183        ipu_cpmem_set_stride(ipu_get_cpmem(ipu_crtc->ipu_ch), fb->pitches[0]);
 184        ipu_cpmem_set_buffer(ipu_get_cpmem(ipu_crtc->ipu_ch),
 185                          0, phys);
 186
 187        return 0;
 188}
 189
 190static int ipu_crtc_mode_set(struct drm_crtc *crtc,
 191                               struct drm_display_mode *orig_mode,
 192                               struct drm_display_mode *mode,
 193                               int x, int y,
 194                               struct drm_framebuffer *old_fb)
 195{
 196        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 197        struct drm_framebuffer *fb = ipu_crtc->base.fb;
 198        int ret;
 199        struct ipu_di_signal_cfg sig_cfg = {};
 200        u32 out_pixel_fmt;
 201        struct ipu_ch_param __iomem *cpmem = ipu_get_cpmem(ipu_crtc->ipu_ch);
 202        int bpp;
 203        u32 v4l2_fmt;
 204
 205        dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
 206                        mode->hdisplay);
 207        dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
 208                        mode->vdisplay);
 209
 210        ipu_ch_param_zero(cpmem);
 211
 212        switch (fb->pixel_format) {
 213        case DRM_FORMAT_XRGB8888:
 214        case DRM_FORMAT_ARGB8888:
 215                v4l2_fmt = V4L2_PIX_FMT_RGB32;
 216                bpp = 32;
 217                break;
 218        case DRM_FORMAT_RGB565:
 219                v4l2_fmt = V4L2_PIX_FMT_RGB565;
 220                bpp = 16;
 221                break;
 222        case DRM_FORMAT_RGB888:
 223                v4l2_fmt = V4L2_PIX_FMT_RGB24;
 224                bpp = 24;
 225                break;
 226        default:
 227                dev_err(ipu_crtc->dev, "unsupported pixel format 0x%08x\n",
 228                                fb->pixel_format);
 229                return -EINVAL;
 230        }
 231
 232        out_pixel_fmt = ipu_crtc->interface_pix_fmt;
 233
 234        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 235                sig_cfg.interlaced = 1;
 236        if (mode->flags & DRM_MODE_FLAG_PHSYNC)
 237                sig_cfg.Hsync_pol = 1;
 238        if (mode->flags & DRM_MODE_FLAG_PVSYNC)
 239                sig_cfg.Vsync_pol = 1;
 240
 241        sig_cfg.enable_pol = 1;
 242        sig_cfg.clk_pol = 0;
 243        sig_cfg.width = mode->hdisplay;
 244        sig_cfg.height = mode->vdisplay;
 245        sig_cfg.pixel_fmt = out_pixel_fmt;
 246        sig_cfg.h_start_width = mode->htotal - mode->hsync_end;
 247        sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start;
 248        sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay;
 249
 250        sig_cfg.v_start_width = mode->vtotal - mode->vsync_end;
 251        sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start;
 252        sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay;
 253        sig_cfg.pixelclock = mode->clock * 1000;
 254        sig_cfg.clkflags = ipu_crtc->di_clkflags;
 255
 256        sig_cfg.v_to_h_sync = 0;
 257
 258        if (ipu_crtc->dp) {
 259                ret = ipu_dp_setup_channel(ipu_crtc->dp, IPUV3_COLORSPACE_RGB,
 260                                IPUV3_COLORSPACE_RGB);
 261                if (ret) {
 262                        dev_err(ipu_crtc->dev,
 263                                "initializing display processor failed with %d\n",
 264                                ret);
 265                        return ret;
 266                }
 267                ipu_dp_set_global_alpha(ipu_crtc->dp, 1, 0, 1);
 268        }
 269
 270        ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
 271                        out_pixel_fmt, mode->hdisplay);
 272        if (ret) {
 273                dev_err(ipu_crtc->dev,
 274                                "initializing display controller failed with %d\n",
 275                                ret);
 276                return ret;
 277        }
 278
 279        ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
 280        if (ret) {
 281                dev_err(ipu_crtc->dev,
 282                                "initializing panel failed with %d\n", ret);
 283                return ret;
 284        }
 285
 286        ipu_cpmem_set_resolution(cpmem, mode->hdisplay, mode->vdisplay);
 287        ipu_cpmem_set_fmt(cpmem, v4l2_fmt);
 288        ipu_cpmem_set_high_priority(ipu_crtc->ipu_ch);
 289
 290        ret = ipu_dmfc_init_channel(ipu_crtc->dmfc, mode->hdisplay);
 291        if (ret) {
 292                dev_err(ipu_crtc->dev,
 293                                "initializing dmfc channel failed with %d\n",
 294                                ret);
 295                return ret;
 296        }
 297
 298        ret = ipu_dmfc_alloc_bandwidth(ipu_crtc->dmfc,
 299                        calc_bandwidth(mode, calc_vref(mode)), 64);
 300        if (ret) {
 301                dev_err(ipu_crtc->dev,
 302                                "allocating dmfc bandwidth failed with %d\n",
 303                                ret);
 304                return ret;
 305        }
 306
 307        ipu_drm_set_base(crtc, x, y);
 308
 309        return 0;
 310}
 311
 312static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
 313{
 314        struct drm_pending_vblank_event *e;
 315        struct timeval now;
 316        unsigned long flags;
 317        struct drm_device *drm = ipu_crtc->base.dev;
 318
 319        spin_lock_irqsave(&drm->event_lock, flags);
 320
 321        e = ipu_crtc->page_flip_event;
 322        if (!e) {
 323                spin_unlock_irqrestore(&drm->event_lock, flags);
 324                return;
 325        }
 326
 327        do_gettimeofday(&now);
 328        e->event.sequence = 0;
 329        e->event.tv_sec = now.tv_sec;
 330        e->event.tv_usec = now.tv_usec;
 331        ipu_crtc->page_flip_event = NULL;
 332
 333        imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
 334
 335        list_add_tail(&e->base.link, &e->base.file_priv->event_list);
 336
 337        wake_up_interruptible(&e->base.file_priv->event_wait);
 338
 339        spin_unlock_irqrestore(&drm->event_lock, flags);
 340}
 341
 342static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
 343{
 344        struct ipu_crtc *ipu_crtc = dev_id;
 345
 346        imx_drm_handle_vblank(ipu_crtc->imx_crtc);
 347
 348        if (ipu_crtc->newfb) {
 349                ipu_crtc->base.fb = ipu_crtc->newfb;
 350                ipu_crtc->newfb = NULL;
 351                ipu_drm_set_base(&ipu_crtc->base, 0, 0);
 352                ipu_crtc_handle_pageflip(ipu_crtc);
 353        }
 354
 355        return IRQ_HANDLED;
 356}
 357
 358static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
 359                                  const struct drm_display_mode *mode,
 360                                  struct drm_display_mode *adjusted_mode)
 361{
 362        return true;
 363}
 364
 365static void ipu_crtc_prepare(struct drm_crtc *crtc)
 366{
 367        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 368
 369        ipu_fb_disable(ipu_crtc);
 370}
 371
 372static void ipu_crtc_commit(struct drm_crtc *crtc)
 373{
 374        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 375
 376        ipu_fb_enable(ipu_crtc);
 377}
 378
 379static void ipu_crtc_load_lut(struct drm_crtc *crtc)
 380{
 381}
 382
 383static struct drm_crtc_helper_funcs ipu_helper_funcs = {
 384        .dpms = ipu_crtc_dpms,
 385        .mode_fixup = ipu_crtc_mode_fixup,
 386        .mode_set = ipu_crtc_mode_set,
 387        .prepare = ipu_crtc_prepare,
 388        .commit = ipu_crtc_commit,
 389        .load_lut = ipu_crtc_load_lut,
 390};
 391
 392static int ipu_enable_vblank(struct drm_crtc *crtc)
 393{
 394        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 395
 396        enable_irq(ipu_crtc->irq);
 397
 398        return 0;
 399}
 400
 401static void ipu_disable_vblank(struct drm_crtc *crtc)
 402{
 403        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 404
 405        disable_irq(ipu_crtc->irq);
 406}
 407
 408static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type,
 409                u32 pixfmt)
 410{
 411        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 412
 413        ipu_crtc->interface_pix_fmt = pixfmt;
 414
 415        switch (encoder_type) {
 416        case DRM_MODE_ENCODER_LVDS:
 417                ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC |
 418                        IPU_DI_CLKMODE_EXT;
 419                break;
 420        case DRM_MODE_ENCODER_NONE:
 421                ipu_crtc->di_clkflags = 0;
 422                break;
 423        }
 424
 425        return 0;
 426}
 427
 428static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {
 429        .enable_vblank = ipu_enable_vblank,
 430        .disable_vblank = ipu_disable_vblank,
 431        .set_interface_pix_fmt = ipu_set_interface_pix_fmt,
 432        .crtc_funcs = &ipu_crtc_funcs,
 433        .crtc_helper_funcs = &ipu_helper_funcs,
 434};
 435
 436static void ipu_put_resources(struct ipu_crtc *ipu_crtc)
 437{
 438        if (!IS_ERR_OR_NULL(ipu_crtc->ipu_ch))
 439                ipu_idmac_put(ipu_crtc->ipu_ch);
 440        if (!IS_ERR_OR_NULL(ipu_crtc->dmfc))
 441                ipu_dmfc_put(ipu_crtc->dmfc);
 442        if (!IS_ERR_OR_NULL(ipu_crtc->dp))
 443                ipu_dp_put(ipu_crtc->dp);
 444        if (!IS_ERR_OR_NULL(ipu_crtc->di))
 445                ipu_di_put(ipu_crtc->di);
 446}
 447
 448static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
 449                struct ipu_client_platformdata *pdata)
 450{
 451        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
 452        int ret;
 453
 454        ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
 455        if (IS_ERR(ipu_crtc->ipu_ch)) {
 456                ret = PTR_ERR(ipu_crtc->ipu_ch);
 457                goto err_out;
 458        }
 459
 460        ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc);
 461        if (IS_ERR(ipu_crtc->dc)) {
 462                ret = PTR_ERR(ipu_crtc->dc);
 463                goto err_out;
 464        }
 465
 466        ipu_crtc->dmfc = ipu_dmfc_get(ipu, pdata->dma[0]);
 467        if (IS_ERR(ipu_crtc->dmfc)) {
 468                ret = PTR_ERR(ipu_crtc->dmfc);
 469                goto err_out;
 470        }
 471
 472        if (pdata->dp >= 0) {
 473                ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
 474                if (IS_ERR(ipu_crtc->dp)) {
 475                        ret = PTR_ERR(ipu_crtc->dp);
 476                        goto err_out;
 477                }
 478        }
 479
 480        ipu_crtc->di = ipu_di_get(ipu, pdata->di);
 481        if (IS_ERR(ipu_crtc->di)) {
 482                ret = PTR_ERR(ipu_crtc->di);
 483                goto err_out;
 484        }
 485
 486        return 0;
 487err_out:
 488        ipu_put_resources(ipu_crtc);
 489
 490        return ret;
 491}
 492
 493static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 494                struct ipu_client_platformdata *pdata)
 495{
 496        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
 497        int ret;
 498
 499        ret = ipu_get_resources(ipu_crtc, pdata);
 500        if (ret) {
 501                dev_err(ipu_crtc->dev, "getting resources failed with %d.\n",
 502                                ret);
 503                return ret;
 504        }
 505
 506        ret = imx_drm_add_crtc(&ipu_crtc->base,
 507                        &ipu_crtc->imx_crtc,
 508                        &ipu_crtc_helper_funcs, THIS_MODULE,
 509                        ipu_crtc->dev->parent->of_node, pdata->di);
 510        if (ret) {
 511                dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
 512                goto err_put_resources;
 513        }
 514
 515        ipu_crtc->irq = ipu_idmac_channel_irq(ipu, ipu_crtc->ipu_ch,
 516                        IPU_IRQ_EOF);
 517        ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0,
 518                        "imx_drm", ipu_crtc);
 519        if (ret < 0) {
 520                dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
 521                goto err_put_resources;
 522        }
 523
 524        disable_irq(ipu_crtc->irq);
 525
 526        return 0;
 527
 528err_put_resources:
 529        ipu_put_resources(ipu_crtc);
 530
 531        return ret;
 532}
 533
 534static int ipu_drm_probe(struct platform_device *pdev)
 535{
 536        struct ipu_client_platformdata *pdata = pdev->dev.platform_data;
 537        struct ipu_crtc *ipu_crtc;
 538        int ret;
 539
 540        if (!pdata)
 541                return -EINVAL;
 542
 543        pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
 544
 545        ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL);
 546        if (!ipu_crtc)
 547                return -ENOMEM;
 548
 549        ipu_crtc->dev = &pdev->dev;
 550
 551        ret = ipu_crtc_init(ipu_crtc, pdata);
 552        if (ret)
 553                return ret;
 554
 555        platform_set_drvdata(pdev, ipu_crtc);
 556
 557        return 0;
 558}
 559
 560static int ipu_drm_remove(struct platform_device *pdev)
 561{
 562        struct ipu_crtc *ipu_crtc = platform_get_drvdata(pdev);
 563
 564        imx_drm_remove_crtc(ipu_crtc->imx_crtc);
 565
 566        ipu_put_resources(ipu_crtc);
 567
 568        return 0;
 569}
 570
 571static struct platform_driver ipu_drm_driver = {
 572        .driver = {
 573                .name = "imx-ipuv3-crtc",
 574        },
 575        .probe = ipu_drm_probe,
 576        .remove = ipu_drm_remove,
 577};
 578module_platform_driver(ipu_drm_driver);
 579
 580MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 581MODULE_DESCRIPTION(DRIVER_DESC);
 582MODULE_LICENSE("GPL");
 583