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 unsigned int mtk_ovl_layer_nr(struct mtk_ddp_comp *comp)
 136{
 137        return 4;
 138}
 139
 140static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx)
 141{
 142        unsigned int reg;
 143
 144        writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
 145        writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
 146
 147        reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
 148        reg = reg | BIT(idx);
 149        writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
 150}
 151
 152static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
 153{
 154        unsigned int reg;
 155
 156        reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
 157        reg = reg & ~BIT(idx);
 158        writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
 159
 160        writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
 161}
 162
 163static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt)
 164{
 165        /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX"
 166         * is defined in mediatek HW data sheet.
 167         * The alphabet order in XXX is no relation to data
 168         * arrangement in memory.
 169         */
 170        switch (fmt) {
 171        default:
 172        case DRM_FORMAT_RGB565:
 173                return OVL_CON_CLRFMT_RGB565(ovl);
 174        case DRM_FORMAT_BGR565:
 175                return OVL_CON_CLRFMT_RGB565(ovl) | OVL_CON_BYTE_SWAP;
 176        case DRM_FORMAT_RGB888:
 177                return OVL_CON_CLRFMT_RGB888(ovl);
 178        case DRM_FORMAT_BGR888:
 179                return OVL_CON_CLRFMT_RGB888(ovl) | OVL_CON_BYTE_SWAP;
 180        case DRM_FORMAT_RGBX8888:
 181        case DRM_FORMAT_RGBA8888:
 182                return OVL_CON_CLRFMT_ARGB8888;
 183        case DRM_FORMAT_BGRX8888:
 184        case DRM_FORMAT_BGRA8888:
 185                return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
 186        case DRM_FORMAT_XRGB8888:
 187        case DRM_FORMAT_ARGB8888:
 188                return OVL_CON_CLRFMT_RGBA8888;
 189        case DRM_FORMAT_XBGR8888:
 190        case DRM_FORMAT_ABGR8888:
 191                return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
 192        case DRM_FORMAT_UYVY:
 193                return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB;
 194        case DRM_FORMAT_YUYV:
 195                return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB;
 196        }
 197}
 198
 199static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
 200                                 struct mtk_plane_state *state)
 201{
 202        struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
 203        struct mtk_plane_pending_state *pending = &state->pending;
 204        unsigned int addr = pending->addr;
 205        unsigned int pitch = pending->pitch & 0xffff;
 206        unsigned int fmt = pending->format;
 207        unsigned int offset = (pending->y << 16) | pending->x;
 208        unsigned int src_size = (pending->height << 16) | pending->width;
 209        unsigned int con;
 210
 211        if (!pending->enable)
 212                mtk_ovl_layer_off(comp, idx);
 213
 214        con = ovl_fmt_convert(ovl, fmt);
 215        if (idx != 0)
 216                con |= OVL_CON_AEN | OVL_CON_ALPHA;
 217
 218        writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
 219        writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
 220        writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
 221        writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
 222        writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(ovl, idx));
 223
 224        if (pending->enable)
 225                mtk_ovl_layer_on(comp, idx);
 226}
 227
 228static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
 229        .config = mtk_ovl_config,
 230        .start = mtk_ovl_start,
 231        .stop = mtk_ovl_stop,
 232        .enable_vblank = mtk_ovl_enable_vblank,
 233        .disable_vblank = mtk_ovl_disable_vblank,
 234        .layer_nr = mtk_ovl_layer_nr,
 235        .layer_on = mtk_ovl_layer_on,
 236        .layer_off = mtk_ovl_layer_off,
 237        .layer_config = mtk_ovl_layer_config,
 238};
 239
 240static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
 241                             void *data)
 242{
 243        struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
 244        struct drm_device *drm_dev = data;
 245        int ret;
 246
 247        ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
 248        if (ret < 0) {
 249                dev_err(dev, "Failed to register component %pOF: %d\n",
 250                        dev->of_node, ret);
 251                return ret;
 252        }
 253
 254        return 0;
 255}
 256
 257static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
 258                                void *data)
 259{
 260        struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
 261        struct drm_device *drm_dev = data;
 262
 263        mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
 264}
 265
 266static const struct component_ops mtk_disp_ovl_component_ops = {
 267        .bind   = mtk_disp_ovl_bind,
 268        .unbind = mtk_disp_ovl_unbind,
 269};
 270
 271static int mtk_disp_ovl_probe(struct platform_device *pdev)
 272{
 273        struct device *dev = &pdev->dev;
 274        struct mtk_disp_ovl *priv;
 275        int comp_id;
 276        int irq;
 277        int ret;
 278
 279        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 280        if (!priv)
 281                return -ENOMEM;
 282
 283        irq = platform_get_irq(pdev, 0);
 284        if (irq < 0)
 285                return irq;
 286
 287        comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
 288        if (comp_id < 0) {
 289                dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
 290                return comp_id;
 291        }
 292
 293        ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
 294                                &mtk_disp_ovl_funcs);
 295        if (ret) {
 296                dev_err(dev, "Failed to initialize component: %d\n", ret);
 297                return ret;
 298        }
 299
 300        priv->data = of_device_get_match_data(dev);
 301
 302        platform_set_drvdata(pdev, priv);
 303
 304        ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
 305                               IRQF_TRIGGER_NONE, dev_name(dev), priv);
 306        if (ret < 0) {
 307                dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
 308                return ret;
 309        }
 310
 311        ret = component_add(dev, &mtk_disp_ovl_component_ops);
 312        if (ret)
 313                dev_err(dev, "Failed to add component: %d\n", ret);
 314
 315        return ret;
 316}
 317
 318static int mtk_disp_ovl_remove(struct platform_device *pdev)
 319{
 320        component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
 321
 322        return 0;
 323}
 324
 325static const struct mtk_disp_ovl_data mt2701_ovl_driver_data = {
 326        .addr = DISP_REG_OVL_ADDR_MT2701,
 327        .fmt_rgb565_is_0 = false,
 328};
 329
 330static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = {
 331        .addr = DISP_REG_OVL_ADDR_MT8173,
 332        .fmt_rgb565_is_0 = true,
 333};
 334
 335static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
 336        { .compatible = "mediatek,mt2701-disp-ovl",
 337          .data = &mt2701_ovl_driver_data},
 338        { .compatible = "mediatek,mt8173-disp-ovl",
 339          .data = &mt8173_ovl_driver_data},
 340        {},
 341};
 342MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
 343
 344struct platform_driver mtk_disp_ovl_driver = {
 345        .probe          = mtk_disp_ovl_probe,
 346        .remove         = mtk_disp_ovl_remove,
 347        .driver         = {
 348                .name   = "mediatek-disp-ovl",
 349                .owner  = THIS_MODULE,
 350                .of_match_table = mtk_disp_ovl_driver_dt_match,
 351        },
 352};
 353