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 <drm/drm_color_mgmt.h>
  14#include <video/imx-ipu-v3.h>
  15#include "ipu-prv.h"
  16
  17#define DP_SYNC 0
  18#define DP_ASYNC0 0x60
  19#define DP_ASYNC1 0xBC
  20
  21#define DP_COM_CONF             0x0
  22#define DP_GRAPH_WIND_CTRL      0x0004
  23#define DP_FG_POS               0x0008
  24#define DP_CSC_A_0              0x0044
  25#define DP_CSC_A_1              0x0048
  26#define DP_CSC_A_2              0x004C
  27#define DP_CSC_A_3              0x0050
  28#define DP_CSC_0                0x0054
  29#define DP_CSC_1                0x0058
  30
  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      8
  37#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         3
  42
  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        else
  76                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        else
  92                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, put
 200                 * 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 color
 210                         * conversion on background
 211                         */
 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                else
 216                        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        else
 327                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