linux/drivers/gpu/ipu-v3/ipu-dc.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
   3 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License as published by the
   7 * Free Software Foundation; either version 2 of the License, or (at your
   8 * option) any later version.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  12 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13 * for more details.
  14 */
  15
  16#include <linux/export.h>
  17#include <linux/module.h>
  18#include <linux/types.h>
  19#include <linux/errno.h>
  20#include <linux/delay.h>
  21#include <linux/interrupt.h>
  22#include <linux/io.h>
  23
  24#include <video/imx-ipu-v3.h>
  25#include "ipu-prv.h"
  26
  27#define DC_MAP_CONF_PTR(n)      (0x108 + ((n) & ~0x1) * 2)
  28#define DC_MAP_CONF_VAL(n)      (0x144 + ((n) & ~0x1) * 2)
  29
  30#define DC_EVT_NF               0
  31#define DC_EVT_NL               1
  32#define DC_EVT_EOF              2
  33#define DC_EVT_NFIELD           3
  34#define DC_EVT_EOL              4
  35#define DC_EVT_EOFIELD          5
  36#define DC_EVT_NEW_ADDR         6
  37#define DC_EVT_NEW_CHAN         7
  38#define DC_EVT_NEW_DATA         8
  39
  40#define DC_EVT_NEW_ADDR_W_0     0
  41#define DC_EVT_NEW_ADDR_W_1     1
  42#define DC_EVT_NEW_CHAN_W_0     2
  43#define DC_EVT_NEW_CHAN_W_1     3
  44#define DC_EVT_NEW_DATA_W_0     4
  45#define DC_EVT_NEW_DATA_W_1     5
  46#define DC_EVT_NEW_ADDR_R_0     6
  47#define DC_EVT_NEW_ADDR_R_1     7
  48#define DC_EVT_NEW_CHAN_R_0     8
  49#define DC_EVT_NEW_CHAN_R_1     9
  50#define DC_EVT_NEW_DATA_R_0     10
  51#define DC_EVT_NEW_DATA_R_1     11
  52
  53#define DC_WR_CH_CONF           0x0
  54#define DC_WR_CH_ADDR           0x4
  55#define DC_RL_CH(evt)           (8 + ((evt) & ~0x1) * 2)
  56
  57#define DC_GEN                  0xd4
  58#define DC_DISP_CONF1(disp)     (0xd8 + (disp) * 4)
  59#define DC_DISP_CONF2(disp)     (0xe8 + (disp) * 4)
  60#define DC_STAT                 0x1c8
  61
  62#define WROD(lf)                (0x18 | ((lf) << 1))
  63#define WRG                     0x01
  64#define WCLK                    0xc9
  65
  66#define SYNC_WAVE 0
  67#define NULL_WAVE (-1)
  68
  69#define DC_GEN_SYNC_1_6_SYNC    (2 << 1)
  70#define DC_GEN_SYNC_PRIORITY_1  (1 << 7)
  71
  72#define DC_WR_CH_CONF_WORD_SIZE_8               (0 << 0)
  73#define DC_WR_CH_CONF_WORD_SIZE_16              (1 << 0)
  74#define DC_WR_CH_CONF_WORD_SIZE_24              (2 << 0)
  75#define DC_WR_CH_CONF_WORD_SIZE_32              (3 << 0)
  76#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i)       (((i) & 0x1) << 3)
  77#define DC_WR_CH_CONF_DISP_ID_SERIAL            (2 << 3)
  78#define DC_WR_CH_CONF_DISP_ID_ASYNC             (3 << 4)
  79#define DC_WR_CH_CONF_FIELD_MODE                (1 << 9)
  80#define DC_WR_CH_CONF_PROG_TYPE_NORMAL          (4 << 5)
  81#define DC_WR_CH_CONF_PROG_TYPE_MASK            (7 << 5)
  82#define DC_WR_CH_CONF_PROG_DI_ID                (1 << 2)
  83#define DC_WR_CH_CONF_PROG_DISP_ID(i)           (((i) & 0x1) << 3)
  84
  85#define IPU_DC_NUM_CHANNELS     10
  86
  87struct ipu_dc_priv;
  88
  89enum ipu_dc_map {
  90        IPU_DC_MAP_RGB24,
  91        IPU_DC_MAP_RGB565,
  92        IPU_DC_MAP_GBR24, /* TVEv2 */
  93        IPU_DC_MAP_BGR666,
  94        IPU_DC_MAP_LVDS666,
  95        IPU_DC_MAP_BGR24,
  96};
  97
  98struct ipu_dc {
  99        /* The display interface number assigned to this dc channel */
 100        unsigned int            di;
 101        void __iomem            *base;
 102        struct ipu_dc_priv      *priv;
 103        int                     chno;
 104        bool                    in_use;
 105};
 106
 107struct ipu_dc_priv {
 108        void __iomem            *dc_reg;
 109        void __iomem            *dc_tmpl_reg;
 110        struct ipu_soc          *ipu;
 111        struct device           *dev;
 112        struct ipu_dc           channels[IPU_DC_NUM_CHANNELS];
 113        struct mutex            mutex;
 114        struct completion       comp;
 115        int                     dc_irq;
 116        int                     dp_irq;
 117        int                     use_count;
 118};
 119
 120static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
 121{
 122        u32 reg;
 123
 124        reg = readl(dc->base + DC_RL_CH(event));
 125        reg &= ~(0xffff << (16 * (event & 0x1)));
 126        reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
 127        writel(reg, dc->base + DC_RL_CH(event));
 128}
 129
 130static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
 131                int map, int wave, int glue, int sync, int stop)
 132{
 133        struct ipu_dc_priv *priv = dc->priv;
 134        u32 reg1, reg2;
 135
 136        if (opcode == WCLK) {
 137                reg1 = (operand << 20) & 0xfff00000;
 138                reg2 = operand >> 12 | opcode << 1 | stop << 9;
 139        } else if (opcode == WRG) {
 140                reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000);
 141                reg2 = operand >> 17 | opcode << 7 | stop << 9;
 142        } else {
 143                reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
 144                reg2 = operand >> 12 | opcode << 4 | stop << 9;
 145        }
 146        writel(reg1, priv->dc_tmpl_reg + word * 8);
 147        writel(reg2, priv->dc_tmpl_reg + word * 8 + 4);
 148}
 149
 150static int ipu_bus_format_to_map(u32 fmt)
 151{
 152        switch (fmt) {
 153        default:
 154                WARN_ON(1);
 155                /* fall-through */
 156        case MEDIA_BUS_FMT_RGB888_1X24:
 157                return IPU_DC_MAP_RGB24;
 158        case MEDIA_BUS_FMT_RGB565_1X16:
 159                return IPU_DC_MAP_RGB565;
 160        case MEDIA_BUS_FMT_GBR888_1X24:
 161                return IPU_DC_MAP_GBR24;
 162        case MEDIA_BUS_FMT_RGB666_1X18:
 163                return IPU_DC_MAP_BGR666;
 164        case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
 165                return IPU_DC_MAP_LVDS666;
 166        case MEDIA_BUS_FMT_BGR888_1X24:
 167                return IPU_DC_MAP_BGR24;
 168        }
 169}
 170
 171int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
 172                u32 bus_format, u32 width)
 173{
 174        struct ipu_dc_priv *priv = dc->priv;
 175        int addr, sync;
 176        u32 reg = 0;
 177        int map;
 178
 179        dc->di = ipu_di_get_num(di);
 180
 181        map = ipu_bus_format_to_map(bus_format);
 182
 183        /*
 184         * In interlaced mode we need more counters to create the asymmetric
 185         * per-field VSYNC signals. The pixel active signal synchronising DC
 186         * to DI moves to signal generator #6 (see ipu-di.c). In progressive
 187         * mode counter #5 is used.
 188         */
 189        sync = interlaced ? 6 : 5;
 190
 191        /* Reserve 5 microcode template words for each DI */
 192        if (dc->di)
 193                addr = 5;
 194        else
 195                addr = 0;
 196
 197        if (interlaced) {
 198                dc_link_event(dc, DC_EVT_NL, addr, 3);
 199                dc_link_event(dc, DC_EVT_EOL, addr, 2);
 200                dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
 201
 202                /* Init template microcode */
 203                dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1);
 204        } else {
 205                dc_link_event(dc, DC_EVT_NL, addr + 2, 3);
 206                dc_link_event(dc, DC_EVT_EOL, addr + 3, 2);
 207                dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1);
 208
 209                /* Init template microcode */
 210                dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1);
 211                dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0);
 212                dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
 213                dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1);
 214        }
 215
 216        dc_link_event(dc, DC_EVT_NF, 0, 0);
 217        dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
 218        dc_link_event(dc, DC_EVT_EOF, 0, 0);
 219        dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
 220        dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
 221        dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
 222
 223        reg = readl(dc->base + DC_WR_CH_CONF);
 224        if (interlaced)
 225                reg |= DC_WR_CH_CONF_FIELD_MODE;
 226        else
 227                reg &= ~DC_WR_CH_CONF_FIELD_MODE;
 228        writel(reg, dc->base + DC_WR_CH_CONF);
 229
 230        writel(0x0, dc->base + DC_WR_CH_ADDR);
 231        writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
 232
 233        return 0;
 234}
 235EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
 236
 237void ipu_dc_enable(struct ipu_soc *ipu)
 238{
 239        struct ipu_dc_priv *priv = ipu->dc_priv;
 240
 241        mutex_lock(&priv->mutex);
 242
 243        if (!priv->use_count)
 244                ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
 245
 246        priv->use_count++;
 247
 248        mutex_unlock(&priv->mutex);
 249}
 250EXPORT_SYMBOL_GPL(ipu_dc_enable);
 251
 252void ipu_dc_enable_channel(struct ipu_dc *dc)
 253{
 254        int di;
 255        u32 reg;
 256
 257        di = dc->di;
 258
 259        reg = readl(dc->base + DC_WR_CH_CONF);
 260        reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
 261        writel(reg, dc->base + DC_WR_CH_CONF);
 262}
 263EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
 264
 265static irqreturn_t dc_irq_handler(int irq, void *dev_id)
 266{
 267        struct ipu_dc *dc = dev_id;
 268        u32 reg;
 269
 270        reg = readl(dc->base + DC_WR_CH_CONF);
 271        reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
 272        writel(reg, dc->base + DC_WR_CH_CONF);
 273
 274        /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */
 275
 276        complete(&dc->priv->comp);
 277        return IRQ_HANDLED;
 278}
 279
 280void ipu_dc_disable_channel(struct ipu_dc *dc)
 281{
 282        struct ipu_dc_priv *priv = dc->priv;
 283        int irq;
 284        unsigned long ret;
 285        u32 val;
 286
 287        /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */
 288        if (dc->chno == 1)
 289                irq = priv->dc_irq;
 290        else if (dc->chno == 5)
 291                irq = priv->dp_irq;
 292        else
 293                return;
 294
 295        init_completion(&priv->comp);
 296        enable_irq(irq);
 297        ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50));
 298        disable_irq(irq);
 299        if (ret == 0) {
 300                dev_warn(priv->dev, "DC stop timeout after 50 ms\n");
 301
 302                val = readl(dc->base + DC_WR_CH_CONF);
 303                val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
 304                writel(val, dc->base + DC_WR_CH_CONF);
 305        }
 306}
 307EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
 308
 309void ipu_dc_disable(struct ipu_soc *ipu)
 310{
 311        struct ipu_dc_priv *priv = ipu->dc_priv;
 312
 313        mutex_lock(&priv->mutex);
 314
 315        priv->use_count--;
 316        if (!priv->use_count)
 317                ipu_module_disable(priv->ipu, IPU_CONF_DC_EN);
 318
 319        if (priv->use_count < 0)
 320                priv->use_count = 0;
 321
 322        mutex_unlock(&priv->mutex);
 323}
 324EXPORT_SYMBOL_GPL(ipu_dc_disable);
 325
 326static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
 327                int byte_num, int offset, int mask)
 328{
 329        int ptr = map * 3 + byte_num;
 330        u32 reg;
 331
 332        reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
 333        reg &= ~(0xffff << (16 * (ptr & 0x1)));
 334        reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
 335        writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
 336
 337        reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
 338        reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
 339        reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
 340        writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
 341}
 342
 343static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
 344{
 345        u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
 346
 347        writel(reg & ~(0xffff << (16 * (map & 0x1))),
 348                     priv->dc_reg + DC_MAP_CONF_PTR(map));
 349}
 350
 351struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
 352{
 353        struct ipu_dc_priv *priv = ipu->dc_priv;
 354        struct ipu_dc *dc;
 355
 356        if (channel >= IPU_DC_NUM_CHANNELS)
 357                return ERR_PTR(-ENODEV);
 358
 359        dc = &priv->channels[channel];
 360
 361        mutex_lock(&priv->mutex);
 362
 363        if (dc->in_use) {
 364                mutex_unlock(&priv->mutex);
 365                return ERR_PTR(-EBUSY);
 366        }
 367
 368        dc->in_use = true;
 369
 370        mutex_unlock(&priv->mutex);
 371
 372        return dc;
 373}
 374EXPORT_SYMBOL_GPL(ipu_dc_get);
 375
 376void ipu_dc_put(struct ipu_dc *dc)
 377{
 378        struct ipu_dc_priv *priv = dc->priv;
 379
 380        mutex_lock(&priv->mutex);
 381        dc->in_use = false;
 382        mutex_unlock(&priv->mutex);
 383}
 384EXPORT_SYMBOL_GPL(ipu_dc_put);
 385
 386int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
 387                unsigned long base, unsigned long template_base)
 388{
 389        struct ipu_dc_priv *priv;
 390        static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
 391                0x78, 0, 0x94, 0xb4};
 392        int i, ret;
 393
 394        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 395        if (!priv)
 396                return -ENOMEM;
 397
 398        mutex_init(&priv->mutex);
 399
 400        priv->dev = dev;
 401        priv->ipu = ipu;
 402        priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE);
 403        priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE);
 404        if (!priv->dc_reg || !priv->dc_tmpl_reg)
 405                return -ENOMEM;
 406
 407        for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
 408                priv->channels[i].chno = i;
 409                priv->channels[i].priv = priv;
 410                priv->channels[i].base = priv->dc_reg + channel_offsets[i];
 411        }
 412
 413        priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1);
 414        if (!priv->dc_irq)
 415                return -EINVAL;
 416        ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL,
 417                               &priv->channels[1]);
 418        if (ret < 0)
 419                return ret;
 420        disable_irq(priv->dc_irq);
 421        priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END);
 422        if (!priv->dp_irq)
 423                return -EINVAL;
 424        ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL,
 425                               &priv->channels[5]);
 426        if (ret < 0)
 427                return ret;
 428        disable_irq(priv->dp_irq);
 429
 430        writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
 431                        DC_WR_CH_CONF_PROG_DI_ID,
 432                        priv->channels[1].base + DC_WR_CH_CONF);
 433        writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
 434                        priv->channels[5].base + DC_WR_CH_CONF);
 435
 436        writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1,
 437                priv->dc_reg + DC_GEN);
 438
 439        ipu->dc_priv = priv;
 440
 441        dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n",
 442                        base, template_base);
 443
 444        /* rgb24 */
 445        ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
 446        ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
 447        ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
 448        ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
 449
 450        /* rgb565 */
 451        ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
 452        ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
 453        ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
 454        ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
 455
 456        /* gbr24 */
 457        ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24);
 458        ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */
 459        ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */
 460        ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */
 461
 462        /* bgr666 */
 463        ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666);
 464        ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */
 465        ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
 466        ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
 467
 468        /* lvds666 */
 469        ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666);
 470        ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */
 471        ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */
 472        ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */
 473
 474        /* bgr24 */
 475        ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
 476        ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */
 477        ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
 478        ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
 479
 480        return 0;
 481}
 482
 483void ipu_dc_exit(struct ipu_soc *ipu)
 484{
 485}
 486