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 87 struct ipu_dc_priv; 88 89 enum 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 98 struct 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 107 struct 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 }; 118 119 static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) 120 { 121 u32 reg; 122 123 reg = readl(dc->base + DC_RL_CH(event)); 124 reg &= ~(0xffff << (16 * (event & 0x1))); 125 reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); 126 writel(reg, dc->base + DC_RL_CH(event)); 127 } 128 129 static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, 130 int map, int wave, int glue, int sync, int stop) 131 { 132 struct ipu_dc_priv *priv = dc->priv; 133 u32 reg1, reg2; 134 135 if (opcode == WCLK) { 136 reg1 = (operand << 20) & 0xfff00000; 137 reg2 = operand >> 12 | opcode << 1 | stop << 9; 138 } else if (opcode == WRG) { 139 reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); 140 reg2 = operand >> 17 | opcode << 7 | stop << 9; 141 } else { 142 reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); 143 reg2 = operand >> 12 | opcode << 4 | stop << 9; 144 } 145 writel(reg1, priv->dc_tmpl_reg + word * 8); 146 writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); 147 } 148 149 static int ipu_pixfmt_to_map(u32 fmt) 150 { 151 switch (fmt) { 152 case V4L2_PIX_FMT_RGB24: 153 return IPU_DC_MAP_RGB24; 154 case V4L2_PIX_FMT_RGB565: 155 return IPU_DC_MAP_RGB565; 156 case IPU_PIX_FMT_GBR24: 157 return IPU_DC_MAP_GBR24; 158 case V4L2_PIX_FMT_BGR666: 159 return IPU_DC_MAP_BGR666; 160 case v4l2_fourcc('L', 'V', 'D', '6'): 161 return IPU_DC_MAP_LVDS666; 162 case V4L2_PIX_FMT_BGR24: 163 return IPU_DC_MAP_BGR24; 164 default: 165 return -EINVAL; 166 } 167 } 168 169 int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, 170 u32 pixel_fmt, u32 width) 171 { 172 struct ipu_dc_priv *priv = dc->priv; 173 u32 reg = 0; 174 int map; 175 176 dc->di = ipu_di_get_num(di); 177 178 map = ipu_pixfmt_to_map(pixel_fmt); 179 if (map < 0) { 180 dev_dbg(priv->dev, "IPU_DISP: No MAP\n"); 181 return map; 182 } 183 184 if (interlaced) { 185 dc_link_event(dc, DC_EVT_NL, 0, 3); 186 dc_link_event(dc, DC_EVT_EOL, 0, 2); 187 dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1); 188 189 /* Init template microcode */ 190 dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1); 191 } else { 192 if (dc->di) { 193 dc_link_event(dc, DC_EVT_NL, 2, 3); 194 dc_link_event(dc, DC_EVT_EOL, 3, 2); 195 dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1); 196 /* Init template microcode */ 197 dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); 198 dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); 199 dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); 200 dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); 201 } else { 202 dc_link_event(dc, DC_EVT_NL, 5, 3); 203 dc_link_event(dc, DC_EVT_EOL, 6, 2); 204 dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1); 205 /* Init template microcode */ 206 dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); 207 dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); 208 dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1); 209 dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); 210 } 211 } 212 dc_link_event(dc, DC_EVT_NF, 0, 0); 213 dc_link_event(dc, DC_EVT_NFIELD, 0, 0); 214 dc_link_event(dc, DC_EVT_EOF, 0, 0); 215 dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); 216 dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); 217 dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); 218 219 reg = readl(dc->base + DC_WR_CH_CONF); 220 if (interlaced) 221 reg |= DC_WR_CH_CONF_FIELD_MODE; 222 else 223 reg &= ~DC_WR_CH_CONF_FIELD_MODE; 224 writel(reg, dc->base + DC_WR_CH_CONF); 225 226 writel(0x0, dc->base + DC_WR_CH_ADDR); 227 writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); 228 229 return 0; 230 } 231 EXPORT_SYMBOL_GPL(ipu_dc_init_sync); 232 233 void ipu_dc_enable(struct ipu_soc *ipu) 234 { 235 ipu_module_enable(ipu, IPU_CONF_DC_EN); 236 } 237 EXPORT_SYMBOL_GPL(ipu_dc_enable); 238 239 void ipu_dc_enable_channel(struct ipu_dc *dc) 240 { 241 int di; 242 u32 reg; 243 244 di = dc->di; 245 246 reg = readl(dc->base + DC_WR_CH_CONF); 247 reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; 248 writel(reg, dc->base + DC_WR_CH_CONF); 249 } 250 EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); 251 252 static irqreturn_t dc_irq_handler(int irq, void *dev_id) 253 { 254 struct ipu_dc *dc = dev_id; 255 u32 reg; 256 257 reg = readl(dc->base + DC_WR_CH_CONF); 258 reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; 259 writel(reg, dc->base + DC_WR_CH_CONF); 260 261 /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */ 262 263 complete(&dc->priv->comp); 264 return IRQ_HANDLED; 265 } 266 267 void ipu_dc_disable_channel(struct ipu_dc *dc) 268 { 269 struct ipu_dc_priv *priv = dc->priv; 270 int irq, ret; 271 u32 val; 272 273 /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */ 274 if (dc->chno == 1) 275 irq = priv->dc_irq; 276 else if (dc->chno == 5) 277 irq = priv->dp_irq; 278 else 279 return; 280 281 init_completion(&priv->comp); 282 enable_irq(irq); 283 ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50)); 284 disable_irq(irq); 285 if (ret <= 0) { 286 dev_warn(priv->dev, "DC stop timeout after 50 ms\n"); 287 288 val = readl(dc->base + DC_WR_CH_CONF); 289 val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; 290 writel(val, dc->base + DC_WR_CH_CONF); 291 } 292 } 293 EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); 294 295 void ipu_dc_disable(struct ipu_soc *ipu) 296 { 297 ipu_module_disable(ipu, IPU_CONF_DC_EN); 298 } 299 EXPORT_SYMBOL_GPL(ipu_dc_disable); 300 301 static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, 302 int byte_num, int offset, int mask) 303 { 304 int ptr = map * 3 + byte_num; 305 u32 reg; 306 307 reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); 308 reg &= ~(0xffff << (16 * (ptr & 0x1))); 309 reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); 310 writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); 311 312 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); 313 reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); 314 reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); 315 writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); 316 } 317 318 static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) 319 { 320 u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); 321 322 writel(reg & ~(0xffff << (16 * (map & 0x1))), 323 priv->dc_reg + DC_MAP_CONF_PTR(map)); 324 } 325 326 struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) 327 { 328 struct ipu_dc_priv *priv = ipu->dc_priv; 329 struct ipu_dc *dc; 330 331 if (channel >= IPU_DC_NUM_CHANNELS) 332 return ERR_PTR(-ENODEV); 333 334 dc = &priv->channels[channel]; 335 336 mutex_lock(&priv->mutex); 337 338 if (dc->in_use) { 339 mutex_unlock(&priv->mutex); 340 return ERR_PTR(-EBUSY); 341 } 342 343 dc->in_use = true; 344 345 mutex_unlock(&priv->mutex); 346 347 return dc; 348 } 349 EXPORT_SYMBOL_GPL(ipu_dc_get); 350 351 void ipu_dc_put(struct ipu_dc *dc) 352 { 353 struct ipu_dc_priv *priv = dc->priv; 354 355 mutex_lock(&priv->mutex); 356 dc->in_use = false; 357 mutex_unlock(&priv->mutex); 358 } 359 EXPORT_SYMBOL_GPL(ipu_dc_put); 360 361 int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, 362 unsigned long base, unsigned long template_base) 363 { 364 struct ipu_dc_priv *priv; 365 static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 366 0x78, 0, 0x94, 0xb4}; 367 int i, ret; 368 369 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 370 if (!priv) 371 return -ENOMEM; 372 373 mutex_init(&priv->mutex); 374 375 priv->dev = dev; 376 priv->ipu = ipu; 377 priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); 378 priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); 379 if (!priv->dc_reg || !priv->dc_tmpl_reg) 380 return -ENOMEM; 381 382 for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { 383 priv->channels[i].chno = i; 384 priv->channels[i].priv = priv; 385 priv->channels[i].base = priv->dc_reg + channel_offsets[i]; 386 } 387 388 priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1); 389 if (!priv->dc_irq) 390 return -EINVAL; 391 ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL, 392 &priv->channels[1]); 393 if (ret < 0) 394 return ret; 395 disable_irq(priv->dc_irq); 396 priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END); 397 if (!priv->dp_irq) 398 return -EINVAL; 399 ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL, 400 &priv->channels[5]); 401 if (ret < 0) 402 return ret; 403 disable_irq(priv->dp_irq); 404 405 writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | 406 DC_WR_CH_CONF_PROG_DI_ID, 407 priv->channels[1].base + DC_WR_CH_CONF); 408 writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), 409 priv->channels[5].base + DC_WR_CH_CONF); 410 411 writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, 412 priv->dc_reg + DC_GEN); 413 414 ipu->dc_priv = priv; 415 416 dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", 417 base, template_base); 418 419 /* rgb24 */ 420 ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); 421 ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ 422 ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ 423 ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ 424 425 /* rgb565 */ 426 ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); 427 ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ 428 ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ 429 ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ 430 431 /* gbr24 */ 432 ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); 433 ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ 434 ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ 435 ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ 436 437 /* bgr666 */ 438 ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); 439 ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ 440 ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ 441 ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ 442 443 /* lvds666 */ 444 ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); 445 ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ 446 ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ 447 ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ 448 449 /* bgr24 */ 450 ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); 451 ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ 452 ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ 453 ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ 454 455 return 0; 456 } 457 458 void ipu_dc_exit(struct ipu_soc *ipu) 459 { 460 } 461