root/drivers/gpu/drm/imx/dcss/dcss-ss.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 NXP.
 */

#include <linux/device.h>
#include <linux/slab.h>

#include "dcss-dev.h"

#define DCSS_SS_SYS_CTRL                        0x00
#define   RUN_EN                                BIT(0)
#define DCSS_SS_DISPLAY                         0x10
#define   LRC_X_POS                             0
#define   LRC_X_MASK                            GENMASK(12, 0)
#define   LRC_Y_POS                             16
#define   LRC_Y_MASK                            GENMASK(28, 16)
#define DCSS_SS_HSYNC                           0x20
#define DCSS_SS_VSYNC                           0x30
#define   SYNC_START_POS                        0
#define   SYNC_START_MASK                       GENMASK(12, 0)
#define   SYNC_END_POS                          16
#define   SYNC_END_MASK                         GENMASK(28, 16)
#define   SYNC_POL                              BIT(31)
#define DCSS_SS_DE_ULC                          0x40
#define   ULC_X_POS                             0
#define   ULC_X_MASK                            GENMASK(12, 0)
#define   ULC_Y_POS                             16
#define   ULC_Y_MASK                            GENMASK(28, 16)
#define   ULC_POL                               BIT(31)
#define DCSS_SS_DE_LRC                          0x50
#define DCSS_SS_MODE                            0x60
#define   PIPE_MODE_POS                         0
#define   PIPE_MODE_MASK                        GENMASK(1, 0)
#define DCSS_SS_COEFF                           0x70
#define   HORIZ_A_POS                           0
#define   HORIZ_A_MASK                          GENMASK(3, 0)
#define   HORIZ_B_POS                           4
#define   HORIZ_B_MASK                          GENMASK(7, 4)
#define   HORIZ_C_POS                           8
#define   HORIZ_C_MASK                          GENMASK(11, 8)
#define   HORIZ_H_NORM_POS                      12
#define   HORIZ_H_NORM_MASK                     GENMASK(14, 12)
#define   VERT_A_POS                            16
#define   VERT_A_MASK                           GENMASK(19, 16)
#define   VERT_B_POS                            20
#define   VERT_B_MASK                           GENMASK(23, 20)
#define   VERT_C_POS                            24
#define   VERT_C_MASK                           GENMASK(27, 24)
#define   VERT_H_NORM_POS                       28
#define   VERT_H_NORM_MASK                      GENMASK(30, 28)
#define DCSS_SS_CLIP_CB                         0x80
#define DCSS_SS_CLIP_CR                         0x90
#define   CLIP_MIN_POS                          0
#define   CLIP_MIN_MASK                         GENMASK(9, 0)
#define   CLIP_MAX_POS                          0
#define   CLIP_MAX_MASK                         GENMASK(23, 16)
#define DCSS_SS_INTER_MODE                      0xA0
#define   INT_EN                                BIT(0)
#define   VSYNC_SHIFT                           BIT(1)

struct dcss_ss {
        struct device *dev;
        void __iomem *base_reg;
        u32 base_ofs;

        struct dcss_ctxld *ctxld;
        u32 ctx_id;

        bool in_use;
};

static void dcss_ss_write(struct dcss_ss *ss, u32 val, u32 ofs)
{
        if (!ss->in_use)
                dcss_writel(val, ss->base_reg + ofs);

        dcss_ctxld_write(ss->ctxld, ss->ctx_id, val,
                         ss->base_ofs + ofs);
}

int dcss_ss_init(struct dcss_dev *dcss, unsigned long ss_base)
{
        struct dcss_ss *ss;

        ss = devm_kzalloc(dcss->dev, sizeof(*ss), GFP_KERNEL);
        if (!ss)
                return -ENOMEM;

        dcss->ss = ss;
        ss->dev = dcss->dev;
        ss->ctxld = dcss->ctxld;

        ss->base_reg = devm_ioremap(ss->dev, ss_base, SZ_4K);
        if (!ss->base_reg) {
                dev_err(ss->dev, "ss: unable to remap ss base\n");
                return -ENOMEM;
        }

        ss->base_ofs = ss_base;
        ss->ctx_id = CTX_SB_HP;

        return 0;
}

void dcss_ss_exit(struct dcss_ss *ss)
{
        /* stop SS */
        dcss_writel(0, ss->base_reg + DCSS_SS_SYS_CTRL);
}

void dcss_ss_subsam_set(struct dcss_ss *ss)
{
        dcss_ss_write(ss, 0x41614161, DCSS_SS_COEFF);
        dcss_ss_write(ss, 0, DCSS_SS_MODE);
        dcss_ss_write(ss, 0x03ff0000, DCSS_SS_CLIP_CB);
        dcss_ss_write(ss, 0x03ff0000, DCSS_SS_CLIP_CR);
}

void dcss_ss_sync_set(struct dcss_ss *ss, struct videomode *vm,
                      bool phsync, bool pvsync)
{
        u16 lrc_x, lrc_y;
        u16 hsync_start, hsync_end;
        u16 vsync_start, vsync_end;
        u16 de_ulc_x, de_ulc_y;
        u16 de_lrc_x, de_lrc_y;

        lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
                vm->hactive - 1;
        lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
                vm->vactive - 1;

        dcss_ss_write(ss, (lrc_y << LRC_Y_POS) | lrc_x, DCSS_SS_DISPLAY);

        hsync_start = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
                      vm->hactive - 1;
        hsync_end = vm->hsync_len - 1;

        dcss_ss_write(ss, (phsync ? SYNC_POL : 0) |
                      ((u32)hsync_end << SYNC_END_POS) | hsync_start,
                      DCSS_SS_HSYNC);

        vsync_start = vm->vfront_porch - 1;
        vsync_end = vm->vfront_porch + vm->vsync_len - 1;

        dcss_ss_write(ss, (pvsync ? SYNC_POL : 0) |
                      ((u32)vsync_end << SYNC_END_POS) | vsync_start,
                      DCSS_SS_VSYNC);

        de_ulc_x = vm->hsync_len + vm->hback_porch - 1;
        de_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch;

        dcss_ss_write(ss, SYNC_POL | ((u32)de_ulc_y << ULC_Y_POS) | de_ulc_x,
                      DCSS_SS_DE_ULC);

        de_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
        de_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
                   vm->vactive - 1;

        dcss_ss_write(ss, (de_lrc_y << LRC_Y_POS) | de_lrc_x, DCSS_SS_DE_LRC);
}

void dcss_ss_enable(struct dcss_ss *ss)
{
        dcss_ss_write(ss, RUN_EN, DCSS_SS_SYS_CTRL);
        ss->in_use = true;
}

void dcss_ss_shutoff(struct dcss_ss *ss)
{
        dcss_writel(0, ss->base_reg + DCSS_SS_SYS_CTRL);
        ss->in_use = false;
}