linux/drivers/gpu/drm/imx/dcss/dcss-dtg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 NXP.
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/delay.h>
   8#include <linux/interrupt.h>
   9#include <linux/of.h>
  10#include <linux/platform_device.h>
  11#include <linux/slab.h>
  12
  13#include "dcss-dev.h"
  14
  15#define DCSS_DTG_TC_CONTROL_STATUS                      0x00
  16#define   CH3_EN                                        BIT(0)
  17#define   CH2_EN                                        BIT(1)
  18#define   CH1_EN                                        BIT(2)
  19#define   OVL_DATA_MODE                                 BIT(3)
  20#define   BLENDER_VIDEO_ALPHA_SEL                       BIT(7)
  21#define   DTG_START                                     BIT(8)
  22#define   DBY_MODE_EN                                   BIT(9)
  23#define   CH1_ALPHA_SEL                                 BIT(10)
  24#define   CSS_PIX_COMP_SWAP_POS                         12
  25#define   CSS_PIX_COMP_SWAP_MASK                        GENMASK(14, 12)
  26#define   DEFAULT_FG_ALPHA_POS                          24
  27#define   DEFAULT_FG_ALPHA_MASK                         GENMASK(31, 24)
  28#define DCSS_DTG_TC_DTG                                 0x04
  29#define DCSS_DTG_TC_DISP_TOP                            0x08
  30#define DCSS_DTG_TC_DISP_BOT                            0x0C
  31#define DCSS_DTG_TC_CH1_TOP                             0x10
  32#define DCSS_DTG_TC_CH1_BOT                             0x14
  33#define DCSS_DTG_TC_CH2_TOP                             0x18
  34#define DCSS_DTG_TC_CH2_BOT                             0x1C
  35#define DCSS_DTG_TC_CH3_TOP                             0x20
  36#define DCSS_DTG_TC_CH3_BOT                             0x24
  37#define   TC_X_POS                                      0
  38#define   TC_X_MASK                                     GENMASK(12, 0)
  39#define   TC_Y_POS                                      16
  40#define   TC_Y_MASK                                     GENMASK(28, 16)
  41#define DCSS_DTG_TC_CTXLD                               0x28
  42#define   TC_CTXLD_DB_Y_POS                             0
  43#define   TC_CTXLD_DB_Y_MASK                            GENMASK(12, 0)
  44#define   TC_CTXLD_SB_Y_POS                             16
  45#define   TC_CTXLD_SB_Y_MASK                            GENMASK(28, 16)
  46#define DCSS_DTG_TC_CH1_BKRND                           0x2C
  47#define DCSS_DTG_TC_CH2_BKRND                           0x30
  48#define   BKRND_R_Y_COMP_POS                            20
  49#define   BKRND_R_Y_COMP_MASK                           GENMASK(29, 20)
  50#define   BKRND_G_U_COMP_POS                            10
  51#define   BKRND_G_U_COMP_MASK                           GENMASK(19, 10)
  52#define   BKRND_B_V_COMP_POS                            0
  53#define   BKRND_B_V_COMP_MASK                           GENMASK(9, 0)
  54#define DCSS_DTG_BLENDER_DBY_RANGEINV                   0x38
  55#define DCSS_DTG_BLENDER_DBY_RANGEMIN                   0x3C
  56#define DCSS_DTG_BLENDER_DBY_BDP                        0x40
  57#define DCSS_DTG_BLENDER_BKRND_I                        0x44
  58#define DCSS_DTG_BLENDER_BKRND_P                        0x48
  59#define DCSS_DTG_BLENDER_BKRND_T                        0x4C
  60#define DCSS_DTG_LINE0_INT                              0x50
  61#define DCSS_DTG_LINE1_INT                              0x54
  62#define DCSS_DTG_BG_ALPHA_DEFAULT                       0x58
  63#define DCSS_DTG_INT_STATUS                             0x5C
  64#define DCSS_DTG_INT_CONTROL                            0x60
  65#define DCSS_DTG_TC_CH3_BKRND                           0x64
  66#define DCSS_DTG_INT_MASK                               0x68
  67#define   LINE0_IRQ                                     BIT(0)
  68#define   LINE1_IRQ                                     BIT(1)
  69#define   LINE2_IRQ                                     BIT(2)
  70#define   LINE3_IRQ                                     BIT(3)
  71#define DCSS_DTG_LINE2_INT                              0x6C
  72#define DCSS_DTG_LINE3_INT                              0x70
  73#define DCSS_DTG_DBY_OL                                 0x74
  74#define DCSS_DTG_DBY_BL                                 0x78
  75#define DCSS_DTG_DBY_EL                                 0x7C
  76
  77struct dcss_dtg {
  78        struct device *dev;
  79        struct dcss_ctxld *ctxld;
  80        void __iomem *base_reg;
  81        u32 base_ofs;
  82
  83        u32 ctx_id;
  84
  85        bool in_use;
  86
  87        u32 dis_ulc_x;
  88        u32 dis_ulc_y;
  89
  90        u32 control_status;
  91        u32 alpha;
  92        u32 alpha_cfg;
  93
  94        int ctxld_kick_irq;
  95        bool ctxld_kick_irq_en;
  96};
  97
  98static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
  99{
 100        if (!dtg->in_use)
 101                dcss_writel(val, dtg->base_reg + ofs);
 102
 103        dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
 104                         val, dtg->base_ofs + ofs);
 105}
 106
 107static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
 108{
 109        struct dcss_dtg *dtg = data;
 110        u32 status;
 111
 112        status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
 113
 114        if (!(status & LINE0_IRQ))
 115                return IRQ_NONE;
 116
 117        dcss_ctxld_kick(dtg->ctxld);
 118
 119        dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
 120
 121        return IRQ_HANDLED;
 122}
 123
 124static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
 125                               struct platform_device *pdev)
 126{
 127        int ret;
 128
 129        dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
 130        if (dtg->ctxld_kick_irq < 0)
 131                return dtg->ctxld_kick_irq;
 132
 133        dcss_update(0, LINE0_IRQ | LINE1_IRQ,
 134                    dtg->base_reg + DCSS_DTG_INT_MASK);
 135
 136        ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
 137                          0, "dcss_ctxld_kick", dtg);
 138        if (ret) {
 139                dev_err(dtg->dev, "dtg: irq request failed.\n");
 140                return ret;
 141        }
 142
 143        disable_irq(dtg->ctxld_kick_irq);
 144
 145        dtg->ctxld_kick_irq_en = false;
 146
 147        return 0;
 148}
 149
 150int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
 151{
 152        int ret = 0;
 153        struct dcss_dtg *dtg;
 154
 155        dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
 156        if (!dtg)
 157                return -ENOMEM;
 158
 159        dcss->dtg = dtg;
 160        dtg->dev = dcss->dev;
 161        dtg->ctxld = dcss->ctxld;
 162
 163        dtg->base_reg = ioremap(dtg_base, SZ_4K);
 164        if (!dtg->base_reg) {
 165                dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
 166                ret = -ENOMEM;
 167                goto err_ioremap;
 168        }
 169
 170        dtg->base_ofs = dtg_base;
 171        dtg->ctx_id = CTX_DB;
 172
 173        dtg->alpha = 255;
 174
 175        dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
 176                ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
 177
 178        ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
 179        if (ret)
 180                goto err_irq;
 181
 182        return 0;
 183
 184err_irq:
 185        iounmap(dtg->base_reg);
 186
 187err_ioremap:
 188        kfree(dtg);
 189
 190        return ret;
 191}
 192
 193void dcss_dtg_exit(struct dcss_dtg *dtg)
 194{
 195        free_irq(dtg->ctxld_kick_irq, dtg);
 196
 197        if (dtg->base_reg)
 198                iounmap(dtg->base_reg);
 199
 200        kfree(dtg);
 201}
 202
 203void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
 204{
 205        struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
 206        u16 dtg_lrc_x, dtg_lrc_y;
 207        u16 dis_ulc_x, dis_ulc_y;
 208        u16 dis_lrc_x, dis_lrc_y;
 209        u32 sb_ctxld_trig, db_ctxld_trig;
 210        u32 pixclock = vm->pixelclock;
 211        u32 actual_clk;
 212
 213        dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
 214                    vm->hactive - 1;
 215        dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
 216                    vm->vactive - 1;
 217        dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
 218        dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
 219        dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
 220        dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
 221                    vm->vactive - 1;
 222
 223        clk_disable_unprepare(dcss->pix_clk);
 224        clk_set_rate(dcss->pix_clk, vm->pixelclock);
 225        clk_prepare_enable(dcss->pix_clk);
 226
 227        actual_clk = clk_get_rate(dcss->pix_clk);
 228        if (pixclock != actual_clk) {
 229                dev_info(dtg->dev,
 230                         "Pixel clock set to %u kHz instead of %u kHz.\n",
 231                         (actual_clk / 1000), (pixclock / 1000));
 232        }
 233
 234        dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
 235                       DCSS_DTG_TC_DTG);
 236        dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
 237                       DCSS_DTG_TC_DISP_TOP);
 238        dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
 239                       DCSS_DTG_TC_DISP_BOT);
 240
 241        dtg->dis_ulc_x = dis_ulc_x;
 242        dtg->dis_ulc_y = dis_ulc_y;
 243
 244        sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
 245                                                        TC_CTXLD_SB_Y_MASK;
 246        db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
 247                                                        TC_CTXLD_DB_Y_MASK;
 248
 249        dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
 250
 251        /* vblank trigger */
 252        dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
 253
 254        /* CTXLD trigger */
 255        dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
 256}
 257
 258void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
 259                            int px, int py, int pw, int ph)
 260{
 261        u16 p_ulc_x, p_ulc_y;
 262        u16 p_lrc_x, p_lrc_y;
 263
 264        p_ulc_x = dtg->dis_ulc_x + px;
 265        p_ulc_y = dtg->dis_ulc_y + py;
 266        p_lrc_x = p_ulc_x + pw;
 267        p_lrc_y = p_ulc_y + ph;
 268
 269        if (!px && !py && !pw && !ph) {
 270                dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
 271                dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
 272        } else {
 273                dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
 274                               DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
 275                dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
 276                               DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
 277        }
 278}
 279
 280bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
 281{
 282        if (ch_num)
 283                return false;
 284
 285        return alpha != dtg->alpha;
 286}
 287
 288void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
 289                              const struct drm_format_info *format, int alpha)
 290{
 291        /* we care about alpha only when channel 0 is concerned */
 292        if (ch_num)
 293                return;
 294
 295        /*
 296         * Use global alpha if pixel format does not have alpha channel or the
 297         * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
 298         */
 299        if (!format->has_alpha || alpha != 255)
 300                dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
 301        else /* use per-pixel alpha otherwise */
 302                dtg->alpha_cfg = CH1_ALPHA_SEL;
 303
 304        dtg->alpha = alpha;
 305}
 306
 307void dcss_dtg_css_set(struct dcss_dtg *dtg)
 308{
 309        dtg->control_status |=
 310                        (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
 311}
 312
 313void dcss_dtg_enable(struct dcss_dtg *dtg)
 314{
 315        dtg->control_status |= DTG_START;
 316
 317        dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
 318        dtg->control_status |= dtg->alpha_cfg;
 319
 320        dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
 321
 322        dtg->in_use = true;
 323}
 324
 325void dcss_dtg_shutoff(struct dcss_dtg *dtg)
 326{
 327        dtg->control_status &= ~DTG_START;
 328
 329        dcss_writel(dtg->control_status,
 330                    dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
 331
 332        dtg->in_use = false;
 333}
 334
 335bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
 336{
 337        return dtg->in_use;
 338}
 339
 340void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
 341{
 342        u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
 343        u32 control_status;
 344
 345        control_status = dtg->control_status & ~ch_en_map[ch_num];
 346        control_status |= en ? ch_en_map[ch_num] : 0;
 347
 348        control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
 349        control_status |= dtg->alpha_cfg;
 350
 351        if (dtg->control_status != control_status)
 352                dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
 353
 354        dtg->control_status = control_status;
 355}
 356
 357void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
 358{
 359        u32 status;
 360        u32 mask = en ? LINE1_IRQ : 0;
 361
 362        if (en) {
 363                status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
 364                dcss_writel(status & LINE1_IRQ,
 365                            dtg->base_reg + DCSS_DTG_INT_CONTROL);
 366        }
 367
 368        dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
 369}
 370
 371void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
 372{
 373        u32 status;
 374        u32 mask = en ? LINE0_IRQ : 0;
 375
 376        if (en) {
 377                status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
 378
 379                if (!dtg->ctxld_kick_irq_en) {
 380                        dcss_writel(status & LINE0_IRQ,
 381                                    dtg->base_reg + DCSS_DTG_INT_CONTROL);
 382                        enable_irq(dtg->ctxld_kick_irq);
 383                        dtg->ctxld_kick_irq_en = true;
 384                        dcss_update(mask, LINE0_IRQ,
 385                                    dtg->base_reg + DCSS_DTG_INT_MASK);
 386                }
 387
 388                return;
 389        }
 390
 391        if (!dtg->ctxld_kick_irq_en)
 392                return;
 393
 394        disable_irq_nosync(dtg->ctxld_kick_irq);
 395        dtg->ctxld_kick_irq_en = false;
 396
 397        dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
 398}
 399
 400void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
 401{
 402        dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
 403}
 404
 405bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
 406{
 407        return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
 408}
 409
 410