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/component.h>
  21#include <linux/module.h>
  22#include <linux/export.h>
  23#include <linux/device.h>
  24#include <linux/platform_device.h>
  25#include <drm/drmP.h>
  26#include <drm/drm_crtc_helper.h>
  27#include <linux/fb.h>
  28#include <linux/clk.h>
  29#include <linux/errno.h>
  30#include <drm/drm_gem_cma_helper.h>
  31#include <drm/drm_fb_cma_helper.h>
  32
  33#include <video/imx-ipu-v3.h>
  34#include "imx-drm.h"
  35#include "ipuv3-plane.h"
  36
  37#define DRIVER_DESC             "i.MX IPUv3 Graphics"
  38
  39struct ipu_crtc {
  40        struct device           *dev;
  41        struct drm_crtc         base;
  42        struct imx_drm_crtc     *imx_crtc;
  43
  44        /* plane[0] is the full plane, plane[1] is the partial plane */
  45        struct ipu_plane        *plane[2];
  46
  47        struct ipu_dc           *dc;
  48        struct ipu_di           *di;
  49        int                     enabled;
  50        struct drm_pending_vblank_event *page_flip_event;
  51        struct drm_framebuffer  *newfb;
  52        int                     irq;
  53        u32                     interface_pix_fmt;
  54        unsigned long           di_clkflags;
  55        int                     di_hsync_pin;
  56        int                     di_vsync_pin;
  57};
  58
  59#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
  60
  61static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
  62{
  63        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
  64
  65        if (ipu_crtc->enabled)
  66                return;
  67
  68        ipu_dc_enable(ipu);
  69        ipu_plane_enable(ipu_crtc->plane[0]);
  70        /* Start DC channel and DI after IDMAC */
  71        ipu_dc_enable_channel(ipu_crtc->dc);
  72        ipu_di_enable(ipu_crtc->di);
  73
  74        ipu_crtc->enabled = 1;
  75}
  76
  77static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
  78{
  79        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
  80
  81        if (!ipu_crtc->enabled)
  82                return;
  83
  84        /* Stop DC channel and DI before IDMAC */
  85        ipu_dc_disable_channel(ipu_crtc->dc);
  86        ipu_di_disable(ipu_crtc->di);
  87        ipu_plane_disable(ipu_crtc->plane[0]);
  88        ipu_dc_disable(ipu);
  89
  90        ipu_crtc->enabled = 0;
  91}
  92
  93static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
  94{
  95        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
  96
  97        dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode);
  98
  99        switch (mode) {
 100        case DRM_MODE_DPMS_ON:
 101                ipu_fb_enable(ipu_crtc);
 102                break;
 103        case DRM_MODE_DPMS_STANDBY:
 104        case DRM_MODE_DPMS_SUSPEND:
 105        case DRM_MODE_DPMS_OFF:
 106                ipu_fb_disable(ipu_crtc);
 107                break;
 108        }
 109}
 110
 111static int ipu_page_flip(struct drm_crtc *crtc,
 112                struct drm_framebuffer *fb,
 113                struct drm_pending_vblank_event *event,
 114                uint32_t page_flip_flags)
 115{
 116        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 117        int ret;
 118
 119        if (ipu_crtc->newfb)
 120                return -EBUSY;
 121
 122        ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
 123        if (ret) {
 124                dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n");
 125                list_del(&event->base.link);
 126
 127                return ret;
 128        }
 129
 130        ipu_crtc->newfb = fb;
 131        ipu_crtc->page_flip_event = event;
 132        crtc->primary->fb = fb;
 133
 134        return 0;
 135}
 136
 137static const struct drm_crtc_funcs ipu_crtc_funcs = {
 138        .set_config = drm_crtc_helper_set_config,
 139        .destroy = drm_crtc_cleanup,
 140        .page_flip = ipu_page_flip,
 141};
 142
 143static int ipu_crtc_mode_set(struct drm_crtc *crtc,
 144                               struct drm_display_mode *orig_mode,
 145                               struct drm_display_mode *mode,
 146                               int x, int y,
 147                               struct drm_framebuffer *old_fb)
 148{
 149        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 150        int ret;
 151        struct ipu_di_signal_cfg sig_cfg = {};
 152        u32 out_pixel_fmt;
 153
 154        dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
 155                        mode->hdisplay);
 156        dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
 157                        mode->vdisplay);
 158
 159        out_pixel_fmt = ipu_crtc->interface_pix_fmt;
 160
 161        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 162                sig_cfg.interlaced = 1;
 163        if (mode->flags & DRM_MODE_FLAG_PHSYNC)
 164                sig_cfg.Hsync_pol = 1;
 165        if (mode->flags & DRM_MODE_FLAG_PVSYNC)
 166                sig_cfg.Vsync_pol = 1;
 167
 168        sig_cfg.enable_pol = 1;
 169        sig_cfg.clk_pol = 0;
 170        sig_cfg.width = mode->hdisplay;
 171        sig_cfg.height = mode->vdisplay;
 172        sig_cfg.pixel_fmt = out_pixel_fmt;
 173        sig_cfg.h_start_width = mode->htotal - mode->hsync_end;
 174        sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start;
 175        sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay;
 176
 177        sig_cfg.v_start_width = mode->vtotal - mode->vsync_end;
 178        sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start;
 179        sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay;
 180        sig_cfg.pixelclock = mode->clock * 1000;
 181        sig_cfg.clkflags = ipu_crtc->di_clkflags;
 182
 183        sig_cfg.v_to_h_sync = 0;
 184
 185        sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
 186        sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;
 187
 188        ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
 189                        out_pixel_fmt, mode->hdisplay);
 190        if (ret) {
 191                dev_err(ipu_crtc->dev,
 192                                "initializing display controller failed with %d\n",
 193                                ret);
 194                return ret;
 195        }
 196
 197        ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
 198        if (ret) {
 199                dev_err(ipu_crtc->dev,
 200                                "initializing panel failed with %d\n", ret);
 201                return ret;
 202        }
 203
 204        return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode,
 205                                  crtc->primary->fb,
 206                                  0, 0, mode->hdisplay, mode->vdisplay,
 207                                  x, y, mode->hdisplay, mode->vdisplay);
 208}
 209
 210static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
 211{
 212        unsigned long flags;
 213        struct drm_device *drm = ipu_crtc->base.dev;
 214
 215        spin_lock_irqsave(&drm->event_lock, flags);
 216        if (ipu_crtc->page_flip_event)
 217                drm_send_vblank_event(drm, -1, ipu_crtc->page_flip_event);
 218        ipu_crtc->page_flip_event = NULL;
 219        imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
 220        spin_unlock_irqrestore(&drm->event_lock, flags);
 221}
 222
 223static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
 224{
 225        struct ipu_crtc *ipu_crtc = dev_id;
 226
 227        imx_drm_handle_vblank(ipu_crtc->imx_crtc);
 228
 229        if (ipu_crtc->newfb) {
 230                struct ipu_plane *plane = ipu_crtc->plane[0];
 231
 232                ipu_crtc->newfb = NULL;
 233                ipu_plane_set_base(plane, ipu_crtc->base.primary->fb,
 234                                   plane->x, plane->y);
 235                ipu_crtc_handle_pageflip(ipu_crtc);
 236        }
 237
 238        return IRQ_HANDLED;
 239}
 240
 241static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
 242                                  const struct drm_display_mode *mode,
 243                                  struct drm_display_mode *adjusted_mode)
 244{
 245        return true;
 246}
 247
 248static void ipu_crtc_prepare(struct drm_crtc *crtc)
 249{
 250        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 251
 252        ipu_fb_disable(ipu_crtc);
 253}
 254
 255static void ipu_crtc_commit(struct drm_crtc *crtc)
 256{
 257        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 258
 259        ipu_fb_enable(ipu_crtc);
 260}
 261
 262static struct drm_crtc_helper_funcs ipu_helper_funcs = {
 263        .dpms = ipu_crtc_dpms,
 264        .mode_fixup = ipu_crtc_mode_fixup,
 265        .mode_set = ipu_crtc_mode_set,
 266        .prepare = ipu_crtc_prepare,
 267        .commit = ipu_crtc_commit,
 268};
 269
 270static int ipu_enable_vblank(struct drm_crtc *crtc)
 271{
 272        return 0;
 273}
 274
 275static void ipu_disable_vblank(struct drm_crtc *crtc)
 276{
 277        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 278
 279        ipu_crtc->page_flip_event = NULL;
 280        ipu_crtc->newfb = NULL;
 281}
 282
 283static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type,
 284                u32 pixfmt, int hsync_pin, int vsync_pin)
 285{
 286        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 287
 288        ipu_crtc->interface_pix_fmt = pixfmt;
 289        ipu_crtc->di_hsync_pin = hsync_pin;
 290        ipu_crtc->di_vsync_pin = vsync_pin;
 291
 292        switch (encoder_type) {
 293        case DRM_MODE_ENCODER_DAC:
 294        case DRM_MODE_ENCODER_TVDAC:
 295        case DRM_MODE_ENCODER_LVDS:
 296                ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC |
 297                        IPU_DI_CLKMODE_EXT;
 298                break;
 299        case DRM_MODE_ENCODER_TMDS:
 300        case DRM_MODE_ENCODER_NONE:
 301                ipu_crtc->di_clkflags = 0;
 302                break;
 303        }
 304
 305        return 0;
 306}
 307
 308static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {
 309        .enable_vblank = ipu_enable_vblank,
 310        .disable_vblank = ipu_disable_vblank,
 311        .set_interface_pix_fmt = ipu_set_interface_pix_fmt,
 312        .crtc_funcs = &ipu_crtc_funcs,
 313        .crtc_helper_funcs = &ipu_helper_funcs,
 314};
 315
 316static void ipu_put_resources(struct ipu_crtc *ipu_crtc)
 317{
 318        if (!IS_ERR_OR_NULL(ipu_crtc->dc))
 319                ipu_dc_put(ipu_crtc->dc);
 320        if (!IS_ERR_OR_NULL(ipu_crtc->di))
 321                ipu_di_put(ipu_crtc->di);
 322}
 323
 324static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
 325                struct ipu_client_platformdata *pdata)
 326{
 327        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
 328        int ret;
 329
 330        ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc);
 331        if (IS_ERR(ipu_crtc->dc)) {
 332                ret = PTR_ERR(ipu_crtc->dc);
 333                goto err_out;
 334        }
 335
 336        ipu_crtc->di = ipu_di_get(ipu, pdata->di);
 337        if (IS_ERR(ipu_crtc->di)) {
 338                ret = PTR_ERR(ipu_crtc->di);
 339                goto err_out;
 340        }
 341
 342        return 0;
 343err_out:
 344        ipu_put_resources(ipu_crtc);
 345
 346        return ret;
 347}
 348
 349static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 350        struct ipu_client_platformdata *pdata, struct drm_device *drm)
 351{
 352        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
 353        int dp = -EINVAL;
 354        int ret;
 355        int id;
 356
 357        ret = ipu_get_resources(ipu_crtc, pdata);
 358        if (ret) {
 359                dev_err(ipu_crtc->dev, "getting resources failed with %d.\n",
 360                                ret);
 361                return ret;
 362        }
 363
 364        ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc,
 365                        &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node);
 366        if (ret) {
 367                dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
 368                goto err_put_resources;
 369        }
 370
 371        if (pdata->dp >= 0)
 372                dp = IPU_DP_FLOW_SYNC_BG;
 373        id = imx_drm_crtc_id(ipu_crtc->imx_crtc);
 374        ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu,
 375                                            pdata->dma[0], dp, BIT(id), true);
 376        ret = ipu_plane_get_resources(ipu_crtc->plane[0]);
 377        if (ret) {
 378                dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n",
 379                        ret);
 380                goto err_remove_crtc;
 381        }
 382
 383        /* If this crtc is using the DP, add an overlay plane */
 384        if (pdata->dp >= 0 && pdata->dma[1] > 0) {
 385                ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu,
 386                                                    pdata->dma[1],
 387                                                    IPU_DP_FLOW_SYNC_FG,
 388                                                    BIT(id), false);
 389                if (IS_ERR(ipu_crtc->plane[1]))
 390                        ipu_crtc->plane[1] = NULL;
 391        }
 392
 393        ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]);
 394        ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0,
 395                        "imx_drm", ipu_crtc);
 396        if (ret < 0) {
 397                dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
 398                goto err_put_plane_res;
 399        }
 400
 401        return 0;
 402
 403err_put_plane_res:
 404        ipu_plane_put_resources(ipu_crtc->plane[0]);
 405err_remove_crtc:
 406        imx_drm_remove_crtc(ipu_crtc->imx_crtc);
 407err_put_resources:
 408        ipu_put_resources(ipu_crtc);
 409
 410        return ret;
 411}
 412
 413static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent,
 414                                                  int port_id)
 415{
 416        struct device_node *port;
 417        int id, ret;
 418
 419        port = of_get_child_by_name(parent, "port");
 420        while (port) {
 421                ret = of_property_read_u32(port, "reg", &id);
 422                if (!ret && id == port_id)
 423                        return port;
 424
 425                do {
 426                        port = of_get_next_child(parent, port);
 427                        if (!port)
 428                                return NULL;
 429                } while (of_node_cmp(port->name, "port"));
 430        }
 431
 432        return NULL;
 433}
 434
 435static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
 436{
 437        struct ipu_client_platformdata *pdata = dev->platform_data;
 438        struct drm_device *drm = data;
 439        struct ipu_crtc *ipu_crtc;
 440        int ret;
 441
 442        ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL);
 443        if (!ipu_crtc)
 444                return -ENOMEM;
 445
 446        ipu_crtc->dev = dev;
 447
 448        ret = ipu_crtc_init(ipu_crtc, pdata, drm);
 449        if (ret)
 450                return ret;
 451
 452        dev_set_drvdata(dev, ipu_crtc);
 453
 454        return 0;
 455}
 456
 457static void ipu_drm_unbind(struct device *dev, struct device *master,
 458        void *data)
 459{
 460        struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev);
 461
 462        imx_drm_remove_crtc(ipu_crtc->imx_crtc);
 463
 464        ipu_plane_put_resources(ipu_crtc->plane[0]);
 465        ipu_put_resources(ipu_crtc);
 466}
 467
 468static const struct component_ops ipu_crtc_ops = {
 469        .bind = ipu_drm_bind,
 470        .unbind = ipu_drm_unbind,
 471};
 472
 473static int ipu_drm_probe(struct platform_device *pdev)
 474{
 475        struct device *dev = &pdev->dev;
 476        struct ipu_client_platformdata *pdata = dev->platform_data;
 477        int ret;
 478
 479        if (!dev->platform_data)
 480                return -EINVAL;
 481
 482        if (!dev->of_node) {
 483                /* Associate crtc device with the corresponding DI port node */
 484                dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node,
 485                                                      pdata->di + 2);
 486                if (!dev->of_node) {
 487                        dev_err(dev, "missing port@%d node in %s\n",
 488                                pdata->di + 2, dev->parent->of_node->full_name);
 489                        return -ENODEV;
 490                }
 491        }
 492
 493        ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
 494        if (ret)
 495                return ret;
 496
 497        return component_add(dev, &ipu_crtc_ops);
 498}
 499
 500static int ipu_drm_remove(struct platform_device *pdev)
 501{
 502        component_del(&pdev->dev, &ipu_crtc_ops);
 503        return 0;
 504}
 505
 506static struct platform_driver ipu_drm_driver = {
 507        .driver = {
 508                .name = "imx-ipuv3-crtc",
 509        },
 510        .probe = ipu_drm_probe,
 511        .remove = ipu_drm_remove,
 512};
 513module_platform_driver(ipu_drm_driver);
 514
 515MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 516MODULE_DESCRIPTION(DRIVER_DESC);
 517MODULE_LICENSE("GPL");
 518MODULE_ALIAS("platform:imx-ipuv3-crtc");
 519