1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 4 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 5 */ 6 #include <linux/export.h> 7 #include <linux/kernel.h> 8 #include <linux/types.h> 9 #include <linux/errno.h> 10 #include <linux/io.h> 11 #include <linux/err.h> 12 13 #include <video/imx-ipu-v3.h> 14 #include "ipu-prv.h" 15 16 #define DP_SYNC 0 17 #define DP_ASYNC0 0x60 18 #define DP_ASYNC1 0xBC 19 20 #define DP_COM_CONF 0x0 21 #define DP_GRAPH_WIND_CTRL 0x0004 22 #define DP_FG_POS 0x0008 23 #define DP_CSC_A_0 0x0044 24 #define DP_CSC_A_1 0x0048 25 #define DP_CSC_A_2 0x004C 26 #define DP_CSC_A_3 0x0050 27 #define DP_CSC_0 0x0054 28 #define DP_CSC_1 0x0058 29 30 #define DP_COM_CONF_FG_EN (1 << 0) 31 #define DP_COM_CONF_GWSEL (1 << 1) 32 #define DP_COM_CONF_GWAM (1 << 2) 33 #define DP_COM_CONF_GWCKE (1 << 3) 34 #define DP_COM_CONF_CSC_DEF_MASK (3 << 8) 35 #define DP_COM_CONF_CSC_DEF_OFFSET 8 36 #define DP_COM_CONF_CSC_DEF_FG (3 << 8) 37 #define DP_COM_CONF_CSC_DEF_BG (2 << 8) 38 #define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) 39 40 #define IPUV3_NUM_FLOWS 3 41 42 struct ipu_dp_priv; 43 44 struct ipu_dp { 45 u32 flow; 46 bool in_use; 47 bool foreground; 48 enum ipu_color_space in_cs; 49 }; 50 51 struct ipu_flow { 52 struct ipu_dp foreground; 53 struct ipu_dp background; 54 enum ipu_color_space out_cs; 55 void __iomem *base; 56 struct ipu_dp_priv *priv; 57 }; 58 59 struct ipu_dp_priv { 60 struct ipu_soc *ipu; 61 struct device *dev; 62 void __iomem *base; 63 struct ipu_flow flow[IPUV3_NUM_FLOWS]; 64 struct mutex mutex; 65 int use_count; 66 }; 67 68 static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; 69 70 static inline struct ipu_flow *to_flow(struct ipu_dp *dp) 71 { 72 if (dp->foreground) 73 return container_of(dp, struct ipu_flow, foreground); 74 else 75 return container_of(dp, struct ipu_flow, background); 76 } 77 78 int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, 79 u8 alpha, bool bg_chan) 80 { 81 struct ipu_flow *flow = to_flow(dp); 82 struct ipu_dp_priv *priv = flow->priv; 83 u32 reg; 84 85 mutex_lock(&priv->mutex); 86 87 reg = readl(flow->base + DP_COM_CONF); 88 if (bg_chan) 89 reg &= ~DP_COM_CONF_GWSEL; 90 else 91 reg |= DP_COM_CONF_GWSEL; 92 writel(reg, flow->base + DP_COM_CONF); 93 94 if (enable) { 95 reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; 96 writel(reg | ((u32) alpha << 24), 97 flow->base + DP_GRAPH_WIND_CTRL); 98 99 reg = readl(flow->base + DP_COM_CONF); 100 writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); 101 } else { 102 reg = readl(flow->base + DP_COM_CONF); 103 writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); 104 } 105 106 ipu_srm_dp_update(priv->ipu, true); 107 108 mutex_unlock(&priv->mutex); 109 110 return 0; 111 } 112 EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); 113 114 int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) 115 { 116 struct ipu_flow *flow = to_flow(dp); 117 struct ipu_dp_priv *priv = flow->priv; 118 119 writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); 120 121 ipu_srm_dp_update(priv->ipu, true); 122 123 return 0; 124 } 125 EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); 126 127 static void ipu_dp_csc_init(struct ipu_flow *flow, 128 enum ipu_color_space in, 129 enum ipu_color_space out, 130 u32 place) 131 { 132 u32 reg; 133 134 reg = readl(flow->base + DP_COM_CONF); 135 reg &= ~DP_COM_CONF_CSC_DEF_MASK; 136 137 if (in == out) { 138 writel(reg, flow->base + DP_COM_CONF); 139 return; 140 } 141 142 if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { 143 writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); 144 writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); 145 writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); 146 writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); 147 writel(0x3d6 | (0x0000 << 16) | (2 << 30), 148 flow->base + DP_CSC_0); 149 writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), 150 flow->base + DP_CSC_1); 151 } else { 152 writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); 153 writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); 154 writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); 155 writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); 156 writel(0x000 | (0x3e42 << 16) | (1 << 30), 157 flow->base + DP_CSC_0); 158 writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), 159 flow->base + DP_CSC_1); 160 } 161 162 reg |= place; 163 164 writel(reg, flow->base + DP_COM_CONF); 165 } 166 167 int ipu_dp_setup_channel(struct ipu_dp *dp, 168 enum ipu_color_space in, 169 enum ipu_color_space out) 170 { 171 struct ipu_flow *flow = to_flow(dp); 172 struct ipu_dp_priv *priv = flow->priv; 173 174 mutex_lock(&priv->mutex); 175 176 dp->in_cs = in; 177 178 if (!dp->foreground) 179 flow->out_cs = out; 180 181 if (flow->foreground.in_cs == flow->background.in_cs) { 182 /* 183 * foreground and background are of same colorspace, put 184 * colorspace converter after combining unit. 185 */ 186 ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, 187 DP_COM_CONF_CSC_DEF_BOTH); 188 } else { 189 if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || 190 flow->foreground.in_cs == flow->out_cs) 191 /* 192 * foreground identical to output, apply color 193 * conversion on background 194 */ 195 ipu_dp_csc_init(flow, flow->background.in_cs, 196 flow->out_cs, DP_COM_CONF_CSC_DEF_BG); 197 else 198 ipu_dp_csc_init(flow, flow->foreground.in_cs, 199 flow->out_cs, DP_COM_CONF_CSC_DEF_FG); 200 } 201 202 ipu_srm_dp_update(priv->ipu, true); 203 204 mutex_unlock(&priv->mutex); 205 206 return 0; 207 } 208 EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); 209 210 int ipu_dp_enable(struct ipu_soc *ipu) 211 { 212 struct ipu_dp_priv *priv = ipu->dp_priv; 213 214 mutex_lock(&priv->mutex); 215 216 if (!priv->use_count) 217 ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); 218 219 priv->use_count++; 220 221 mutex_unlock(&priv->mutex); 222 223 return 0; 224 } 225 EXPORT_SYMBOL_GPL(ipu_dp_enable); 226 227 int ipu_dp_enable_channel(struct ipu_dp *dp) 228 { 229 struct ipu_flow *flow = to_flow(dp); 230 struct ipu_dp_priv *priv = flow->priv; 231 u32 reg; 232 233 if (!dp->foreground) 234 return 0; 235 236 mutex_lock(&priv->mutex); 237 238 reg = readl(flow->base + DP_COM_CONF); 239 reg |= DP_COM_CONF_FG_EN; 240 writel(reg, flow->base + DP_COM_CONF); 241 242 ipu_srm_dp_update(priv->ipu, true); 243 244 mutex_unlock(&priv->mutex); 245 246 return 0; 247 } 248 EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); 249 250 void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) 251 { 252 struct ipu_flow *flow = to_flow(dp); 253 struct ipu_dp_priv *priv = flow->priv; 254 u32 reg, csc; 255 256 dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; 257 258 if (!dp->foreground) 259 return; 260 261 mutex_lock(&priv->mutex); 262 263 reg = readl(flow->base + DP_COM_CONF); 264 csc = reg & DP_COM_CONF_CSC_DEF_MASK; 265 reg &= ~DP_COM_CONF_CSC_DEF_MASK; 266 if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) 267 reg |= DP_COM_CONF_CSC_DEF_BG; 268 269 reg &= ~DP_COM_CONF_FG_EN; 270 writel(reg, flow->base + DP_COM_CONF); 271 272 writel(0, flow->base + DP_FG_POS); 273 ipu_srm_dp_update(priv->ipu, sync); 274 275 mutex_unlock(&priv->mutex); 276 } 277 EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); 278 279 void ipu_dp_disable(struct ipu_soc *ipu) 280 { 281 struct ipu_dp_priv *priv = ipu->dp_priv; 282 283 mutex_lock(&priv->mutex); 284 285 priv->use_count--; 286 287 if (!priv->use_count) 288 ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); 289 290 if (priv->use_count < 0) 291 priv->use_count = 0; 292 293 mutex_unlock(&priv->mutex); 294 } 295 EXPORT_SYMBOL_GPL(ipu_dp_disable); 296 297 struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) 298 { 299 struct ipu_dp_priv *priv = ipu->dp_priv; 300 struct ipu_dp *dp; 301 302 if ((flow >> 1) >= IPUV3_NUM_FLOWS) 303 return ERR_PTR(-EINVAL); 304 305 if (flow & 1) 306 dp = &priv->flow[flow >> 1].foreground; 307 else 308 dp = &priv->flow[flow >> 1].background; 309 310 if (dp->in_use) 311 return ERR_PTR(-EBUSY); 312 313 dp->in_use = true; 314 315 return dp; 316 } 317 EXPORT_SYMBOL_GPL(ipu_dp_get); 318 319 void ipu_dp_put(struct ipu_dp *dp) 320 { 321 dp->in_use = false; 322 } 323 EXPORT_SYMBOL_GPL(ipu_dp_put); 324 325 int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) 326 { 327 struct ipu_dp_priv *priv; 328 int i; 329 330 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 331 if (!priv) 332 return -ENOMEM; 333 priv->dev = dev; 334 priv->ipu = ipu; 335 336 ipu->dp_priv = priv; 337 338 priv->base = devm_ioremap(dev, base, PAGE_SIZE); 339 if (!priv->base) 340 return -ENOMEM; 341 342 mutex_init(&priv->mutex); 343 344 for (i = 0; i < IPUV3_NUM_FLOWS; i++) { 345 priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; 346 priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; 347 priv->flow[i].foreground.foreground = true; 348 priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; 349 priv->flow[i].priv = priv; 350 } 351 352 return 0; 353 } 354 355 void ipu_dp_exit(struct ipu_soc *ipu) 356 { 357 } 358