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