Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2019 NXP.
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/delay.h>
0008 #include <linux/interrupt.h>
0009 #include <linux/of.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/slab.h>
0012 
0013 #include "dcss-dev.h"
0014 
0015 #define DCSS_DTG_TC_CONTROL_STATUS          0x00
0016 #define   CH3_EN                    BIT(0)
0017 #define   CH2_EN                    BIT(1)
0018 #define   CH1_EN                    BIT(2)
0019 #define   OVL_DATA_MODE                 BIT(3)
0020 #define   BLENDER_VIDEO_ALPHA_SEL           BIT(7)
0021 #define   DTG_START                 BIT(8)
0022 #define   DBY_MODE_EN                   BIT(9)
0023 #define   CH1_ALPHA_SEL                 BIT(10)
0024 #define   CSS_PIX_COMP_SWAP_POS             12
0025 #define   CSS_PIX_COMP_SWAP_MASK            GENMASK(14, 12)
0026 #define   DEFAULT_FG_ALPHA_POS              24
0027 #define   DEFAULT_FG_ALPHA_MASK             GENMASK(31, 24)
0028 #define DCSS_DTG_TC_DTG                 0x04
0029 #define DCSS_DTG_TC_DISP_TOP                0x08
0030 #define DCSS_DTG_TC_DISP_BOT                0x0C
0031 #define DCSS_DTG_TC_CH1_TOP             0x10
0032 #define DCSS_DTG_TC_CH1_BOT             0x14
0033 #define DCSS_DTG_TC_CH2_TOP             0x18
0034 #define DCSS_DTG_TC_CH2_BOT             0x1C
0035 #define DCSS_DTG_TC_CH3_TOP             0x20
0036 #define DCSS_DTG_TC_CH3_BOT             0x24
0037 #define   TC_X_POS                  0
0038 #define   TC_X_MASK                 GENMASK(12, 0)
0039 #define   TC_Y_POS                  16
0040 #define   TC_Y_MASK                 GENMASK(28, 16)
0041 #define DCSS_DTG_TC_CTXLD               0x28
0042 #define   TC_CTXLD_DB_Y_POS             0
0043 #define   TC_CTXLD_DB_Y_MASK                GENMASK(12, 0)
0044 #define   TC_CTXLD_SB_Y_POS             16
0045 #define   TC_CTXLD_SB_Y_MASK                GENMASK(28, 16)
0046 #define DCSS_DTG_TC_CH1_BKRND               0x2C
0047 #define DCSS_DTG_TC_CH2_BKRND               0x30
0048 #define   BKRND_R_Y_COMP_POS                20
0049 #define   BKRND_R_Y_COMP_MASK               GENMASK(29, 20)
0050 #define   BKRND_G_U_COMP_POS                10
0051 #define   BKRND_G_U_COMP_MASK               GENMASK(19, 10)
0052 #define   BKRND_B_V_COMP_POS                0
0053 #define   BKRND_B_V_COMP_MASK               GENMASK(9, 0)
0054 #define DCSS_DTG_BLENDER_DBY_RANGEINV           0x38
0055 #define DCSS_DTG_BLENDER_DBY_RANGEMIN           0x3C
0056 #define DCSS_DTG_BLENDER_DBY_BDP            0x40
0057 #define DCSS_DTG_BLENDER_BKRND_I            0x44
0058 #define DCSS_DTG_BLENDER_BKRND_P            0x48
0059 #define DCSS_DTG_BLENDER_BKRND_T            0x4C
0060 #define DCSS_DTG_LINE0_INT              0x50
0061 #define DCSS_DTG_LINE1_INT              0x54
0062 #define DCSS_DTG_BG_ALPHA_DEFAULT           0x58
0063 #define DCSS_DTG_INT_STATUS             0x5C
0064 #define DCSS_DTG_INT_CONTROL                0x60
0065 #define DCSS_DTG_TC_CH3_BKRND               0x64
0066 #define DCSS_DTG_INT_MASK               0x68
0067 #define   LINE0_IRQ                 BIT(0)
0068 #define   LINE1_IRQ                 BIT(1)
0069 #define   LINE2_IRQ                 BIT(2)
0070 #define   LINE3_IRQ                 BIT(3)
0071 #define DCSS_DTG_LINE2_INT              0x6C
0072 #define DCSS_DTG_LINE3_INT              0x70
0073 #define DCSS_DTG_DBY_OL                 0x74
0074 #define DCSS_DTG_DBY_BL                 0x78
0075 #define DCSS_DTG_DBY_EL                 0x7C
0076 
0077 struct dcss_dtg {
0078     struct device *dev;
0079     struct dcss_ctxld *ctxld;
0080     void __iomem *base_reg;
0081     u32 base_ofs;
0082 
0083     u32 ctx_id;
0084 
0085     bool in_use;
0086 
0087     u32 dis_ulc_x;
0088     u32 dis_ulc_y;
0089 
0090     u32 control_status;
0091     u32 alpha;
0092     u32 alpha_cfg;
0093 
0094     int ctxld_kick_irq;
0095     bool ctxld_kick_irq_en;
0096 };
0097 
0098 static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
0099 {
0100     if (!dtg->in_use)
0101         dcss_writel(val, dtg->base_reg + ofs);
0102 
0103     dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
0104              val, dtg->base_ofs + ofs);
0105 }
0106 
0107 static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
0108 {
0109     struct dcss_dtg *dtg = data;
0110     u32 status;
0111 
0112     status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
0113 
0114     if (!(status & LINE0_IRQ))
0115         return IRQ_NONE;
0116 
0117     dcss_ctxld_kick(dtg->ctxld);
0118 
0119     dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
0120 
0121     return IRQ_HANDLED;
0122 }
0123 
0124 static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
0125                    struct platform_device *pdev)
0126 {
0127     int ret;
0128 
0129     dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
0130     if (dtg->ctxld_kick_irq < 0)
0131         return dtg->ctxld_kick_irq;
0132 
0133     dcss_update(0, LINE0_IRQ | LINE1_IRQ,
0134             dtg->base_reg + DCSS_DTG_INT_MASK);
0135 
0136     ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
0137               0, "dcss_ctxld_kick", dtg);
0138     if (ret) {
0139         dev_err(dtg->dev, "dtg: irq request failed.\n");
0140         return ret;
0141     }
0142 
0143     disable_irq(dtg->ctxld_kick_irq);
0144 
0145     dtg->ctxld_kick_irq_en = false;
0146 
0147     return 0;
0148 }
0149 
0150 int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
0151 {
0152     int ret = 0;
0153     struct dcss_dtg *dtg;
0154 
0155     dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
0156     if (!dtg)
0157         return -ENOMEM;
0158 
0159     dcss->dtg = dtg;
0160     dtg->dev = dcss->dev;
0161     dtg->ctxld = dcss->ctxld;
0162 
0163     dtg->base_reg = ioremap(dtg_base, SZ_4K);
0164     if (!dtg->base_reg) {
0165         dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
0166         ret = -ENOMEM;
0167         goto err_ioremap;
0168     }
0169 
0170     dtg->base_ofs = dtg_base;
0171     dtg->ctx_id = CTX_DB;
0172 
0173     dtg->alpha = 255;
0174 
0175     dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
0176         ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
0177 
0178     ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
0179     if (ret)
0180         goto err_irq;
0181 
0182     return 0;
0183 
0184 err_irq:
0185     iounmap(dtg->base_reg);
0186 
0187 err_ioremap:
0188     kfree(dtg);
0189 
0190     return ret;
0191 }
0192 
0193 void dcss_dtg_exit(struct dcss_dtg *dtg)
0194 {
0195     free_irq(dtg->ctxld_kick_irq, dtg);
0196 
0197     if (dtg->base_reg)
0198         iounmap(dtg->base_reg);
0199 
0200     kfree(dtg);
0201 }
0202 
0203 void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
0204 {
0205     struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
0206     u16 dtg_lrc_x, dtg_lrc_y;
0207     u16 dis_ulc_x, dis_ulc_y;
0208     u16 dis_lrc_x, dis_lrc_y;
0209     u32 sb_ctxld_trig, db_ctxld_trig;
0210     u32 pixclock = vm->pixelclock;
0211     u32 actual_clk;
0212 
0213     dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
0214             vm->hactive - 1;
0215     dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
0216             vm->vactive - 1;
0217     dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
0218     dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
0219     dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
0220     dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
0221             vm->vactive - 1;
0222 
0223     clk_disable_unprepare(dcss->pix_clk);
0224     clk_set_rate(dcss->pix_clk, vm->pixelclock);
0225     clk_prepare_enable(dcss->pix_clk);
0226 
0227     actual_clk = clk_get_rate(dcss->pix_clk);
0228     if (pixclock != actual_clk) {
0229         dev_info(dtg->dev,
0230              "Pixel clock set to %u kHz instead of %u kHz.\n",
0231              (actual_clk / 1000), (pixclock / 1000));
0232     }
0233 
0234     dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
0235                DCSS_DTG_TC_DTG);
0236     dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
0237                DCSS_DTG_TC_DISP_TOP);
0238     dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
0239                DCSS_DTG_TC_DISP_BOT);
0240 
0241     dtg->dis_ulc_x = dis_ulc_x;
0242     dtg->dis_ulc_y = dis_ulc_y;
0243 
0244     sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
0245                             TC_CTXLD_SB_Y_MASK;
0246     db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
0247                             TC_CTXLD_DB_Y_MASK;
0248 
0249     dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
0250 
0251     /* vblank trigger */
0252     dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
0253 
0254     /* CTXLD trigger */
0255     dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
0256 }
0257 
0258 void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
0259                 int px, int py, int pw, int ph)
0260 {
0261     u16 p_ulc_x, p_ulc_y;
0262     u16 p_lrc_x, p_lrc_y;
0263 
0264     p_ulc_x = dtg->dis_ulc_x + px;
0265     p_ulc_y = dtg->dis_ulc_y + py;
0266     p_lrc_x = p_ulc_x + pw;
0267     p_lrc_y = p_ulc_y + ph;
0268 
0269     if (!px && !py && !pw && !ph) {
0270         dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
0271         dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
0272     } else {
0273         dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
0274                    DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
0275         dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
0276                    DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
0277     }
0278 }
0279 
0280 bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
0281 {
0282     if (ch_num)
0283         return false;
0284 
0285     return alpha != dtg->alpha;
0286 }
0287 
0288 void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
0289                   const struct drm_format_info *format, int alpha)
0290 {
0291     /* we care about alpha only when channel 0 is concerned */
0292     if (ch_num)
0293         return;
0294 
0295     /*
0296      * Use global alpha if pixel format does not have alpha channel or the
0297      * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
0298      */
0299     if (!format->has_alpha || alpha != 255)
0300         dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
0301     else /* use per-pixel alpha otherwise */
0302         dtg->alpha_cfg = CH1_ALPHA_SEL;
0303 
0304     dtg->alpha = alpha;
0305 }
0306 
0307 void dcss_dtg_css_set(struct dcss_dtg *dtg)
0308 {
0309     dtg->control_status |=
0310             (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
0311 }
0312 
0313 void dcss_dtg_enable(struct dcss_dtg *dtg)
0314 {
0315     dtg->control_status |= DTG_START;
0316 
0317     dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
0318     dtg->control_status |= dtg->alpha_cfg;
0319 
0320     dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
0321 
0322     dtg->in_use = true;
0323 }
0324 
0325 void dcss_dtg_shutoff(struct dcss_dtg *dtg)
0326 {
0327     dtg->control_status &= ~DTG_START;
0328 
0329     dcss_writel(dtg->control_status,
0330             dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
0331 
0332     dtg->in_use = false;
0333 }
0334 
0335 bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
0336 {
0337     return dtg->in_use;
0338 }
0339 
0340 void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
0341 {
0342     u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
0343     u32 control_status;
0344 
0345     control_status = dtg->control_status & ~ch_en_map[ch_num];
0346     control_status |= en ? ch_en_map[ch_num] : 0;
0347 
0348     control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
0349     control_status |= dtg->alpha_cfg;
0350 
0351     if (dtg->control_status != control_status)
0352         dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
0353 
0354     dtg->control_status = control_status;
0355 }
0356 
0357 void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
0358 {
0359     u32 status;
0360     u32 mask = en ? LINE1_IRQ : 0;
0361 
0362     if (en) {
0363         status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
0364         dcss_writel(status & LINE1_IRQ,
0365                 dtg->base_reg + DCSS_DTG_INT_CONTROL);
0366     }
0367 
0368     dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
0369 }
0370 
0371 void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
0372 {
0373     u32 status;
0374     u32 mask = en ? LINE0_IRQ : 0;
0375 
0376     if (en) {
0377         status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
0378 
0379         if (!dtg->ctxld_kick_irq_en) {
0380             dcss_writel(status & LINE0_IRQ,
0381                     dtg->base_reg + DCSS_DTG_INT_CONTROL);
0382             enable_irq(dtg->ctxld_kick_irq);
0383             dtg->ctxld_kick_irq_en = true;
0384             dcss_update(mask, LINE0_IRQ,
0385                     dtg->base_reg + DCSS_DTG_INT_MASK);
0386         }
0387 
0388         return;
0389     }
0390 
0391     if (!dtg->ctxld_kick_irq_en)
0392         return;
0393 
0394     disable_irq_nosync(dtg->ctxld_kick_irq);
0395     dtg->ctxld_kick_irq_en = false;
0396 
0397     dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
0398 }
0399 
0400 void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
0401 {
0402     dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
0403 }
0404 
0405 bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
0406 {
0407     return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
0408 }
0409