linux/drivers/gpu/ipu-v3/ipu-dp.c
<<
>>
Prefs
   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
  42struct ipu_dp_priv;
  43
  44struct ipu_dp {
  45        u32 flow;
  46        bool in_use;
  47        bool foreground;
  48        enum ipu_color_space in_cs;
  49};
  50
  51struct 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
  59struct 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
  68static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
  69
  70static 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
  78int 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}
 112EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
 113
 114int 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}
 125EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
 126
 127static 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
 167int 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}
 208EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
 209
 210int 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}
 225EXPORT_SYMBOL_GPL(ipu_dp_enable);
 226
 227int 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}
 248EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
 249
 250void 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}
 277EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
 278
 279void 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}
 295EXPORT_SYMBOL_GPL(ipu_dp_disable);
 296
 297struct 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}
 317EXPORT_SYMBOL_GPL(ipu_dp_get);
 318
 319void ipu_dp_put(struct ipu_dp *dp)
 320{
 321        dp->in_use = false;
 322}
 323EXPORT_SYMBOL_GPL(ipu_dp_put);
 324
 325int 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
 355void ipu_dp_exit(struct ipu_soc *ipu)
 356{
 357}
 358