0001
0002
0003
0004
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
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
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
0200
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
0210
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 }