Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
0004  * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
0005  */
0006 #include <linux/export.h>
0007 #include <linux/kernel.h>
0008 #include <linux/types.h>
0009 #include <linux/errno.h>
0010 #include <linux/io.h>
0011 #include <linux/err.h>
0012 
0013 #include <drm/drm_color_mgmt.h>
0014 #include <video/imx-ipu-v3.h>
0015 #include "ipu-prv.h"
0016 
0017 #define DP_SYNC 0
0018 #define DP_ASYNC0 0x60
0019 #define DP_ASYNC1 0xBC
0020 
0021 #define DP_COM_CONF     0x0
0022 #define DP_GRAPH_WIND_CTRL  0x0004
0023 #define DP_FG_POS       0x0008
0024 #define DP_CSC_A_0      0x0044
0025 #define DP_CSC_A_1      0x0048
0026 #define DP_CSC_A_2      0x004C
0027 #define DP_CSC_A_3      0x0050
0028 #define DP_CSC_0        0x0054
0029 #define DP_CSC_1        0x0058
0030 
0031 #define DP_COM_CONF_FG_EN       (1 << 0)
0032 #define DP_COM_CONF_GWSEL       (1 << 1)
0033 #define DP_COM_CONF_GWAM        (1 << 2)
0034 #define DP_COM_CONF_GWCKE       (1 << 3)
0035 #define DP_COM_CONF_CSC_DEF_MASK    (3 << 8)
0036 #define DP_COM_CONF_CSC_DEF_OFFSET  8
0037 #define DP_COM_CONF_CSC_DEF_FG      (3 << 8)
0038 #define DP_COM_CONF_CSC_DEF_BG      (2 << 8)
0039 #define DP_COM_CONF_CSC_DEF_BOTH    (1 << 8)
0040 
0041 #define IPUV3_NUM_FLOWS     3
0042 
0043 struct ipu_dp_priv;
0044 
0045 struct ipu_dp {
0046     u32 flow;
0047     bool in_use;
0048     bool foreground;
0049     enum ipu_color_space in_cs;
0050 };
0051 
0052 struct ipu_flow {
0053     struct ipu_dp foreground;
0054     struct ipu_dp background;
0055     enum ipu_color_space out_cs;
0056     void __iomem *base;
0057     struct ipu_dp_priv *priv;
0058 };
0059 
0060 struct ipu_dp_priv {
0061     struct ipu_soc *ipu;
0062     struct device *dev;
0063     void __iomem *base;
0064     struct ipu_flow flow[IPUV3_NUM_FLOWS];
0065     struct mutex mutex;
0066     int use_count;
0067 };
0068 
0069 static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
0070 
0071 static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
0072 {
0073     if (dp->foreground)
0074         return container_of(dp, struct ipu_flow, foreground);
0075     else
0076         return container_of(dp, struct ipu_flow, background);
0077 }
0078 
0079 int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
0080         u8 alpha, bool bg_chan)
0081 {
0082     struct ipu_flow *flow = to_flow(dp);
0083     struct ipu_dp_priv *priv = flow->priv;
0084     u32 reg;
0085 
0086     mutex_lock(&priv->mutex);
0087 
0088     reg = readl(flow->base + DP_COM_CONF);
0089     if (bg_chan)
0090         reg &= ~DP_COM_CONF_GWSEL;
0091     else
0092         reg |= DP_COM_CONF_GWSEL;
0093     writel(reg, flow->base + DP_COM_CONF);
0094 
0095     if (enable) {
0096         reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
0097         writel(reg | ((u32) alpha << 24),
0098                  flow->base + DP_GRAPH_WIND_CTRL);
0099 
0100         reg = readl(flow->base + DP_COM_CONF);
0101         writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
0102     } else {
0103         reg = readl(flow->base + DP_COM_CONF);
0104         writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
0105     }
0106 
0107     ipu_srm_dp_update(priv->ipu, true);
0108 
0109     mutex_unlock(&priv->mutex);
0110 
0111     return 0;
0112 }
0113 EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
0114 
0115 int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
0116 {
0117     struct ipu_flow *flow = to_flow(dp);
0118     struct ipu_dp_priv *priv = flow->priv;
0119 
0120     writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
0121 
0122     ipu_srm_dp_update(priv->ipu, true);
0123 
0124     return 0;
0125 }
0126 EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
0127 
0128 static void ipu_dp_csc_init(struct ipu_flow *flow,
0129         enum drm_color_encoding ycbcr_enc,
0130         enum drm_color_range range,
0131         enum ipu_color_space in,
0132         enum ipu_color_space out,
0133         u32 place)
0134 {
0135     u32 reg;
0136 
0137     reg = readl(flow->base + DP_COM_CONF);
0138     reg &= ~DP_COM_CONF_CSC_DEF_MASK;
0139 
0140     if (in == out) {
0141         writel(reg, flow->base + DP_COM_CONF);
0142         return;
0143     }
0144 
0145     if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
0146         writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
0147         writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
0148         writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
0149         writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
0150         writel(0x3d6 | (0x0000 << 16) | (2 << 30),
0151                 flow->base + DP_CSC_0);
0152         writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
0153                 flow->base + DP_CSC_1);
0154     } else if (ycbcr_enc == DRM_COLOR_YCBCR_BT709) {
0155         /* Rec.709 limited range */
0156         writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
0157         writel(0x0e5 | (0x095 << 16), flow->base + DP_CSC_A_1);
0158         writel(0x3e5 | (0x3bc << 16), flow->base + DP_CSC_A_2);
0159         writel(0x095 | (0x10e << 16), flow->base + DP_CSC_A_3);
0160         writel(0x000 | (0x3e10 << 16) | (1 << 30),
0161                 flow->base + DP_CSC_0);
0162         writel(0x09a | (1 << 14) | (0x3dbe << 16) | (1 << 30),
0163                 flow->base + DP_CSC_1);
0164     } else {
0165         /* BT.601 limited range */
0166         writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
0167         writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
0168         writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
0169         writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
0170         writel(0x000 | (0x3e42 << 16) | (1 << 30),
0171                 flow->base + DP_CSC_0);
0172         writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
0173                 flow->base + DP_CSC_1);
0174     }
0175 
0176     reg |= place;
0177 
0178     writel(reg, flow->base + DP_COM_CONF);
0179 }
0180 
0181 int ipu_dp_setup_channel(struct ipu_dp *dp,
0182         enum drm_color_encoding ycbcr_enc,
0183         enum drm_color_range range,
0184         enum ipu_color_space in,
0185         enum ipu_color_space out)
0186 {
0187     struct ipu_flow *flow = to_flow(dp);
0188     struct ipu_dp_priv *priv = flow->priv;
0189 
0190     mutex_lock(&priv->mutex);
0191 
0192     dp->in_cs = in;
0193 
0194     if (!dp->foreground)
0195         flow->out_cs = out;
0196 
0197     if (flow->foreground.in_cs == flow->background.in_cs) {
0198         /*
0199          * foreground and background are of same colorspace, put
0200          * colorspace converter after combining unit.
0201          */
0202         ipu_dp_csc_init(flow, ycbcr_enc, range,
0203                 flow->foreground.in_cs, flow->out_cs,
0204                 DP_COM_CONF_CSC_DEF_BOTH);
0205     } else {
0206         if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN ||
0207             flow->foreground.in_cs == flow->out_cs)
0208             /*
0209              * foreground identical to output, apply color
0210              * conversion on background
0211              */
0212             ipu_dp_csc_init(flow, ycbcr_enc, range,
0213                     flow->background.in_cs,
0214                     flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
0215         else
0216             ipu_dp_csc_init(flow, ycbcr_enc, range,
0217                     flow->foreground.in_cs,
0218                     flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
0219     }
0220 
0221     ipu_srm_dp_update(priv->ipu, true);
0222 
0223     mutex_unlock(&priv->mutex);
0224 
0225     return 0;
0226 }
0227 EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
0228 
0229 int ipu_dp_enable(struct ipu_soc *ipu)
0230 {
0231     struct ipu_dp_priv *priv = ipu->dp_priv;
0232 
0233     mutex_lock(&priv->mutex);
0234 
0235     if (!priv->use_count)
0236         ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
0237 
0238     priv->use_count++;
0239 
0240     mutex_unlock(&priv->mutex);
0241 
0242     return 0;
0243 }
0244 EXPORT_SYMBOL_GPL(ipu_dp_enable);
0245 
0246 int ipu_dp_enable_channel(struct ipu_dp *dp)
0247 {
0248     struct ipu_flow *flow = to_flow(dp);
0249     struct ipu_dp_priv *priv = flow->priv;
0250     u32 reg;
0251 
0252     if (!dp->foreground)
0253         return 0;
0254 
0255     mutex_lock(&priv->mutex);
0256 
0257     reg = readl(flow->base + DP_COM_CONF);
0258     reg |= DP_COM_CONF_FG_EN;
0259     writel(reg, flow->base + DP_COM_CONF);
0260 
0261     ipu_srm_dp_update(priv->ipu, true);
0262 
0263     mutex_unlock(&priv->mutex);
0264 
0265     return 0;
0266 }
0267 EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
0268 
0269 void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync)
0270 {
0271     struct ipu_flow *flow = to_flow(dp);
0272     struct ipu_dp_priv *priv = flow->priv;
0273     u32 reg, csc;
0274 
0275     dp->in_cs = IPUV3_COLORSPACE_UNKNOWN;
0276 
0277     if (!dp->foreground)
0278         return;
0279 
0280     mutex_lock(&priv->mutex);
0281 
0282     reg = readl(flow->base + DP_COM_CONF);
0283     csc = reg & DP_COM_CONF_CSC_DEF_MASK;
0284     reg &= ~DP_COM_CONF_CSC_DEF_MASK;
0285     if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG)
0286         reg |= DP_COM_CONF_CSC_DEF_BG;
0287 
0288     reg &= ~DP_COM_CONF_FG_EN;
0289     writel(reg, flow->base + DP_COM_CONF);
0290 
0291     writel(0, flow->base + DP_FG_POS);
0292     ipu_srm_dp_update(priv->ipu, sync);
0293 
0294     mutex_unlock(&priv->mutex);
0295 }
0296 EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
0297 
0298 void ipu_dp_disable(struct ipu_soc *ipu)
0299 {
0300     struct ipu_dp_priv *priv = ipu->dp_priv;
0301 
0302     mutex_lock(&priv->mutex);
0303 
0304     priv->use_count--;
0305 
0306     if (!priv->use_count)
0307         ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
0308 
0309     if (priv->use_count < 0)
0310         priv->use_count = 0;
0311 
0312     mutex_unlock(&priv->mutex);
0313 }
0314 EXPORT_SYMBOL_GPL(ipu_dp_disable);
0315 
0316 struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
0317 {
0318     struct ipu_dp_priv *priv = ipu->dp_priv;
0319     struct ipu_dp *dp;
0320 
0321     if ((flow >> 1) >= IPUV3_NUM_FLOWS)
0322         return ERR_PTR(-EINVAL);
0323 
0324     if (flow & 1)
0325         dp = &priv->flow[flow >> 1].foreground;
0326     else
0327         dp = &priv->flow[flow >> 1].background;
0328 
0329     if (dp->in_use)
0330         return ERR_PTR(-EBUSY);
0331 
0332     dp->in_use = true;
0333 
0334     return dp;
0335 }
0336 EXPORT_SYMBOL_GPL(ipu_dp_get);
0337 
0338 void ipu_dp_put(struct ipu_dp *dp)
0339 {
0340     dp->in_use = false;
0341 }
0342 EXPORT_SYMBOL_GPL(ipu_dp_put);
0343 
0344 int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
0345 {
0346     struct ipu_dp_priv *priv;
0347     int i;
0348 
0349     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0350     if (!priv)
0351         return -ENOMEM;
0352     priv->dev = dev;
0353     priv->ipu = ipu;
0354 
0355     ipu->dp_priv = priv;
0356 
0357     priv->base = devm_ioremap(dev, base, PAGE_SIZE);
0358     if (!priv->base)
0359         return -ENOMEM;
0360 
0361     mutex_init(&priv->mutex);
0362 
0363     for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
0364         priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN;
0365         priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN;
0366         priv->flow[i].foreground.foreground = true;
0367         priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
0368         priv->flow[i].priv = priv;
0369     }
0370 
0371     return 0;
0372 }
0373 
0374 void ipu_dp_exit(struct ipu_soc *ipu)
0375 {
0376 }