linux/drivers/gpu/drm/mediatek/mtk_disp_rdma.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_RDMA_INT_ENABLE                0x0000
  25#define DISP_REG_RDMA_INT_STATUS                0x0004
  26#define RDMA_TARGET_LINE_INT                            BIT(5)
  27#define RDMA_FIFO_UNDERFLOW_INT                         BIT(4)
  28#define RDMA_EOF_ABNORMAL_INT                           BIT(3)
  29#define RDMA_FRAME_END_INT                              BIT(2)
  30#define RDMA_FRAME_START_INT                            BIT(1)
  31#define RDMA_REG_UPDATE_INT                             BIT(0)
  32#define DISP_REG_RDMA_GLOBAL_CON                0x0010
  33#define RDMA_ENGINE_EN                                  BIT(0)
  34#define DISP_REG_RDMA_SIZE_CON_0                0x0014
  35#define DISP_REG_RDMA_SIZE_CON_1                0x0018
  36#define DISP_REG_RDMA_TARGET_LINE               0x001c
  37#define DISP_REG_RDMA_FIFO_CON                  0x0040
  38#define RDMA_FIFO_UNDERFLOW_EN                          BIT(31)
  39#define RDMA_FIFO_PSEUDO_SIZE(bytes)                    (((bytes) / 16) << 16)
  40#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes)         ((bytes) / 16)
  41#define RDMA_FIFO_SIZE(rdma)                    ((rdma)->data->fifo_size)
  42
  43struct mtk_disp_rdma_data {
  44        unsigned int fifo_size;
  45};
  46
  47/**
  48 * struct mtk_disp_rdma - DISP_RDMA driver structure
  49 * @ddp_comp - structure containing type enum and hardware resources
  50 * @crtc - associated crtc to report irq events to
  51 */
  52struct mtk_disp_rdma {
  53        struct mtk_ddp_comp             ddp_comp;
  54        struct drm_crtc                 *crtc;
  55        const struct mtk_disp_rdma_data *data;
  56};
  57
  58static inline struct mtk_disp_rdma *comp_to_rdma(struct mtk_ddp_comp *comp)
  59{
  60        return container_of(comp, struct mtk_disp_rdma, ddp_comp);
  61}
  62
  63static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id)
  64{
  65        struct mtk_disp_rdma *priv = dev_id;
  66        struct mtk_ddp_comp *rdma = &priv->ddp_comp;
  67
  68        /* Clear frame completion interrupt */
  69        writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS);
  70
  71        if (!priv->crtc)
  72                return IRQ_NONE;
  73
  74        mtk_crtc_ddp_irq(priv->crtc, rdma);
  75
  76        return IRQ_HANDLED;
  77}
  78
  79static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg,
  80                             unsigned int mask, unsigned int val)
  81{
  82        unsigned int tmp = readl(comp->regs + reg);
  83
  84        tmp = (tmp & ~mask) | (val & mask);
  85        writel(tmp, comp->regs + reg);
  86}
  87
  88static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp,
  89                                   struct drm_crtc *crtc)
  90{
  91        struct mtk_disp_rdma *rdma = comp_to_rdma(comp);
  92
  93        rdma->crtc = crtc;
  94        rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
  95                         RDMA_FRAME_END_INT);
  96}
  97
  98static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp)
  99{
 100        struct mtk_disp_rdma *rdma = comp_to_rdma(comp);
 101
 102        rdma->crtc = NULL;
 103        rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
 104}
 105
 106static void mtk_rdma_start(struct mtk_ddp_comp *comp)
 107{
 108        rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
 109                         RDMA_ENGINE_EN);
 110}
 111
 112static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
 113{
 114        rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
 115}
 116
 117static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
 118                            unsigned int height, unsigned int vrefresh,
 119                            unsigned int bpc)
 120{
 121        unsigned int threshold;
 122        unsigned int reg;
 123        struct mtk_disp_rdma *rdma = comp_to_rdma(comp);
 124
 125        rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width);
 126        rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height);
 127
 128        /*
 129         * Enable FIFO underflow since DSI and DPI can't be blocked.
 130         * Keep the FIFO pseudo size reset default of 8 KiB. Set the
 131         * output threshold to 6 microseconds with 7/6 overhead to
 132         * account for blanking, and with a pixel depth of 4 bytes:
 133         */
 134        threshold = width * height * vrefresh * 4 * 7 / 1000000;
 135        reg = RDMA_FIFO_UNDERFLOW_EN |
 136              RDMA_FIFO_PSEUDO_SIZE(RDMA_FIFO_SIZE(rdma)) |
 137              RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
 138        writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
 139}
 140
 141static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = {
 142        .config = mtk_rdma_config,
 143        .start = mtk_rdma_start,
 144        .stop = mtk_rdma_stop,
 145        .enable_vblank = mtk_rdma_enable_vblank,
 146        .disable_vblank = mtk_rdma_disable_vblank,
 147};
 148
 149static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
 150                              void *data)
 151{
 152        struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
 153        struct drm_device *drm_dev = data;
 154        int ret;
 155
 156        ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
 157        if (ret < 0) {
 158                dev_err(dev, "Failed to register component %pOF: %d\n",
 159                        dev->of_node, ret);
 160                return ret;
 161        }
 162
 163        return 0;
 164
 165}
 166
 167static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
 168                                 void *data)
 169{
 170        struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
 171        struct drm_device *drm_dev = data;
 172
 173        mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
 174}
 175
 176static const struct component_ops mtk_disp_rdma_component_ops = {
 177        .bind   = mtk_disp_rdma_bind,
 178        .unbind = mtk_disp_rdma_unbind,
 179};
 180
 181static int mtk_disp_rdma_probe(struct platform_device *pdev)
 182{
 183        struct device *dev = &pdev->dev;
 184        struct mtk_disp_rdma *priv;
 185        int comp_id;
 186        int irq;
 187        int ret;
 188
 189        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 190        if (!priv)
 191                return -ENOMEM;
 192
 193        irq = platform_get_irq(pdev, 0);
 194        if (irq < 0)
 195                return irq;
 196
 197        comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA);
 198        if (comp_id < 0) {
 199                dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
 200                return comp_id;
 201        }
 202
 203        ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
 204                                &mtk_disp_rdma_funcs);
 205        if (ret) {
 206                dev_err(dev, "Failed to initialize component: %d\n", ret);
 207                return ret;
 208        }
 209
 210        /* Disable and clear pending interrupts */
 211        writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE);
 212        writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS);
 213
 214        ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
 215                               IRQF_TRIGGER_NONE, dev_name(dev), priv);
 216        if (ret < 0) {
 217                dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
 218                return ret;
 219        }
 220
 221        priv->data = of_device_get_match_data(dev);
 222
 223        platform_set_drvdata(pdev, priv);
 224
 225        ret = component_add(dev, &mtk_disp_rdma_component_ops);
 226        if (ret)
 227                dev_err(dev, "Failed to add component: %d\n", ret);
 228
 229        return ret;
 230}
 231
 232static int mtk_disp_rdma_remove(struct platform_device *pdev)
 233{
 234        component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
 235
 236        return 0;
 237}
 238
 239static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = {
 240        .fifo_size = SZ_4K,
 241};
 242
 243static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = {
 244        .fifo_size = SZ_8K,
 245};
 246
 247static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
 248        { .compatible = "mediatek,mt2701-disp-rdma",
 249          .data = &mt2701_rdma_driver_data},
 250        { .compatible = "mediatek,mt8173-disp-rdma",
 251          .data = &mt8173_rdma_driver_data},
 252        {},
 253};
 254MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
 255
 256struct platform_driver mtk_disp_rdma_driver = {
 257        .probe          = mtk_disp_rdma_probe,
 258        .remove         = mtk_disp_rdma_remove,
 259        .driver         = {
 260                .name   = "mediatek-disp-rdma",
 261                .owner  = THIS_MODULE,
 262                .of_match_table = mtk_disp_rdma_driver_dt_match,
 263        },
 264};
 265