linux/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015 MediaTek Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#include <drm/drmP.h>
  15#include <linux/clk.h>
  16#include <linux/component.h>
  17#include <linux/of_device.h>
  18#include <linux/of_irq.h>
  19#include <linux/platform_device.h>
  20
  21#include "mtk_drm_crtc.h"
  22#include "mtk_drm_ddp_comp.h"
  23
  24#define DISP_REG_OVL_INTEN                      0x0004
  25#define OVL_FME_CPL_INT                                 BIT(1)
  26#define DISP_REG_OVL_INTSTA                     0x0008
  27#define DISP_REG_OVL_EN                         0x000c
  28#define DISP_REG_OVL_RST                        0x0014
  29#define DISP_REG_OVL_ROI_SIZE                   0x0020
  30#define DISP_REG_OVL_ROI_BGCLR                  0x0028
  31#define DISP_REG_OVL_SRC_CON                    0x002c
  32#define DISP_REG_OVL_CON(n)                     (0x0030 + 0x20 * (n))
  33#define DISP_REG_OVL_SRC_SIZE(n)                (0x0038 + 0x20 * (n))
  34#define DISP_REG_OVL_OFFSET(n)                  (0x003c + 0x20 * (n))
  35#define DISP_REG_OVL_PITCH(n)                   (0x0044 + 0x20 * (n))
  36#define DISP_REG_OVL_RDMA_CTRL(n)               (0x00c0 + 0x20 * (n))
  37#define DISP_REG_OVL_RDMA_GMC(n)                (0x00c8 + 0x20 * (n))
  38#define DISP_REG_OVL_ADDR_MT2701                0x0040
  39#define DISP_REG_OVL_ADDR_MT8173                0x0f40
  40#define DISP_REG_OVL_ADDR(ovl, n)               ((ovl)->data->addr + 0x20 * (n))
  41
  42#define OVL_RDMA_MEM_GMC        0x40402020
  43
  44#define OVL_CON_BYTE_SWAP       BIT(24)
  45#define OVL_CON_MTX_YUV_TO_RGB  (6 << 16)
  46#define OVL_CON_CLRFMT_RGB      (1 << 12)
  47#define OVL_CON_CLRFMT_RGBA8888 (2 << 12)
  48#define OVL_CON_CLRFMT_ARGB8888 (3 << 12)
  49#define OVL_CON_CLRFMT_UYVY     (4 << 12)
  50#define OVL_CON_CLRFMT_YUYV     (5 << 12)
  51#define OVL_CON_CLRFMT_RGB565(ovl)      ((ovl)->data->fmt_rgb565_is_0 ? \
  52                                        0 : OVL_CON_CLRFMT_RGB)
  53#define OVL_CON_CLRFMT_RGB888(ovl)      ((ovl)->data->fmt_rgb565_is_0 ? \
  54                                        OVL_CON_CLRFMT_RGB : 0)
  55#define OVL_CON_AEN             BIT(8)
  56#define OVL_CON_ALPHA           0xff
  57
  58struct mtk_disp_ovl_data {
  59        unsigned int addr;
  60        bool fmt_rgb565_is_0;
  61};
  62
  63/**
  64 * struct mtk_disp_ovl - DISP_OVL driver structure
  65 * @ddp_comp - structure containing type enum and hardware resources
  66 * @crtc - associated crtc to report vblank events to
  67 */
  68struct mtk_disp_ovl {
  69        struct mtk_ddp_comp             ddp_comp;
  70        struct drm_crtc                 *crtc;
  71        const struct mtk_disp_ovl_data  *data;
  72};
  73
  74static inline struct mtk_disp_ovl *comp_to_ovl(struct mtk_ddp_comp *comp)
  75{
  76        return container_of(comp, struct mtk_disp_ovl, ddp_comp);
  77}
  78
  79static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
  80{
  81        struct mtk_disp_ovl *priv = dev_id;
  82        struct mtk_ddp_comp *ovl = &priv->ddp_comp;
  83
  84        /* Clear frame completion interrupt */
  85        writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
  86
  87        if (!priv->crtc)
  88                return IRQ_NONE;
  89
  90        mtk_crtc_ddp_irq(priv->crtc, ovl);
  91
  92        return IRQ_HANDLED;
  93}
  94
  95static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
  96                                  struct drm_crtc *crtc)
  97{
  98        struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
  99
 100        ovl->crtc = crtc;
 101        writel(0x0, comp->regs + DISP_REG_OVL_INTSTA);
 102        writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
 103}
 104
 105static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp)
 106{
 107        struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
 108
 109        ovl->crtc = NULL;
 110        writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN);
 111}
 112
 113static void mtk_ovl_start(struct mtk_ddp_comp *comp)
 114{
 115        writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
 116}
 117
 118static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
 119{
 120        writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
 121}
 122
 123static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
 124                           unsigned int h, unsigned int vrefresh,
 125                           unsigned int bpc)
 126{
 127        if (w != 0 && h != 0)
 128                writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
 129        writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR);
 130
 131        writel(0x1, comp->regs + DISP_REG_OVL_RST);
 132        writel(0x0, comp->regs + DISP_REG_OVL_RST);
 133}
 134
 135static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx)
 136{
 137        unsigned int reg;
 138
 139        writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
 140        writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
 141
 142        reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
 143        reg = reg | BIT(idx);
 144        writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
 145}
 146
 147static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
 148{
 149        unsigned int reg;
 150
 151        reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
 152        reg = reg & ~BIT(idx);
 153        writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
 154
 155        writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
 156}
 157
 158static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt)
 159{
 160        switch (fmt) {
 161        default:
 162        case DRM_FORMAT_RGB565:
 163                return OVL_CON_CLRFMT_RGB565(ovl);
 164        case DRM_FORMAT_BGR565:
 165                return OVL_CON_CLRFMT_RGB565(ovl) | OVL_CON_BYTE_SWAP;
 166        case DRM_FORMAT_RGB888:
 167                return OVL_CON_CLRFMT_RGB888(ovl);
 168        case DRM_FORMAT_BGR888:
 169                return OVL_CON_CLRFMT_RGB888(ovl) | OVL_CON_BYTE_SWAP;
 170        case DRM_FORMAT_RGBX8888:
 171        case DRM_FORMAT_RGBA8888:
 172                return OVL_CON_CLRFMT_ARGB8888;
 173        case DRM_FORMAT_BGRX8888:
 174        case DRM_FORMAT_BGRA8888:
 175                return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
 176        case DRM_FORMAT_XRGB8888:
 177        case DRM_FORMAT_ARGB8888:
 178                return OVL_CON_CLRFMT_RGBA8888;
 179        case DRM_FORMAT_XBGR8888:
 180        case DRM_FORMAT_ABGR8888:
 181                return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
 182        case DRM_FORMAT_UYVY:
 183                return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB;
 184        case DRM_FORMAT_YUYV:
 185                return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB;
 186        }
 187}
 188
 189static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
 190                                 struct mtk_plane_state *state)
 191{
 192        struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
 193        struct mtk_plane_pending_state *pending = &state->pending;
 194        unsigned int addr = pending->addr;
 195        unsigned int pitch = pending->pitch & 0xffff;
 196        unsigned int fmt = pending->format;
 197        unsigned int offset = (pending->y << 16) | pending->x;
 198        unsigned int src_size = (pending->height << 16) | pending->width;
 199        unsigned int con;
 200
 201        if (!pending->enable)
 202                mtk_ovl_layer_off(comp, idx);
 203
 204        con = ovl_fmt_convert(ovl, fmt);
 205        if (idx != 0)
 206                con |= OVL_CON_AEN | OVL_CON_ALPHA;
 207
 208        writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
 209        writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
 210        writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
 211        writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
 212        writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(ovl, idx));
 213
 214        if (pending->enable)
 215                mtk_ovl_layer_on(comp, idx);
 216}
 217
 218static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
 219        .config = mtk_ovl_config,
 220        .start = mtk_ovl_start,
 221        .stop = mtk_ovl_stop,
 222        .enable_vblank = mtk_ovl_enable_vblank,
 223        .disable_vblank = mtk_ovl_disable_vblank,
 224        .layer_on = mtk_ovl_layer_on,
 225        .layer_off = mtk_ovl_layer_off,
 226        .layer_config = mtk_ovl_layer_config,
 227};
 228
 229static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
 230                             void *data)
 231{
 232        struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
 233        struct drm_device *drm_dev = data;
 234        int ret;
 235
 236        ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
 237        if (ret < 0) {
 238                dev_err(dev, "Failed to register component %pOF: %d\n",
 239                        dev->of_node, ret);
 240                return ret;
 241        }
 242
 243        return 0;
 244}
 245
 246static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
 247                                void *data)
 248{
 249        struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
 250        struct drm_device *drm_dev = data;
 251
 252        mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
 253}
 254
 255static const struct component_ops mtk_disp_ovl_component_ops = {
 256        .bind   = mtk_disp_ovl_bind,
 257        .unbind = mtk_disp_ovl_unbind,
 258};
 259
 260static int mtk_disp_ovl_probe(struct platform_device *pdev)
 261{
 262        struct device *dev = &pdev->dev;
 263        struct mtk_disp_ovl *priv;
 264        int comp_id;
 265        int irq;
 266        int ret;
 267
 268        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 269        if (!priv)
 270                return -ENOMEM;
 271
 272        irq = platform_get_irq(pdev, 0);
 273        if (irq < 0)
 274                return irq;
 275
 276        comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
 277        if (comp_id < 0) {
 278                dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
 279                return comp_id;
 280        }
 281
 282        ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
 283                                &mtk_disp_ovl_funcs);
 284        if (ret) {
 285                dev_err(dev, "Failed to initialize component: %d\n", ret);
 286                return ret;
 287        }
 288
 289        priv->data = of_device_get_match_data(dev);
 290
 291        platform_set_drvdata(pdev, priv);
 292
 293        ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
 294                               IRQF_TRIGGER_NONE, dev_name(dev), priv);
 295        if (ret < 0) {
 296                dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
 297                return ret;
 298        }
 299
 300        ret = component_add(dev, &mtk_disp_ovl_component_ops);
 301        if (ret)
 302                dev_err(dev, "Failed to add component: %d\n", ret);
 303
 304        return ret;
 305}
 306
 307static int mtk_disp_ovl_remove(struct platform_device *pdev)
 308{
 309        component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
 310
 311        return 0;
 312}
 313
 314static const struct mtk_disp_ovl_data mt2701_ovl_driver_data = {
 315        .addr = DISP_REG_OVL_ADDR_MT2701,
 316        .fmt_rgb565_is_0 = false,
 317};
 318
 319static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = {
 320        .addr = DISP_REG_OVL_ADDR_MT8173,
 321        .fmt_rgb565_is_0 = true,
 322};
 323
 324static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
 325        { .compatible = "mediatek,mt2701-disp-ovl",
 326          .data = &mt2701_ovl_driver_data},
 327        { .compatible = "mediatek,mt8173-disp-ovl",
 328          .data = &mt8173_ovl_driver_data},
 329        {},
 330};
 331MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
 332
 333struct platform_driver mtk_disp_ovl_driver = {
 334        .probe          = mtk_disp_ovl_probe,
 335        .remove         = mtk_disp_ovl_remove,
 336        .driver         = {
 337                .name   = "mediatek-disp-ovl",
 338                .owner  = THIS_MODULE,
 339                .of_match_table = mtk_disp_ovl_driver_dt_match,
 340        },
 341};
 342