Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
0004  * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
0005  */
0006 
0007 #include <linux/export.h>
0008 #include <linux/module.h>
0009 #include <linux/types.h>
0010 #include <linux/errno.h>
0011 #include <linux/delay.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/io.h>
0014 
0015 #include <video/imx-ipu-v3.h>
0016 #include "ipu-prv.h"
0017 
0018 #define DC_MAP_CONF_PTR(n)  (0x108 + ((n) & ~0x1) * 2)
0019 #define DC_MAP_CONF_VAL(n)  (0x144 + ((n) & ~0x1) * 2)
0020 
0021 #define DC_EVT_NF       0
0022 #define DC_EVT_NL       1
0023 #define DC_EVT_EOF      2
0024 #define DC_EVT_NFIELD       3
0025 #define DC_EVT_EOL      4
0026 #define DC_EVT_EOFIELD      5
0027 #define DC_EVT_NEW_ADDR     6
0028 #define DC_EVT_NEW_CHAN     7
0029 #define DC_EVT_NEW_DATA     8
0030 
0031 #define DC_EVT_NEW_ADDR_W_0 0
0032 #define DC_EVT_NEW_ADDR_W_1 1
0033 #define DC_EVT_NEW_CHAN_W_0 2
0034 #define DC_EVT_NEW_CHAN_W_1 3
0035 #define DC_EVT_NEW_DATA_W_0 4
0036 #define DC_EVT_NEW_DATA_W_1 5
0037 #define DC_EVT_NEW_ADDR_R_0 6
0038 #define DC_EVT_NEW_ADDR_R_1 7
0039 #define DC_EVT_NEW_CHAN_R_0 8
0040 #define DC_EVT_NEW_CHAN_R_1 9
0041 #define DC_EVT_NEW_DATA_R_0 10
0042 #define DC_EVT_NEW_DATA_R_1 11
0043 
0044 #define DC_WR_CH_CONF       0x0
0045 #define DC_WR_CH_ADDR       0x4
0046 #define DC_RL_CH(evt)       (8 + ((evt) & ~0x1) * 2)
0047 
0048 #define DC_GEN          0xd4
0049 #define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4)
0050 #define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4)
0051 #define DC_STAT         0x1c8
0052 
0053 #define WROD(lf)        (0x18 | ((lf) << 1))
0054 #define WRG         0x01
0055 #define WCLK            0xc9
0056 
0057 #define SYNC_WAVE 0
0058 #define NULL_WAVE (-1)
0059 
0060 #define DC_GEN_SYNC_1_6_SYNC    (2 << 1)
0061 #define DC_GEN_SYNC_PRIORITY_1  (1 << 7)
0062 
0063 #define DC_WR_CH_CONF_WORD_SIZE_8       (0 << 0)
0064 #define DC_WR_CH_CONF_WORD_SIZE_16      (1 << 0)
0065 #define DC_WR_CH_CONF_WORD_SIZE_24      (2 << 0)
0066 #define DC_WR_CH_CONF_WORD_SIZE_32      (3 << 0)
0067 #define DC_WR_CH_CONF_DISP_ID_PARALLEL(i)   (((i) & 0x1) << 3)
0068 #define DC_WR_CH_CONF_DISP_ID_SERIAL        (2 << 3)
0069 #define DC_WR_CH_CONF_DISP_ID_ASYNC     (3 << 4)
0070 #define DC_WR_CH_CONF_FIELD_MODE        (1 << 9)
0071 #define DC_WR_CH_CONF_PROG_TYPE_NORMAL      (4 << 5)
0072 #define DC_WR_CH_CONF_PROG_TYPE_MASK        (7 << 5)
0073 #define DC_WR_CH_CONF_PROG_DI_ID        (1 << 2)
0074 #define DC_WR_CH_CONF_PROG_DISP_ID(i)       (((i) & 0x1) << 3)
0075 
0076 #define IPU_DC_NUM_CHANNELS 10
0077 
0078 struct ipu_dc_priv;
0079 
0080 enum ipu_dc_map {
0081     IPU_DC_MAP_RGB24,
0082     IPU_DC_MAP_RGB565,
0083     IPU_DC_MAP_GBR24, /* TVEv2 */
0084     IPU_DC_MAP_BGR666,
0085     IPU_DC_MAP_LVDS666,
0086     IPU_DC_MAP_BGR24,
0087 };
0088 
0089 struct ipu_dc {
0090     /* The display interface number assigned to this dc channel */
0091     unsigned int        di;
0092     void __iomem        *base;
0093     struct ipu_dc_priv  *priv;
0094     int         chno;
0095     bool            in_use;
0096 };
0097 
0098 struct ipu_dc_priv {
0099     void __iomem        *dc_reg;
0100     void __iomem        *dc_tmpl_reg;
0101     struct ipu_soc      *ipu;
0102     struct device       *dev;
0103     struct ipu_dc       channels[IPU_DC_NUM_CHANNELS];
0104     struct mutex        mutex;
0105     struct completion   comp;
0106     int         use_count;
0107 };
0108 
0109 static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
0110 {
0111     u32 reg;
0112 
0113     reg = readl(dc->base + DC_RL_CH(event));
0114     reg &= ~(0xffff << (16 * (event & 0x1)));
0115     reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
0116     writel(reg, dc->base + DC_RL_CH(event));
0117 }
0118 
0119 static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
0120         int map, int wave, int glue, int sync, int stop)
0121 {
0122     struct ipu_dc_priv *priv = dc->priv;
0123     u32 reg1, reg2;
0124 
0125     if (opcode == WCLK) {
0126         reg1 = (operand << 20) & 0xfff00000;
0127         reg2 = operand >> 12 | opcode << 1 | stop << 9;
0128     } else if (opcode == WRG) {
0129         reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000);
0130         reg2 = operand >> 17 | opcode << 7 | stop << 9;
0131     } else {
0132         reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
0133         reg2 = operand >> 12 | opcode << 4 | stop << 9;
0134     }
0135     writel(reg1, priv->dc_tmpl_reg + word * 8);
0136     writel(reg2, priv->dc_tmpl_reg + word * 8 + 4);
0137 }
0138 
0139 static int ipu_bus_format_to_map(u32 fmt)
0140 {
0141     switch (fmt) {
0142     default:
0143         WARN_ON(1);
0144         fallthrough;
0145     case MEDIA_BUS_FMT_RGB888_1X24:
0146         return IPU_DC_MAP_RGB24;
0147     case MEDIA_BUS_FMT_RGB565_1X16:
0148         return IPU_DC_MAP_RGB565;
0149     case MEDIA_BUS_FMT_GBR888_1X24:
0150         return IPU_DC_MAP_GBR24;
0151     case MEDIA_BUS_FMT_RGB666_1X18:
0152         return IPU_DC_MAP_BGR666;
0153     case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
0154         return IPU_DC_MAP_LVDS666;
0155     case MEDIA_BUS_FMT_BGR888_1X24:
0156         return IPU_DC_MAP_BGR24;
0157     }
0158 }
0159 
0160 int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
0161         u32 bus_format, u32 width)
0162 {
0163     struct ipu_dc_priv *priv = dc->priv;
0164     int addr, sync;
0165     u32 reg = 0;
0166     int map;
0167 
0168     dc->di = ipu_di_get_num(di);
0169 
0170     if (!IS_ALIGNED(width, 8)) {
0171         dev_warn(priv->dev,
0172              "%s: hactive does not align to 8 byte\n", __func__);
0173     }
0174 
0175     map = ipu_bus_format_to_map(bus_format);
0176 
0177     /*
0178      * In interlaced mode we need more counters to create the asymmetric
0179      * per-field VSYNC signals. The pixel active signal synchronising DC
0180      * to DI moves to signal generator #6 (see ipu-di.c). In progressive
0181      * mode counter #5 is used.
0182      */
0183     sync = interlaced ? 6 : 5;
0184 
0185     /* Reserve 5 microcode template words for each DI */
0186     if (dc->di)
0187         addr = 5;
0188     else
0189         addr = 0;
0190 
0191     if (interlaced) {
0192         dc_link_event(dc, DC_EVT_NL, addr, 3);
0193         dc_link_event(dc, DC_EVT_EOL, addr, 2);
0194         dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1);
0195 
0196         /* Init template microcode */
0197         dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1);
0198     } else {
0199         dc_link_event(dc, DC_EVT_NL, addr + 2, 3);
0200         dc_link_event(dc, DC_EVT_EOL, addr + 3, 2);
0201         dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1);
0202 
0203         /* Init template microcode */
0204         dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1);
0205         dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0);
0206         dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
0207         dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1);
0208     }
0209 
0210     dc_link_event(dc, DC_EVT_NF, 0, 0);
0211     dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
0212     dc_link_event(dc, DC_EVT_EOF, 0, 0);
0213     dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
0214     dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
0215     dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
0216 
0217     reg = readl(dc->base + DC_WR_CH_CONF);
0218     if (interlaced)
0219         reg |= DC_WR_CH_CONF_FIELD_MODE;
0220     else
0221         reg &= ~DC_WR_CH_CONF_FIELD_MODE;
0222     writel(reg, dc->base + DC_WR_CH_CONF);
0223 
0224     writel(0x0, dc->base + DC_WR_CH_ADDR);
0225     writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
0226 
0227     return 0;
0228 }
0229 EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
0230 
0231 void ipu_dc_enable(struct ipu_soc *ipu)
0232 {
0233     struct ipu_dc_priv *priv = ipu->dc_priv;
0234 
0235     mutex_lock(&priv->mutex);
0236 
0237     if (!priv->use_count)
0238         ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
0239 
0240     priv->use_count++;
0241 
0242     mutex_unlock(&priv->mutex);
0243 }
0244 EXPORT_SYMBOL_GPL(ipu_dc_enable);
0245 
0246 void ipu_dc_enable_channel(struct ipu_dc *dc)
0247 {
0248     u32 reg;
0249 
0250     reg = readl(dc->base + DC_WR_CH_CONF);
0251     reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
0252     writel(reg, dc->base + DC_WR_CH_CONF);
0253 }
0254 EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
0255 
0256 void ipu_dc_disable_channel(struct ipu_dc *dc)
0257 {
0258     u32 val;
0259 
0260     val = readl(dc->base + DC_WR_CH_CONF);
0261     val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
0262     writel(val, dc->base + DC_WR_CH_CONF);
0263 }
0264 EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
0265 
0266 void ipu_dc_disable(struct ipu_soc *ipu)
0267 {
0268     struct ipu_dc_priv *priv = ipu->dc_priv;
0269 
0270     mutex_lock(&priv->mutex);
0271 
0272     priv->use_count--;
0273     if (!priv->use_count)
0274         ipu_module_disable(priv->ipu, IPU_CONF_DC_EN);
0275 
0276     if (priv->use_count < 0)
0277         priv->use_count = 0;
0278 
0279     mutex_unlock(&priv->mutex);
0280 }
0281 EXPORT_SYMBOL_GPL(ipu_dc_disable);
0282 
0283 static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
0284         int byte_num, int offset, int mask)
0285 {
0286     int ptr = map * 3 + byte_num;
0287     u32 reg;
0288 
0289     reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
0290     reg &= ~(0xffff << (16 * (ptr & 0x1)));
0291     reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
0292     writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
0293 
0294     reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
0295     reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
0296     reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
0297     writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
0298 }
0299 
0300 static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
0301 {
0302     u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
0303 
0304     writel(reg & ~(0xffff << (16 * (map & 0x1))),
0305              priv->dc_reg + DC_MAP_CONF_PTR(map));
0306 }
0307 
0308 struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
0309 {
0310     struct ipu_dc_priv *priv = ipu->dc_priv;
0311     struct ipu_dc *dc;
0312 
0313     if (channel >= IPU_DC_NUM_CHANNELS)
0314         return ERR_PTR(-ENODEV);
0315 
0316     dc = &priv->channels[channel];
0317 
0318     mutex_lock(&priv->mutex);
0319 
0320     if (dc->in_use) {
0321         mutex_unlock(&priv->mutex);
0322         return ERR_PTR(-EBUSY);
0323     }
0324 
0325     dc->in_use = true;
0326 
0327     mutex_unlock(&priv->mutex);
0328 
0329     return dc;
0330 }
0331 EXPORT_SYMBOL_GPL(ipu_dc_get);
0332 
0333 void ipu_dc_put(struct ipu_dc *dc)
0334 {
0335     struct ipu_dc_priv *priv = dc->priv;
0336 
0337     mutex_lock(&priv->mutex);
0338     dc->in_use = false;
0339     mutex_unlock(&priv->mutex);
0340 }
0341 EXPORT_SYMBOL_GPL(ipu_dc_put);
0342 
0343 int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
0344         unsigned long base, unsigned long template_base)
0345 {
0346     struct ipu_dc_priv *priv;
0347     static const int channel_offsets[] = {
0348         0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 0x78, 0, 0x94, 0xb4
0349     };
0350     int i;
0351 
0352     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0353     if (!priv)
0354         return -ENOMEM;
0355 
0356     mutex_init(&priv->mutex);
0357 
0358     priv->dev = dev;
0359     priv->ipu = ipu;
0360     priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE);
0361     priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE);
0362     if (!priv->dc_reg || !priv->dc_tmpl_reg)
0363         return -ENOMEM;
0364 
0365     for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
0366         priv->channels[i].chno = i;
0367         priv->channels[i].priv = priv;
0368         priv->channels[i].base = priv->dc_reg + channel_offsets[i];
0369     }
0370 
0371     writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
0372             DC_WR_CH_CONF_PROG_DI_ID,
0373             priv->channels[1].base + DC_WR_CH_CONF);
0374     writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
0375             priv->channels[5].base + DC_WR_CH_CONF);
0376 
0377     writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1,
0378         priv->dc_reg + DC_GEN);
0379 
0380     ipu->dc_priv = priv;
0381 
0382     dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n",
0383             base, template_base);
0384 
0385     /* rgb24 */
0386     ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
0387     ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
0388     ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
0389     ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
0390 
0391     /* rgb565 */
0392     ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
0393     ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
0394     ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
0395     ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
0396 
0397     /* gbr24 */
0398     ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24);
0399     ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */
0400     ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */
0401     ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */
0402 
0403     /* bgr666 */
0404     ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666);
0405     ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */
0406     ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
0407     ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
0408 
0409     /* lvds666 */
0410     ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666);
0411     ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */
0412     ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */
0413     ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */
0414 
0415     /* bgr24 */
0416     ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
0417     ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */
0418     ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
0419     ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
0420 
0421     return 0;
0422 }
0423 
0424 void ipu_dc_exit(struct ipu_soc *ipu)
0425 {
0426 }