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